COM

COM Start Page

COM (Component Object Model)

Purpose

COM is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents) and ActiveX® (Internet-enabled components) technologies, as well as others.

Where Applicable

COM objects can be created with a variety of programming languages. Object-oriented languages, such as C++, provide programming mechanisms that simplify the implementation of COM objects. These objects can be within a single process, in other processes, even on remote machines.

Developer Audience

COM is designed primarily for C++ and Microsoft Visual Basic® developers.

Run-time Requirements

Runs on a wide variety of operating systems.

See Also

COM+ Start Page

Automation Start Page

Guide

General information about using COM.

Feedback

Make error reports and feature requests directly to Microsoft.

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


The Component Object Model

The Microsoft Component Object Model (COM) is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents), ActiveX® (Internet-enabled components), as well as others.

To understand COM (and therefore all COM-based technologies), it is crucial to understand that it is not an object-oriented language but a standard. Nor does COM specify how an application should be structured; language, structure, and implementation details are left to the application programmer. Rather, COM specifies an object model and programming requirements that enable COM objects (also called COM components, or sometimes simply objects) to interact with other objects. These objects can be within a single process, in other processes, and can even be on remote machines. They can have been written in other languages, and they may be structurally quite dissimilar, which is why COM is referred to as a binary standard—a standard that applies after a program has been translated to binary machine code.

The only language requirement for COM is that code is generated in a language that can create structures of pointers and, either explicitly or implicitly, call functions through pointers. Object-oriented languages such as Microsoft® Visual C++® and Smalltalk provide programming mechanisms that simplify the implementation of COM objects, but languages such as C, Pascal, Ada, Java, and even BASIC programming environments can create and use COM objects.

COM defines the essential nature of a COM object. In general, a software object is made up of a set of data and the functions that manipulate the data. A COM object is one in which access to an object's data is achieved exclusively through one or more sets of related functions. These function sets are called interfaces, and the functions of an interface are called methods. Further, COM requires that the only way to gain access to the methods of an interface is through a pointer to the interface.

Besides specifying the basic binary object standard, COM defines certain basic interfaces that provide functions common to all COM-based technologies, and it provides a small number of API functions that all components require. COM also defines how objects work together over a distributed environment and has added security features to ensure system and component integrity.

The following topics in this section describe basic COM issues related to designing COM objects:

  • COM Objects and Interfaces
  • Using and Implementing IUnknown
  • Reusing Objects
  • The COM Library
  • Managing Memory Allocation


COM Objects and Interfaces

COM is a technology that allows objects to interact across process and machine boundaries as easily as within a single process. COM enables this by specifying that the only way to manipulate the data associated with an object is through an interface on the object. When this term is used in this documentation, it refers to an implementation in code of a COM binary-compliant interface that is associated with an object.

COM uses the word interface in a sense different from that typically used in Visual C++ programming. A C++ interface refers to all of the functions that a class supports and that clients of an object can call to interact with it. A COM interface refers to a predefined group of related functions that a COM class implements, but a specific interface does not necessarily represent all the functions that the class supports. (Java programmers will find themselves at home with COM interfaces because Java defines interfaces in just the same way.)

Referring to an object implementing an interface means that the object uses code that implements each method of the interface and provides COM binary-compliant pointers to those functions to the COM library. COM then makes those functions available to any client who asks for a pointer to the interface, whether the client is inside or outside of the process that implements those functions.

See Also

Interfaces and Interface Implementations

Interface Pointers and Interfaces

IUnknown and Interface Inheritance


Interfaces and Interface Implementations

COM makes a fundamental distinction between interface definitions and their implementations. An interface is actually a contract that consists of a group of related function prototypes whose usage is defined but whose implementation is not. These function prototypes are equivalent to pure virtual base classes in C++ programming. An interface definition specifies the interface's member functions, called methods, their return types, the number and types of their parameters, and what they must do. There is no implementation associated with an interface.

An interface implementation is the code a programmer supplies to carry out the actions specified in an interface definition. Implementations of many of the interfaces a programmer can use in an object-based application are included in the COM libraries. However, programmers are free to ignore these implementations and write their own. An interface implementation is to be associated with an object when an instance of that object is created, and the implementation provides the services that the object offers.

For example, a hypothetical interface named IStack might define two methods, named Push and Pop, specifying that successive calls to the Pop method return, in reverse order, values previously passed to the Push method. This interface definition would not specify how the functions are to be implemented in code. In implementing the interface, one programmer might implement the stack as an array and implement the Push and Pop methods in such a way as to access that array, while another programmer might prefer to use a linked list and would implement the methods accordingly. Regardless of a particular implementation of the Push and Pop methods, the in-memory representation of a pointer to an IStack interface, and therefore its use by a client, is completely determined by the interface definition.

Simple objects support only a single interface. More complicated objects, such as embeddable objects, typically support several interfaces. Clients have access to a COM object only through a pointer to one of its interfaces, which, in turn, allows the client to call any of the methods that make up that interface. These methods determine how a client can use the object's data.

Interfaces define a contract between an object and its clients. The contract specifies the methods that must be associated with each interface and what the behavior of each of the methods must be in terms of input and output. The contract generally does not define how to implement the methods in an interface. Another important aspect of the contract is that if an object supports an interface, it must support all of that interface's methods in some way. Not all of the methods in an implementation need to do something—if an object does not support the function implied by a method, its implementation may be a simple return or perhaps the return of a meaningful error message—but the methods must exist.


Interface Pointers and Interfaces

An instance of an interface implementation is actually a pointer to an array of pointers to methods—that is, a function table that refers to an implementation of all of the methods specified in the interface. Objects with multiple interfaces can provide pointers to more than one function table. Any code that has a pointer through which it can access the array can call the methods in that interface.

Speaking precisely about this multiple indirection is inconvenient, so instead, the pointer to the interface function table that another object must have to call its methods is called simply an interface pointer. You can manually create function tables in a C application or almost automatically by using Visual C++ (or other object-oriented languages that support COM).

With appropriate compiler support (which is inherent in C and C++), a client can call an interface method through its name, not its position in the array. Because an interface is a type, the compiler, given the names of methods, can check the types of parameters and return values of each interface method call. In contrast, if a client uses a position-based calling scheme, such type-checking is not available, even in C or C++.

Each interface—the immutable contract of a functional group of methods—is referred to at run time with a globally unique interface identifier (IID). This IID, which is a specific instance of a globally unique identifier (GUID) supported by COM, allows a client to ask an object precisely whether it supports the semantics of the interface, without unnecessary overhead and without the confusion that could arise in a system from having multiple versions of the same interface with the same name.

To summarize, it is important to understand what a COM interface is, and is not:

  • A COM interface is not the same as a C++ class—The pure virtual definition carries no implementation. If you are a C++ programmer, you can define your implementation of an interface as a class, but this falls under the heading of implementation details, which COM does not specify. An instance of an object that implements an interface must be created for the interface actually to exist. Furthermore, different object classes may implement an interface differently yet be used interchangeably in binary form, as long as the behavior conforms to the interface definition.
  • A COM interface is not an object—It is simply a related group of functions and is the binary standard through which clients and objects communicate. As long as it can provide pointers to interface methods, the object can be implemented in any language with any internal state representation.
  • COM interfaces are strongly typed—Every interface has its own interface identifier (a GUID), which eliminates the possibility of duplication that could occur with any other naming scheme.
  • COM interfaces are immutable—You cannot define a new version of an old interface and give it the same identifier. Adding or removing methods of an interface or changing semantics creates a new interface, not a new version of an old interface. Therefore, a new interface cannot conflict with an old interface. However, objects can support multiple interfaces simultaneously and can expose interfaces that are successive revisions of an interface, with different identifiers. Thus, each interface is a separate contract, and systemwide objects need not be concerned about whether the version of the interface they are calling is the one they expect. The interface ID (IID) defines the interface contract explicitly and uniquely.


IUnknown and Interface Inheritance

Inheritance in COM does not mean code reuse. Because no implementations are associated with interfaces, interface inheritance does not mean code inheritance. It means only that the contract associated with an interface is inherited in a C++ pure-virtual base-class fashion and modified—either by adding new methods or by further qualifying the allowed usage of methods. There is no selective inheritance in COM. If one interface inherits from another, it includes all the methods that the other interface defines.

Inheritance is used sparingly in the predefined COM interfaces. All predefined interfaces (and any custom interfaces you define) inherit their definitions from the important interface IUnknown, which contains three vital methods: QueryInterface, AddRef, and Release. All COM objects must implement the IUnknown interface because it provides the means, using QueryInterface, to move freely between the different interfaces that an object supports as well as the means to manage its lifetime by using AddRef and Release.

In creating an object that supports aggregation, you would need to implement one set of IUnknown functions for all interfaces as well as a stand-alone IUnknown interface. In any case, any object implementor will implement IUnknown methods. Refer to the section Using and Implementing IUnknown for more information.

While there are a few interfaces that inherit their definitions from a second interface in addition to IUnknown, the majority simply inherit the IUnknown interface methods along with their own already defined methods. This makes most interfaces relatively compact and easy to encapsulate.


Using and Implementing IUnknown

COM provides a rich set of standards for implementing and using objects and for inter-object communication. The topics in this section describe basic information relating to implementing objects. For information on how clients and servers interact, refer to COM Clients and Servers. For specific information on threading models and their implementation and use, refer to Processes, Apartments, and Threads.

For details on using and implementing IUnknown, see the following topics:

  • QueryInterface: Navigating in an Object
  • Rules for Implementing QueryInterface
  • Managing Object Lifetimes Through Reference Counting


QueryInterface: Navigating in an Object

Once you have an initial pointer to an interface on an object, COM has a very simple mechanism to find out whether the object supports another specific interface and, if so, to get a pointer to it. (For information on getting an initial pointer to an interface on an object, refer to Getting a Pointer to an Object.) This mechanism is the QueryInterface method of the IUnknown interface. If the object supports the requested interface, the method must return a pointer to that interface. This permits an object to navigate freely through the interfaces that an object supports. QueryInterface separates the request "Do you support a given contract?" from the high-performance use of that contract once negotiations have been successful.

When a client initially gains access to an object, that client will receive, at a minimum, an IUnknown interface pointer (the most fundamental interface) through which it can control the lifetime of the object—by telling the object when it is done using the object—and invoke QueryInterface. The client is programmed to ask each object it manages to perform some operations, but the IUnknown interface has no functions for those operations. Instead, those operations are expressed through other interfaces. The client is thus programmed to negotiate with objects for those interfaces. Specifically, the client will call QueryInterface to ask an object for an interface through which the client may invoke the desired operations.

Because the object implements QueryInterface, it has the ability to accept or reject the request. If the object accepts the client's request, QueryInterface returns a new pointer to the requested interface to the client. Through that interface pointer, the client has access to the methods of that interface. If, on the other hand, the object rejects the client's request, QueryInterface returns a null pointer—an error—and the client has no pointer through which to call the desired functions. In this case, the client must deal gracefully with that possibility. For example, suppose a client has a pointer to interface A on an object and asks for interfaces B and C. Suppose also that the object supports interface B but does not support interface C. The result is that the object returns a pointer to B and reports that C is not supported.

A key point is that when an object rejects a call to QueryInterface, it is impossible for the client to ask the object to perform the operations expressed through the requested interface. A client must have an interface pointer to invoke methods in that interface. If the object refuses to provide the requested pointer, the client must be prepared to do without, either by not doing whatever it had intended to do with that object or by attempting to fall back on another, perhaps less powerful, interface. This feature of COM functionality works well in comparison with other object-oriented systems in which you cannot know whether a function will work until you call that function, and even then, handling failure is uncertain. QueryInterface provides a reliable and consistent way to know whether an object supports an interface before attempting to call its methods.

The QueryInterface method also provides a robust and reliable way for an object to indicate that it does not support a given contract. That is, if in a call to QueryInterface one asks an "old" object whether it supports a "new" interface (one, for example, that was invented after the old object had been shipped), the old object will reliably, without causing a crash, answer "no." The technology that supports this is the algorithm by which IIDs are allocated. While this may seem like a small point, it is extremely important to the overall architecture of the system, and the ability to inquire of legacy elements about new functionality is, surprisingly, a feature not present in most other object architectures.


Rules for Implementing QueryInterface

There are three main rules that govern implementing the IUnknown::QueryInterface method on a COM object:

  • Objects must have identity.
  • The set of interfaces on an object instance must be static.
  • It must be possible to query successfully for any interface on an object from any other interface.

Objects Must Have Identity

For any given object instance, a call to QueryInterface must always return the same physical pointer value. This allows you to call QueryInterface(IID_IUnknown, ...) on any two interfaces and compare the results to determine whether they point to the same instance of an object.

The Set of Interfaces on an Object Instance Must Be Static

The set of interfaces accessible on an object via QueryInterface must be static, not dynamic. Specifically, if QueryInterface returns S_OK for a given IID once, it must never return E_NOINTERFACE on subsequent calls on the same object; and if QueryInterface returns E_NOINTERFACE for a given IID, subsequent calls for the same IID on the same object must never return S_OK.

It Must Be Possible to Query Successfully for Any Interface on an Object from Any Other Interface

That is, given the following code:

IA * pA = (some function returning an IA *);
IB * pB = NULL;
HRESULT   hr;
hr = pA->QueryInterface(IID_IB, &pB); // line 4

the following rules apply:

  • If you have a pointer to an interface on an object, a call like the following to QueryInterface for that same interface must succeed:
    pA->QueryInterface(IID_IA, ...)
  • If a call to QueryInterface for a second interface pointer succeeds, a call to QueryInterface from that pointer for the first interface must also succeed. If in line 4 of the example, pB was successfully obtained, the following must also succeed:
    pB->QueryInterface(IID_IA, ...)
  • Any interface must be able to query for any other interface on an object. So in line 4 of the example, if pB was successfully obtained and you successfully query for a third interface (IC) using that pointer, you must also be able to query successfully for IC using the first pointer, pA. In this case, the following sequence must succeed:
    IC * pC = NULL;
hr = pB->QueryInterface(IID_IC, &pC);      //Line 7
pA->QueryInterface(IID_IC, ...)

Interface implementations must maintain a counter that is large enough to support 231-1 outstanding pointer references to all the interfaces on a given object. Therefore, you should use a 32-bit unsigned integer for the counter.

If a client needs to know that resources have been freed, it must use a method in some interface on the object with higher-level semantics before calling IUnknown::Release.


Managing Object Lifetimes Through Reference Counting

In traditional object systems, the life cycle of objects—that is, the issues surrounding the creation and deletion of objects—is handled implicitly by the language (or the language run time) or explicitly by application programmers.

In an evolving, decentrally constructed system made up of reused components, it is no longer true that any client, or even any programmer, always "knows" how to deal with a component's lifetime. For a client with the right security privileges, it is still relatively easy to create objects through a simple request, but object deletion is another matter entirely. It is not necessarily clear when an object is no longer needed and should be deleted. (Readers familiar with garbage-collected programming environments, such as Java, may disagree; however, Java objects do not span machine or even process boundaries, and therefore the garbage collection is restricted to objects living within a single-process space. In addition, Java forces the use of a single programming language.) Even when the original client is done with the object, it cannot simply shut the object down, because some other client or clients might still have a reference to it.

One way to ensure that an object is no longer needed is to depend entirely on an underlying communication channel to inform the system when all connections to a cross-process or cross-channel object have disappeared. However, schemes that use this method are unacceptable for several reasons. One problem is that it could require a major difference between the cross-process/cross-network programming model and the single-process programming model. In the cross-process/cross-network programming model, the communication system would provide the hooks necessary for object lifetime management, while in the single-process programming model, objects are directly connected without any intervening communications channel. Another problem is that this scheme could also result in a layer of system-provided software that would interfere with component performance in the in-process case. Furthermore, a mechanism based on explicit monitoring would not tend to scale towards many thousands or millions of objects.

COM offers a scalable and distributed approach to this set of problems. Clients tell an object when they are using it and when they are done, and objects delete themselves when they are no longer needed. This approach mandates that all objects count references to themselves. Programming languages such as Java, which inherently have their own lifetime management schemes, such as garbage collection, can use COM's reference counting to implement and use COM objects internally, allowing the programmer to avoid dealing with it.

Just as an application must free memory it has allocated once that memory is no longer in use, a client of an object is responsible for freeing its references to the object when that object is no longer needed. In an object-oriented system, the client can do this only by giving the object an instruction to free itself.

It is important that an object be deallocated when it is no longer being used. The difficulty lies in determining when it is safe to deallocate an object. This is easy with automatic variables (those allocated on the stack)—they cannot be used outside the block in which they're declared, so the compiler deallocates them when the end of the block is reached. For COM objects, which are dynamically allocated, it is up to the clients of an object to decide when they no longer need to use the object—especially local or remote objects that might be in use by multiple clients at the same time. The object must wait until all clients are finished with it before freeing itself. Because COM objects are manipulated through interface pointers and can be used by objects in different processes or on other machines, the system cannot keep track of an object's clients.

COM's method of determining when it is safe to deallocate an object is manual reference counting. Each object maintains a 32-bit (at least) reference count that tracks how many clients are connected to it—that is, how many pointers exist to any of its interfaces in any client.


Implementing Reference Counting

Reference counting requires work on the part of both the implementor of a class and the clients who use objects of that class. When you implement a class, you must implement the AddRef and Release methods as part of the IUnknown interface. These two methods have the following simple implementations:

  • AddRef increments the object's internal reference count.
  • Release first decrements the object's internal reference count, and then it checks whether the reference count has fallen to zero. If it has, that means no one is using the object any longer, so the Release function deallocates the object.

A common implementation approach for most objects is to have only one implementation of these methods (along with QueryInterface), which is shared between all interfaces, and therefore a reference count that applies to the entire object. However, from a client's perspective, reference counting is strictly and clearly a per-interface-pointer notion, and therefore objects that take advantage of this capability by dynamically constructing, destroying, loading, or unloading portions of their functionality based on the currently extant interface pointers may be implemented. These are colloquially called tear-off interfaces.

Whenever a client calls a method (or API function), such as QueryInterface, that returns a new interface pointer, the method being called is responsible for incrementing the reference count through the returned pointer. For example, when a client first creates an object, it receives an interface pointer to an object that, from the client's point of view, has a reference count of one. If the client then calls AddRef on the interface pointer, the reference count becomes two. The client must call Release twice on the interface pointer to drop all of its references to the object.

An example of how reference counts are strictly per-interface-pointer occurs when a client calls QueryInterface on the first pointer for either a new interface or the same interface. In either of these cases, the client is required to call Release once for each pointer. COM does not require that an object return the same pointer when asked for the same interface multiple times. (The only exception to this is a query to IUnknown, which identifies an object to COM.) This allows the object implementation to manage resources efficiently.

Thread-safety is also an important issue in implementing AddRef and Release. For more information, see Processes, Apartments, and Threads.


Rules for Managing Reference Counts

Using a reference count to manage an object's lifetime allows multiple clients to obtain and release access to a single object without having to coordinate with one another in managing the object's lifetime. As long as the client object conforms to certain rules of use, the object, in effect, provides this management. These rules specify how to manage references between objects. (COM does not specify internal implementations of objects, although these rules are a reasonable starting point for a policy within an object.)

Conceptually, interface pointers can be thought of as residing within pointer variables that include all the internal computation state that holds an interface pointer. This would include variables in memory locations, in internal processor registers, and both programmer-generated and compiler-generated variables. Assignment to or initialization of a pointer variable involves creating a new copy of an already existing pointer. Where there was one copy of the pointer in some variable (the value used in the assignment/initialization), there are now two. An assignment to a pointer variable destroys the pointer copy presently in the variable, as does the destruction of the variable itself. (That is, the scope in which the variable is found, such as the stack frame, is destroyed.)

From a COM client's perspective, reference counting is always done for each interface. Clients should never assume that an object uses the same counter for all interfaces.

The default case is that AddRef must be called for every new copy of an interface pointer and Release must be called for every destruction of an interface pointer, except where the following rules permit otherwise:

  • In-out parameters to functions The caller must call AddRef on the parameter because it will be released (with a call to Release) in the implementing code when the out value is stored on top of it.
  • Fetching a global variable When creating a local copy of an interface pointer from an existing copy of the pointer in a global variable, you must call AddRef on the local copy because another function might destroy the copy in the global variable while the local copy is still valid.
  • New pointers synthesized out of "thin air." A function that synthesizes an interface pointer using special internal knowledge rather than obtaining it from some other source must call AddRef initially on the newly synthesized pointer. Important examples of such routines include instance creation routines, implementations of IUnknown::QueryInterface, and so on.
  • Retrieving a copy of an internally stored pointer When a function retrieves a copy of a pointer that is stored internally by the object called, that object's code must call AddRef on the pointer before the function returns. Once the pointer has been retrieved, the originating object has no other way of determining how its lifetime relates to that of the internally stored copy of the pointer.

The only exceptions to the default case require that the managing code know the relationships of the lifetimes of two or more copies of a pointer to the same interface on an object and simply ensure that the object is not destroyed by allowing its reference count to go to zero. There are generally two cases, as follows:

  • When one copy of a pointer already exists and a second is created subsequently and then is destroyed while the first copy still exists, calls to AddRef and Release for the second copy can be omitted.
  • When one copy of a pointer exists and a second is created and then the first is destroyed before the second, the calls to AddRef for the second copy and to Release for the first copy can be omitted.

The following are specific examples of these situations, the first two being especially common:

  • In parameters to functions The lifetime of the copy of an interface pointer passed as a parameter to a function is nested in that of the pointer used to initialize the value, so there is no need for a separate reference count on the parameter.
  • Out parameters from functions, including return values To set the out parameter, the function must have a stable copy of the interface pointer. On return, the caller is responsible for releasing the pointer. Therefore, the out parameter does not need a separate reference count.
  • Local variables A method implementation has control of the lifetimes of each of the pointer variables allocated on the stack frame and can use this to determine how to omit redundant AddRef/Release pairs.
  • Backpointers Some data structures contain two objects, each with a pointer to the other. If the lifetime of the first object is known to contain the lifetime of the second, it is not necessary to have a reference count on the second object's pointer to the first object. Often, avoiding this cycle is important in maintaining the appropriate deallocation behavior. However, uncounted pointers should be used with extreme caution because the portion of the operating system that handles remote processing has no way of knowing about this relationship. Therefore, in almost all cases, having the backpointer refer to a second, "friend" object of the first pointer (thus avoiding the circularity) is the preferred solution. COM's connectable objects architecture, for example, uses this approach.

When implementing or using reference-counted objects, it may be useful to apply artificial reference counts, which guarantee object stability during processing of a function. In implementing a method of an interface, you might call functions that have a chance of decrementing your reference count to an object, causing a premature release of the object and failure of the implementation. A robust way to avoid this is to insert a call to AddRef at the beginning of the method implementation and pair it with a call to Release just before the method returns.

In some situations, the return values of AddRef and Release may be unstable and should not be relied upon; they should be used only for debugging or diagnostic purposes.


Reusing Objects

An important goal of any object model is to enable object authors to reuse and extend objects provided by others as pieces of their own implementations. One way to do this in Microsoft® Visual C++® and other languages is by using implementation inheritance, which allows an object to inherit ("subclass") some of its functions from another object while overriding other functions.

The problem for systemwide object interaction using traditional implementation inheritance is that the contract (the interface) between objects in an implementation hierarchy is not clearly defined. In fact, it is implicit and ambiguous. When the parent or child object changes its implementation, the behavior of related components might become undefined or unstably implemented. In any single application, where the implementation can be managed by a single engineering team that updates all of the components at the same time, this is not always a major concern. In an environment where the components of one team are built through black-box reuse of other components built by other teams, this type of instability jeopardizes reuse. Additionally, implementation inheritance usually works only within process boundaries. This makes traditional implementation inheritance impractical for large, evolving systems composed of software components built by many engineering teams.

The key to building reusable components is to be able to treat the object as a black box. This means that the piece of code attempting to reuse another object 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 on the behavior of the object and not its exact implementation.

To achieve black-box reusability, COM adopts other established reusability mechanisms such as containment/delegation and aggregation, which are discussed in detail in this section of the COM guide.

Note For convenience, the object being reused is called the inner object and the object making use of that inner object is the outer object.

It is important to remember in both these mechanisms how the outer object appears to its clients. As far as the clients are concerned, both objects implement any interfaces to which the client can get a pointer. The client treats the outer object as a black box and therefore does not care, nor does it need to care, about the internal structure of the outer object—the client cares only about behavior.

See Also

Containment/Delegation

Aggregation


Containment/Delegation

The most common mechanism for object reuse is containment/delegation. This type of reuse is a familiar concept found in most object-oriented languages and systems. The outer object, which needs to make use of the inner object, acts as an object client to the inner object. The outer object "contains" the inner object, and when the outer object requires the services of the inner object, the outer object explicitly delegates implementation to the inner object's methods. That is, the outer object uses the inner object's services to implement itself.

It is not necessary for the outer and inner objects to support the same interfaces, although it certainly is reasonable to contain an object that implements an interface that the outer object does not and implement the methods of the outer object simply as calls to the corresponding methods in the inner object. When the complexity of the outer and inner objects differs greatly, however, the outer object may implement some of the methods of its interfaces by delegating calls to interface methods implemented in the inner object.

It is simple to implement containment for an outer object. The outer object creates the inner objects it needs to use as any other client would. This is nothing new—the process is like a C++ object that itself contains a C++ string object that it uses to perform certain string functions, even if the outer object is not considered a string object in its own right. Then, using its pointer to the inner object, a call to a method in the outer object generates a call to an inner object method.

See Also

Aggregation


Aggregation

Aggregation is the object reuse mechanism in which the outer object exposes interfaces from the inner object as if they were implemented on the outer object itself. This is useful when the outer object delegates every call to one of its interfaces to the same interface in the inner object. Aggregation is available as a convenience to avoid extra implementation overhead in the outer object in this case. Aggregation is actually a specialized case of containment/delegation.

Aggregation is almost as simple to implement as containment is, except for the three IUnknown functions: QueryInterface, AddRef, and Release. The catch is that from the client's perspective, any IUnknown function on the outer object must affect the outer object. That is, AddRef and Release affect the outer object and QueryInterface exposes all the interfaces available on the outer object. However, if the outer object simply exposes an inner object's interface as its own, that inner object's IUnknown members called through that interface will behave differently than those IUnknown members on the outer object's interfaces, an absolute violation of the rules and properties governing IUnknown.

The solution is that aggregation requires an explicit implementation of IUnknown on the inner object and delegation of the IUnknown methods of any other interface to the outer object's IUnknown methods.

Creating Aggregable Objects

Creating objects that can be aggregated is optional; however, it is simple to do and provides significant benefits. The following rules apply to creating an aggregable object:

  • The aggregable (or inner) object's implementation of QueryInterface, AddRef, and Release controls the inner object's reference count, and this implementation must not delegate to the outer object's unknown (the controlling IUnknown).
  • The QueryInterface, AddRef, and Release methods of all other interfaces implemented on the inner object must delegate to the controlling IUnknown and not directly affect the inner object's reference count.
  • The inner IUnknown must implement QueryInterface only for the inner object.
  • The aggregable object must not call AddRef when holding a reference to the controlling IUnknown pointer.
  • When the object is created, if any interface other than IUnknown is requested, the creation must fail with E_UNKNOWN.

The following code fragment illustrates a correct implementation of an aggregable object by using the nested class method of implementing interfaces:

// CSomeObject is an aggregable object that implements
// IUnknown and ISomeInterface
class CSomeObject : public IUnknown
{
    private:
        DWORD        m_cRef;         // Object reference count
        IUnknown*    m_pUnkOuter;    // Controlling IUnknown, no AddRef

        // Nested class to implement the ISomeInterface interface
        class CImpSomeInterface : public ISomeInterface
        {
            friend class CSomeObject ;
            private:
                DWORD    m_cRef;    // Interface ref-count, for debugging
                IUnknown*    m_pUnkOuter;    // Controlling IUnknown
            public:
                CImpSomeInterface() { m_cRef = 0;   };
                ~ CImpSomeInterface(void) {};

                // IUnknown members delegate to the outer unknown
                // IUnknown members do not control lifetime of object
                STDMETHODIMP     QueryInterface(REFIID riid, void** ppv)
                {    return m_pUnkOuter->QueryInterface(riid,ppv);   };

                STDMETHODIMP_(DWORD) AddRef(void)
                {    return m_pUnkOuter->AddRef();   };

                STDMETHODIMP_(DWORD) Release(void)
                {    return m_pUnkOuter->Release();   };

                // ISomeInterface members
                STDMETHODIMP SomeMethod(void)
                {    return S_OK;   };
        } ;
        CImpSomeInterface m_ImpSomeInterface ;
    public:
        CSomeObject(IUnknown * pUnkOuter)
        {
            m_cRef=0;
            // No AddRef necessary if non-NULL as we're aggregated.
            m_pUnkOuter=pUnkOuter;
            m_ImpSomeInterface.m_pUnkOuter=pUnkOuter;
        } ;
        ~CSomeObject(void) {} ;

        // Static member function for creating new instances (don't use
        // new directly). Protects against outer objects asking for
        // interfaces other than Iunknown.
        static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void **ppv)
        {
            CSomeObject*        pObj;
            if (pUnkOuter != NULL && riid != IID_IUnknown)
                return CLASS_E_NOAGGREGATION;
            pObj = new CSomeObject(pUnkOuter);
            if (pObj == NULL)
                return E_OUTOFMEMORY;
            // Set up the right unknown for delegation (the non-
            // aggregation case)
            if (pUnkOuter == NULL)
                pObj->m_pUnkOuter = (IUnknown*)pObj ;
            HRESULT hr;
            if (FAILED(hr = pObj->QueryInterface(riid, (void**)ppv)))
                delete pObj ;
            return hr;
        }

        // Inner IUnknown members, non-delegating
        // Inner QueryInterface only controls inner object
        STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
        {
            *ppv=NULL;
            if (riid == IID_IUnknown)
                *ppv=this;
            if (riid == IID_ISomeInterface)
                *ppv=&m_ImpSomeInterface;
            if (NULL==*ppv)
                return ResultFromScode(E_NOINTERFACE);
            ((IUnknown*)*ppv)->AddRef();
            return NOERROR;
        } ;
        STDMETHODIMP_(DWORD) AddRef(void)
        {    return ++m_cRef; };
        STDMETHODIMP_(DWORD) Release(void)
        {
            if (--m_cRef != 0)
                return m_cRef;
            delete this;
            return 0;
        };
};

Aggregating Objects

When developing an aggregable object, the following rules apply:

  • When creating the inner object, the outer object must explicitly ask for its IUnknown.
  • The outer object must protect its implementation of Release from reentrancy with an artificial reference count around its destruction code.
  • The outer object must call its controlling IUnknown Release method if it queries for a pointer to any of the inner object's interfaces. To free this pointer, the outer object calls its controlling IUnknown AddRef method, followed by Release on the inner object's pointer.
    // Obtaining inner object interface pointer
pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface);
pUnkOuter->Release();

// Releasing inner object interface pointer
pUnkOuter->AddRef();
pISomeInterface->Release();
  • The outer object must not blindly delegate a query for any unrecognized interface to the inner object, unless that behavior is specifically the intention of the outer object.

See Also

Containment/Delegation


The COM Library

Any process that uses COM must both initialize and uninitialize the COM library. In addition to being a specification, COM also implements some important services in this library. Provided as a set of DLLs and EXEs (primarily ole32.dll and rpcss.exe) in Microsoft® Windows®, the COM library includes the following:

  • A small number of fundamental API functions that facilitate the creation of COM applications, both client and server. For clients, COM supplies basic functions for creating objects. For servers, COM supplies the means of exposing their objects.
  • Implementation-locator services through which COM determines, from a unique class identifier (CLSID), which server implements that class and where that server is located. This service includes support for a level of indirection, usually a system registry, between the identity of an object class and the packaging of the implementation so that clients are independent of the packaging, which can change in the future.
  • Transparent remote procedure calls when an object is running in a local or remote server.
  • A standard mechanism to allow an application to control how memory is allocated within its process, particularly memory that needs to be passed between cooperating objects so that it can be freed properly.

To use basic COM services, all COM threads of execution in clients and out-of-process servers must call either the CoInitialize or the CoInitializeEx function before calling any other COM function except memory allocation calls. CoInitializeEx replaces the other function, adding a parameter that allows you to specify the threading model of the thread—either apartment-threaded or free-threaded. A call to CoInitialize simply sets the threading model to apartment-threaded.

OLE compound document applications call the OleInitialize function, which calls CoInitializeEx and also does some initialization required for compound documents. Therefore, threads that call OleInitialize cannot be free-threaded. For information on threading in clients and servers, refer to Processes, Apartments, and Threads.

In-process servers do not call the initialization functions because they are being loaded into a process that has already done so. As a result, in-process servers must set their threading model in the registry under the InprocServer32 key. For detailed information on threading issues in in-process servers, refer to In-Process Server Threading Issues.

It is also important to uninitialize the library. For each call to CoInitialize or CoInitializeEx, there must be a corresponding call to CoUninitialize. For each call to OleInitialize, there must be a corresponding call to OleUninitialize.

In-process servers can assume that the process they are being loaded into has already performed these steps.


Managing Memory Allocation

In COM, many, if not most, interface methods and APIs are called by code written by one programming organization and implemented by code written by another. Many of the parameters and return values of these functions are of types that can be passed around by value. Sometimes, however, it is necessary to pass data structures for which this is not the case, so it is necessary for both caller and called to have a compatible allocation and de-allocation policy. COM defines a universal convention for memory allocation, because it is more reasonable than defining case-by-case rules and so that the COM remote procedure call implementation can correctly manage memory.

The methods of a COM interface always provide memory management of pointers to the interface by calling the AddRef and Release functions found in the IUnknown interface, from which all other COM interfaces derive. (Refer to Rules for Managing Reference Counts for more information.)

This section describes only how to allocate memory for parameters that are not passed by value—not pointers to interfaces, but more mundane things like strings, pointers to structures, and so forth.

See Also

The OLE Memory Allocator

Memory Management Rules

Debugging Memory Allocations


The OLE Memory Allocator

The COM library provides an implementation of a memory allocator that is thread-safe. (That is, it cannot cause problems in multithreaded situations.) Whenever ownership of an allocated chunk of memory is passed through a COM interface or between a client and the COM library, you must use this COM allocator to allocate the memory. Allocation internal to an object can use any allocation scheme desired, but the COM memory allocator is a handy, efficient, and thread-safe allocator.

A call to the API function CoGetMalloc provides a pointer to the OLE allocator, which is an implementation of the IMalloc interface. However, it is more efficient to call the helper functions CoTaskMemAlloc, CoTaskMemRealloc, and CoTaskMemFree, which wrap getting a pointer to the task memory allocator, calling the corresponding IMalloc method, and then releasing the pointer to the allocator.

See Also

The COM Library


Memory Management Rules

The lifetime of pointers to interfaces is always managed through the AddRef and Release methods on every COM interface. For more information, refer to Rules for Managing Reference Counts.

For all other parameters, it is important to adhere to certain rules for managing memory. The following rules apply to all parameters of interface methods—including the return value—that are not passed by value:

  • In parameters—Must be allocated and freed by the caller.
  • Out-parameter—Must be allocated by the one called; freed by the caller using the standard COM task memory allocator. Refer to The OLE Memory Allocator for more information.
  • In-out parameter—Initially allocated by the caller, and then freed and reallocated by the one called, if necessary. As is true for out parameters, the caller is responsible for freeing the final returned value. The standard COM memory allocator must be used.

In the latter two cases, where one piece of code allocates the memory and a different piece of code frees it, using the COM allocator ensures that the two pieces of code are using the same allocation methods.

Another area that needs special attention is the treatment of out and in-out parameters in failure conditions. If a function returns a a failure code, the caller typically has no way to clean up the out or in-out parameters. This leads to the following additional rules:

  • In case of an error condition, parameters must always be reliably set to a value that will be cleaned up without any action by the caller.
  • All out pointer parameters must explicitly be set to NULL. These are usually passed in a pointer-to-pointer parameter but can also be passed as members of a structure that the caller allocates and the called code fills. The most straightforward way to ensure this is (in part) to set these values to NULL on function entry. This rule is important because it promotes more robust application interoperability.
  • Under error conditions, all in-out parameters must either be left alone by the code called (thus remaining at the value to which they were initialized by the caller) or be explicitly set, as in the out parameter error return case.

Remember that these memory management conventions for COM applications apply only across public interfaces and APIs—there is no requirement at all that memory allocation strictly internal to a COM application need be done using these mechanisms.


Debugging Memory Allocations

COM provides the IMallocSpy interface for developers to use to debug their memory allocations. For each method in IMalloc, there are two methods in IMallocSpy, a "pre" method and a "post" method. After a developer implements it and publishes it to the system, the system calls the IMallocSpy "pre" method just before the corresponding IMalloc method, effectively allowing the debug code to "spy" on the allocation operation, and calls the "post" method to release the spy.

For example, when COM detects that the next call is a call to IMalloc::Alloc, it calls IMallocSpy::PreAlloc, executing whatever debug operations the developer wants during the Alloc execution, and then, when the Alloc call returns, calls IMallocSpy::PostAlloc to release the spy and return control to the code.


Processes, Threads, and Apartments

A process is a collection of virtual memory space, code, data, and system resources. A thread is code that is to be serially executed within a process. A processor executes threads, not processes, so each 32-bit application has at least one process, and a process always has at least one thread of execution, known as the primary thread. A process can have multiple threads in addition to the primary thread. Prior to the introduction of multiple threads of execution, applications were all designed to run on a single thread of execution.

Processes communicate with one another through messages, using Microsoft's Remote Procedure Call (RPC) technology to pass information to one another. There is no difference to the caller between a call coming from a process on a remote machine and a call coming from another process on the same machine.

When a thread begins to execute, it continues until it is killed or until it is interrupted by a thread with higher priority (by a user action or the kernel's thread scheduler). Each thread can run separate sections of code, or multiple threads can execute the same section of code. Threads executing the same block of code maintain separate stacks. Each thread in a process shares that process's global variables and resources.

The thread scheduler determines when and how often to execute a thread, according to a combination of the process's priority class attribute and the thread's base priority. You set a process's priority class attribute by calling the Win32® function SetPriorityClass, and you set a thread's base priority with a call to SetThreadPriority.

Multithreaded applications must avoid two threading problems: deadlocks and races. A deadlock occurs when each thread is waiting for the other to do something. The COM call control helps prevent deadlocks in calls between objects. A race condition occurs when one thread finishes before another on which it depends, causing the former to use a bogus value because the latter has not yet supplied a valid one. COM supplies some functions specifically designed to help avoid race conditions in out-of-process servers. (See Out-of-Process Server Implementation Helpers.)

The Apartment and the COM Threading Architecture

While COM supports the single-thread-per-process model prevalent before the introduction of multiple threads of execution, writing code to take advantage of multiple threads makes it possible to create more efficient applications than ever before by allowing a thread, while it waits for some time-consuming operation to complete, to allow another thread to be executed.

Note Using multiple threads is not a guarantee of better performance. In fact, because thread factoring is a difficult problem, using multiple threads often causes performance problems. The key is to use multiple threads only if you are very sure of what you are doing.

In general, the simplest way to view the COM threading architecture is to think of all the COM objects in the process as divided into groups called apartments. A COM object lives in exactly one apartment, in the sense that its methods can legally be directly called only by a thread that belongs to that apartment. Any other thread that wants to call the object must go through a proxy.

There are two types of apartments: single-threaded apartments, and multithreaded apartments.

  • Single-threaded Apartments—Single-threaded apartments consist of exactly one thread, so all COM objects that live in a single-threaded apartment can receive method calls only from the one thread that belongs to that apartment. All method calls to a COM object in a single-threaded apartment are synchronized with the windows message queue for the single-threaded apartment's thread. A process with a single thread of execution is simply a special case of this model.
  • Multithreaded Apartments—Multithreaded apartments consist of one or more threads, so all COM objects that live in an multithreaded apartment can receive method calls directly from any of the threads that belong to the multithreaded apartment. Threads in a multithreaded apartment use a model called free-threading. Calls to COM objects in a multithreaded apartment are synchronized by the objects themselves.

Note For a description of communication between single-threaded apartments and multithreaded apartments within the same process, see Single-Threaded and Multithreaded Communication.

A process can have zero or more single-threaded apartments and zero or one multithreaded apartment. The different types of processes can be defined as follows:

  • A process that consists of just one single-threaded apartment is referred to as a single-threaded process.
  • A process that has two or more single-threaded apartments and no multithreaded apartments is called an apartment model process.
  • A process that has a multithreaded apartment and no single-threaded apartments is referred to as a free-threaded process.
  • A process that has a multithreaded apartment and one or more single-threaded apartments is a mixed model process.

In reality, all processes are apartment model processes, and some apartments have a single thread and some apartments have multiple threads. The threading model really applies to an apartment, not to a process. It can also apply to a class of objects, but it doesn't really apply to a component, such as a DLL, but to the object classes within the DLL. Different classes in a DLL can have different threading models.

In a process, the main apartment is the first to be initialized. In a single-threaded process, this is the only apartment. Call parameters are marshaled between apartments, and COM handles the synchronization through messaging. If you designate multiple threads in a process to be free-threaded, all free threads reside in a single apartment, parameters are passed directly to any thread in the apartment, and you must handle all synchronization. In a process with both free-threading and apartment threading, all free threads reside in a single apartment and all other apartments are single-threaded apartments. A process that does COM work is a collection of apartments with, at most, one multithreaded apartment but any number of single-threaded apartments.

The threading models in COM provide the mechanism for clients and servers that use different threading architectures to work together. Calls among objects with different threading models in different processes are naturally supported. From the perspective of the calling object, all calls to objects outside a process behave identically, no matter how the object being called is threaded. Likewise, from the perspective of the object being called, arriving calls behave identically, regardless of the threading model of the caller.

Interaction between a client and an out-of-process object is straightforward, even when they use different threading models because the client and object are in different processes. COM, interposed between the client and the server, can provide the code for the threading models to interoperate, using standard marshaling and RPC. For example, if a single-threaded object is called simultaneously by multiple free-threaded clients, the calls will be synchronized by COM by placing corresponding window messages in the server's message queue. The object's apartment will receive one call each time it retrieves and dispatches messages. However, some care must be taken to ensure that in-process servers interact properly with their clients. (See In-Process Server Threading Issues.)

The most important issue in programming with a multithreaded model is to ensure that the code is thread-safe so that messages intended for a particular thread go only to that thread and access to threads is protected.

See Also

Choosing the Threading Model

Single-Threaded Apartments

Multithreaded Apartments

Single-Threaded and Multithreaded Communication

In-Process Server Threading Issues

Accessing Interfaces Across Apartments


Choosing the Threading Model

Choosing the threading model for an object depends on the object's function. An object that does extensive I/O might support free-threading to provide maximum response to clients by allowing interface calls during I/O latency. On the other hand, an object that interacts with the user might support apartment threading to synchronize incoming COM calls with its window operations.

It is easier to support apartment threading in single-threaded apartments because COM provides synchronization on a per-call basis. Supporting free-threading is more difficult because the object must implement synchronization; however, response to clients may be better because synchronization can be implemented for smaller sections of code.

See Also

Processes, Threads, and Apartments

Single-Threaded Apartments

Multithreaded Apartments

Single-Threaded and Multithreaded Communication

In-Process Server Threading Issues

Accessing Interfaces Across Apartments


Single-Threaded Apartments

Using single-threaded apartments (the apartment model process) offers a message-based paradigm for dealing with multiple objects running concurrently. It enables you to write more efficient code by allowing a thread, while it waits for some time-consuming operation to complete, to allow another thread to be executed.

Each thread in a process that is initialized as an apartment model process, and that retrieves and dispatches window messages, is a single-threaded apartment thread. Each thread lives within its own apartment. Within an apartment, interface pointers can be passed without marshaling, and therefore, all objects in one single-threaded apartment thread communicate directly.

A logical grouping of related objects that all execute on the same thread, and therefore must have synchronous execution, could live on the same single-threaded apartment thread. However, an apartment model object cannot reside on more than one thread. Calls to objects in other processes must be made within the context of the owning process, so distributed COM switches threads for you automatically when you call on a proxy.

The interprocess and interthread models are similar. When it is necessary to pass an interface pointer to an object in another apartment (on another thread) within the same process, you use the same marshaling model that objects in different processes use to pass pointers across process boundaries. By getting a pointer to the standard marshaling object, you can marshal interface pointers across thread boundaries (between apartments) in the same way you do between processes. (Interface pointers must be marshaled when passed between apartments.)

Rules for single-threaded apartments are simple, but it is important to follow them carefully:

  • Every object should live on only one thread (within a single-threaded apartment).
  • Initialize the COM library for each thread.
  • Marshal all pointers to objects when passing them between apartments.
  • Each single-threaded apartment must have a message loop to handle calls from other processes and apartments within the same process. Single-threaded apartments without objects (client only) also need a message loop to dispatch broadcast sendmessages that some applications use.
  • DLL-based or in-process objects do not call the COM initialization functions; instead, they register their threading model with the ThreadingModel named-value under the InprocServer32 key in the registry. Apartment-aware objects must also write DLL entry points carefully. There are special considerations that apply to threading in-process servers. For more information, see In-Process Server Threading Issues.

While multiple objects can live on a single thread, no apartment model object can live on more than one thread.

Each thread of a client process or out-of-process server must call or call CoInitializeEx and specify COINIT_APARTMENTTHREADED for the dwCoInit parameter. The main apartment is the thread that calls CoInitializeEx first. For information on in-process servers, refer to In-Process Server Threading Issues.

All calls to an object must be made on its thread (within its apartment). It is forbidden to call an object directly from another thread; using objects in this free-threaded manner could cause problems for applications. The implication of this rule is that all pointers to objects must be marshaled when passed between apartments. COM provides the following two functions for this purpose:

  • CoMarshalInterThreadInterfaceInStream marshals an interface into a stream object that is returned to the caller.
  • CoGetInterfaceAndReleaseStream unmarshals an interface pointer from a stream object and releases it.

These functions wrap calls to CoMarshalInterface and CoUnmarshalInterface functions, which require the use of the MSHCTX_INPROC flag.

In general, the marshaling is accomplished automatically by COM. For example, when passing an interface pointer as a parameter in a method call on a proxy to an object in another apartment, or when calling CoCreateInstance, COM does the marshaling automatically. However, in some special cases, where the application writer is passing interface pointers between apartments without using the normal COM mechanisms, the writer must handle the marshaling manually.

If one apartment (Apartment 1) in a process has an interface pointer and another apartment (Apartment 2) requires its use, Apartment 1 must call CoMarshalInterThreadInterfaceInStream to marshal the interface. The stream that is created by this function is thread-safe and must be stored in a variable that is accessible by Apartment 2. Apartment 2 must pass this stream to CoGetInterfaceAndReleaseStream to unmarshal the interface and will get back a pointer to a proxy through which it can access the interface. The main apartment must remain alive until the client has completed all COM work (because some in-process objects are loaded in the main apartment, as described in In-Process Server Threading Issues). After one object has been passed between threads in this manner, it is very easy to pass interface pointers as parameters. That way, distributed COM does the marshaling and thread switching for the application.

To handle calls from other processes and apartments within the same process, each single-threaded apartment must have a message loop. This means that the thread's work function must have a GetMessage/DispatchMessage loop. If other synchronization primitives are being used to communicate between threads, the Win32® function MsgWaitForMultipleObjects can be used to wait both for messages and for thread synchronization events. The Platform SDK documentation for this function has an example of this sort of combination loop.

COM creates a hidden window in each single-threaded apartment. A call to an object is received as a window message to this hidden window. When the object's apartment retrieves and dispatches the message, the hidden window will receive it. The window procedure will then call the corresponding interface method of the object.

When multiple clients call an object, the calls are queued in the message queue and the object will receive a call each time its apartment retrieves and dispatches messages. Because the calls are synchronized by COM and the calls are always delivered by the thread that belongs to the object's apartment, the object's interface implementations need not provide synchronization. Single-threaded apartments can implement IMessageFilter to permit them to cancel calls or receive window messages when necessary.

The object can be reentered if one of its interface method implementations retrieves and dispatches messages or makes an ORPC call to another thread, thereby causing another call to be delivered to the object (by the same apartment). OLE does not prevent reentrancy on the same thread, but it provides thread safety. This is identical to the way in which a window procedure can be reentered if it retrieves and dispatches messages while processing a message. However, calling an out-of-process single-threaded apartment server that calls another single-threaded apartment server will allow the first server to be reentered.

See Also

Processes, Threads, and Apartments

Choosing the Threading Model

Multithreaded Apartments

Single-Threaded and Multithreaded Communication

In-Process Server Threading Issues

Accessing Interfaces Across Apartments


Multithreaded Apartments

In a multithreaded apartment model, all the threads in the process that have been initialized as free-threaded reside in a single apartment. Therefore, there is no need to marshal between threads. The threads need not retrieve and dispatch messages because COM does not use window messages in this model.

Calls to methods of objects in the multithreaded apartment can be run on any thread in the apartment. There is no serialization of calls—many calls may occur to the same method or to the same object simultaneously. Objects created in the multithreaded apartment must be able to handle calls on their methods from other threads at any time.

Because calls to objects are not serialized in any way, multithreaded object concurrency offers the highest performance and takes the best advantage of multiprocessor hardware for cross-thread, cross-process, and cross-machine calling. This means, however, that the code for objects must provide synchronization in their interface implementations, typically through the use of Win32® synchronization primitives such as event objects, critical sections, mutexes, or semaphores, which are described later in this section. In addition, because the object doesn't control the lifetime of the threads that are accessing it, no thread-specific state may be stored in the object (in Thread-Local-Storage).

Following are some important considerations regarding synchronization for multithreaded apartments:

  • COM provides call synchronization for single-threaded apartments only.
  • Multithreaded apartments do not receive calls while making calls (on the same thread).
  • Multithreaded apartments cannot make input-synchronized calls.
  • Asynchronous calls are converted to synchronous calls in multithreaded apartments.
  • The message filter is not called for any thread in a multithreaded apartment.

To initialize a thread as free-threaded, call CoInitializeEx, specifying COINIT_MULTITHREADED. For information on in-process server threading, see In-Process Server Threading Issues.

Multiple clients can simultaneously call, from different threads, an object that supports free-threading. In free-threaded out-of-process servers, COM, through the RPC subsystem, creates a pool of threads in the server process and a client call (or multiple client calls) can be delivered by any of these threads at any time. An out-of-process server must also implement synchronization in its class factory. Free-threaded, in-process objects can receive direct calls from multiple threads of the client.

The client can do COM work in multiple threads. All threads belong to the same multithreaded apartment. Interface pointers are passed directly from thread to thread within a multithreaded apartment, so interface pointers are not marshaled between its threads. Message filters (implementations of IMessageFilter) are not used in multithreaded apartments. The client thread will suspend when it makes a COM call to out-of-apartment objects and will resume when the call returns. Calls between processes are still handled by RPC.

Threads initialized with the free-threaded model must implement their own synchronization. As mentioned earlier in this section, Win32 enables this implementation by using the following synchronization primitives:

  • Event object—Provides a way of signaling one or more threads that an event has occurred, essentially acting as a traffic cop. Any thread within a process can create an event object. A handle to the event is returned by the event-creating function CreateEvent. Once an event object has been created, threads with a handle to the object can wait on it before continuing execution.
  • Critical section—A section of code that requires exclusive access to some set of shared data before it can be executed and that is used only by the threads within a single process. A critical section is like a turnstile through which only one thread at a time may pass, working as follows:
    • To ensure that no more than one thread at a time accesses shared data, a process's primary thread allocates a global CRITICAL_SECTION data structure and initializes its members. A thread entering a critical section calls the Win32 function EnterCriticalSection and modifies the data structure's members.
    • A thread attempting to enter a critical section calls EnterCriticalSection which checks to see whether the CRITICAL_SECTION data structure has been modified. If so, another thread is currently in the critical section and the subsequent thread is put to sleep. A thread leaving a critical section calls LeaveCriticalSection, which resets the data structure. When a thread leaves a critical section, Microsoft® Windows NT® or Microsoft Windows® 2000 wakes up one of the sleeping threads, which then enters the critical section.
  • Mutex—(Abbreviation for mutual exclusion.) A kernel object that performs the same function as a critical section, except that the mutex is accessible to threads running in different processes. Owning a mutex object is like having the floor in a debate. A process creates a mutex object by calling the Win32 function CreateMutex, which returns a handle. The first thread requesting a mutex object obtains ownership of it. When the thread has finished with the mutex, ownership passes to other threads on a first-come, first-served basis.
  • Semaphore—A kernel object used to maintain a reference count on some available resource. A thread creates a semaphore for a resource by calling the Win32 function CreateSemaphore and passing a pointer to the resource, an initial resource count, and the maximum resource count. This function returns a handle. A thread requesting a resource passes its semaphore handle in a call to WaitForSingleObject. The semaphore object polls the resource to determine whether it is available. If so, the semaphore decrements the resource count and wakes the waiting thread. If the count is zero, the thread remains asleep until another thread releases a resource, causing the semaphore to increment the count to one.

More information about synchronization primitives is available in the Platform SDK.

See Also

Processes, Threads, and Apartments

Choosing the Threading Model

Single-Threaded Apartments

Single-Threaded and Multithreaded Communication

In-Process Server Threading Issues

Accessing Interfaces Across Apartments


Single-Threaded and Multithreaded Communication

A client or server that supports both single-threaded and multithreaded apartments will have one multithreaded apartment, containing all threads initialized as free-threaded, and one or more single-threaded apartments. Interface pointers must be marshaled between apartments but can be used without marshaling within an apartment. Calls to objects in a single-threaded apartment will be synchronized by COM. Calls to objects in the multithreaded apartment will not be synchronized by COM.

All of the information on single-threaded apartments applies to the threads marked as apartment model, and all of the information on multithreaded apartments applies to all of the threads marked as free-threaded. Apartment threading rules apply to inter-apartment communication, requiring that interface pointers be marshaled between apartments with calls to CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream, as described in the section Single-Threaded Apartments.

Note Some special considerations apply when dealing with in-process servers. For more information, see In-Process Server Threading Issues.

See Also

Processes, Threads, and Apartments

Choosing the Threading Model

Single-Threaded Apartments

Multithreaded Apartments

In-Process Server Threading Issues

Accessing Interfaces Across Apartments


In-Process Server Threading Issues

An in-process server does not call CoInitialize, CoInitializeEx, or OleInitialize to mark its threading model. For thread-aware DLL-based or in-process objects, you need to set the threading model in the registry. The default model when you do not specify a threading model is single-thread-per-process. To specify a model, you add the ThreadingModel named-value to the InprocServer32 key in the registry.

DLLs that support instantiation of a class object must implement and export the functions DllGetClassObject and DllCanUnloadNow. When a client wants an instance of the class the DLL supports, a call to CoGetClassObject (either directly or through a call to CoCreateInstance) calls DllGetClassObject to get a pointer to its class object when the object is implemented in a DLL. DllGetClassObject should therefore be able to give away multiple class objects or a single thread-safe object (essentially just using InterlockedIncrement/InterlockedDecrement on their internal reference counts).

As its name implies, DllCanUnloadNow is called to determine whether the DLL that implements it is in use, enabling the caller to safely unload it if it is not. Calls to CoFreeUnusedLibraries from any thread always route through the main apartment's thread to call DllCanUnloadNow.

Like other servers, in-process servers can be single-threaded, apartment-threaded, or free-threaded. These servers can be used by any OLE client, regardless of the threading model used by that client.

All combinations of threading model interoperability are allowed between clients and in-process objects. Interaction between a client and an in-process object that use different threading models is exactly like the interaction between clients and out-of-process servers. For an in-process server, when the threading model of the client and in-process server differ, COM must interpose itself between the client and the object.

When an in-process object that supports the single-threaded model is called simultaneously by multiple threads of a client, COM cannot allow the client threads to directly access the object's interface—the object was not designed for such access. Instead, COM must ensure that calls are synchronized and are made only by the client thread that created the object. Therefore, COM creates the object in the client's main apartment and requires all the other client apartments to access the object by using proxies.

When a free-threaded apartment (multithreaded apartment model) in a client creates an apartment-threaded in-process server, COM spins up a single-threaded apartment model "host" thread in the client. This host thread will create the object, and the interface pointer will be marshaled back to the client's free-threaded apartment. Similarly, when a single-threaded apartment in an apartment-model client creates a free-threaded in-process server, COM spins up a free-threaded host thread (multithreaded apartment on which the object will be created and then marshaled back to the client single-threaded apartment).

Note In general, if you design a custom interface on an in-process server, you should also provide the marshaling code for it so that COM can marshal the interface between client apartments.

COM protects access to objects provided by a single-threaded DLL by requiring access from the same client apartment in which they were created. In addition, all of the DLL entry points (like DllGetClassObject and DllCanUnloadNow) and global data should always be accessed by the same apartment. COM creates such objects in the main apartment of the client, giving the main apartment direct access to the object's pointers. Calls from the other apartments use interthread marshaling to go from the proxy to the stub in the main apartment and then to the object. This allows COM to synchronize calls to the object. Interthread calls are slow, so it is recommended that these servers be rewritten to support multiple apartments.

Like a single-threaded in-process server, an object provided by an apartment model DLL must be accessed by the same client apartment from which it was created. However, objects provided by this server can be created in multiple apartments of the client, so the server must implement its entry points (like DllGetClassObject and DllCanUnloadNow) for multithreaded use. For example, if two apartments of a client try to create two instances of the in-process object simultaneously, DllGetClassObject can be called simultaneously by both apartments. DllCanUnloadNow must be written so that the DLL is protected from being unloaded while code is still executing in the DLL.

If the DLL provides only one instance of the class factory to create all the objects, the class factory implementation must also be designed for multithreaded use, because it will be accessed by multiple client apartments. If the DLL creates a new instance of the class factory each time DllGetClassObject is called, the class factory need not be thread-safe.

Objects created by the class factory need not be thread-safe. Once created by a thread, the object is always accessed through that thread and all calls to the object are synchronized by COM. The apartment model apartment of a client that creates this object will get a direct pointer to the object. Client apartments that are different from the apartment in which the object was created must access the object through proxies. These proxies are created when the client marshals the interface between its apartments.

When an in-process DLL ThreadingModel named-value is set to Both, an object provided by this DLL can be created and used directly (without a proxy) in single-threaded or multithreaded client apartments. However, it can be used directly only within the apartment in which it was created. To give the object to any other apartment, the object must be marshaled. The DLL object must implement its own synchronization and can be accessed by multiple client apartments at the same time.

To speed performance for free-threaded access to in-process DLL objects, COM provides the CoCreateFreeThreadedMarshaler function. This function creates a free-threaded marshaling object that can be aggregated with an in-process server object. When a client apartment in the same process needs access to an object in another apartment, aggregating the free-threaded marshaler provides the client with a direct pointer to the server object, rather than to a proxy, when the client marshals the object's interface to a different apartment. The client does not need to do any synchronization. This works only within the same process—standard marshaling is used for a reference to the object that is sent to another process.

An object provided by an in-process DLL that supports only free threading is a free-threaded object. It implements its own synchronization and can be accessed by multiple client threads at the same time. This server does not marshal interfaces between threads, so this server can be created and used directly (without a proxy) only by multithreaded apartments in a client. Single-threaded apartments that create it will access it through a proxy.

See Also

Processes, Threads, and Apartments

Choosing the Threading Model

Single-Threaded Apartments

Multithreaded Apartments

Single-Threaded and Multithreaded Communication

Accessing Interfaces Across Apartments


Accessing Interfaces Across Apartments

COM provides a way for any apartment in a process to get access to an interface implemented on an object in any other apartment in the process. This is done through the IGlobalInterfaceTable interface. This interface has three methods, which allow you to do the following:

  • Register an interface as a global (processwide) interface.
  • Get a pointer to that interface from any other apartment through a cookie.
  • Revoke the global registration of an interface.

The IGlobalInterfaceTable interface is an efficient way for a process to store an interface pointer in a memory location that can be accessed from multiple apartments within the process, such as processwide variables and agile objects (free-threaded, marshaled objects) containing interface pointers to other objects.

An agile object is unaware of the underlying COM infrastructure in which it runs—in other words, what apartment, context, and thread it is executing on. The object may be holding on to interfaces that are specific to an apartment or context. For this reason, calling these interfaces from wherever the agile component is executing might not always work properly. The global interface table avoids this problem by guaranteeing that a valid proxy (or direct pointer) to the object is used, based on where the agile object is executing.

Note The global interface table is not portable across process or machine boundaries, so it cannot be used in place of the normal parameter-passing mechanism.

For information about creating and using a global interface table, see the following topics:

  • Creating the Global Interface Table
  • When to Use the Global Interface Table

See Also

Processes, Threads, and Apartments

Choosing the Threading Model

Single-Threaded Apartments

Multithreaded Apartments

Single-Threaded and Multithreaded Communication

In-Process Server Threading Issues


Creating the Global Interface Table

Use the following call to create the global interface table object and get a pointer to IGlobalInterfaceTable:

CoCreateInstance(CLSID_StdGlobalInterfaceTable,
                 NULL,
                 CLSCTX_INPROC_SERVER,
                 IID_IGlobalInterfaceTable,
                 (void **)&gpGIT)

Note When creating the global interface table object using the preceding call, it is necessary to link to the library uuid.lib. This will resolve the external symbols CLSID_StdGlobalInterfaceTable and IID_IGlobalInterfaceTable.

There is a single instance of the global interface table per process, so all calls to this function in a process return the same instance.

After the call to the CoCreateInstance function, register the interface from the apartment in which it resides with a call to the RegisterInterfaceInGlobal method. This method supplies a cookie that identifies the interface and its location. An apartment seeking a pointer to this interface then calls the GetInterfaceFromGlobal method with this cookie, and the implementation then supplies an interface pointer to the calling apartment. To revoke the interface's global registration, any apartment can call the RevokeInterfaceFromGlobal method.

A simple example of using IGlobalInterfaceTable would be when you want to pass an interface pointer on an object in a single-threaded apartment (STA) to a worker thread in another apartment. Rather than having to marshal it into a stream and pass the stream to the worker thread as a thread parameter, IGlobalInterfaceTable allows you simply to pass a cookie.

When you register the interface in the global interface table, you get a cookie that you can use instead of passing the actual pointer (whenever you need to pass the pointer), either to a nonmethod parameter that is going to another apartment (as a parameter to ThreadProc via CreateThread) or to in-process memory accessible outside your apartment.

Care is required because using global interfaces places the extra burden on the programmer of managing problems such as race conditions and mutual exclusion, which are associated with accessing global state from multiple threads simultaneously.

Note COM provides a standard implementation of the IGlobalInterfaceTable interface. It is highly recommended that you use this standard implementation because it provides complete thread-safe functionality.

See Also

When to Use the Global Interface Table


When to Use the Global Interface Table

If you are unmarshaling an interface pointer multiple times between apartments in a process, you might prefer to use the IGlobalInterfaceTable interface. With other techniques, you would have to remarshal each time.

Note If the interface pointer is unmarshaled only once, you may want to use the CoMarshalInterThreadInterfaceInStream function. It can also be used to pass an interface pointer from one thread to another thread in the same process.

The IGlobalInterfaceTable interface also makes another previously difficult problem simpler for the programmer. This problem occurs when the following conditions apply:

  • An in-process agile object aggregates the free-threaded marshaler.
  • This same agile object also holds (as member variables) interface pointers to other objects that are not agile and do not aggregate the free-threaded marshaler.

In this situation, if the outer object gets marshaled to another apartment and the application calls on it, and the object tries to call on any of its member variable interface pointers that are not free-threaded or are proxies to objects in other apartments, it might get incorrect results or the error RPC_E_WRONG_THREAD. This error occurs because the inner interface is designed to be callable only from the apartment in which it was first stored in the member variable.

To solve this problem, the outer object aggregating the free-threaded marshaler should call IGlobalInterfaceTable::RegisterInterfaceInGlobal on the inner interface and store the resulting cookie in its member variable, instead of storing the actual interface pointer. When the outer object wants to call on an inner object's interface pointer, it should call IGlobalInterfaceTable::GetInterfaceFromGlobal, use the returned interface pointer, and then release it. When the outer object goes away, it should call IGlobalInterfaceTable::RevokeInterfaceFromGlobal to remove the interface from the global interface table.

See Also

Creating the Global Interface Table


COM Clients and Servers

A critical aspect of COM is how clients and servers interact. A COM client is whatever code or object gets a pointer to a COM server and uses its services by calling the methods of its interfaces. A COM server is any object that provides services to clients; these services are in the form of COM interface implementations that can be called by any client that is able to get a pointer to one of the interfaces on the server object.

There are two main types of servers, in-process and out-of-process. In-process servers are implemented in a dynamic linked library (DLL), and out-of-process servers are implemented in an executable file (EXE). Out-of-process servers can reside either on the local machine or on a remote machine. In addition, COM provides a mechanism that allows an in-process server (a DLL) to run in a surrogate EXE process to gain the advantage of being able to run the process on a remote machine. For more information, see DLL Surrogates.

The COM programming model and constructs have now been extended so that COM clients and servers can work together across the network, not just within a given machine. This enables existing applications to interact with new applications and with each other across networks with proper administration, and new applications can be written to take advantage of networking features.

COM client applications do not need to be aware of how server objects are packaged, whether they are packaged as in-process objects (in DLLs) or as local or remote objects (in EXEs). Distributed COM further allows objects to be packaged as Microsoft® Windows NT® or Microsoft Windows® 2000 Services, synchronizing COM with the rich administrative and system-integration capabilities of Windows NT and Windows 2000.

Note Throughout this documentation the acronym COM is used in preference to DCOM. This is because DCOM is not separate—it is just COM with a longer wire. In cases where what is being described is specifically a remote operation, the term distributed COM is used.

COM is designed to make it possible to add the support for location transparency that extends across a network. It allows applications written for single machines to run across a network and provides features that extend these capabilities and add the security necessary in a network. (For more information, see Security in COM.)

COM specifies a mechanism by which the class code can be used by many different applications.

See Also

Getting a Pointer to an Object

Creating an Object Through a Class Object

COM Server Responsibilities

Persistent Object State

Security in COM

Providing Class Information

Inter-Object Communication

Call Synchronization


Getting a Pointer to an Object

Because COM does not have a strict class model, there are four ways that a client can instantiate or get a pointer to an interface on an object:

  • Call a COM Library API function that creates an object of a predetermined type—that is, the function will return a pointer to only one specific interface for a specific object class.
  • Call a COM Library API function that can create an object based on a class identifier (CLSID) and that returns any type of interface pointer requested.
  • Call a method of some interface that creates another object (or connects to an existing one) and returns an interface pointer on that separate object.
  • Implement an object with an interface through which other objects pass their interface pointer to the client directly.

Note For information on getting pointers to other interfaces on an object after you have the first one, see QueryInterface: Navigating in an Object.

Creating an Object of a Predetermined Type

There are numerous COM functions, such as CoGetMalloc, that return pointers to specific interface implementations. (CoGetMalloc retrieves a pointer to the standard COM memory allocator.) Most of these are helper functions, and most of these functions are described in the reference sections of this documentation, under the specific area they are related to, such as storage or data transfer.

Creating an Object Based on a CLSID

There are several functions that, given a CLSID, a client can call to create an object instance and get a pointer to it. All of these functions are based on the function CoGetClassObject, which creates a class object and supplies a pointer to an interface that allows you to create instances of that class. While there must be information that says which system the server resides on, there is no need for that information to be contained in the client. The client needs to know only the CLSID and never the absolute path of the server code. For more information, see Creating an Object Through a Class Object.

Returning a Pointer to a Separate Object

Among the many interface methods that return a pointer to a separate object are several that create and return a pointer to an enumerator object, which allows you to determine how many items of a given type an object maintains. COM defines interfaces for enumerating a wide variety of items, such as strings, important structures, monikers, and IUnknown interface pointers. The typical way to create an enumerator instance and get a pointer to its interface is to call a method from another interface. For example, the IDataObject interface defines two methods, EnumDAdvise and EnumFormatEtc, that return pointers to interfaces on two different enumeration objects. There are many other examples in COM of methods that return pointers to objects, such as the OLE compound document interface IOleObject::GetClientSite, which, when called on the embedded or linked object, returns a pointer to the container object's implementation of IOleClientSite.

Implementing an Object Through Which to Pass an Interface Pointer Directly to the Client

When two objects, such as an OLE compound document container and server, need bidirectional communication, each implements an object containing an interface method through which it can pass an interface pointer to the other object. The implementing object, which is also the client of the created object, can then call the method and get the pointer that was passed.

See Also

COM Server Responsibilities


Creating an Object Through a Class Object

With the increasing importance of computer networks, it has become necessary for clients and servers to interact easily and efficiently, whether they reside on the same machine or across a network. Crucial to this is a client's ability to launch a server, create an instance of the server's object, and have access to the methods of the interfaces on the object.

COM provides extensions to this basic COM process that make it virtually seamless across a network. If a client is able to identify the server through its CLSID, calling a few simple functions permit COM to do all the work of locating and launching the server and activating the object. Subkeys in the registry allow remote servers to register their location, so the client does not require that information. For applications that want to take advantage of networking features, the COM object creation functions allow more flexibility and efficiency.

See Also

COM Class Objects and CLSIDs

Locating a Remote Object

Instance Creation Helper Functions


COM Class Objects and CLSIDs

A COM server is implemented as a COM class. A COM class is an implementation of a group of interfaces in code executed whenever you interact with a given object. There is an important distinction between a Microsoft® Visual C++® class and a COM class: In Visual C++, a class is a type, while a COM class is simply a definition of the object and carries no type, although a C++ programmer might implement it by using a C++ class. COM is designed to allow a class to be used by different applications, including applications written without knowledge of that particular class's existence. Therefore, class code for a given type of object exists either in a dynamic linked library (DLL) or in another executable application (EXE).

Each COM class is identified by a CLSID, a unique 128-bit GUID, which the server must register. COM uses this CLSID, at the request of a client, to associate specific data with the DLL or EXE containing the code that implements the class, thus creating an instance of the object.

For clients and servers on the same machine, the CLSID of the server is all the client ever needs. On each machine, COM maintains a database (it makes use of the system registry on Microsoft Windows® and Macintosh platforms) of all the CLSIDs for the servers installed on the system. This is a mapping between each CLSID and the location of the DLL or EXE that houses the code for that CLSID. COM consults this database whenever a client wants to create an instance of a COM class and use its services, so the client never needs to know the absolute location of the code on the machine.

For distributed systems, COM provides registry entries that allow a remote server to register itself for use by a client. While applications need know only a server's CLSID, because they can rely on the registry to locate the server, COM allows clients to override registry entries and to specify server locations, to take full advantage of the network. (See Locating a Remote Object.)

The basic way to create an instance of a class is through a COM class object. This is simply an intermediate object that supports functions common to creating new instances of a given class. Most class objects used to create objects from a CLSID support the IClassFactory interface, an interface that includes the important CreateInstance method. You implement an IClassFactory interface for each class of object that you offer to be instantiated. (For more information on implementing IClassFactory, see Implementing IClassFactory.)

Note Servers that support some other custom class factory interface are not required to support IClassFactory specifically. However, calls to activation functions other than CoGetClassObject (such as CoCreateInstanceEx) require that the server support IClassFactory.

When a client wants to create an instance of the server's object, it uses the desired object's CLSID in a call to CoGetClassObject. (This call can be either direct or implicit, through one of the object creation helper functions.) This function locates the code associated with the CLSID, and creates a class object, and supplies a pointer to the interface requested. (CoGetClassObject takes a riid param that specifies the client's desired interface pointer.)

Note COM has just a few API functions on which many of the others are built. The most important of these is probably CoGetClassObject, which underlies all of the instance creation functions.

With this pointer, the caller can create an instance of the object and retrieve a pointer to a requested interface on the object. This is usually an initialization interface, used to activate the object (put it in the running state) so that the client can do whatever work with the object that it wants to. Using COM's basic API functions, the client must also take care to release all object pointers.

Another mechanism for activating object instances is through the class moniker. Class monikers bind to the class object of the class for which they are created. For more information, see Class Monikers.

COM provides several helper functions that reduce the work of creating object instances. These are described in Instance Creation Helper Functions.


Locating a Remote Object

With the advent of COM for distributed systems, COM uses the basic model for object creation described in COM Class Objects and CLSIDs and adds more than one way to locate an object that might reside on another system in a network, without overburdening the client application.

COM has added registry keys that permit a server to register the name of the machine on which it resides or the machine where an existing storage is located. Therefore, client applications need know only the CLSID of the server.

However, for cases where it is desired, COM has replaced a previously reserved parameter of CoGetClassObject with a COSERVERINFO structure, which allows a client to specify the location of a server. Another important value in the CoGetClassObject function is the CLSCTX enumeration, which specifies whether the expected object is to be run in-process, out-of-process local, or out-of-process remote. Taken together, these two values and the values in the registry determine how and where the object is to be run.

Note Instance creation calls, when they specify a server location, can override a registry setting. The algorithm COM uses for doing this is described in the reference for the CLSCTX enumeration.

Remote activation depends on the security relationship between client and server. For more information, see Security in COM.


Instance Creation Helper Functions

In previous releases of COM, the primary mechanism used to create an object instance was the CoCreateInstance function. This function encapsulates the process of creating a class object, using that to create a new instance and releasing the class object. Another function of this kind is the more specific OleCreate, the OLE compound document helper that creates a class object and retrieves a pointer to a requested object.

To smooth the process of instance creation on distributed systems, COM has introduced four important new instance creation mechanisms:

  • Class Monikers and IClassActivator
  • CoCreateInstanceEx
  • CoGetInstanceFromFile
  • CoGetInstanceFromIStorage

A class moniker permits you to identify the class of an object and is typically used with another moniker, like a file moniker, to indicate the location of the object. This permits you to bind to an object and specify the server that is to be launched for that object. Class monikers may also be composed to the right of monikers supporting binding to the IClassActivator interface. For more information, see Class Monikers.

CoCreateInstanceEx extends CoCreateInstance to make it possible to create a single uninitialized object associated with the given CLSID on a specified remote machine. In addition, rather than requesting a single interface and obtaining a single pointer to that interface, CoCreateInstanceEx makes it possible to query for multiple interfaces and (if available) receive pointers to them in a single round-trip, thus permitting fewer round-trips between machines. This can make remote object interaction much more efficient. To do this, the function uses an array of MULTI_QI structures.

Creating an object through CoCreateInstanceEx still requires that the object be initialized through a call to one of the initialization interfaces (such as IPersistStorage::Load). The helper functions CoGetInstanceFromFile and CoGetInstanceFromIStorage encapsulate both the instance creation power of CoCreateInstanceEx and the initialization, the former from a file and the latter from a storage.


COM Server Responsibilities

One of the most important ways for a client to get a pointer to an object is for the client to ask that a server be launched and that an instance of the object provided by the server be created and activated. It is the responsibility of the server to ensure that this happens properly. There are several important parts to this.

The server must implement code for a class object through an implementation of either the IClassFactory or IClassFactory2 interface.

The server must register its CLSID in the system registry on the machine on which it resides and further, has the option of publishing its machine location to other systems on a network to allow clients to call it without requiring the client to know the server's location.

The server is primarily responsible for security—that is, for the most part, the server determines whether it will provide a pointer to one of its objects to a client.

In-process servers should implement and export certain functions that allow the client process to instantiate them.

This section contains the following topics, which detail the responsibilities of the COM server:

  • Implementing IClassFactory
  • Licensing and IClassFactory2
  • Registering COM Servers
  • Out-of-Process Server Implementation Helpers
  • GUID Creation and Optimizations


Implementing IClassFactory

When a client uses a CLSID to request the creation of an object instance, the first step is creation of a class object, an intermediate object that contains an implementation of the methods of the IClassFactory interface. While COM provides several instance creation functions, the first step in the implementation of these functions is the creation of a class object.

As a result, all servers must implement the methods of the IClassFactory interface, which contains two methods:

  • CreateInstance This method must create an uninitialized instance of the object and return a pointer to a requested interface on the object.
  • LockServer This method just increments the reference count on the class object to ensure that the server stays in memory and does not shut down before the client is ready for it to do so.

To enable a server to be responsible for its own licensing, COM defines IClassFactory2, which inherits its definition from IClassFactory. Thus, a server implementing IClassFactory2 must, by definition, implement the methods of IClassFactory.

COM also provides helper functions for implementing out-of-process servers. For more information, see Out-of-Process Server Implementation Helpers.

See Also

Licensing and IClassFactory2


Licensing and IClassFactory2

The IClassFactory interface on a class object provides the basic object creation mechanism of COM. Using IClassFactory, a server can control object creation on a machine basis. The implementation of the IClassFactory::CreateInstance method can allow or disallow object creation based on the existence of a machine license. A machine license is a piece of information separate from the application that exists on a machine to indicate that the software was installed from a valid source, such as the vendor's installation disks. If the machine license does not exist, the server can disallow object creation. Machine licensing prevents piracy in cases where a user attempts to copy the software from one machine to another, because the license information is not copied with the software and the machine that receives the copy is not licensed.

However, in a component software industry, vendors need a finer level of control over licensing. In addition to machine license control, a vendor needs to allow some clients to create a component object while denying other clients the same capability. This requires that the client application obtain a license key from the component while the client application is still under development. The client application uses the license key at run time to create objects on an unlicensed machine.

For example, if a vendor provides a library of controls to developers, the developer who purchases the library will have a full machine license, allowing the objects to be created on the development machine. The developer can then build a client application on the licensed machine incorporating one or more of the controls. When the resulting client application is run on another machine, the controls used in the client application must be created on the other machine even if that machine does not possess a machine license for the controls from the original vendor.

The IClassFactory2 interface provides this level of control. To allow key-based licensing for any given component, you implement IClassFactory2 on the class factory object for that component. IClassFactory2 is derived from IClassFactory, so by implementing IClassFactory2, the class factory object fulfills the basic COM requirements. IClassFactory2 is defined as follows:

interface IClassFactory2 : IClassFactory
    {
    HRESULT GetLicInfo(LPLICINFO pLicInfo);
    HRESULT RequestLicKey(DWORD dwResrved, BSTR FAR* pbstrKey);
    HRESULT CreateInstanceLic(IUnknown *pUnkOuter,
        IUnknown *pUnkReserved, REFIID riid, BSTR bstrKey,
        void **ppvObject);
    };

To incorporate a licensed component into your client application, use the following methods in IClassFactory2:

  • The GetLicInfo method fills a LICINFO structure with information describing the licensing behavior of the class factory. For example, the class factory can provide license keys for run-time licensing if the fRunTimeKeyAvail member is TRUE.
  • The RequestLicKey method provides a license key for the component. A machine license must be available when the client calls this method.
  • The CreateInstanceLic method creates an instance of the licensed component if the license key parameter (BSTR bstrKey) is valid.

Note In its type information, a component uses the attribute licensed to mark the coclass that supports licensing through IClassFactory2.

First, you need a separate development tool that is also a client of the licensed component. The purpose of this tool is to obtain the run-time license key and save it in your client application. This tool runs only on a machine that possesses a machine license for the component. The tool calls the GetLicInfo and RequestLicKey methods to obtain the run-time license key and then saves the license key in your client application. For example, the development tool could create a header (.h) file containing the BSTR license key, and then you would include that .h file in your client application.

To instantiate the component within your client application, first try to instantiate the object directly with IClassFactory::CreateInstance. If CreateInstance succeeds, the second machine is licensed for the component and objects can be created at will. If CreateInstance fails with the return code CLASS_E_NOTLICENSED, the only way to create the object is to pass the run-time key to the IClassFactory2::CreateInstanceLic method. CreateInstanceLic verifies the key and creates the object if the key is valid.

In this way, an application built with components (such as controls) can run on a machine that has no other license—only the client application containing the run-time license is allowed to create the component objects in question.

The IClassFactory2 interface supports flexibility in licensing schemes. For example, the server implementor can encrypt license keys in the component for added security. Server implementers can also enable or disable levels of functionality in their objects by providing different license keys for different functions. For example, one key might allow a base level of functionality while another allows basic and advanced functionality, and so on. See OLE Controls Inside Out (Microsoft® Press, 1996), by Adam Denning, for detailed consideration of these issues.


Registering COM Servers

After you have defined a class in code (ensuring that it implements IClassFactory or IClassFactory2) and assigned it a CLSID, you need to put information in the registry that will allow COM, on request of a client with the CLSID, to create instances of its objects. This information tells the system, for a given CLSID, where the DLL or EXE code for that class is located and how it is to be launched.

There is more than one way of registering a class in the registry. In addition, there are other ways of "registering" a class with the system when it is running, so that the system is aware that a running object is currently in the system. See the following topics in this section for more information about registering:

  • Registering a Class at Installation
  • Registering a Running EXE Server
  • Registering Objects in the ROT
  • Self-registration
  • Installing as a Win32 Service or User Account


Registering a Class at Installation

If a class is intended to be available to clients at any time, as most applications are, you usually register it through an installation and setup program. This means putting information about the application into the registry, including how and where its objects are to be instantiated. This information must be registered for all CLSIDs. Other information is optional. Win32® tools, such as Regsvr32, make it simple to write a setup program that registers servers at installation.

If you are not relying on system defaults, there are two important keys in the registry: the CLSID and the AppID. Among the important pieces of information under these keys is how the object is to be instantiated. Objects can be designated as in-process, out-of-process local, or out-of-process remote.

Under the new AppID key are several named-values that define information specific to that application. Among these are RemoteServerName and ActivateAtStorage, both of which can be used to permit a client to create an object, with the client having no built-in knowledge of the location of the server. (For more information on remote instantiation, see Locating a Remote Object and Instance Creation Helper Functions.)

A server can also be installed as a Win32 service, or to run under a specific user account. For more information, see Installing as a Win32 Service or User Account.

A server or ROT object that is not a Win32 service or running under a specific user account can be referred to as an "activate as activator" server. For these servers, the security context and the window station/desktop of the client must match the server's. A client attempting to connect to a remote server is considered to have a NULL window station/desktop, so only the server security context is compared in this instance. (For information on Microsoft® Windows NT® or Windows® 2000 SID, see security.) COM caches the window station/desktop of a process when the process first connects to the distributed COM service. Therefore, COM clients and servers should not change their window station or thread desktops of the process after calling CoInitialize or CoInitializeEx.

When a class is registered as in-process, a call to CoGetClassObject to create its class object is automatically passed by COM to the DllGetClassObject function, which the class must implement to give the calling object a pointer to its class object.

Classes implemented in executables can specify that COM should execute their process and wait for the process to register their class object's IClassFactory interface through a call to the CoRegisterClassObject function.

See Also

Registering a Running EXE Server

Registering Objects in the ROT

Self-Registration

Installing as a Win32 Service or User Account

Registering Components

COM Registry Keys


Registering a Running EXE Server

When an executable (EXE) server is launched, it should call CoRegisterClassObject, which registers the CLSID for the server in what is called the class table (a different table than the running object table). When a server is registered in the class table, it allows the Service Control Manager (SCM) to determine that it is not necessary to launch the class again, because the server is already running. Only if the server is not listed in the class table will the SCM check the registry for appropriate values and launch the server associated with the given CLSID.

You pass CoRegisterClassObject the CLSID for the class and a pointer to its IUnknown interface. Clients who subsequently call CoGetClassObject with this CLSID will retrieve a pointer to their requested interface, as long as security does not forbid it. (See Instance Creation Helper Functions for a description of several instance creation and activation functions.)

The server for a class object should call CoRevokeClassObject to revoke the class object (remove its registration) when all of the following are true:

  • There are no existing instances of the object definition.
  • There are no locks on the class object.
  • The application providing services to the class object is not under user control (not visible to the user on the display).

See Also

Registering a Class at Installation

Registering Objects in the ROT

Self-Registration

Installing as a Win32 Service or User Account


Registering Objects in the ROT

Typically, when a client asks a server to create an object instance, the server typically creates a moniker for the object and registers it in the running object table (ROT) through a call to IRunningObjectTable::Register.

When the server calls CreateFileMoniker to create a file moniker to be registered in the ROT, servers should pass local file names that are drive-based, not in UNC format. This ensures that the moniker comparison data that is generated by the ROT register call will match what is used while doing a ROT lookup on the part of a remote client. This is because when the distribed COM service receives an activation request for a file local to the server from a remote client, the file is converted to a local-drive-based path.

See Also

Registering a Class at Installation

Registering a Running EXE Server

Self-Registration

Installing as a Win32 Service or User Account


Self-Registration

As component software continues to grow as a market, there will be more and more instances where a user obtains a new software component as a single DLL or EXE module, such as when downloading a new component from an on-line service or receiving one from a friend on a floppy disk. In these cases, it is not practical to require the user to go through a lengthy installation procedure or setup program. Besides the licensing issues, which are handled through IClassFactory2, an installation procedure typically creates the necessary registry entries for a component to run properly in the COM and OLE context.

Self-registration is the standard means through which a server module can package its own registry operations, both registration and unregistration, into the module itself. When used with licensing handled through IClassFactory2, a server can become an entirely self-contained module with no need for external installation programs or .reg files.

Any self-registering module, DLL or EXE, should first include a string called OleSelfRegister in the StringFileInfo section of its version information resource:

VS_VERSION_INFO VERSIONINFO

 ...

 BEGIN
   BLOCK "StringFileInfo"
    BEGIN
    #ifdef UNICODE
     BLOCK "040904B0" // Lang=US English, CharSet=Unicode
    #else
     BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
    #endif
      BEGIN
       ...
       VALUE "OLESelfRegister", "\0"
      END

   ...

   END

 ...

 END

The existence of this data allows any interested party, such as an application that wishes to integrate this new component, to determine whether the server supports self-registration without having to load the DLL or EXE first.

If the server is packaged in a DLL module, the DLL must export the functions DllRegisterServer and DllUnregisterServer. Any application that wishes to instruct the server to register itself (that is, all its CLSIDs and type library IDs) can obtain a pointer to DllRegisterServer through the Win32® API function GetProcAddress. Within DllRegisterServer, the DLL creates all its necessary registry entries, storing the correct path to the DLL for all InprocServer32 or InprocHandler32 entries.

When an application wishes to remove the component from the system, it should unregister that component by calling DllUnregisterServer. Within this call, the server removes exactly those entries it previously created in DllRegisterServer. The server should not blindly remove all entries for its classes because other software may have stored additional entries, such as a TreatAs key.

If the server is packaged in an EXE module, the application wishing to register the server launches the EXE server with the command-line argument /RegServer or -RegServer (case-insensitive). If the application wishes to unregister the server, it launches the EXE with the command-line argument /UnregServer or -UnregServer. The self-registering EXE detects these command-line arguments and invokes the same operations as a DLL would within DllRegisterServer and DllUnregisterServer, respectively, registering its module path under LocalServer32 instead of InprocServer32 or InprocHandler32.

The server must register the full path to the installation location of the DLL or EXE module for their respective InprocServer32, InprocHandler32, and LocalServer32 keys in the registry. The module path is easily obtained through the Win32 API function GetModuleFileName.

See Also

Registering a Class at Installation

Registering a Running EXE Server

Registering Objects in the ROT

Installing as a Win32 Service or User Account


Installing as a Win32 Service or User Account

In addition to running as a local server executable (EXE), a COM object may also package itself to run as a Win32® service when activated by a local or remote client. Win32 services support numerous useful and user interface–integrated administrative features, including local and remote starting, stopping, pausing, and restarting, as well as the ability to establish the server to run under a specific user account and Window Station and optionally to be interactive with the desktop.

An object written as a Win32 service is installed for use by COM by establishing a LocalService named-value under its AppID key and performing a standard service installation. (See the Win32 documentation and the SECSVR distributed COM sample application for more information about installation.)

Classes may also be configured to run under a specific user account when activated by a remote client without being written as a Win32 service. To do this, the class installs a user name and a password to be used when the SCM launches its local server process.

When a class is configured in this fashion, calls to CoRegisterClassObject with this CLSID will fail unless the process was launched by COM on behalf of an actual activation request. In other words, classes configured to run as a particular user may not be registered under any other identity.

The user name is taken from the RunAs named-value under the class's APPID key. If the user name is "Interactive User", the class code is run in the security context of the currently logged on user and is connected to the interactive window station.

Otherwise, the password is retrieved from a secret and safe portion of the registry available only to administrators of the machine and to the system. The user name and password are then used to create a logon session in which the class code is run. When launched in this way, the class code runs with its own desktop and window-station and does not share window handles, the clipboard, or other user interface elements with the interactive user or other classes running in other user accounts.

A server registered either with LocalService or RunAs can register an object in the running object table to allow any client to connect to it. To do so, the server's call to IRunningObjectTable::Register must set the ROTFLAGS_ALLOWANYCLIENT flag. A server setting this bit must have its executable name in the AppID section of the registry that refers to the AppID for the executable. An "activate as activator" server (not registered as either LocalService or RunAs) may not register an object with this flag.

See Also

Registering a Class at Installation

Registering a Running EXE Server

Registering Objects in the ROT

Self-Registration


Out-of-Process Server Implementation Helpers

Four helper functions that can be called by out-of-process servers are available to simplify the job of writing server code. COM clients and COM in-process servers typically would not call them. These functions are designed to help prevent race conditions in server activation when the servers have multiple apartments or multiple class objects. They can also, however, as easily be used for single-threaded and single class object servers. The functions are as follows:

  • CoAddRefServerProcess
  • CoReleaseServerProcess
  • CoSuspendClassObjects
  • CoResumeClassObjects

To shut down properly, a COM server must keep track of how many object instances it has instantiated and how many times its IClassFactory::LockServer method has been called. Only when both of these counts reach zero can a server shut down. In single-threaded COM servers, the decision to shut down was coordinated with incoming activation requests, which were serialized by the message queue. The server, upon receiving a Release on its final object instance and deciding to shut down, would revoke its class objects before any more activation requests were dispatched. If an activation request did come in after this point, COM would recognize that the class objects were revoked and would return an error to the Service Control Manager (SCM), which would then cause a new instance of the local server process to be run.

However, in an apartment model server, in which different class objects are registered on different apartments, and in all free-threaded servers, this decision to shut down must be coordinated with activation requests across multiple threads so that one thread of the server does not decide to shut down while another thread of the server is busy handing out class objects or object instances. One classical but cumbersome approach to solving this is to have the server, after it has revoked its class objects, recheck its instance count and stay alive until all instances have been released.

To make it easier for server writers to handle these types of race conditions, COM provides two reference counting functions:

  • CoAddRefServerProcess increments a global per-process reference count.
  • CoReleaseServerProcess decrements the global per-process reference count.

When the global per-process reference count reaches zero, COM automatically calls CoSuspendClassObjects, which prevents any new activation requests from coming in. The server can then deregister its various class objects from its various threads at leisure without worry that another activation request may come in. All new activation requests are henceforth handled by the SCM launching a new instance of the local server process.

The simplest way for a local server application to make use of these APIs is to call CoAddRefServerProcess in the constructor for each of its instance objects and in each of its IClassFactory::LockServer methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess in the destructor of each of its instance objects and in each of its IClassFactory::LockServer methods when the fLock parameter is FALSE.

Finally, the server application should pay attention to the return code from CoReleaseServerProcess, and if it returns 0, the server application should initiate its cleanup, which, for a server with multiple threads, typically means that it should signal its various threads to exit their message loops and call CoRevokeClassObject and CoUninitialize. If these last two functions are used at all, they must be used in both the object instances and the LockServer method; otherwise, the server application may be shut down prematurely.

In the latest versions of Microsoft® Windows NT® and Microsoft Windows® 2000, when a CoGetClassObject request is made, COM contacts the server, marshals the IClassFactory interface of the class object, returns to the client process, unmarshals the IClassFactory interface, and returns this to the client. At this point, clients typically call IClassFactory::LockServer(TRUE) to prevent the server process from shutting down. However, there is a window of time between when the class object is marshaled and when the client calls LockServer in which another client could connect to the same server, get an instance, and Release that instance, thus causing the server to shut down and leaving the first client high and dry with a disconnected IClassFactory pointer. To prevent this race condition, COM adds an implicit IClassFactory::LockServer(TRUE) to the class object when it marshals the IClassFactory interface and an implicit IClassFactory::LockServer(FALSE) when the client releases the IClassFactory interface. Therefore, it is not necessary to remote LockServer calls back to the server, and the proxy for IClassFactory::LockServer simply returns S_OK without actually remoting the call.

There is another activation-related race condition during initialization of an out-of-process server process. A COM server that registers multiple classes typically calls CoRegisterClassObject (....REGCLS_LOCAL_SERVER) for each CLSID it supports. After it has done this for all classes, the server enters its message loop. For a single-threaded COM server, all activation requests are blocked until the server enters the message loop. However, for an apartment model server that registers different class objects in different apartments and for all free-threaded servers, activation requests can arrive earlier than this. In the case of apartment model servers, activation requests could arrive as soon as any one thread has entered its message loop. In the case of free-threaded servers, an activation request could arrive as soon as the first class object is registered. Since an activation can happen this early, it is also possible for the final Release to occur (and therefore cause the server to begin shutting down) before the rest of the server has had a chance to finish initializing.

To eliminate these race conditions and simplify the job of the server writer, any server that wants to register multiple class objects with COM should call CoRegisterClassObject(...., REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED) for each different CLSID the server supports. After all classes have been registered and the server process is ready to accept incoming activation requests, the server should make one call to CoResumeClassObjects. This API tells COM to inform the SCM about all the registered classes, and it begins letting activation requests into the server process. Using these APIs provides the following advantages:

  • Only one call is made to the SCM, regardless of how many CLSIDs are registered, thus reducing the overall registration time (and hence startup time of the server application).
  • If the server has multiple apartments and different CLSIDs are registered in different apartments, or if the server is a free-threaded server, no activation requests will come in until the server calls CoResumeClassObjects, giving the server a chance to register all of its CLSIDs and get properly set up before having to deal with activation requests and possible shut down requests.


GUID Creation and Optimizations

Because a CLSID, like an interface identifier (IID), is a GUID, no other class, no matter who writes it, has a duplicate CLSID. Server implementers generally obtain CLSIDs through the CoCreateGUID function in COM. This function is guaranteed to produce unique CLSIDs, so server implementors across the world can independently develop and deploy their software without fear of accidental collision with software written by others.

Using unique CLSIDs avoids the possibility of name collisions among classes because CLSIDs are in no way connected to the names used in the underlying implementation. For example, two different vendors can write classes called "StackClass," but each would have a unique CLSID and therefore could not be confused.

COM frequently must map GUIDs (IIDs and CLSIDs) to some arbitrarily large set of other values. As an application developer, you can help speed up such searches, and thereby enhance system performance, by generating the GUIDs for your application as a block of consecutive values.

The most efficient way to generate a block of consecutive GUIDs is to run the uuidgen utility using the /n switch, which generates a block of UUIDs, each of whose first DWORD value is incremented by one. (For more information on using the uuidgen utility, see Generating Interface UUIDs.)

For example, if you were to type

uuidgen -n5 -s >guids.txt

the uuidgen utility would generate a block of UUIDs similar to the following:

{12340001-4980-1920-6788-123456789012}
{12340002-4980-1920-6788-123456789012}
{12340003-4980-1920-6788-123456789012}
{12340004-4980-1920-6788-123456789012}
{12340005-4980-1920-6788-123456789012}

One method for generating and tracking GUIDs for an entire project begins with generating a block of some arbitrarily large number of UUIDs—say, 500. For example, if you were to type

uuidgen -n500 -s >guids.txt

the utility would generate 500 consecutive UUIDs and write them to the specified text file. You could then check this file into your source tree, providing a single repository for all GUIDs to be used in a project. As people require GUIDs for their portions of the project, they can check out the file, take however many GUIDs they need, marking them as taken and leaving a note about where in the code or "spec" they are using them.

In addition to improving system performance, generating blocks of consecutive GUIDs in this way has the following benefits:

  • A central file containing all GUIDs for an application makes it easy to keep track of which GUIDs are for what and which people are using them.
  • A block of consecutive GUIDs associated with a particular application helps developers and testers recognize internal GUIDs during debugging and makes it easier to find them in the system registry because they are stored sequentially.


Persistent Object State

Some COM objects can save their internal state when asked to do so by a client. COM defines standards through which clients can request objects to be initialized, loaded, and saved to and from a data store (for example, flat file, structured storage, or memory). It is the client's responsibility to manage the location where the object's persistent data is stored but not the format of the data. COM objects that adhere to these standards are called persistent objects.

See Also

Persistent Object Interfaces

Initializing Persistent Objects


Persistent Object Interfaces

A persistent object implements one or more persistent object interfaces. Clients use persistent object interfaces to tell those objects when and where to store their state. All persistent object interfaces are derived from IPersist, so any object that implements any persistent object interface also implements IPersist.

The following persistent object interfaces are currently defined:

  • IPersistStream
  • IPersistStreamInit
  • IPersistStorage
  • IPersistFile
  • IPersistMoniker
  • IPersistMemory
  • IPersistPropertyBag

Implementers choose which persistent object interfaces an object supports depending on how the object is to be used. By not supporting any persistent object interfaces, the implementer is effectively saying, "This object's state cannot be persistently stored." By supporting one or more persistent object interfaces, the implementer is effectively saying, "This object's state can be persistently stored in one or more data store mediums."

For example, the following table lists several object types that allow support for different persistent object interfaces.

Category | Persistent Object Interfaces Typically Supported
Monikers | IPersistStream
OLE embeddable objects | IPersistStorage, IPersistFile
ActiveX™ controls | IPersistStreamInit, IPersistStorage, IPersistMemory, IPersistPropertyBag, IPersistMoniker
ActiveX document objects | IPersistStorage, IPersistFile

Client implementers can also choose which persistent object interfaces the client can use. The interfaces a client uses is usually determined by where the client can store its own data. A client that can store its data only in a flat file will probably use only IPersistStreamInit, IPersistMoniker, and IPersistPropertyBag. (IPersistStreamInit can replace IPersistStream in most applications, because it contains that definition and adds an initialization method.) A client that can save its data to a structured storage file will, in addition, use IPersistStorage.

See Also

Initializing Persistent Objects


Initializing Persistent Objects

Several of the persistent object interfaces (IPersistStreamInit, IPersistStorage, IPersistMemory, and IPersistPropertyBag) allow clients to initialize objects to a "fresh" or "default" state. This initial state is different from that of a newly created object, which has no state. Initializing an object's state, even to the default state, may be a compute-intensive or resource-intensive operation. By separating creation from initialization, the initialization can be performed only when it is actually needed and clients can avoid initializing objects to the default state only to immediately load previously stored data.

See Also

Persistent Object Interfaces


Providing Class Information

It is often useful for a client of an object to examine the object's type information. Given the object's CLSID, a client can locate the object's type library using registry entries and then can scan the type library for the coclass entry in the library matching the CLSID.

However, not all objects have a CLSID, although they still need to provide type information. In addition, it is convenient for a client to have a way to simply ask an object for its type information instead of going through all the tedium to extract the same information from registry entries. This capability is important when dealing with outgoing interfaces on connectable objects. (See Using IProvideClassInfo for more information on how connectable objects provide this capability.)

In these cases, a client can query the object for any of the IProvideClassInfo[x] interfaces. If these interfaces exist, the client calls IProvideClassInfo[x]::GetClassInfo to get the type information for the interface.

By implementing IProvideClassInfo[x], an object specifies that it can provide type information for its entire class—that is, what it would describe in its coclass section of its type library, if it has one. The IprovideClassInfo::GetClassInfo method returns an ITypeInfo pointer corresponding to the object's coclass information. Through this ITypeInfo pointer, the client can examine all the object's incoming and outgoing interface definitions.

The object can also provide IProvideClassInfo2. The IProvideClassInfo2 interface is a simple extension to IProvideClassInfo that makes it quick and easy to retrieve an object's outgoing interface identifiers for its default event set. IProvideClassInfo2 is derived from IProvideClassInf.


Inter-Object Communication

COM is designed to allow clients to communicate transparently with objects, regardless of where those objects are running—in the same process, on the same machine, or on a different machine. This provides a single programming model for all types of objects, and for both object clients and object servers.

From a client's point of view, all objects are accessed through interface pointers. A pointer must be in-process. In fact, any call to an interface function always reaches some piece of in-process code first. If the object is in-process, the call reaches it directly, with no intervening system-infrastructure code. If the object is out-of-process, the call first reaches what is called a "proxy" object provided either by COM or by the object (if the implementor wishes). The proxy packages call parameters (including any interface pointers) and generate the appropriate remote procedure call (or other communication mechanism in the case of custom generated proxies) to the other process or the other machine where the object implementation is located. This process of packaging pointers for transmission across process boundaries is called marshaling.

From a server's point of view, all calls to an object's interface functions are made through a pointer to that interface. Again, a pointer has context only in a single process, and the caller must always be some piece of in-process code. If the object is in-process, the caller is the client itself. Otherwise, the caller is a "stub" object provided either by COM or by the object itself. The stub receives the remote procedure call (or other communication mechanism in the case of custom generated proxies) from the "proxy" in the client process, unmarshals the parameters, and calls the appropriate interface on the server object. From the points of view of both clients and servers, they always communicate directly with some other in-process code.

COM provides an implementation of marshaling, referred to as standard marshaling. This implementation works very well for most objects and greatly reduces programming requirements, making the marshaling process effectively transparent.

The clear separation of interface from implementation of COM's process transparency can, however, get in the way in some situations. The design of an interface that focuses on its function from the client's point of view can sometimes lead to design decisions that conflict with efficient implementation of that interface across a network. In cases like this, what is needed is not pure process transparency but "process transparency, unless you need to care." COM provides this capability by allowing an object implementor to support custom marshaling (also called IMarshal marshaling). Standard marshaling is, in fact, an instance of custom marshaling—it is the default implementation used when an object does not require custom marshaling.

You can implement custom marshaling to allow an object to take different actions when used from across a network than it takes under local access—and it is completely transparent to the client. This architecture makes it possible to design client/object interfaces without regard to network performance issues and then later to address network performance issues without disrupting the established design.

COM does not specify how components are structured; it specifies how they interact. COM leaves the concern about the internal structure of a component to programming languages and development environments. Conversely, programming environments have no set standards for working with objects outside of the immediate application. Microsoft® Visual C++®, for example, works extremely well for manipulating objects inside an application but has no support for working with objects outside the application. Generally, all other programming languages are the same in this regard. Therefore, to provide networkwide interoperability, COM, through language-independent interfaces, picks up where programming languages leave off.

The double indirection of the vtbl structure means that the pointers in the table of function pointers do not need to point directly to the real implementation in the real object. This is the heart of process transparency.

For in-process servers, where the object is loaded directly into the client process, the function pointers in the table point directly to the actual implementation. In this case, a function call from the client to an interface method directly transfers execution control to the method. However, this cannot work for local, let alone remote, objects because pointers to memory cannot be shared between processes. Nevertheless, the client must be able to call interface methods as if it were calling the actual implementation. Thus, the client uniformly transfers control to a method in some object by making the call.

A client always calls interface methods in some in-process object. If the actual object is local or remote, the call is made to a proxy object, which then makes a remote procedure call to the actual object.

So what method is actually executed? The answer is that whenever there is a call to an out-of-process interface, each interface method is implemented by a proxy object. The proxy object is always an in-process object that acts on behalf of the object being called. This proxy object knows that the actual object is running in a local or remote server.

The proxy object packages up the function parameters in some data packets and generates an RPC call to the local or remote object. That packet is picked up by a stub object in the server's process on the local or a remote machine, which unpacks the parameters and makes the call to the real implementation of the method. When that function returns, the stub packages up any out-parameters and the return value and sends it back to the proxy, which unpacks them and returns them to the original client.

Thus, client and server always talk to each other as if everything was in-process. All calls from the client and all calls to the server are, at some point, in-process. But because the vtbl structure allows some agent, like COM, to intercept all function calls and all returns from functions, that agent can redirect those calls to an RPC call as necessary. Although in-process calls are faster than out-of-process calls, the process differences are completely transparent to the client and server.

See Also

Marshaling Details

Proxy

Stub

Channel

Microsoft RPC

Interface Marshaling


Marshaling Details

If you use standard marshaling, COM handles all of the details described in this section for you. This section is provided for those few programmers who need these details and for those interested in the underlying information. Marshaling is the process of packaging and unpackaging parameters so a remote procedure call can take place.

Different parameter types are marshaled in different ways. For example, marshaling an integer parameter involves simply copying the value into the message buffer. (Although even in this simple case, there are issues such as byte ordering to deal with in cross-machine calls.) Marshaling an array, however, is a more complex process. Array members are copied in a specific order so that the other side can reconstruct the array exactly. When a pointer is marshaled, the data that the pointer is pointing to is copied following rules and conventions for dealing with nested pointers in structures. Unique functions exist to handle the marshaling of each parameter type.

With standard marshaling, the proxies and stubs are systemwide resources for the interface and they interact with the channel through a standard protocol. Standard marshaling can be used both by standard COM-defined interfaces and by custom interfaces, as follows:

  • In the case of most COM interfaces, the proxies and stubs for standard marshaling are in-process component objects which are loaded from a systemwide DLL provided by COM in ole32.dll.
  • In the case of custom interfaces, the proxies and stubs for standard marshaling are generated by the interface designer, typically with MIDL. These proxies and stubs are statically configured in the registry, so any potential client can use the custom interface across process boundaries. These proxies and stubs are loaded from a DLL that is located via the system registry, using the interface ID (IID) for the custom interface they marshal.
  • An alternative to using MIDL to generate proxies and stubs for custom interfaces, a type library can be generated instead and the system provided, type-library–driven marshaling engine will marshal the interface.

As an alternative to standard marshaling, an interface (standard or custom) can use custom marshaling. With custom marshaling, an object dynamically implements the proxies at run time for each interface that it supports. For any given interface, the object can select COM-provided standard marshaling or custom marshaling. This choice is made by the object on an interface-by-interface basis. Once the choice is made for a given interface, it remains in effect during the object's lifetime. However, one interface on an object can use custom marshaling while another uses standard marshaling.

Custom marshaling is inherently unique to the object that implements it. It uses proxies implemented by the object and provided to the system on request at run time. Objects that implement custom marshaling must implement the IMarshal interface, whereas objects that support standard marshaling do not.

If you decide to write a custom interface, you must provide marshaling support for it. Typically, you will provide a standard marshaling DLL for the interface you design. You can use the tools contained in the Platform SDK CD to create the proxy/stub code and the proxy/stub DLL. Alternatively, you can use these tools to create a type library which COM will use to do data-driven marshaling (using the data in the type library).

For a client to make a call to an interface method in an object in another process involves the cooperation of several components. The standard proxy is a piece of interface-specific code that resides in the client's process space and prepares the interface parameters for transmittal. It packages, or marshals, them in such a way that they can be re-created and understood in the receiving process. The standard stub, also a piece of interface-specific code, resides in the server's process space and reverses the work of the proxy. The stub unpackages, or unmarshals, the sent parameters and forwards them to the object application. It also packages reply information to send back to the client.

Note Readers more familiar with RPC than COM may be used to seeing the terms client stub and server stub. These terms are analogous to proxy and stub.

The following diagram shows the flow of communication between the components involved. On the client side of the process boundary, the client's method call goes through the proxy and then onto the channel, which is part of the COM library. The channel sends the buffer containing the marshaled parameters to the RPC run-time library, which transmits it across the process boundary. The RPC run time and the COM libraries exist on both sides of the process. The distinction between the channel and the RPC run time is a characteristic of this implementation and is not part of the programming model or the conceptual model for COM client/server objects. COM servers see only the proxy or stub and, indirectly, the channel. Future implementations may use different layers below the channel or no layers.

Components of interprocess communications

See Also

Inter-Object Communication

Proxy

Stub

Channel

Microsoft RPC


Proxy

A proxy resides in the address space of the calling process and acts as a surrogate for the remote object. From the perspective of the calling object, the proxy is the object. Typically, the proxy's role is to package the interface parameters for calls to methods in its object interfaces. The proxy packages the parameters into a message buffer and passes the buffer onto the channel, which handles the transport between processes. The proxy is implemented as an aggregate, or composite, object. It contains a system-provided, manager piece called the proxy manager and one or more interface-specific components called interface proxies. The number of interface proxies equals the number of object interfaces that have been exposed to that particular client. To the client complying with the component object model, the proxy appears to be the real object.

Note With custom marshaling, the proxy can be implemented similarly or it can communicate directly with the object without using a stub.

Each interface proxy is a component object that implements the marshaling code for one of the object's interfaces. The proxy represents the object for which it provides marshaling code. Each proxy also implements the IRpcProxyBuffer interface. Although the object interface represented by the proxy is public, the IRpcProxyBuffer implementation is private and is used internally within the proxy. The proxy manager keeps track of the interface proxies and also contains the public implementation of the controlling IUnknown interface for the aggregate. Each interface proxy can exist in a separate DLL that is loaded when the interface it supports is materialized to the client.

The following diagram shows the structure of a proxy that supports the standard marshaling of parameters belonging to two interfaces: IA1 and IA2. Each interface proxy implements IRpcProxyBuffer for internal communication between the aggregate pieces. When the proxy is ready to pass its marshaled parameters across the process boundary, it calls methods in the IRpcChannelBuffer interface, which is implemented by the channel. The channel in turn forwards the call to the RPC run-time library so that it can reach its destination in the object.

Structure of the Proxy

See Also

Inter-Object Communication

Marshaling Details

Stub

Channel

Microsoft RPC


Stub

The stub, like the proxy, is made up of one or more interface pieces and a manager. Each interface stub provides code to unmarshal the parameters and code that calls one of the object's supported interfaces. Each stub also provides an interface for internal communication. The stub manager keeps track of the available interface stubs.

There are, however, the following differences between the stub and the proxy:

  • The most important difference is that the stub represents the client in the object's address space.
  • The stub is not implemented as an aggregate object because there is no requirement that the client be viewed as a single unit; each piece in the stub is a separate component.
  • The interface stubs are private rather than public.
  • The interface stubs implement IRpcStubBuffer, not IRpcProxyBuffer.
  • Instead of packaging parameters to be marshaled, the stub unpackages them after they have been marshaled and then packages the reply.

The following diagram shows the structure of the stub. Each interface stub is connected to an interface on the object. The channel dispatches incoming messages to the appropriate interface stub. All the components talk to the channel through IRpcChannelBuffer, the interface that provides access to the RPC run-time library.

Structure of the Stub

See Also

Inter-Object Communication

Marshaling Details

Proxy

Channel

Microsoft RPC


Channel

The channel has the responsibility of transmitting all messages between client and object across the process boundary. The channel has been designed to work transparently with different channel types, is compatible with OSF DCE standard RPC, and supports single and multithreaded applications.

See Also

Inter-Object Communication

Marshaling Details

Proxy

Stub

Microsoft RPC


Microsoft RPC

Microsoft RPC is a model for programming in a distributed computing environment. The goal of RPC is to provide transparent communication so that the client appears to be directly communicating with the server. Microsoft's implementation of RPC is compatible with the Open Software Foundation (OSF) Distributed Computing Environment (DCE) RPC.

You can configure RPC to use one or more transports, one or more name services, and one or more security servers. The interfaces to those providers are handled by RPC. Because Microsoft RPC is designed to work with multiple providers, you can choose the providers that work best for your network. The transport is responsible for transmitting the data across the network. The name service takes an object name, such as a moniker, and finds its location on the network. The security server offers applications the option of denying access to specific users and/or groups. See Interface Design Rules for more detailed information about application security.

In addition to the RPC run-time libraries, Microsoft RPC includes the Interface Definition Language (IDL) and its compiler. Although the IDL file is a standard part of RPC, Microsoft has enhanced it to extend its functionality to support custom COM interfaces. The Microsoft Interface Definition Language (MIDL) compiler uses the IDL file that describes your custom interface to generate several files discussed in the section Building and Registering a Proxy DLL.

See Also

Inter-Object Communication

Marshaling Details

Proxy

Stub

Channel


Making and Processing Asynchronous Calls

With the release of Microsoft® Windows® 2000, COM objects can support asynchronous calling. When a client makes an asynchronous call, control returns to the client immediately. While the server processes the call, the client is free to do other work. When the client can no longer proceed without the results of the call, it can get the results of the call at that time.

For example, a request for a large or complex recordset can be time-consuming. A client can request the recordset by an asynchronous call and then do other work. When the recordset is available, the client can obtain it quickly without blocking.

Clients do not make asynchronous calls directly on the server object. Instead, they obtain a call object that implements an asynchronous version of a synchronous interface on the server object. The asynchronous interface on the call object has a name of the form AsyncInterfaceName. For example, if a server object implements a synchronous interface named IMyInterface, there will be a call object that implements an asynchronous interface named AsyncIMyInterface.

Note Asynchronous support is not available for IDispatch or for interfaces that inherit IDispatch.

Server objects that support asynchronous calls implement the ICallFactory interface. This interface exposes a single method, CreateCall, which creates an instance of a specified call object. Clients can query for ICallFactory to determine whether an object supports asynchronous calling.

For each method on a synchronous interface, the corresponding asynchronous interface implements two methods. These methods attach the prefixes Begin_ and Finish_ to the name of the synchronous method. For example, if an interface named ISimpleStream has a Read method, the AsyncISimpleStream interface will have a Begin_Read and a Finish_Read method. To begin an asynchronous call, the client calls the Begin_ method.

When you implement a server object, you do not have to provide a call object for every interface the object implements. If the server object implements the ICallFactory interface and uses standard marshaling, a marshaled client can always obtain a proxy call object, even if there is no call object on the server side. This proxy will marshal the Begin_ method as a synchronous call, the server will process the call synchronously, and the client can obtain the out parameters by calling the Finish_ method.

Conversely, if a client makes a marshaled synchronous call on an interface for which there is a call object on the server side, the server will always process the call asynchronously. This behavior will not be apparent to the client, because the client will receive the same out parameters and the same return value it would have received from the synchronous method.

In either case, the interaction between client and server is marshaled as if the call were synchronous: The output of synchronous and asynchronous proxies is indistinguishable, as is the output of the corresponding stubs. This behavior greatly simplifies the programming model both of clients and of servers. If a server object implements ICallFactory, a marshaled client does not have to attempt to create a call object that may not be available—to the client, a call object is always available.

When client and server are in the same apartment, the server object will process whichever call the client makes. If a call object is not available, the client must explicitly obtain the synchronous interface and make a synchronous call.

See Also

Making an Asynchronous Call

Canceling an Asynchronous Call

Canceling Method Calls

Call Synchronization


Making an Asynchronous Call

The procedure for making a synchronous call is straightforward: The client obtains an interface pointer on the server object and calls methods through that pointer. Asynchronous calling involves a call object, so it involves a few more steps.

For each method on a synchronous interface, the corresponding asynchronous interface implements two methods. These methods attach the prefixes Begin_ and Finish_ to the name of the synchronous method. For example, if an interface named ISimpleStream has a Read method, the AsyncISimpleStream interface will have a Begin_Read and a Finish_Read method. To begin an asynchronous call, the client calls the Begin_ method.

To begin an asynchronous call

  1. Query the server object for the ICallFactory interface. If QueryInterface returns E_NOINTERFACE, the server object does not support asynchronous calling.
  2. Call ICallFactory::CreateCall to create a call object corresponding to the interface you want, and then release the pointer to ICallFactory.
  3. If you did not request a pointer to the asynchronous interface from the call to CreateCall, query the call object for the asynchronous interface.
  4. Call the appropriate Begin_ method.

The server object is now processing the asynchronous call, and the client is free to do other work until it needs the results of the call.

A call object can process only one asynchronous call at a time. If the same or a second client calls a Begin_ method before a pending asynchronous call is finished, the Begin_ method will return RPC_E_CALL_PENDING.

If the client does not need the results of the Begin_ method, it can release the call object at the end of this procedure. COM detects this condition and cleans up the call. The Finish_ method is not called, and the client does not get any out parameters or a return value.

When the server object is ready to return from the Begin_ method, it signals the call object that it is done. When the client is ready, it checks to see whether the call object has been signaled. If so, the client can complete the asynchronous call.

The mechanism for this signaling and checking between client and server is the ISynchronize interface on the call object. The call object normally implements this interface by aggregating a system-supplied synchronization object. The synchronization object wraps a Win32® event handle, which the server signals just before returning from the Begin_ method by calling ISynchronize::Signal.

To complete an asynchronous call

  1. Query the call object for the ISynchronize interface.
  2. Call ISynchronize::Wait .
  3. If Wait returns RPC_E_TIMEOUT, the Begin_ method is not finished processing. The client can continue with other work and call Wait again later. It cannot call the Finish_ method until Wait returns S_OK.

    If Wait returns S_OK, the Begin_ method has returned. Call the appropriate Finish_ method.

The Finish_ method passes the client any out parameters. The behavior of the asynchronous methods, including the return value of the Finish_ method, should match exactly that of the corresponding synchronous method.

The client can release the call object as soon as the Finish_ method returns, or it can hold a pointer to the call object to make additional calls. In either case, the client is responsible for releasing the call object when the object is no longer needed.

If you call a Finish_ method when no call is in progress, the method will return RPC_E_CALL_COMPLETE.

Note If the client and server objects are in the same apartment, calls to ICallFactory::CreateCall are not guaranteed to succeed. If the server object does not support asynchronous calling on a particular interface, the attempt to create a call object will fail and the client must use the synchronous interface.

See Also

Client Security During an Asynchronous Call

Impersonation and Asynchronous Calls

Canceling an Asynchronous Call


Client Security During an Asynchronous Call

The proxy manager created by MIDL for objects that use standard marshaling implements the IClientSecurity interface. Clients can manage the security of marshaled calls by querying for IClientSecurity on the call object and obtaining or changing security settings.


Impersonation and Asynchronous Calls

The server cannot impersonate the client after the server's call to ISynchronize::Signal completes, even if the Begin_ method has not yet completed. For example, suppose a client calls the Begin_ method, the server processes the call immediately, and the server calls ISynchronize::Signal to indicate it is finished processing. Even if work remains to be done in the Begin_ method, the server cannot impersonate the client after the call to ISynchronize::Signal completes.

If the server impersonates the client before it calls ISynchronize::Signal, the impersonation token will not be removed from the thread until the server calls IServerSecurity::Revert or until the server's call to Begin_ returns, whichever comes first.

See Also

Delegation and Impersonation.


Canceling an Asynchronous Call

A client can cancel an asynchronous call that is in progress if the call object implements the ICancelMethodCalls interface. For objects that use standard marshaling, ICancelMethodCalls is always available for marshaled calls. For objects that use custom marshaling or for calls to server objects within the same apartment, this functionality is available only if the call object implements ICancelMethodCalls.

The client can cancel the call at any time, from when the Begin_ method is called until the Finish_ method returns. If the client cancels the call before calling the Finish_ method, it must call the Finish_ method to clean up the state of the call object. Until the client has done so, any calls to any Begin_ method on the call object will return RPC_E_CALL_PENDING.

To cancel an asynchronous call

  1. Query the call object for ICancelMethodCalls.
  2. Call ICancelMethodCalls::Cancel, and then call Release to release the pointer obtained by the QueryInterface call in step 1.
  3. If the client has not called the Finish_ method already, call it now.

There is no guarantee that the server actually stopped execution of the call. If the client's further work depends on some server state that the call may or may not have changed, the client should determine that state before proceeding.

See Also

Making an Asynchronous Call


Canceling Method Calls

With the introduction of distributed and Web-based applications, some method calls can take an unacceptably long time to return. The latency of the network connection may be high, the server machine may be serving many clients, or the server component may be passing a large amount of data, such as a multimedia file. Users should be able to cancel a request that is taking too long, and the application should be able to handle cancellation requests and continue with its other work. In COM, you can use the IMessageFilter interface to cancel a pending call that originates from a single-threaded apartment.

With Microsoft® Windows® 2000, a more comprehensive approach is available for managing cancellation requests made from any thread. When a call is marshaled, the proxy creates a cancel object, which implements the ICancelMethodCalls interface. The cancel object is associated with both the call and the thread on which the call is pending.

To cancel the pending call, the client passes a cancellation request through the cancel object, which handles the details of notifying the server object that the call has been canceled. If the called method has not returned, the server object, on detecting the cancellation request, cleans up any program resources it has allocated and notifies its client, by returning an appropriate HRESULT, that it canceled execution of the call. If the called method has already returned, the cancel object notifies the client. In either case, the client thread is unblocked and can continue processing.

How the server object responds to a cancellation request is at the discretion of the server implementer, but the calling thread on the client will always be unblocked and will ignore whatever results the server tries to pass to it. Cancel objects provide a means to request that a currently running method be canceled, but there is no guarantee that the server object will stop processing the call. For example, the call may have already returned, or the server object may not support cancel objects.

COM automatically provides a standard implementation of cancel objects for client objects and interfaces that use standard marshaling. For objects and interfaces that use custom marshaling, you will need to implement your own cancel object.

At this time, cancel objects handle only synchronous calls.

See Also

Canceling an Asynchronous Call

CoGetCancelObject, CoSetCancelObject, CoTestCancel, ICancelMethodCalls


Call Synchronization

COM applications must be able to deal correctly with user input while processing one or more calls from COM or the operating system. COM provides call synchronization for single-threaded apartments only. Multithreaded apartments (containing free-threaded threads) do not receive calls while making calls (on the same thread). Multithreaded apartments cannot make input synchronized calls. Asynchronous calls are converted to synchronous calls in multithreaded apartments. The message filter is not called for any thread in a multithreaded apartment. For more information on threading issues, see Processes, Threads, and Apartments .

COM calls between processes fall into three categories, as follows:

  • Synchronous calls Most of the communication that takes place within COM is synchronous. When making synchronous calls, the caller waits for the reply before continuing and can receive incoming messages while waiting. COM enters a modal loop to wait for the reply, receiving and dispatching other messages in a controlled manner.
  • Asynchronous notifications When sending asynchronous notifications, the caller does not wait for the reply. COM uses PostMessage or high-level events to send asynchronous notifications, depending on the platform. COM defines five asynchronous methods—
    • IAdviseSink::OnDataChange
    • IAdviseSink::OnViewChange
    • IAdviseSink::OnRename
    • IAdviseSink::OnSave
    • IAdviseSink::OnClose

      Note While COM is processing an asynchronous call, synchronous calls cannot be made. For example, a container application's implementation of IAdviseSink::OnDataChange cannot contain a call to IPersistStorage::Save. These calls are the only asynchronous calls supported by COM. There is no way to create a custom interface that is asynchronous at this time.

  • Input-synchronized calls When making input-synchronized calls, the object called must complete the call before yielding control. This ensures that focus management works correctly and that data entered by the user is processed appropriately. These calls are made by COM through the Windows SendMessage function, without entering a modal loop. While processing an input-synchronized call, the object called must not call any function or method (including synchronous methods) that might yield control. The following methods are input synchronized—
    • IOleWindow::GetWindow
    • IOleInPlaceActiveObject::OnFrameWindowActivate
    • IOleInPlaceActiveObject::OnDocWindowActivate
    • IOleInPlaceActiveObject::ResizeBorder
    • IOleInPlaceUIWindow::GetBorder
    • IOleInPlaceUIWindow::RequestBorderSpace
    • IOleInPlaceUIWindow::SetBorderSpace
    • IOleInPlaceFrame::SetMenu
    • IOleInPlaceFrame::SetStatusText
    • IOleInPlaceObject::SetObjectRects

To minimize problems that can arise from asynchronous message processing, the majority of COM method calls are synchronous. With synchronous communication, there is no need for special code to dispatch and handle incoming messages. When an application makes a synchronous method call, COM enters a modal wait loop that handles the required replies and dispatches incoming messages to applications capable of processing them.

COM manages method calls by assigning an identifier called a logical thread ID. A new one is assigned when a user selects a menu command or when the application initiates a new COM operation. Subsequent calls that relate to the initial COM call are assigned the same logical thread ID as the initial call.


Defining COM Interfaces

There are nearly 200 Microsoft-defined COM interfaces, many of which are implemented in the COM libraries. In most cases, you can reuse these generic interfaces. However, some applications have specific requirements that make it desirable or necessary to define your own object interfaces.

All COM interfaces must derive, directly or indirectly, from the IUnknown interface. Within that constraint, your custom interface can support almost any method or parameter, including asynchronous methods. You can also generate a type library for your custom interfaces so that clients can access information about your object's methods at run time. Once you define an interface, describe it in Microsoft Interface Definition Language (MIDL), compile and register it, you use it just like any generic interface. With distributed COM, interface methods are available both to remote processes and to other processes on the same machine.

This section explains how to use MIDL to define COM interfaces and how to build a type library or a proxy/stub DLL to handle the interprocess communication. COM programming experience is assumed. For a discussion of the fundamentals of COM interfaces, see The Component Object Model, and see COM Clients and Servers for information on how clients and servers interact.

Because you will be using the same steps and tools that you would use to build a conventional client/server RPC application, you should first work through the short Tutorial in the RPC Programmer's Guide and Reference if you are not already familiar with RPC and MIDL.

Finally, building COM interfaces requires a development environment that includes Microsoft® Windows NT® version 4.0 or later, Microsoft Windows® 2000, or Microsoft Windows 95; also required is a 32-bit C/C++ compiler and the Microsoft Platform Software Development Kit (SDK), which includes the Midl.exe compiler.

The steps in creating a COM interface are as follows:

  • Decide how you want to provide marshaling support for your interface—either with type-library–driven marshaling or with a proxy/stub DLL. Even in-process interfaces must be marshaled if they are to be used across apartment boundaries. It is a good idea to build marshaling support into every COM interface, even if you don't think you will need it. See Interface Marshaling for more information.
  • Describe the interface or interfaces in an interface definition (IDL) file In addition, you can specify certain local aspects of your interface in an application configuration file (ACF). If you are using type-library–driven marshaling, add a library statement that references the interfaces for which you want to generate type information.
  • Use the MIDL compiler to generate a type library file and header file, or C-language proxy/stub files, interface identifier file, DLL data file and header file. See MIDL Compilation for more information.
  • Depending on the marshaling method you chose, write a module definition (DEF) file, compile and link all the MIDL-generated files into a single proxy DLL, and register the interface in the system registry, or register the type library. See Loading and Registering a Type Library and Building and Registering a Proxy DLL for more information.


Interface Marshaling

Unless you know beyond all doubt that your interface will never be used across apartment, thread, or process boundaries, you need to decide how to provide marshaling support for your interfaces. There are three ways to provide marshaling support:

  • Write your own proxy/stub code that calls the COM channel, which in turn calls the RPC run-time libraries. Theoretically, it is possible to do this, but in practice it is almost impossible to do without a significant amount of effort.
  • Describe your interfaces in an interface definition language (IDL) file and use the MIDL compiler to generate a proxy/stub DLL. This method provides the best performance and the most flexibility in terms of acceptable data types. Using MIDL-generated proxy stubs, you can control not only memory management but even the marshaling and unmarshaling of complex data types across different platforms.
  • Use MIDL to generate a type library that the system uses to provide marshaling support at run time. This is the easiest way to implement marshaling support. All you have to do is generate a type library and register it by calling RegTypeLib. Your interfaces must be Automation-compatible (either oleautomation or dual), which places some restrictions on the kinds of data types you can use as method parameters. However, in most cases, the advantage of having your interfaces accessible to programs written in other languages, such as Microsoft® Visual Basic® and Java, outweighs the limitations on data types.

See Also

Inter-Object Communication.


Anatomy of an IDL File

This section contains examples that show how the most common MIDL attributes are used to define COM interfaces. The following example IDL file defines two COM interfaces. From this IDL file, Midl.exe will generate proxy/stub and marshaling code and header files. A line-by-line dissection follows the example.

//example.idl
//
import "mydefs.h","unknwn.idl";
[
object,
uuid(a03d1420-b1ec-11d0-8c3a-00c04fc31d2f),
] interface IFace1 : IUnknown
{
HRESULT MethodA([in] short Bread,
                [out] BKFST * pBToast);
HRESULT MethodB([in, out] BKFST * pBPoptart);
};

[
object,
uuid(a03d1421-b1ec-11d0-8c3a-00c04fc31d2f),
pointer_default(unique)
] interface IFace2 : IUnknown
{HRESULT MethodC([in] long Max,
                 [in, max_is(Max)] BkfstStuff[ ],
                 [out] long * pSize,
                     [out, size_is( , *pSize)] BKFST ** ppBKFST);
}; //end IFace2 def
//end example.idl

The IDL import statement is used here to bring in a header file, Mydefs.h, which contains user-defined types, and Unknwn.idl, which contains the definition of IUnknown, from which IFace1 and IFace2 derive.

The object attribute identifies the interface as an object interface and tells the MIDL compiler to generate proxy/stub code instead of RPC client and server stubs. Object interface methods must have a return type of HRESULT, to allow the underlying RPC mechanism to report errors for calls that fail to complete due to network problems.

The uuid attribute specifies the interface identifier (IID). Each interface, class, and type library must be identified with its own unique identifier. Use the utility Uuidgen.exe to generate a set of unique IDs for your interfaces and other components.

The interface keyword defines the interface name. All object interfaces must derive, directly or indirectly, from IUnknown.

The in directional parameter specifies a parameter that is set only by the caller. The out parameter specifies data that is passed back to the caller. Using both directional attributes on one parameter specifies that the parameter is used both to send data to the method and to pass data back to the caller.

The pointer_default attribute specifies the default pointer type (unique, ref, or ptr) for all pointers except for those included in parameter lists. If no default type is specified, MIDL assumes that single pointers are unique. However, when you have multiple levels of pointers, you must explicitly specify a default pointer type, even if you want the the default type to be unique.

In the preceding example, the array BkfstStuff[ ] is a conformant array, the size of which is determined at run time.The max_is attribute specifies the variable that contains the maximum value for the array index.

The size_is attribute is also used to specify the size of an array or, as in the preceding example, multiple levels of pointers. In the example, the call can be made without knowing in advance how much data will be returned.

The following IDL example (which reuses the interfaces described in the previous IDL example) shows the various ways to generate type library information for interfaces.

//example2.idl
import "example.idl","oaidl.idl";
[
uuid(a03d1422-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace3 interface"),
pointer_default(unique);
dual,
oleautomation
] interface IFace3 : IDispatch
{
   HRESULT MethodD([in] BSTR OrderIn,
                   [out, retval] * pTakeOut);
}; //end IFace3 def
[
uuid(a03d1423-b1ec-11d0-8c3a-00c04fc31d2f),
version(1.0),
helpstring("Example Type Library"),
] library ExampleLib
{
  importlib("stdole32.tlb");
  interface IFace3;
  [
  uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
  helpstring("Breakfast Component Class")
  ] coclass BkfstComponent
    {
    [default]interface IFace1;
    interfaceIFace2
    }; //end coclass def
[
uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace4 interface"),
pointer_default(unique);
dual,
oleautomation
] interface IFace4 : IDispatch
{
[propput] HRESULT MethodD([in] BSTR OrderIn);
[propget] HRESULT MethodE([out, retval] * pTakeOut);
}; //end IFace4 def

}; //end library def
//end example2.idl

The helpstring attribute is optional; you use it to briefly describe the object or to provide a status line. These help strings are readable with an object browser such as the one provided with Microsoft® Visual Basic®.

The dual attribute on IFace3 creates an interface that is both a dispatch interface and a COM interface. Because it is derived from IDispatch, a dual interface supports Automation, which is what the oleautomation attribute specifies. IFace3 imports Oaidl.idl to get the definition of IDispatch.

The library statement defines the ExampleLib type library, which has its own uuid, helpstring, and version attributes.

Within the type library definition, the importlib directive brings in a compiled type library. All type library definitions should bring in the base type library defined in Stdole32.tlb.

This type library definition demonstrates three different ways to include interfaces in the type library. IFace3 is included merely by referencing it within the library statement.

The coclass statement defines an entirely new component class, BkfstComponent, that includes two previously defined interfaces, IFace1 and IFace2. The default attribute designates IFace1 as the default interface.

IFace4 is described within the library statement. The propput attribute on MethodD indicates that the method performs a set action on a property of the same name. The propget attribute indicates that the method retrieves information from a property of the same name as the method. The retval attribute in MethodD designates an output parameter that contains the return value of the function.

These example IDL files demonstrate the fundamental constructs of interface definition. Memory allocation, custom marshaling, and asynchronous messaging are just a few of the features you can implement in a custom COM interface. For more information on implementing interfaces and type libraries, including a summary of MIDL attributes, see Interface Definitions and Type Libraries in the MIDL Programmer's Guide and Reference. For a complete reference of all MIDL attributes, keywords, and directives, see the MIDL Language Reference .


MIDL Compilation

Given an IDL file such as example2.idl, which defines one or more COM interfaces and a type library, the MIDL compiler (Midl.exe) generates the files described in the following table as the default output.

Filename | Description
Example2.h | The header file, containing type definitions and function declarations for all of the interfaces defined in the IDL file as well as forward declarations for routines that the stubs call.
Example2_p.c | The proxy/stub file, which includes the surrogate entry points both for clients and for servers.
Example2_i.c | The interface ID file, which defines the GUID for every interface specified in the IDL file.
Example2.tlb | A compound document file that contains information about types and objects.
Dlldata.c | Contains the data you need to create a proxy/stub DLL.

You use the header file and all of the .c files to create a proxy DLL that can support the interface when used both by client applications and by object servers. You use the interface header file (Example2.h) and the interface ID (Example2_i.c) file when creating the executable file for a client application that uses the interface. You can choose to include the type library file as a resource in your EXE or DLL, or you can ship it as a separate file.

See Also

Files Generated for a COM Interface


MIDL Compiler Options

You can use the following command-line options to override some of the default behavior of the MIDL compiler and to choose optimizations appropriate for your application. For a complete listing of MIDL command-line options, see the MIDL Command-Line Reference.

Command line switch | Description
/acf | Use to supply an explicit ACF filename. This switch also enables the use of different interface names in the IDL and ACF files.
/dlldata | Specifies a filename for the generated DLL data file for a proxy DLL. The default filename is Dlldata.c.
/env | Directs MIDL to generate stubs or a type library for a target environment, such as Macintosh or Microsoft® Windows® 3.11 or earlier.
/header, /h | Specifies the name of the interface header file. The default name is that of the IDL file with an .h extension.
/iid | Specifies an interface identifier filename that overrides the default interface identifier filename for a COM interface.
/lcid | Provides full DBCS support so that you can use international characters in your input files, filenames, and directory paths.
/no_format_opt | By default, to reduce code size, MIDL eliminates duplicate descriptors. This switch turns off this optimizing behavior.
/Oi, /Oic, /Oif | Directs MIDL to use a fully interpreted marshaling method. The /Oic and /Oicf switches provide additional performance enhancements.
/out | Specifies the directory to which the MIDL compiler writes output files. The output directory can be specified with a drive letter, an absolute pathname, or both. The default is that MIDL writes the files to the current directory.
/proxy | Specifies the name of the interface proxy file for a COM interface. The default name is that of the IDL file plus "_p.c".
/tlb | Specifies the name of the type library file. The default name is that of the IDL file, with a .tlb extension.

See Also

General MIDL Command-Line Syntax


Loading and Registering a Type Library

The Automation dynamic link library, Oleaut32.dll, provides several functions that you can call to load and register a type library. Calling LoadTypeLibEx, as shown in the following example, both loads the library and creates the registry entries.

Example

ITypeLib *pTypeLib;
HRESULT hr;
hr = LoadTypeLibEx("example.tlb", REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
{
    pTypeLib->Release();
}

For a complete listing of the functions in Oleaut32.dll that provide loading, registering, and querying of type libraries, see Overview of Type Compilation and Library Functions and Registering a Type Library, in the Automation Programmer's Reference.


Building and Registering a Proxy DLL

If you chose proxy/stub marshaling for your application, the .c and .h files that MIDL generated must be compiled and linked to create a proxy DLL, and that DLL must be entered into the system registry so that clients can locate your interfaces. The MIDL-generated file Dlldata.c contains the necessary routines and other information to build and register a proxy/stub DLL.

The first step in building the DLL is to write a module definition file for the linker, as shown in the following example:

LIBRARY        example.dll
DESCRIPTION    'generic proxy/stub DLL'
EXPORTS        DllGetClassObject      @1 PRIVATE
               DllCanUnloadNow        @2 PRIVATE
               DllRegisterServer      @4 PRIVATE
               DllUnregisterServer    @5 PRIVATE

Alternatively, you can specify these exported functions on the LINK command line of your makefile.

The exported functions are declared in Rpcproxy.h, which Dlldata.c includes, and default implementations are part of the RPC run-time library. COM uses these functions to create a class factory, unload DLLs (after making sure that no objects or locks exist), retrieve information about the proxy DLL, and to self-register and unregister the proxy DLL. To take advantage of these predefined functions, you need to invoke the Cpreprocessor /D (or -D) option when you compile the Dlldata.c and Example_p.c files, as shown in the following makefile:

example.h example.tlb example_p.c example_i.c dlldata.c : example.idl
    midl example.idl
dlldata.obj : dlldata.c
    CL /c /DWIN32 /DREGISTER_PROXY_DLL dlldata.c
example.obj : example_p.c
    CL .c.DWIN32 /DREGISTER_PROXY_DLL example_p.c
iids.obj : example_i.c
PROXYSTUBOBJS = dlldata.obj example.obj iids.obj
PROXYSTUBLIBS = kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib uuid.lib
proxy.dll : $(PROXYSTUBOBJX) example.def
    link /dll /out:proxy.dll /def:example.def
        $(PROXYSTUBOBJS) $(ORIXYSTUBLIBS)
    regsvr32 /s proxy.dll

If you do not specify these preprocessor definitions at compile time, these functions are not automatically defined. (That is, the macros in Rpcproxy.c expand to nothing.) You would have to have defined them explicitly in another source file, whose module would also be included in the final linking and compilation on the C compiler command line.

When REGISTER_PROXY_DLL is defined, Rpcproxy.h provides for additional conditional compilation control with PROXY_CLSID=<guid>, PROXY_CLSID_IS=<explicit value of guid>, and ENTRY_PREFIX=<prefix string>. These macro definitions are described in greater detail in C-Compiler Definitions for Proxy/Stubs in the MIDL Programmer's Guide. Also, the sample programs Marshal and Marshal2, in the COM Tutorial section of the Platform SDK, use these macros to register the Proxy DLL.

Manually Registering the Proxy DLL

If for some reason you cannot use the default proxy stub registration routines, you can manually register the DLL by adding the following entries to the system registry, using Regedit.exe (for Microsoft Windows® 95) or Regedt32.exe (for Microsoft Windows NT® or Microsoft Windows® 2000).

\HKEY_CLASSES_ROOT\Interface\{<iid>}
    =ICustomInterfaceName
\HKEY_CLASSES_ROOT\Interface\{<iid>}\ProxyStubClsid32
    ={<clsid>}
\HKEY_CLASSES_ROOT\CLSID\{<clsid>}
    =ICustomInterfaceName_PSFactory
\HKEY_CLASSES_ROOT\CLSID\{<clsid>}\InprocServer32
    =proxstub.dll

See Also

Registering COM Servers

Self-Registration

C-Compiler Definitions for Proxy/Stubs


Interface Design Rules

This section provides a short summary of interface design rules and guidelines. Some of these rules are specific to the COM architecture, while others are restrictions imposed by the interface design language, MIDL. For details of COM interface design, see Anatomy of an IDL File.

By definition, an object is not a COM object unless it implements either the IUnknown interface or an interface that is derived from IUnknown. In addition, the following rules apply to all interfaces implemented on a COM object:

  • They must have a unique interface identifier (IID).
  • They must be immutable. Once they are created and published, no part of their definition may change.
  • All interface methods must return an HRESULT so that the portions of the system that handle remote processing can report RPC errors.
  • All string parameters in interface methods must be Unicode.
  • Your data types must be remotable. If you cannot convert a data type to a remotable type, you will have to create your own marshaling and unmarshaling routines. Also, LPVOID, or void *, has no meaning on a remote machine. Use a pointer to IUnknown, if necessary.

Note The current implementation of MIDL does not handle function overloading or multiple inheritance.

Other Interface Design Considerations

Use pointers to data very carefully. To re-create the data in the address space of the process that is called, the RPC run time must know the exact size of the data. If, for example, a CHAR * parameter points to a buffer of characters rather than to a single character, the data cannot be correctly re-created. Use the syntax available with MIDL to accurately describe the data structures represented by your type definitions.

Initialization is essential for pointers that are embedded in arrays and structures and passed across process boundaries. Uninitialized pointers may work when passed to a program in the same process space, but proxies and stubs assume that all pointers are initialized with valid addresses or are null.

Be careful when aliasing pointers (allowing pointers to point to the same piece of memory). If the aliasing is intentional, these pointers should be declared aliased in the IDL file. Pointers declared as nonaliased should never alias each other.

Pay attention to how you allocate and free memory. Remember that, unless you explicitly tell a COM object (by using the allocate attribute) not to free a data structure that was created during an out-of-process call, that structure will be destroyed when the call completes. Also, consider the potentially destructive overhead created by inefficient allocation of data structures that now need to be marshaled and unmarshaled.

Finally, be careful when defining your HRESULT return values so that you don't create error codes that conflict with COM-defined FACILITY_ITF codes (values between 0x0000 and 0x01FF are reserved) or that conflict with other HRESULTs with the same value. Whenever possible, use the universal COM success and failure return values, and use an out parameter, rather than an HRESULT, to return information specific to the function call.

See Also

Interface Definitions and Type Libraries


Designing Remotable Interfaces

With the advent of the distributed component object model, it is important that your custom interface be remotable, even if you intend to use it in-process only.

MIDL is more than just a way to generate header files for your interfaces. It is a programming language for remoting that allows you to use your interfaces across machine, process, and thread boundaries. This means that you need to verify the behavior of your MIDL-defined interfaces under those conditions before you release your program to customers. If you made a mistake in your IDL and the interface is not remoted correctly, it can be difficult to remedy that mistake. Either you have to revise your interface with a new IID and leave the old one in for backwards compatibility or you have to convert every client and every server machine everywhere at the same time.

Even if your interface will never be used out-of-process, it may be used cross-thread. The worst problem for an unchecked IDL file can arise for in-process servers that do not support multiple single-threaded apartments). A server that does not specify a threading model is implicitly single-threaded. Everything marked single-threaded is forced over to the thread that first called CoInitialize or CoInitializeEx. If some other thread was the one that activated the object, all the interfaces on that single-threaded server must be remoted back to the activating thread, which can result in a return of REGDB_E_IID_NOTREG in response to a call to QueryInterface). Unless you can absolutely assert that your interface is both in-process and always going to be called on the same thread, you will get remoted at some time.

Finally, as an interface designer, you need to consider how client applications will use your interface. Two things, together, determine whether an interface will be efficient across process and machine boundaries: the frequency of method calls across the interface boundary, and the amount of data to be transferred in a given method call. Although COM makes cross-process and cross-network calls transparent to programs, it cannot make high-frequency and high-bandwidth calls efficient across address spaces. In some cases, it is more appropriate to design interfaces that will normally be implemented only as in-process servers while other interfaces are more appropriate for remote use.


Using a COM Interface

The client code is the user of the COM interface. To use any COM interface, custom or standard, a client must know its IID. In the following example, the driver that calls CustomRpt passes it the name of the object that is converted to a wide-character format. The object name is fed to CreateFileMoniker so that a file moniker can be created and the client can bind to the running object. Once the object is running, CustomRpt can access a pointer to either an interface in the standard proxy/stub, such as IPersistFile, or to the custom interface, ICustomInterface.

void CustomRpt(char *pszObject)
{
    HRESULT          hr;
    WCHAR               szObject[128];
    WCHAR               wszMsg[128] = {L"Your Message Here...\n"};
    IMoniker            *pmkObject = NULL;
    IUnknown            *pIUnk = NULL;
    IPersistFile        *pIPersistFile = NULL;
    ICustomInterface    *pICustomInterface = NULL;

    // Create a wide-character version of the object's file name.
    wsprintf(wszObject, L"%hs", pszObject);

    // Get a file moniker for the object (a *.smp file).
    hr = CreateFileMoniker(wszObject, &pmkObject);

    if(FAILED(hr))
    {
        printf("Client: CreateFileMoniker for Object failed");
        return;
    }

    // BindMoniker is equivalent to calling CreateBindCtx() followed by
    // a call to BindToObject(). It has the net result of binding the
    // interface (specified by the IID) to the moniker.

    hr = BindMoniker(pmkObject, 0, IID_IUnknown, (void **)&pIUnk);
    if (FAILED(hr))
    {
        printf("Client: BindMoniker failed (%x)\n", hr);
        return;
    }

    // Try a couple QueryInterface calls into the object code; first a
    // QueryInterface to IPersistFile...

    hr = pIUnk->QueryInterface(IID_IPersistFile,
       (void **)&pIPersistFile);

    if (FAILED(hr)) {
        printf("Client: QueryInterface IPersistFile failed (%x)\n", hr);
        pIUnk->Release();
        return;
    }

    // Followed by a QueryInterface to ICustomInterface.
    hr = pIUnk->QueryInterface(IID_ICustomInterface,
                    (void **)&pICustomInterface);

    if (FAILED(hr)) {
        printf("Client: QueryInterface failed (%x)\n", hr);
        pIUnk->Release();
        pIPersistFile->Release();
        return;
    }

    // CustomReport() is the object function that displays the time and
    // date information on the object.
    hr = pICustomInterface->CustomReport();

    if (FAILED(hr))
    {
        printf("Client: pICustomInterface->CustomReport failed (%x)\n",
             hr);
        pIUnk->Release();
        pIPersistFile->Release();
        return;
    }

    // Clean up resources by calling release on each of the interfaces.
    pIPersistFile->Release();
    pICustomInterface->Release();
    pIUnk->Release();
    return;
}


Registering COM Applications

The Microsoft® Windows® registry is the system database for Windows operating systems. It contains information about the configuration of system hardware and software as well as about users of the system. Any Windows-based program can add information to the registry and read information back from the registry. Clients search the registry for interesting components to use.

The registry maintains information about all the COM objects installed in the system. Whenever an application creates an instance of a COM component, the registry is consulted to resolve either the CLSID or ProgID of the component into the pathname of the server DLL or EXE that contains it. After determining the component's server, Windows either loads the server into the process space of the client application (in-process components) or starts the server in its own process space (local and remote servers). The server creates an instance of the component and returns to the client a reference to one of the component's interfaces.

For more information about the Windows registry, see the following topics in this section:

  • Registry Hierarchy
  • Classes and Servers
  • Classifying Components
  • Using OleView
  • Registry Editor
  • Registering Components
  • Checking Registration
  • Unknown User Types
  • COM Registry Keys


Registry Hierarchy

The registry is structured as a hierarchy of keys, subkeys, and named values or single default values. Subkeys can have multiple subkeys and values. Keys are named by backslash-delimited strings. Each key in the registry can have one or more values, which can contain strings, integral values, or binary data.


Classes and Servers

COM uses HKEY_CLASSES_ROOT for machinewide settings but also allows per-user configuration of CLSIDS for greater security and flexibility. COM first consults HKEY_CURRENT_USER\Software\Classes before looking under HKEY_CLASSES_ROOT. COM keeps machinewide information related to CLSIDs under HKEY_CLASSES_ROOT\CLSID and keeps per-user class information under HKEY_CURRENT_USER\Classes\CLSID.

COM servers support self-registration. For an in-process server, this means that the DLL must export the following functions:

  • DllRegisterServer
  • DllUnregisterServer

You must explicitly export these functions by using a module definition file, linker switches, or compiler directives. The class store uses these functions to configure the local registry after downloading the file to the client machine. In addition to class store, these functions are also used by other environments, such as Microsoft Transaction Server (MTS) and ActiveX® code download, to install servers on host computers.


Classifying Components

While a client is able to browse through the list of CLSIDs in the registry and select a component to use, loading each component in the registry and querying it for its supported interfaces is very time-consuming. To determine whether a component supports the interfaces required before creating an instance of the component, a method to classify components into categories was developed.

A component category is a set of interfaces that have been assigned a GUID named CATID. Components that implement all of the interfaces in a component category register themselves as members of that component category. Components that belong to a certain component category can then be selected from the registry. By registering itself as a member of a component category, the component is guaranteeing that it supports all of the member interfaces in the component category.

A component can be a member of many categories. It is not limited to supporting interfaces in a component category. It can support any interface, in addition to those in a component category.

In contrast to the standard registration of components, in which developers must write code that manually registers objects, component categories automates much of this work. The six methods of the ICatRegister interface define component categories and register objects that implement or require them. The Component Categories Manager object implements this interface. See ICatRegister and ICatInformation for additional information on using component categories.


Using OleView

While the Registry Editor shows an unprocessed view of the registry and is helpful for learning the organization of the registry, using the Registry Editor can be time-consuming. OleView provides a higher-level view of the information contained in the registry. Rather than looking at a long list of CLSIDs, OleView features tree controls with friendly names.


Registry Editor

To view and edit the registry, use regedt32.exe on Microsoft® Windows NT® or Microsoft Windows® 2000 systems and regedit.exe on Microsoft Windows® 95 systems. Most of the information required to service COM servers and containers is stored in keys and named values under the HKEY_LOCAL_MACHINE\SOFTWARE\Classes and HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE keys.

Note HKEY_CLASSES_ROOT is equivalent to HKEY_LOCAL_MACHINE\SOFTWARE\Classes and can be used interchangeably.

The CLSID key lists the CLSIDs for all the components installed on the system. Each CLSID is a GUID that appears in the registry in the form {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}, and each has a default component name.


Registering Components

When the following types of applications are installed, installation information must be added to the registry, usually through a setup program:

  • Server applications
  • Container/server applications
  • Container applications that allow linking to embedded objects

For all three instances, register COM library (DLL) information and application-specific information.

The DLL registers the information for all its components by exporting DllRegisterServer and DllUnregisterServer. Use the following functions to register and unregister a component:

  • RegOpenKeyEx
  • RegCreateKeyEx
  • RegSetValueEx
  • RegEnumKeyEx
  • RegDeleteKey
  • RegCloseKey


Checking Registration

Each time an application loads, it should check its registration to determine the following:

  • Whether the CLSIDs are present in the registry. If they are not present, the application should register as the original setup.
  • Whether the application's CLSIDs are present in the register but have no OLE 2-related information in them. If this is the case, the application should register as the original setup.
  • Whether the path containing server entries (LocalServer and LocalServer32, InprocServer and InprocServer32, and DefaultIcon) points to the location at which the application is currently installed. If the path does not, rewrite the path entries to point to the current location.


Unknown User Types

You can ease product localization by adding the following key to the registry:

HKEY_LOCAL_MACHINES\SOFTWARE\Microsoft\OLE2\UnknownUserType=<usertype>

This key allows functions to return a specified string rather than a default value of "Unknown", enabling localizers to specify a user type of a different language.

The COM default handler's implementation of IOleObject::GetUserType examines the registry by calling OleRegGetUserType. If the object's class is not found in the registry, the user type from the object's IStorage instance is returned. If the class is not found in the object's IStorage instance, the string "Unknown" is returned.


COM Registry Keys

While the registry consists of a lengthy database of information, most of the important information is stored in the following keys:

  • AppID
  • CLSID
  • ProgID
  • VersionIndependentProgID
  • File Extension
  • Interface
  • OLE


The AppID Key

The AppID key groups the configuration options (a set of named values) for one or more distributed COM objects into one location in the registry. Subkeys under this key are used to map an application identifier (AppID) to a remote server name. To simplify the management of common security and configuration settings, distributed COM objects hosted by the same executable are grouped into one AppID.


The CLSID Key

A class identifier (CLSID) is a globally unique identifier that identifies a COM class object. If the server or container allows linking to embedded objects, register a CLSID for each supported class of objects.

The CLSID key contains information used by the default COM handler to return information about a class when it is in the running state.

To obtain a CLSID for your application, use uuidgen.exe, found in the \TOOLs directory of the COM Toolkit, or use CoCreateGuid.


The ProgID Key

A programmatic identifier (ProgID), is a registry entry that can be associated with a CLSID. The ProgID key maps a user-friendly string to a CLSID. Like the CLSID, the ProgID identifies a class, but with less precision.

Use a ProgID in programming situations where it is not possible to use a CLSID. ProgIDs should not appear in the user interface. ProgIDs are not guaranteed to be unique, so they can be used only where name collisions do not occur.


VersionIndependentProgID

The VersionIndependentProgID associates a ProgID with a CLSID. It is used to determine the latest version of an object application. Like the ProgID, the version-independent ProgID can be registered with a human-readable name.

Applications must register a version-independent programmatic identifier under the VersionIndependentProgID key. The version independent ProgID refers to the application's class and does not change from version to version, instead remaining constant across all versions. It is used with macro languages and refers to the currently installed version of the application's class. The version-independent ProgID must correspond to the name of the latest version of the object application.


File Extension

The File Extension key associates a file extension with a ProgID, indicating that an OLE 2 application can handle requests from the Microsoft® Windows® 3.1 File Manager.

Information contained in the file extension key is used by both File Manager and File Monikers. GetClassFile uses the file extension key to supply the associated CLSID.


Interface

The Interface key registers new interfaces by associating an interface name with an interface identifier (IID). It maps IIDs to information specific to an interface. The information is required mainly for using interfaces across process boundaries.

When adding a new interface, the Interface key must be completed for COM to register the new interface. There must be one IID subkey for each new interface.


OLE

The OLE key controls default launch and access permissions for distributed COM objects as well as call-level security capabilities for applications that do not call CoInitializeSecurity. Only administrators have full access to this portion of the registry. All other users have read-only access.


Security in COM

Security in COM is firmly based on the security provided by Microsoft® Windows NT® and Microsoft Windows® 2000 and the underlying RPC security mechanisms. COM security relies on authentication (the process of verifying a caller's identity) and authorization (the process of determining whether a caller is authorized to do what it is asking to do). There are two main types of security in COM—activation security and call security. Activation security determines whether a client can launch a server at all. Once a server has been launched, you can use call security to control access to a server's objects.

Prior to Windows NT 4.0, there was no special support for security in COM beyond that provided by the operating system. Therefore, out-of-process servers had the same permissions as the interactive user, and all COM objects were activated using the identity of the activator (not the interactive user). In addition, an object instance could be created for any CLSID in the registry, no matter who the user was.

Windows NT 4.0 introduced security for COM that uses and enhances the security model provided by the operating system alone. In this model, servers manage and protect objects, clients get access to objects through servers, and servers can attempt access while impersonating the client.

Windows 2000 implements the Kerberos v5 authentication protocol and the SChannel security package. Windows 2000 also includes features such as delegate-level impersonation, mutual authentication, the ability to set authentication levels for an AppID in the registry, and cloaking. Using COM security, you can implement objects that can perform privileged operations without compromising security.

Because there is a wide range of COM security features available, it is helpful to initially determine what kind of security your application needs. For most applications, setting an acceptable level of security can be a painless process, but you can also use COM security to support very complex security scenarios.

You can set security processwide, either by using Dcomcnfg.exe to set the registry or by calling CoInitializeSecurity. Two primary interfaces, IClientSecurity and IServerSecurity (and associated helper functions), allow you to set call-level security within your program. To learn more about COM security, see the following topics in this section:

  • Determining Your Security Needs
  • COM Security Defaults
  • Activation Security
  • Security Values
  • Setting Security for COM Applications
  • Enabling COM Security Using DCOMCNFG
  • Turning Off Security
  • Server-Side Security
  • Security Blanket Negotiation
  • COM and Security Packages
  • COM Security, Windows 95, and Windows 98


Determining Your Security Needs

How you set up COM security for your application depends on what kind of security your application needs. There are several common situations that determine what you should do.

If you decide to use the COM security defaults, you do not have to do anything—COM handles it all. For information on what these default settings are, see COM Security Defaults.

You can also prevent any remote calls into your machine by disabling DCOM altogether (COM between remote machines). For more information, see Enabling and Disabling DCOM.

For legacy or new applications, you can set processwide security in the registry. For more information, see Setting Processwide Security Through the Registry.

You can also override default security settings for calls to certain interfaces in the process while setting default security for the remainder of the process (to allow COM to handle the general cases). For more information, see Setting Security at the Interface Proxy Level.

For complex security requirements, you can handle all security programmatically rather than allowing COM to handle it for you. To do this, call CoInitializeSecurity to disable automatic authentication, and then control all the security settings by setting security on a per-interface proxy basis. For more information, see Setting Processwide Security with CoInitializeSecurity and Setting Security at the Interface Proxy Level.

In some scenarios, you might want to turn off security completely. You might decide that your application does not need any security, or you might want to disable security during development time so that you can enable security features individually. To learn how to disable COM security, see Turning Off Security .

Most of the preceding options are open to you if you run your application under Microsoft® Windows® 95 or Microsoft Windows 98, but there are some limitations. For more information, see COM Security, Windows 95, and Windows 98.

Security in COM relies on authentication services administered by security packages. The default security package for Microsoft Windows NT® 4.0 is NTLMSSP (Windows NT LAN Manager Security Support Provider). This package works well for many applications, but does not provide the more robust security offered by other packages. Therefore, starting with Microsoft Windows 2000, COM now supports the SChannel security package and the Kerberos v5 security protocol. For more details on using these security packages, refer to COM and Security Packages.


COM Security Defaults

You can use the COM security defaults for your application rather than specifying your own security settings. In that case, COM will initialize and manage security for you. You do not need to configure the registry or call any security functions in your program.

However, if certain registry named values have been set or modified, the security defaults that COM uses will be affected. The list below describes COM security default values and explains how some values are influenced by registry settings.

Following are the default security values that COM uses:

  • The default security service provider is the one that is determined by COM to be the best for the environment. For Microsoft® Windows NT® 4.0, it is NTLMSSP, the provider that is built into Windows NT. For Microsoft Windows® 2000, COM chooses either the Kerberos v5 protocol or NTLMSSP, with the Kerberos protocol being the default choice. None of the protocols provided by SChannel are ever chosen as the default.
  • The system identifies a caller through user name and password and automatically creates an identification token used by the security system.
  • If the LegacyAuthenticationLevel named value exists and if its value has been set, that value is used. Otherwise, the authentication level is set at connect (RPC_C_AUTHN_LEVEL_CONNECT). This level means that at the first call a client makes to the server, COM does an authentication check. If the client passes the check, no further authentication is done. For Windows NT 4.0 SP4 and Windows 2000, the AuthenticationLevel value can also be set under the AppID key.
  • If the LegacyImpersonationLevel named value exists and if its value has been set, that value is used. Otherwise, the impersonation level is set to identify (RPC_C_IMP_LEVEL_IDENTIFY). Impersonation rights are granted by the client to the server. Identify level means that the server can obtain the client's identity. The server can impersonate the client for Access Control List (ACL) checking but cannot access system objects as the client. For more information, see Impersonation Levels and Cloaking.
  • If the AccessPermission named value under the AppID key exists and has been set, that value is used. Otherwise, COM checks for a DefaultAccessPermission entry. If present, that value is used. If this value is not present, COM constructs an ACL that grants permissions to the server identity and the local system.


Activation Security

Activation security (also called launch security) controls who can launch a server. Activation security is automatically applied by the Service Control Manager (SCM) of a particular machine. Upon receipt of a request from a client to activate an object (as described in Instance Creation Helper Functions), the SCM checks the request against activation-security information stored within its registry. (Activation security is also checked for same-machine activations.)

In Microsoft® Windows® 2000, when determining the identity of the client, activation examines the cloaking flag set in the client's call to CoInitializeSecurity. If the cloaking flag is set (for either dynamic or static cloaking), the thread token is used, if present, to determine the identity of the client. If no cloaking is set, the process token is used instead of the thread token. In operating system versions prior to Windows 2000, the process token determines the client identity in most cases.

The Microsoft Windows 95, Microsoft Windows 98, and Macintosh platforms do not support activation security. For more details on the Windows 95 and Windows 98 platforms, see COM Security, Windows 95, and Windows 98.

For more information about activation security, see COAUTHINFO and COSERVERINFO.


Security Values

Several values affect security for COM applications, including authentication level, impersonation level, cloaking, application identity, and reference tracking.

The following topics are included in this section:

  • Authentication Level describes how to control the amount of protection for communication between COM objects.
  • Delegation and Impersonation explains how cloaking affects the identity presented to the server during impersonation and how the client grants the server the authority to act on its behalf.
  • Application Identity identifies and explains the identities your application can use.
  • Reference Tracking explains how you can ensure that objects are not released too early.


Authentication Level

The authentication level controls how much security a client or server wants from its SSP. The authentication level is set by passing an appropriate RPC_C_AUTHN_LEVEL_xxx value to CoInitializeSecurity or CoSetProxyBlanket through the dwAuthnLevel parameter. The authentication levels from the client and server are compared during the handshake, and the more secure setting is used for the connection.

The different authentication levels are described as follows, from least secure to most:

  • None (RPC_C_AUTHN_LEVEL_NONE) No authentication is performed during the communication between client and server. All security settings are ignored. This authentication level can be set only if the authentication service is RPC_C_AUTHN_NONE.
  • Default (RPC_C_AUTHN_LEVEL_DEFAULT) COM chooses the authentication level by using its normal security blanket negotiation. It will never choose an authentication level of none.
  • Connect (RPC_C_AUTHN_LEVEL_CONNECT) The normal, secure authentication handshake occurs between the client and server, and a session key is established but that key is never used for communication between the client and server. All communication after the handshake is insecure.
  • Call (RPC_C_AUTHN_LEVEL_CALL) Only the headers of the beginning of each call are signed. The rest of the data exchanged between the client and server is neither signed nor encrypted. Most SSPs do not support this authentication level and silently promote it to Packet.
  • Packet (RPC_C_AUTHN_LEVEL_PKT) The header of each packet is signed but not encrypted. The packets themselves are not signed or encrypted.
  • Packet Integrity (RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) Each packet of data is signed in its entirety but is not encrypted. Because all of the data is signed by the sender, the recipient can be certain that none of the data has been tampered with during transit.
  • Packet Privacy (RPC_C_AUTHN_LEVEL_PKT_PRIVACY) Each data packet is signed and encrypted. This ensures that the entire communication between the client and server is confidential.

See Also

AuthenticationLevel

LegacyAuthenticationLevel


Delegation and Impersonation

In client/server scenarios, it is common for one server to call another server to accomplish some task on a client's behalf. The situation where a server is given the authority to act on a client's behalf is called delegation.

From a security standpoint, two issues arise regarding delegation:

  • What should the server be allowed to do when acting on the client's behalf?
  • What identity is presented by the server when it calls other servers on behalf of a client?

To deal with these issues, COM provides the following functionality. The client can set an impersonation level that determines to what extent the server will be able to act as the client. If the client grants enough authority to the server, the server can impersonate (pretend to be) the client. When impersonating the client, the server is given access to only those objects or resources that the client has permission to use. The server, acting as a client, can also enable cloaking to mask its own identity and project the client's identity in calls to other COM components.

Consider the scenario illustrated by the preceding figure, where A and B are processes on a different machine from C. Process A calls B, and B calls C. Client A sets the impersonation level. B sets the cloaking capability. If A sets an impersonation level that permits impersonation, B can impersonate A when calling C on A's behalf. The identity that is presented to process C will be either A's identity or B's identity, depending on whether cloaking was enabled by B. If cloaking is enabled, the identity presented to process C will be that of A. If cloaking is not enabled, B's identity will be presented to C.

See Also

Impersonation

Impersonation Levels

Cloaking


Impersonation

Impersonation is the ability of a thread to execute in a security context that is different from the context of the process that owns the thread. When running in the client's security context, the server "is" the client, to some degree. The server thread uses an access token representing the client's credentials to obtain access to the objects to which the client has access.

The primary reason for impersonation is to cause access checks to be performed against the client's identity. Using the client's identity for access checks can cause access to be either restricted or expanded, depending on what the client has permission to do. For example, suppose a file server has files containing confidential information and that each of these files is protected by an ACL. To prevent a client from obtaining unauthorized access to information in these files, the server can impersonate the client before accessing the files.

Access Tokens for Impersonation

Access tokens are objects that describe the security context of a process or thread. They provide information that includes the identity of a user account and a subset of the privileges available to the user account. Every process has a primary access token that describes the security context of the user account associated with the process. By default, the system uses the primary token when a thread of the process interacts with a securable object. However, when a thread impersonates a client, the impersonating thread has both a primary access token and an impersonation token. The impersonation token represents the client's security context, and this access token is the one that is used for access checks during impersonation. When impersonation is over, the thread reverts to using only the primary access token.

You can use the OpenProcessToken function to get a handle to the primary token of a process. Use the OpenThreadToken function to get a handle to the impersonation token of a thread.

See Also

Impersonation Levels

Cloaking

Access Tokens

The Client's Security Context


Impersonation Levels

If impersonation succeeds, it means that the client has agreed to let the server "be" the client to some degree. The varying degrees of impersonation are called impersonation levels, and they indicate how much authority is given to the server when it is impersonating the client.

Currently, there are four impersonation levels: anonymous, identify, impersonate, and delegate. Prior to Microsoft® Windows® 2000, the only supported impersonation levels were identify and impersonate. In Windows 2000, delegate-level impersonation is supported. The following list briefly describes each impersonation level.

  • Anonymous level (RPC_C_IMP_LEVEL_ANONYMOUS) The client is anonymous to the server. The server process can impersonate the client, but the impersonation token does not contain any information about the client. This level is only supported over the local interprocess communication transport. All other transports silently promote this level to identify.
  • Identify level (RPC_C_IMP_LEVEL_IDENTIFY) The system default level. The server can obtain the client's identity, and the server can impersonate the client to do ACL checks.
  • Impersonate level (RPC_C_IMP_LEVEL_IMPERSONATE) The server can impersonate the client's security context while acting on behalf of the client. The server can access local resources as the client. If the server is local, it can access network resources as the client. If the server is remote, it can access only resources that are on the same machine as the server.
  • Delegate level (RPC_C_IMP_LEVEL_DELEGATE) The most powerful impersonation level. When this level is selected, the server (whether local or remote) can impersonate the client's security context while acting on behalf of the client. During impersonation, the client's credentials (both local and network) can be passed to any number of machines. This level is supported only in Windows 2000 and later versions. For impersonation to work at the delegate level, the following requirements must be met:
    • The client must set the impersonation level to RPC_C_IMP_LEVEL_DELEGATE.
    • The client account must not be marked "Account is sensitive and cannot be delegated" in the Active Directory Service.
    • The server account must be marked with the "Trusted for delegation" attribute in the Active Directory Service.
    • The computers hosting the client, the server, and any "downstream" servers must all be running Windows 2000 in a Windows 2000 domain.

By choosing the impersonation level, the client tells the server how far it can go in impersonating the client. The client sets the impersonation level on the proxy it uses to communicate with the server.

Setting the Impersonation Level

There are two ways to set the impersonation level:

  • The client can set it processwide, through a call to CoInitializeSecurity.
  • A client can set proxy-level security on an interface of a remote object through a call to IClientSecurity::SetBlanket (or the helper function CoSetProxyBlanket).

You set the impersonation level by passing an appropriate RPC_C_IMP_LEVEL_xxx value to CoInitializeSecurity or CoSetProxyBlanket through the dwImpLevel parameter.

Different authentication services support delegate-level impersonation to different extents. For instance, NTLMSSP on Windows 2000 supports cross-thread and cross-process delegate-level impersonation, but not cross-machine. On the other hand, the Kerberos protocol (implemented by Windows 2000) supports delegate-level impersonation across machine boundaries, while SChannel does not support any impersonation at the delegate level. If you have a proxy at impersonate level and you want to set the impersonation level to delegate, you should call IClientSecurity::SetBlanket using the default constants for every parameter except the impersonation level. COM will choose NTLM locally and the Kerberos protocol remotely (when the Kerberos protocol will work).

See Also

Impersonation

Cloaking


Cloaking

Cloaking is a COM security capability introduced with the release of Microsoft® Windows® 2000. Cloaking determines what identity the client projects toward the server during impersonation. When cloaking is set, the intermediate server masks its own identity and presents the client's identity to the server that it calls on the client's behalf. Basically; the client identity that is seen by the server is the identity associated with the proxy. The proxy's identity is determined by several factors, one of which is the type of cloaking that is set (if any). Cloaking is not supported by the SChannel security provider.

The following topics in this section provide more information about cloaking:

  • Types of Cloaking
  • How Cloaking Affects Client Identity
  • Setting Cloaking
  • Cloaking and Impersonation Levels
  • Cloaking Scenarios

Types of Cloaking

There are two types of cloaking: static cloaking and dynamic cloaking:

  • With static cloaking (EOAC_STATIC_CLOAKING), the server sees the thread token from the first call from a client to the server. For the first call, if the proxy identity was previously set during a call to CoSetProxyBlanket, that proxy identity is used. However, if the proxy identity was not previously set, the thread token is used. If no thread token is present, the process token is used. For all future calls, the identity set on the first call is used.
  • With dynamic cloaking (EOAC_DYNAMIC_CLOAKING), on each call the current thread token (if there is a thread token) is used to determine the client's identity. If there is no thread token, the process token is used. This means that servers called on the client's behalf during impersonation see the identity of the COM client that originated the call, which is generally the desired behavior. (Of course, for impersonation to succeed, the client must have given the server authority to impersonate by setting an appropriate impersonation level. For more information, see Impersonation Levels.) This type of cloaking is expensive.

How Cloaking Affects Client Identity

When a secure call is made and the server asks the client for its identity, it usually gets the identity tied to the proxy. (Sometimes the authentication service performs a translation from the real identity, but generally the proxy identity is the identity the server sees.) The proxy presents an identity to the server that depends on the type of cloaking that is set and other factors.

To summarize, the identity of the client is a function of the cloaking flag set, the process token, the presence or absence of a thread token, and whether the proxy identity has been previously set. The following table shows the resulting proxy identity (client identity) when these factors vary.

Cloaking Flags | Thread Token Presence | Proxy Identity Previously Set | Proxy Identity (Client Identity)
Cloaking not set | Don't care | Don't care | Process token or authentication identity
EOAC_STATIC_CLOAKING | Present | No | Thread token
EOAC_STATIC_CLOAKING | Present | Yes | Current proxy identity
EOAC_STATIC_CLOAKING | Not present | No | Process token
EOAC_STATIC_CLOAKING | Not present | Yes | Current proxy identity
EOAC_DYNAMIC_CLOAKING | Present | Don't care | Thread token
EOAC_DYNAMIC_CLOAKING | Not present | Don't care | Process token

The following flowchart illustrates how the proxy identity is determined in different situations.

Setting Cloaking

Cloaking is set as a capability flag in a call to CoInitializeSecurity, which sets cloaking for the entire process. The cloaking capability is then set until the client changes it through a call to IClientSecurity::SetBlanket (or to CoSetProxyBlanket), which sets cloaking for the proxy.

By default, cloaking is not set. To set it, pass EOAC_STATIC_CLOAKING or EOAC_DYNAMIC_CLOAKING to the pCapabilities parameter in CoInitializeSecurity or IClientSecurity::SetBlanket.

When static cloaking is enabled using CoInitializeSecurity, each proxy picks up a token (thread or process) the first time you make a call on the proxy. When static cloaking is enabled using IClientSecurity::SetBlanket, the proxy picks up the token on the thread at that time. If no thread token is available when SetBlanket is called, the process token is used for the proxy's identity. Basically, SetBlanket fixes the identity of the proxy.

With dynamic cloaking, the proxy's identity is determined the same way regardless of whether dynamic cloaking is set using CoInitializeSecurity or with IClientSecurity::SetBlanket. The current thread token is used if there is one; otherwise, the process token is used.

If cloaking is set for the entire process through a call to CoInitializeSecurity and you want to make calls with the process token, do not impersonate while making calls.

Cloaking and Impersonation Levels

As mentioned previously, the cloaking capability determines what identity is presented to a server during impersonation. Cloaking provides a way for a server to project an identity other than its own to another server it is calling on behalf of the client. The impersonation level indicates how much authority the client has given the server.

Impersonation without cloaking works, but it may not be the best choice because, in some cases, the final server needs to know the identity of the initial caller. This cannot be achieved without using cloaking because it is difficult to ensure that only authorized clients can access a remote machine. When impersonation is used without cloaking, the identity presented to a downstream server is that of the immediate calling process.

However, cloaking is not useful without impersonation. Cloaking makes sense only when the client has set an impersonation level of impersonate or delegate. (With lower impersonation levels, the server cannot make cloaked calls.) Whether cloaking is successful depends on the number of machine boundaries crossed and on the impersonation level, which indicates how much authority the server has to act on behalf of the client.

In some situations, it makes sense for the server to set cloaking when the client sets the impersonation level to RPC_C_IMP_LEVEL_IMPERSONATE. However, certain limitations are in effect. If the original client sets the impersonation level to RPC_C_IMP_LEVEL_IMPERSONATE, the intermediate server (acting as a client on the same machine) can cloak across only one machine boundary. This is because an impersonate-level impersonation token can be passed across only one machine boundary. Once the machine boundary has been crossed, only local resources can be accessed. The identity presented to the server depends on the type of cloaking that is set. If no cloaking is set, the identity presented to a server will be that of the process making the immediate call.

To cloak over multiple machine boundaries, you must specify both an appropriate cloaking capability flag and delegate-level impersonation. With this type of impersonation, both the local and network credentials of the client are given to the server, so the impersonation token can cross any number of machine boundaries. Again, the identity presented to the server depends on the type of cloaking that is set. If no cloaking is set with delegate-level impersonation, the identity presented to a server is that of the process making the call.

For example, suppose Process A calls B, and B calls C. B has set cloaking and A has set the impersonation level to impersonate. If A, B, and C are on the same machine, passing the impersonation token from A to B and then to C will work. But if A and C are on the same machine, and B is not, passing the token will work between A and B, but not from B to C. The call from B to C will fail because B cannot call C while cloaking. However, if A sets the impersonation level to delegate, the token can be passed from B to C and the call may succeed.

Cloaking Scenarios

In the following illustration, Process A calls B, calls C, calls D when cloaking is not set. As a result, each intermediate process sees the identity of the process that called it.

With static cloaking, the server sees the proxy identity that was set during the first call from the client to the server. The following figure shows an example of the proxy identity being set during a call from B to C. On a subsequent call, Process D sees B's identity when static cloaking is set by B and C.

With dynamic cloaking, the identity of the caller during impersonation is based on the current thread token, if there is one. The following illustration shows the situation where B and C set dynamic cloaking and D sees the identity of A, despite an earlier call from B to C.

See Also

Impersonation

Impersonation Levels


Application Identity

An application's identity is the account that is used to run the application. The identity can be that of the user that is currently logged on (the interactive user), the user that launched the server, a specified user, or a service. To learn how to set application identity using Dcomcnfg.exe, see Setting the Identity for an Application.

The following topics in this section describe each of the four types of application identity and explain the limitations of each type:

  • Interactive User
  • Launching User
  • Specified User
  • Service Identity


Interactive User

The interactive user is the user that is currently logged on to the machine where the application is running. If the identity is set to be the interactive user, all clients use the same instance of the server if the server registers its class factory as multi-use. If no user is logged on, the application will not run. If the server has a graphical user interface (GUI) that the client needs to see, you should use interactive user for the application's identity. However, choosing this identity carries some security risks because the server runs under the identity of the logged on user without the logged on user's knowledge or consent.

See Also

Launching User

Specified User

Service Identity


Launching User

This is the default setting for the application identity. When the launching user is chosen for the application's identity, each client account gets a new instance of the server and each server gets its own WinStation. Because of the separate server instances, launching user is the most secure identity setting. However, there are finite limits on resource consumption. Also, any GUI the server displays will not be seen by the client.

If the application has the identity of the launching user, it runs with an impersonation token. For more information about impersonation and access tokens, see Impersonation Levels and Cloaking.

See Also

Interactive User

Specified User

Service Identity


Specified User

Specifying a particular user (and the user's password) is the preferred identity for COM servers. The reason this identity is preferred is that no one has to be logged on the machine where the server is running for the server to run, and every client talks to the same instance of the server if the server registers its class factory as multi-use. If the server has a GUI, you should not choose this identity; if you do, the user will not be able to see the user interface.

This type of server has a primary token and can access remote resources where a server that has the launching-user identity might not be able to. For more information about impersonation and access tokens, see Impersonation Levels and Cloaking.

Running as a fixed user account is more secure than the interactive user identity because this identity can be assigned to the application only by someone who has the specific user's password.

See Also

Launching User

Interactive User

Service Identity


Service Identity

Choosing this identity causes the application to be run as a service. You can set service accounts by choosing the Services option from the Control Panel. For more information, see Installing as a Win32 Service or User Account.

See Also

Launching User

Interactive User

Service


Reference Tracking

Reference tracking, which is sometimes known as secure reference counting, can prevent the unintentional or malicious early release of objects.

When you enable reference tracking, you are requesting that distributed AddRef and Release calls be authenticated by COM. When reference tracking is enabled, COM keeps track of per-user reference counts so that a user can call Release only on objects that that the user previously called AddRef on. Although reference tracking can decrease performance, it ensures that no matter how many times a given user calls Release, the objects and stubs will still exist if someone else has a reference to them.

The client can set reference tracking for a process by passing the EOAC_SECURE_REFS capability flag in a call to CoInitializeSecurity. You can also enable or disable reference tracking for all applications on a computer by using Dcomcnfg.exe. For more information, see Setting Machine-Wide Reference Tracking.

If reference tracking is enabled, IUnknown always uses default security settings. In this case, calls to CoSetProxyBlanket on IUnknown will fail.


Setting Security for COM Applications

There are several ways you can control security for your applications. You can change a computer's default security settings, which are used by all applications on the computer that do not supply their own security values. You can set security for a particular process, either by using Dcomcnfg.exe or by calling CoInitializeSecurity. You can also programmatically control security settings at the interface proxy level.

The following topics provide procedures that explain how to set security:

  • Modifying the Security Defaults for a Computer
  • Setting Processwide Security
  • Setting Security at the Interface Proxy Level


Modifying the Security Defaults for a Computer

Certain values in the registry determine security settings for applications that do not call CoInitializeSecurity. You can modify these settings using Dcomcnfg.exe. For more information, see the following topics in this section:

  • Registry Values for Machine-Wide Security
  • Setting Machine-Wide Security Using DCOMCNFG


Registry Values for Machine-Wide Security

Certain values in the registry are used to determine security settings for applications that do not call CoInitializeSecurity. The best way to modify these default security settings for a machine is to use Dcomcnfg.exe. For step-by-step procedures that describe how to use Dcomcnfg.exe for this purpose, see Setting Machine-Wide Security Using DCOMCNFG.

Another way to change default machine-wide settings is to manipulate registry values directly. However, only machine administrators and the system have full access to the portion of the registry that contains the default machine-wide call-security settings. All other users have read-access only.

The named values that affect machine-wide security defaults are as follows:

  • DefaultLaunchPermission
  • DefaultAccessPermission
  • LegacyAuthenticationLevel
  • LegacyImpersonationLevel
  • LegacyMutualAuthentication
  • LegacySecureReferences


Setting Machine-Wide Security Using DCOMCNFG

When you want all of the applications on one computer that do not provide their own security to share the same default security settings, you would set security on a machine-wide basis. Using Dcomcnfg.exe makes it easy to set default values in the registry that apply to all applications on a machine.

It is important to understand that if the client or server explicitly calls CoInitializeSecurity to set processwide security, the default settings in the registry will be ignored and the parameters to CoInitializeSecurity will be used instead for the security settings for the process. Also, if you use Dcomcnfg.exe to specify security settings for a particular process, the default machine settings are overridden by the settings for the process.

When enabling machine-wide security, you must set the authentication level to a value other than None and you must set launch and access permissions. You have the option of setting the default impersonation level, and you also can enable reference tracking. The following topics in this section provide step-by-step procedures:

  • Setting Machine-Wide Default Authentication Level
  • Setting Machine-Wide Launch Permissions
  • Setting Machine-Wide Access Permissions
  • Setting Machine-Wide Impersonation Level
  • Setting Machine-Wide Reference Tracking
  • Enabling and Disabling DCOM

Setting Machine-Wide Default Authentication Level

The authentication level is used to tell COM at what level you want the client to be authenticated. These levels offer various levels of protection, from no protection to full encryption. To enable security for a machine, you need to choose an authentication level other than None. You can choose such a setting, using Dcomcnfg.exe, by completing the following steps.

To set the authentication level on a machine-wide basis:

  1. Run Dcomcnfg.exe.
  2. Choose the Default Properties tab.
  3. From the Default Authentication Level list box, choose a value other than (None).
  4. If you will be setting more properties for the machine, click the Apply button to apply the new authentication level. Otherwise, click OK to apply the changes and exit Dcomcnfg.exe.

Setting Machine-Wide Launch Permissions

The launch permissions you set with Dcomcnfg.exe determine a list of users, each of which is explicitly granted or denied permission to launch any server that does not provide its own launch-permission settings. When setting launch permissions, you can add or remove one or more users or groups from this list. For each user that you add, you must specify whether the user is being granted or denied launch permission.

To set launch permissions for a machine:

  1. On the Default Security property page in Dcomcnfg.exe, choose the Edit Default button in the Default Launch Permissions area.
  2. To remove users or groups, select the user or group you want to remove and choose the Remove button. The selected user or group will no longer appear in the list box. When you have finished removing users and groups, choose OK.
  3. If you want to add a user or group, choose the Add button.
  4. If you know the fully qualified user name you want to add, type it in the Add Names text box. If you do not know the user name, you can browse the user database to find it. When you have located the user name, select the user or group from the Names list box and choose the Add button.
  5. From the Type of Access list box, select the access type (either Allow Launch or Deny Launch). To add other users that will also have the selected type of access, repeat step 4. When you have finished adding users for the selected access type, choose the OK button.
  6. To add users that will have a different type of access, repeat steps 4 and 5. Otherwise, choose OK to apply the changes.

Setting Machine-Wide Access Permissions

Dcomcnfg.exe allows you to set access permissions to control the list of users who are granted or denied access to the methods of those servers that do not provide their own access permissions. You can add users or groups to the list, specifying whether access permission is being granted or denied. You can also remove users from the list.

When setting access permissions, you must ensure that SYSTEM is included in the list of users that are granted access. If you have granted access permissions to Everyone, SYSTEM is included implicitly.

The process of setting access permissions for a machine is similar to setting launch permissions. The following steps should be taken.

To set access permissions for a machine:

  1. On the Default Security property page in Dcomcnfg.exe, choose the Edit Default button in the Default Access Permissions area.
  2. To remove users or groups, select the user or group you want to remove and choose the Remove button. The selected user or group will no longer appear in the list box. When you have finished removing user and groups, choose OK.
  3. If you want to add a user or a group, choose the Add button.
  4. If you know the fully qualified user name you want to add, type it in the Add Names text box. If you do not know the user name, you can browse the user database to find it. When you have located the user name, select the user or group from the Names list box and choose the Add button.
  5. From the Type of Access list box, select the access type (either Allow Access or Deny Access). To add other users that will have the selected type of access, repeat step 4. When you have finished adding users for the selected access type, choose the OK button.
  6. To add users that will have a different type of access, repeat steps 4 and 5. Otherwise, choose OK to apply the changes.

Setting Machine-Wide Impersonation Level

The impersonation level, set by the client, determines the amount of authority given to the server to act on the client's behalf. For example, when the client has set its impersonation level to delegate, the server can access local and remote resources as the client, and the server can cloak over multiple machine boundaries if the cloaking capability is set. To help determine which impersonation level you should choose, see Impersonation Levels and Cloaking.

Setting the default impersonation level for the whole machine tells COM what impersonation level to use when a particular client on the machine does not specify an impersonation level programmatically by using CoInitializeSecurity or CoSetProxyBlanket.

To set the impersonation level for a machine:

  1. With Dcomcnfg.exe running, choose the Default Properties tab.
  2. From the Default Impersonation Level list box, choose the impersonation level you want.
  3. If you will be setting more properties for the machine, choose the Apply button to apply the new impersonation level. Otherwise, choose OK to apply the changes and exit Dcomcnfg.exe.

Setting Machine-Wide Reference Tracking

When you enable reference tracking, you are asking COM to do additional security checks and to keep track of information that will keep objects from being released too early. Keep in mind that these additional checks are expensive. For more information on reference tracking (also known as secure reference counting), see Reference Tracking. Use the following steps to enable or disable reference tracking.

To set reference tracking for a machine:

  1. With Dcomcnfg.exe running, choose the Default Properties tab.
  2. To enable (or disable) reference tracking, select (or clear) the Provide additional security for reference tracking check box near the bottom of the page.
  3. If you will be setting more properties for the machine, choose the Apply button to apply the new setting. Otherwise, choose OK to apply the changes and exit Dcomcnfg.exe.

Enabling and Disabling DCOM

When a computer is part of a network, the DCOM wire protocol enables COM objects on that computer to communicate with COM objects on other computers. You can disable DCOM for a particular computer, but doing so will disable all communication between objects on that computer and objects on other computers.

Disabling DCOM on a computer has no effect on local COM objects. COM still looks for launch permissions that you have specified. If no launch permissions have been specified, default launch permissions are used. Even if you disable DCOM, if a user has physical access to the computer, they could launch a server on the computer unless you set launch permissions not to allow it.

Warning If you disable DCOM on a remote computer, you will not be able to remotely access that computer afterwards to reenable DCOM. To reenable DCOM, you will need physical access to that computer.

To manually enable (or disable) DCOM for a computer:

  1. Run Dcomcnfg.exe.
  2. Choose the Default Properties tab.
  3. Select (or clear) the Enable Distributed COM on this Computer check box.
  4. If you will be setting more properties for the machine, click the Apply button to enable (or disable) DCOM. Otherwise, click OK to apply the changes and exit Dcomcnfg.exe.


Setting Processwide Security

There are several ways to set processwide security. The first way, using the Dcomcnfg.exe tool, is easiest because it requires no programming. The second technique offers the programmer more control, and it requires a call to CoInitializeSecurity. Another technique is to set processwide security in the registry by using the AppID key.

For more information on these techniques, see the following topics in this section:

  • Setting Processwide Security Using DCOMCNFG
  • Setting Processwide Security with CoInitializeSecurity
  • Setting Processwide Security Through the Registry

See Also

Setting Security at the Interface Proxy Level


Setting Processwide Security Using DCOMCNFG

You might want to enable security for a particular application if an application has security needs that are different from those required by other applications on the machine. For instance, you might decide to use machine-wide settings for your applications that require a low level of security while setting a higher level of security for a particular application.

However, security settings in the registry that apply to a particular application are sometimes not used. For example, the application-wide settings that you set in the registry using Dcomcnfg.exe will be overridden if a client calls CoSetProxyBlanket to set security for a particular interface proxy. Similarly, if a client or server (or both) call CoInitializeSecurity to set security for a process, the settings in the registry will be ignored and the parameters specified to CoInitializeSecurity will be used instead.

When enabling security for an application, several settings may need to be modified. These include authentication level, location, launch permissions, access permissions, and identity. For step-by-step procedures, see the following topics in this section:

  • Setting the Authentication Level for an Application
  • Setting the Location for an Application
  • Setting Launch Permissions for an Application
  • Setting Access Permissions for an Application
  • Setting the Identity for an Application
  • Browsing the User Database

Setting the Authentication Level for an Application

To enable security for an application, you must set an authentication level other than None. The authentication level tells COM how much authentication protection is required, and it can range from authenticating the client at the first method call to encrypting parameter states fully. For more information on authentication levels, see RPC_C_AUTHN_LEVEL_xxx.

To set an application's authentication level:

  1. On the Applications property page in Dcomcnfg.exe, select the application and click the Properties button (or double-click the selected application).
  2. On the General page, select an authentication level other than (None) from the Authentication Level list box.
  3. If you will be setting other properties for this application, choose the Apply button to apply the new authentication level. Click OK if you are finished setting properties for this application and you wish to apply the changes.

Setting the Location for an Application

The location you set for your application determines the computer on which the application will run. You can choose to run your application on the machine where the data is located, on the machine you use to set the location, or on a specified machine.

To set an application's location:

  1. With Dcomcnfg.exe running, select the application from the Applications page and choose the Properties button (or double-click the selected application).
  2. On the Location page, select one or more check boxes that correspond to locations where you want the application to run. If you select more than one check box, COM uses the first one that applies. If Dcomcnfg.exe is being run on the server machine, always select Run Application On This Computer.
  3. If you will be setting other properties for this application, choose the Apply button to apply the new location. Choose OK if you are finished setting properties for this application and you wish to apply the changes.

Setting Launch Permissions for an Application

With Dcomcnfg.exe, you can set launch permissions to control the list of users who are granted or denied permission to launch a particular server. You can add users or groups to the list, specifying whether access permission is being granted or denied. You can also remove users from the list.

To set launch permissions for an application:

  1. With Dcomcnfg.exe running, select the application from the Applications page and choose the Properties button (or double-click the selected application).
  2. On the Security property page, select the Use custom launch permissions option button and choose the Edit button in the same area.
  3. To remove users or groups, select the user or group you want to remove and choose the Remove button. The selected user or group will no longer appear in the list box. When you have finished removing user and groups, choose OK.
  4. If you want to add users or groups, choose the Add button.
  5. If you know the fully qualified user name you want to add, type it in the Add Names text box. If you do not know the user name, you can browse the user database to find it. When you have located the user name, select the user or group from the Names list box and choose the Add button.
  6. From the Type of Access list box, select the access type (either Allow Launch or Deny Launch). To add other users that will have the selected type of access, repeat step 5. When you have finished adding users for the selected access type, choose the OK button.
  7. To add users that will have a different type of access, repeat steps 5 and 6. Otherwise, choose OK to apply the changes.

Setting Access Permissions for an Application

With Dcomcnfg.exe, you can control the list of users who are granted or denied access to the methods of a particular server by setting access permissions. You can add users or groups to the list, specifying whether access permission is being granted or denied. You can also remove users from the list.

When setting access permissions, you must ensure that SYSTEM is included in the list of users that are granted access. If you have granted access permissions to Everyone, SYSTEM is included implicitly.

The process of setting access permissions for an application is similar to setting launch permissions. The steps are as follows.

To set access permissions for an application:

  1. With Dcomcnfg.exe running, select the application from the Applications page and choose the Properties button (or double-click the selected application).
  2. On the Security property page, select the Use custom access permissions option button and choose the Edit button in the same area.
  3. To remove users or groups, select the user or group you want to remove and choose the Remove button. The selected user or group will no longer appear in the list box. When you have finished removing user and groups, choose OK.
  4. If you want to add a user or a group, choose the Add button.
  5. If you know the fully qualified user name you want to add, type it in the Add Names text box. If you do not know the user name, you can browse the user database to find it. When you have located the user name, select the user or group from the Names list box and choose the Add button.
  6. From the Type of Access list box, select the access type (either Allow Access or Deny Access). To add other users that will have the selected type of access, repeat step 5. When you have finished adding users for the selected access type, choose the OK button.
  7. To add users that will have a different type of access, repeat steps 5 and 6. Otherwise, choose OK to apply the changes.

Setting the Identity for an Application

An application's identity is the account that is used to run the application. The identity can be that of the user that is currently logged on (the interactive user), the user account of the client process that launched the server, a specified user, or a service. You can use Dcomcnfg.exe to choose one of these identities for the application. For help with deciding which identity to set for your application, see Application Identity.

To set identity for an application:

  1. With Dcomcnfg.exe running, select the application from the Applications page and choose the Properties button (or double-click the selected application).
  2. On the Identity property page, select the option button for the identity you want. If you choose the This User: option, you must type in the user name, the password, and the confirmed password.
  3. If you will be setting other properties for this application, choose the Apply button to apply the new identity. Choose OK if you are finished setting properties for this application and you wish to apply the changes.

Browsing the User Database

You would browse the user database in Dcomcnfg.exe when you need to find the fully qualified user name for a particular user. For instance, you can browse the user database to locate a user that you want to add for access or launch permissions.

To browse the user database:

  1. In the List Names From list box, select the domain containing the user or group you want to add.
  2. To see the users that belong to the selected domain, choose the Show Users button.
  3. To see the members of a particular group, select the group in the Names list box and choose the Show Members button.
  4. If you cannot locate the user or group you want to add, choose the Search button, which brings up the Find Account dialog box. Select the domain you want to search (or select Search All), type the user name you want to look for, and choose the Search button.

See Also

Setting Processwide Security with CoInitializeSecurity

Setting Processwide Security Through the Registry


Setting Processwide Security with CoInitializeSecurity

The CoInitializeSecurity function allows you to control complex security scenarios by setting security for an application programmatically. This topic describes scenarios when you might use CoInitializeSecurity and provides some details on how you use it.

There are several reasons why you might want to use CoInitializeSecurity to set processwide security within your program. For example, although you can set the authentication level and the access permissions for the application using dcomcnfg.exe, the default impersonation level for the machine might not be the one you want for your process. The only way to change this setting for your process is to call CoInitializeSecurity.

If you want to use the SChannel security provider, you must specify it as an authentication service in a call to CoInitializeSecurity.

Another common scenario in which you might set processwide security programmatically is when you would like to set default security for the entire process but you have one or more objects within that process that expose interfaces with special security requirements. In this case, you can call CoInitializeSecurity to set security for the process, allowing COM to handle most of the security checks, and you can call other methods to set security for the objects with special security needs. Calling these methods and functions is described in Setting Security at the Interface Proxy Level.

If your application has very specialized security requirements, such as allowing certain groups access to different objects depending on the time of day, you might want to handle all of your security programmatically, ensuring that COM does no automatic checking for you at all. To do this, you must call CoInitializeSecurity, setting the dwAuthnLevel parameter to none and the pVoid parameter to NULL. If you have your own security package you would also need to register it in the pAuthnSvc parameter. Then you can handle all of your own security programmatically through calls to the proxy-level interface and functions described in Setting Security at the Interface Proxy Level.

CoInitializeSecurity offers a rich set of capabilities. If you call CoInitializeSecurity, the registry values are ignored and the security initialization values you pass in to the call are used instead. Depending on the result you want, the first parameter, pVoid, can point to three different types of values: a SECURITY_DESCRIPTOR , an IAccessControl object, or a pointer to an AppID. In most cases, you will use Win32® functions to create a SECURITY_DESCRIPTOR that pVoid will point to.

However, pVoid can also point to an IAccessControl object. You can pass this type of value in Microsoft® Windows® 2000 as well as Microsoft Windows NT® 4.0 SP2 and all later versions. Microsoft Windows 95 and Microsoft Windows 98 do not support security descriptors, so if you want your application to run on Windows NT, Windows 2000, Windows 95, and Windows 98, you should use an IAccessControl object instead of a security descriptor.

To indicate to CoInitializeSecurity that you are passing an IAccessControl object to pVoid, you must pass the EOAC_ACCESS_CONTROL value to the dwCapabilities parameter. Since CoInitializeSecurity caches the results of access checks, the access control list must not be changed after calling CoInitializeSecurity.

Another type of value you can pass to the pVoid parameter is a pointer to a GUID, which is the AppID of your application. If pVoid is a pointer to an AppID, you must specify EOAC_APPID in the pCapabilities parameter so that the function knows what value to expect in pVoid. If pVoid points to an AppID, CoInitializeSecurity uses only the registry for authentication values and ignores all other parameters to CoInitializeSecurity.

See Also

Setting Processwide Security Using DCOMCNFG

Setting Processwide Security Through the Registry


Setting Processwide Security Through the Registry

If you want to set security for an entire process, one solution is to set the security levels you want in the registry. If your application cannot call CoInitializeSecurity or if you prefer not to use programmatic security, this might be a good option. If you decide to set processwide security using the registry, you should not call CoInitializeSecurity within your program because if you do, COM will use the values in CoInitializeSecurity and ignore the registry values. However, if you set security values for your application in the registry, you can still call CoSetProxyBlanket within your program if you want to override the security settings on a specific interface proxy.

There are two ways to set security in the registry for your application:

  • You can use Dcomcnfg.exe, which provides a simple user interface for modifying security values. All COM servers can be configured using Dcomcnfg.exe. For more information, see Setting Processwide Security Using DCOMCNFG. However, client applications do not normally appear in Dcomcnfg.exe unless the client creates a GUID and enters it in the registry.
  • You can set security values under the AppID key for the application. The rest of this topic explains how to set security in the registry using the AppID key.

An AppID is a GUID that represents a server process for one or more classes. Each class is associated with exactly one AppID, and AppIDs can be assigned only to EXEs. DLLs do not get AppIDs unless they are running in surrogate—and then it is the surrogate process that has the AppID. If multiple DLLs are loaded into a surrogate, each surrogate has only one AppID.

For some COM servers, the registration code generates an AppID and places entries in the registry that map the AppID to the name of the executable. But some COM servers do not provide this functionality. However, if the server's registration code adds an entry for HKCR\CLSID{ServerCLSID}\LocalServer32 when dcomcnfg.exe is run, it will automatically add an AppID for the CLSID.

For a COM client that is not a server, this mapping is not created because the client is never registered. Therefore, to set security using the AppID key, the client must create the necessary registry entries, either programmatically by using Win32® registry functions or by using regedit. For more information about how to create the necessary entries, see the documentation for the AppID key.

If you decide to set processwide security in the registry under the AppId key, be aware that there are two named values under the AppID key that you can set without having administrator permissions:

  • AccessPermission
  • AuthenticationLevel

The AuthenticationLevel named value is available in Microsoft® Windows NT® 4.0 SP4 and Microsoft Windows® 2000 and later versions as well as DCOM951.1.

The AuthenticationLevel and AccessPermission values are set independently and have separate default values. If the AuthenticationLevel value is not present, the LegacyAuthenticationLevel value is used as the default. Similarly, if the AccessPermission value is not present, the DefaultAccessPermission value is used as the default. However, the AuthenticationLevel and the AccessPermission values are interrelated in the following ways:

  • If the AuthenticationLevel is none, the AccessPermission and DefaultAccessPermission values are ignored for that application.
  • If the AuthenticationLevel is not present and the LegacyAuthenticationLevel is none, the AccessPermission and DefaultAccessPermission values are ignored for that application.

See Also

Setting Processwide Security Using DCOMCNFG

Setting Processwide Security with CoInitializeSecurity


Setting Security at the Interface Proxy Level

Sometimes the client needs fine-grained control over the security on calls to particular interfaces. For example, security might be set at a low level for the process but calls to a particular interface might require a higher authentication level, such as encryption. The methods of the IClientSecurity interface allow the client to change the security settings associated with calls to a particular interface by controlling the security settings at the interface-proxy level.

The client can query an existing object for IClientSecurity and then call the IClientSecurity::QueryBlanket method to find out what the current security settings are for a particular interface proxy. The IClientSecurity::SetBlanket method can be used to modify the security settings for an individual interface proxy on the object before calling one of the interface's methods. The new settings apply to any future callers of this particular interface. The IClientSecurity::CopyProxy method provides a way for the client to copy an interface proxy so that subsequent calls to SetBlanket on the copy do not affect callers of the original proxy.

IClientSecurity::SetBlanket is commonly used to raise the authentication level for a particular interface proxy to a more secure level. However, in some situations, it might also be helpful to lower the authentication level for a particular interface proxy. For instance, suppose the default authentication level for the process is some value other than RPC_C_AUTHN_LEVEL_NONE and the client and server are in separate domains that do not trust each other. In this case, calls to the server will fail unless the client calls SetBlanket to lower the authentication level to RPC_C_AUTHN_LEVEL_NONE.

Clients using the default implementation of IClientSecurity provided by the proxy manager can call the CoQueryProxyBlanket, CoSetProxyBlanket, and CoCopyProxy helper functions instead of calling IClientSecurity methods directly. The helper functions simplify the code but are slightly less efficient than calling the corresponding IClientSecurity methods directly.

The IClientSecurity interface is implemented locally for the client by the proxy manager. Some custom marshaled objects might not support IClientSecurity.

IClientSecurity works with all supported authentication services (currently NTLMSSP, SChannel, and the Kerberos v5 protocol).

See Also

Setting Processwide Security


Enabling COM Security Using DCOMCNFG

Dcomcnfg.exe provides a user interface for modifying certain settings in the registry. By using Dcomcnfg.exe, you can enable security either on a machine-wide or a processwide basis. You can enable security for a particular computer so that when a process does not provide its own security settings, either programmatically or through registry values, the values set by Dcomcnfg.exe will be used. Or you can use Dcomcnfg.exe to enable security for a particular application only.

When enabling security, there are two primary tasks to accomplish:

  • Set an authentication level that is not None.
  • Set permissions, including both launch and access permissions.

The steps taken to accomplish these tasks depend on whether you are enabling security for the whole machine or just for a particular application. Also, you may want to set other values for the machine or application.

Note You must be an administrator to run Dcomcnfg.exe.

The following topics provide step-by-step procedures on how to set security with Dcomcnfg.exe:

  • Setting Machine-Wide Security Using DCOMCNFG
  • Setting Processwide Security Using DCOMCNFG

See Also

Turning Off Security


Turning Off Security

There are several reasons why you might decide to turn off the security that COM provides. When developing applications, it might be helpful to turn security off initially to allow you to focus on other functionality within your application. Then, you can add security features to your design when you are ready. To facilitate debugging, you might want to turn off security and then add security features one at a time. Finally, some applications do not need security and might run faster if no security checks are done.

The following topics in this section provide step-by-step procedures: Turning Off Activation Security and Turning Off Call Security.

See Also

Enabling COM Security Using DCOMCNFG


Turning Off Activation Security

Normally, activation uses default security settings. However, you can control activation security by specifying a COAUTHINFO structure, which is a member of the COSERVERINFO structure that is passed to the activation functions. If the client specifies an authentication level of RPC_C_AUTHN_LEVEL_NONE in the COAUTHINFO structure, authentication is not attempted. Otherwise, secure activation is attempted, and if authentication fails, activation fails.

If the client does not specify an explicit COAUTHINFO structure and instead sets the pointer to NULL, COM will attempt to authenticate the client. If it cannot authenticate the client, COM checks the launch permission security descriptor to see whether there is a NULL DACL or an ACL that allows access to Everyone. If this check succeeds, the server is launched. Therefore, even if the client does not specify a COAUTHINFO structure, unsecure activation may take place when the server allows it.

Why would a client want to turn off activation security explicitly even though unsecure activation will eventually take place if the server allows it? Because explicitly turning off activation security increases performance when the client does not want or need security checks.

The following things must be done to explicitly turn off activation security:

  • The client must specify an authentication level of RPC_C_AUTHN_LEVEL_NONE in the COAUTHINFO structure that is a member of the COSERVERINFO structure supplied to the activation function.
  • The client must specify an impersonation level of RPC_C_IMP_LEVEL_IMPERSONATE in the COAUTHINFO structure that is a member of the COSERVERINFO structure supplied to the activation function. If this value is not passed, you will get RPC_S_SERVER_UNAVAILABLE.
  • The server must specify Everyone for Default Launch Permissions. The recommended way to perform this task is to use Dcomcnfg.exe as follows:
    1. Run Dcomcnfg.exe.
    2. On the Applications page, select the application that represents the server. Click the Properties button (or double-click the selected application).
    3. On the Security property page, click the Use Custom Launch Permissions button.
    4. Click the Edit button in the Launch Permissions area.
    5. In the Registry Value Permissions dialog box, click the Add button.
    6. Select the entry for Everyone in the list box.
    7. In the Type of Access list box, choose Allow Launch.
    8. Click the OK button.

See Also

Turning Off Call Security


Turning Off Call Security

Call security determines whether a client has permission to call a server's methods. There are two ways to disable call security: One involves using Dcomcnfg.exe to modify the registry, and the other requires calls to CoInitializeSecurity.

Turning Off Call Security Using DCOMCNFG

Call security can most easily be turned off by using Dcomcnfg.exe to modify the registry. However, using Dcomcnfg.exe to turn security off will work only if both the client and the server do not call CoInitializeSecurity. This is because when CoInitializeSecurity is called, DCOM ignores the registry settings and uses the values supplied to CoInitializeSecurity instead.

To turn off security with Dcomcnfg.exe, both the client and the server must set their Authentication Levels to None. The following steps must be completed:

  1. Run Dcomcnfg.exe.
  2. On the Applications page, select the application that represents the server. Click the Properties button (or double-click the selected application).
  3. Click the General tab.
  4. From the Default Authentication Level list box, select (None).
  5. Click the Apply button to apply changes; however, changes are not applied to any running instances of the application.
  6. If the client appears on the list on the Applications page, repeat steps 2 through 5, choosing the client instead of the server for step 2. Then click the OK button. If the client is not on the list, you can do one of the following three things:
    • You can set the client's Authentication Level to None on a machine-wide basis by using Dcomcnfg.exe. (See the warning and the procedure below.)
    • You can set the client's authentication level to None programmatically. (See Turning Off Call Security Programmatically.)
    • If you are using Microsoft® Windows NT® 4.0 SP4 or Microsoft Windows® 2000, you can create an AppID key for the client to indicate an authentication level of None. (See Setting Processwide Security Through the Registry.)

To set the Authentication Level to None on a machine-wide basis:

Warning Setting the machine-wide Authentication Level to None is extremely unsecure.

  1. Run Dcomcnfg.exe.
  2. Choose the Default Properties tab.
  3. From the Default Authentication Level list box, choose (None).
  4. Click the OK button.

Turning Off Call Security Programmatically

To turn call security off programmatically, both the client and the server must call CoInitializeSecurity, setting the authentication level in the dwAuthnLevel parameter to RPC_C_AUTHN_LEVEL_NONE.

See Also

Turning Off Activation Security


Server-Side Security

The server can retrieve security information about a caller or impersonate the caller by using the methods of IServerSecurity. An implementation of IServerSecurity is supplied by COM on the context object for the current call when standard marshaling is used. However, this interface may be absent for some custom-marshaled interfaces.

When a call arrives at the server, the server can call CoGetCallContext to obtain a pointer to the IServerSecurity interface. With this pointer, IServerSecurity methods can be called by the server to find out what the client's authentication settings are and to impersonate the client, if needed. In Microsoft® Windows® 2000, the IServerSecurity object is valid on any thread in the apartment until the call represented by IServerSecurity completes. For more information about impersonation, see Impersonation and Cloaking.

The following helper functions that rely on the call context object's IServerSecurity interface implementation are also available:

  • CoQueryClientBlanket
  • CoImpersonateClient
  • CoRevertToSelf


Security Blanket Negotiation

A security blanket is a group of values that describe the security settings that apply to all proxies in a process or to just a particular interface proxy. A security blanket comprises the following values:

  • Authentication service
  • Authorization service
  • Principal name
  • Authentication level
  • Impersonation level
  • Authentication identity
  • Capabilities
  • An Access Control List (ACL) (servers only)

Security blanket negotiation is the process that COM uses to choose the security settings for a proxy when it is created. This process involves comparing the server's security blanket with the client's security blanket and using those values to create an appropriate default security blanket for the proxy. The following paragraphs explain where the client's and the server's security blankets come from and describe how COM negotiates the security blanket for the proxy using the client's and server's security blankets.

The client and the server can each call CoInitializeSecurity to specify their respective security blankets. If an application doesn't call CoInitializeSecurity explicitly, COM calls it implicitly for the application, using appropriate default values. For more information about these default values, see COM Security Defaults.

Some parameters to CoInitializeSecurity apply when the application is a server, and some apply when the application is a client. When the application is acting as a server, these parameters are relevant: an ACL, a list of authentication service/authorization service/principal name tuples, and an authentication level. A server's call to CoInitializeSecurity, whether implicit or explicit, determines the server's security blanket, which remains fixed.

When the application is acting as a client, the following values passed to CoInitializeSecurity are relevant: an authentication level, an impersonation level, the authentication identity, and capabilities. A client's implicit or explicit call to CoInitializeSecurity indicates the security blanket that the client wants.

When a proxy is created, COM uses the values specified by the server's security blanket and the client's security blanket to negotiate a default security blanket that is appropriate for the proxy. COM picks an authentication service that works on both the client and server. The authorization service and principal name are chosen to work with the authentication service. For the authentication level, COM chooses the higher of the authentication levels specified by the client and the server. The impersonation level and the capabilities chosen by COM are the ones specified by the client. The authentication identity is the one specified by the client for the chosen authentication service.

Once the default security blanket has been computed, its values are assigned to the newly created proxy. The client can override the security settings for the proxy by calling IClientSecurity::SetBlanket. The values specified to SetBlanket are not negotiated; they are simply assigned to the specified proxy. However, if default parameters (such as RPC_C_IMP_LEVEL_DEFAULT) are passed to SetBlanket, COM uses the previously described security blanket negotiation algorithm to compute the default parameters.


COM and Security Packages

Prior to Microsoft® Windows® 2000, full support was provided only for the default security package, NTLMSSP (Windows NT LAN Manager Security Support Provider). Since Windows 2000, full support is also provided for the Kerberos v5 authentication protocol and for the SChannel security package, which provides the PCT 1.0, SSL 2.0, SSL 3.0, and TLS 1.0 protocols. Also supported is Snego, which checks for available security packages and selects the most appropriate one.

The following table shows the levels of authentication supported by the various security packages.

Server/Client Authentication | Security Package Support
Neither can get the name of the other. | None
The client can authenticate the server, but not vice versa. | SChannel
The client cannot discover the server, but the server can get the user ID of the client. | NTLMSSP
Mutual authentication: Both the client and server can know the name of the other, if permission is granted. | NTLMSSP (locally), Kerberos v5 protocol, and SChannel

For more information about these security packages, see the following topics in this section:

  • NTLMSSP
  • Kerberos v5 Protocol
  • SChannel
  • Snego


NTLMSSP

NTLMSSP, whose authentication service identifier is RPC_C_AUTHN_WINNT, is a security support provider that is available on all versions of DCOM. It uses the Microsoft® Windows NT® LAN Manager (NTLM) protocol for authentication. NTLM never actually transmits the user's password to the server during authentication. Therefore, the server cannot use the password during impersonation to access network resources that the user would have access to. Only local resources can be accessed.

NTLM works both locally and across machines. That is, if the client and server are on different machines, NTLM can still make sure the client is who it claims to be.

With NTLM, the client's identity is represented by a domain name, user name, and a password or token. When a server calls CoQueryClientBlanket, the client's domain name and user name are returned. However, when a server calls CoImpersonateClient, the client's token is returned. If there is no trust relationship between client and server and if the server has a local account with the same name and password as the client, that account will be used to represent the client.

NTLM supports mutual authentication cross-thread and cross-process on Microsoft Windows® 2000 (locally only). If the client specifies the principal name of the server in the form domain\user in a call to IClientSecurity::SetBlanket, the server's identity must match that principal name or the call will fail. If the client specifies NULL, the server's identity will not be checked.

On Windows NT 4.0, NTLM supports the impersonation levels identify and impersonate. In Windows NT 4.0, if a server has a process token that has used its one machine hop and it attempts to make a secure call to the client using a connection-oriented protocol, NTLM will allow the call to succeed. The call will use the NULL session as the server's identity, and the call will appear to be secure when it is not. With Windows 2000, NTLM additionally supports the delegate impersonation level cross-thread and cross-process (locally only).

See Also

Kerberos v5 Protocol

SChannel

Snego


Kerberos

Platform SDK: COM

Kerberos v5 Protocol

Microsoft® Windows® 2000 implements the Kerberos v5 authentication protocol, which has an authentication service identifier of RPC_C_AUTHN_GSS_KERBEROS. The Kerberos protocol defines how clients interact with a network authentication service and was standardized by the Internet Engineering Task Force (IETF) in September 1993, in document RFC 1510. Clients obtain tickets from the Kerberos Key Distribution Center (KDC), and they present these tickets to servers when connections are established. Kerberos tickets represent the client's network credentials.

Like NTLM, the Kerberos protocol uses the domain name, user name, and password to represent the client's identity. The initial Kerberos ticket obtained from the KDC when the user logs on is based on an encrypted hash of the user's password. This initial ticket is cached. When the user tries to connect to a server, the Kerberos protocol checks the ticket cache for a valid ticket for that server. If one is not available, the initial ticket for the user is sent to the KDC along with a request for a ticket for the specified server. That session ticket is added to the cache, and it can be used to connect to the same server until the ticket expires.

When a server calls CoQueryClientBlanket using the Kerberos protocol, the client's domain name and user name are returned. When a server calls CoImpersonateClient, the client's token is returned. These behaviors are the same as when using NTLM.

The Kerberos protocol works across machine boundaries. For this protocol to work, the client and server machines must both be running Windows 2000. The client and server machines must both be in Windows 2000 domains, and those domains must have a trust relationship.

The Kerberos protocol requires mutual authentication and supports it remotely. The client must specify the principal name of the server, and the server's identity must match that principal name exactly. If the client specifies NULL for the server's principal name or if the principal name doesn't match the server, the call will fail.

With the Kerberos protocol, the impersonation levels identify, impersonate, and delegate can be used. When a server calls CoImpersonateClient, the token returned is valid off the machine for some time period between 5 minutes and 8 hours. After this time, it can be used on the server machine only. If a server is "run as activator" and the activation is done with the Kerberos protocol, the server's token will expire between 5 minutes and 8 hours after activation.

The Kerberos v5 authentication protocol implemented by Windows 2000 supports cloaking.

See Also

NTLMSSP

SChannel

Snego


SChannel

The Secure Channel (SChannel) security package, whose authentication service identifier is RPC_C_AUTHN_GSS_SCHANNEL, supports the following public-key–based protocols: SSL (Secure Sockets Layer) versions 2.0 and 3.0, Transport Layer Security (TLS) 1.0, and Private Communication Technology (PCT) 1.0. TLS 1.0 is a standardized, slightly modified version of SSL 3.0 that was issued by the Internet Engineering Task Force (IETF) in January 1999, in document RFC 2246. Because TLS has been standardized, developers are encouraged to use TLS rather than SSL. PCT is included for backward compatibility only and should not be used for new development. When the SChannel security package is used, DCOM automatically negotiates the best protocol, depending on the client and server capabilities.

The following topics in this section briefly describe the TLS protocol and how it works with DCOM.

Note All the information about the TLS protocol in these sections also applies to the SSL and PCT protocols.

  • When to Use TLS
  • Brief Overview of How TLS Works
  • X.509 Certificates
  • Using TLS in COM

When to Use TLS

TLS is the only security option available when servers need to prove their identity to anonymous clients. This is particularly important for Web sites that want to participate in e-commerce because it allows the secure transmission of sensitive information such as credit card numbers. TLS assures that the e-commerce customers can be certain of whom they are doing business with because they are given proof of the server's identity. It also gives the e-commerce server the efficiency of not having to concern itself with authenticating the identity of each of its customers.

TLS requires that all servers prove their identity to clients. Additionally, TLS provides the option of having clients prove their identity to servers. This mutual authentication can be useful in restricting the access of certain Web pages in a large corporate intranet.

TLS supports the strongest authentication levels and offers an open architecture that permits encryption strength to increase over time to keep up with technological innovation. TLS is the best choice for environments where the highest level of security is desired for the data in transit.

Brief Overview of How TLS Works

TLS is built upon a public key infrastructure (PKI), which uses public/private key pairs for enabling data encryption and establishing data integrity, and uses X.509 certificates for authentication.

Many security protocols, such as the Kerberos v5 protocol, depend on a single key to both encrypt and decrypt data. Such protocols therefore depend on the secure exchange of encryption keys; in the Kerberos protocol, this is done through tickets obtained from the Key Distribution Center (KDC). This requires that everyone using the Kerberos protocol be registered with the KDC, which would be an impractical limitation for an e-commerce Web server that is intended to attract millions of customers from around the world. TLS therefore relies upon a PKI, which uses two keys for data encryption—when one key of the pair encrypts the data, only the other key of the pair can decrypt it. The principle benefit of this design is that encryption can be performed without requiring the secure exchange of encryption keys.

A PKI uses a technique where one of the keys is kept private and is available only to the principal to whom it is registered, while the other key is made public for anyone to access. If someone wants to send a private message to the owner of a key pair, the message can be encrypted with the public key and only the private key can be used to decrypt the message.

Key pairs are also used to verify the integrity of the data being sent. To do this, the owner of the key pair can attach a digital signature to the data before sending it. Creating a digital signature involves calculating a hash of the data and encrypting the hash with the private key. Anyone who uses the public key to decrypt the digital signature is assured that the digital signature must have come only from the person who owns the private key. Additionally, the recipient can calculate a hash of the data using the same algorithm as the sender, and if the calculated hash matches the one sent in the digital signature, the recipient can be certain that the data was not modified after it was digitally signed.

One disadvantage of using a PKI for high-volume data encryption is its relatively slow performance. Because of the intensive mathematics involved, encryption and decryption of data using an asymmetric cipher that depends on a key pair can be up to 1,000 times slower than encryption and decryption using a symmetric cipher that depends only on a single key. TLS therefore uses a PKI only for generating digital signatures and for securely negotiating the session-specific single key that will be used by both the client and the server for bulk data encryption and decryption. TLS supports a wide variety of single-key symmetric ciphers, and additional ciphers may be added in the future.

For more information about the TLS handshake protocol, see TLS Handshake Protocol.

For more details regarding the cryptography behind the TLS protocol, see About CryptoAPI.

X.509 Certificates

A critical issue that must be handled by a PKI is the ability to trust the authenticity of the public key that is being used. When you use a public key issued to a company that you want to do business with, you want to be certain that the key actually belongs to the company rather than to a thief who wants to discover your credit card number.

To guarantee the identity of a principal holding a key pair, the principal is issued an X.509 certificate by a certification authority (CA). This certificate contains information that identifies the principal, contains the principal's public key, and is digitally signed by the CA. This digital signature indicates that the CA believes that the public key contained in the certificate truly belongs to the principal identified by the certificate.

And how do you trust the CA? Because the CA itself holds an X.509 certificate that has been signed by a higher-level CA. This chain of certificate signatures continues until it reaches a root CA, which is a CA that signs its own certificates. If you trust the integrity of the root CA of a certificate, you should be able to trust the authenticity of the certificate itself. Therefore, picking root CAs that you are willing to trust is an important duty for a system administrator.

For more information about the certificate services provided by Windows, see Certificate Services and Components.

Client Certificates

When secure transport-layer protocols first emerged, their primary purpose was to guarantee that a client was connecting to an authentic server and ensure the privacy of data while in transit. However, SSL 3.0 and TLS 1.0 also include support for the transmission of a client's certificate during the protocol's handshake. This optional feature enables the mutual authentication of the client and server.

The decision of whether to use a client certificate should be made in the context of the application. Client certificates are unnecessary if the primary requirement is authenticating the server. However, if client authentication is essential, a client's certificate can be used rather than relying on custom authentication within the application. Using client certificates is preferable over custom authentication because it gives users a single sign-on scenario.

Using TLS in COM

TLS supports only the impersonate (RPC_C_IMP_LEVEL_IMPERSONATE) level of impersonation. If COM negotiates TLS as the authentication service on a proxy, COM will set the impersonation level to impersonate regardless of the process default. For impersonation to work properly in TLS, the client must provide an X.509 certificate to the server and the server must have that certificate mapped to a particular user account on the server. For more information, see the Step-by-Step Guide to Mapping Certificates to User Accounts in the Microsoft Windows 2000 Technical Library.

TLS does not support cloaking. If a cloaking flag and TLS are specified in a CoInitializeSecurity or a SetBlanket call, E_INVALIDARG will be returned.

TLS does not work with the authentication level set to none. The handshake between the client and server examines the authentication level set by each and chooses the more secure setting for the connection.

The security parameters for TLS can be set by calling CoInitializeSecurity and CoSetProxyBlanket. The following sections describe the nuances involved in making those calls.

How a Server Sets the Security Blanket

If a server wants to use TLS, it must specify SChannel (RPC_C_AUTHN_GSS_SCHANNEL) as an authentication service in the asAuthSvc parameter of CoInitializeSecurity. To prevent clients from connecting to the server by using a less secure authentication service, the server should specify only SChannel as an authentication service when it calls CoInitializeSecurity. The server cannot change the security blanket after it has called CoInitializeSecurity.

To use TLS, the following parameters should be specified when a server calls CoInitializeSecurity:

  • pVoid should be either a pointer to an IAccessControl object or a pointer to a SECURITY_DESCRIPTOR. It should not be NULL or a pointer to an AppID.
  • cAuthSvc cannot be 0 or -1. COM never chooses SChannel when cAuthSvc is -1.
  • asAuthSvc must specify SChannel as a possible authentication service. This is done by setting the following SOLE_AUTHENTICATION_SERVICE parameters for the SChannel member of the SOLE_AUTHENTICATION_LIST:
    • dwAuthnSvc must be RPC_C_AUTHN_GSS_SCHANNEL.
    • dwAuthzSvc should be RPC_C_AUTHZ_NONE. Currently, it is ignored.
    • pPrincipalName must be a pointer to a CERT_CONTEXT, cast as a pointer to OLECHAR, which represents the server's X.509 certificate.
  • dwAuthnLevel indicates the minimum authentication level that will be accepted from clients for a successful connection. It cannot be RPC_C_AUTHN_LEVEL_NONE.
  • dwCapabilities should not have the EOAC_APPID flag set. The EOAC_ACCESS_CONTROL flag should be set if pVoid points to an IAccessControl object; it should not be set if pVoid points to a SECURITY_DESCRIPTOR. For other flags that may be set, refer to CoInitializeSecurity.

For more information about using CoInitializeSecurity, see Setting Processwide Security with CoInitializeSecurity.

How a Client Sets the Security Blanket

If a client wants to use TLS, it must specify SChannel (RPC_C_AUTHN_GSS_SCHANNEL) in its list of authentication services in the pAuthList parameter of CoInitializeSecurity. If SChannel is not specified as a possible authentication service when CoInitializeSecurity is called, a later call to CoSetProxyBlanket (or IClientSecurity::SetBlanket) will fail if it tries to specify SChannel as the authentication service.

The following parameters should be specified when a client calls CoInitializeSecurity:

  • dwAuthnLevel specifies the default authentication level that the client wants to use. It cannot be RPC_C_AUTHN_LEVEL_NONE.
  • dwImpLevel must be RPC_C_IMP_LEVEL_IMPERSONATE.
  • pAuthList must have the following SOLE_AUTHENTICATION_INFO parameters as a member of the list:
    • dwAuthnSvc must be RPC_C_AUTHN_GSS_SCHANNEL.
    • dwAuthzSvc must be RPC_C_AUTHZ_NONE.
    • pAuthInfo is a pointer to a CERT_CONTEXT, cast as a pointer to void, which represents the client's X.509 certificate. If the client does not have a certificate or does not wish to present its certificate to the server, pAuthInfo must be NULL and an anonymous connection will be attempted with the server.
  • dwCapabilities is a set of flags that indicate additional client capabilities. See CoInitializeSecurity for information about which flags should be set.

For more information about using CoInitializeSecurity, see Setting Processwide Security with CoInitializeSecurity.

How a Client Changes the Security Blanket

If a client wants to use TLS but change the security blanket after calling CoInitializeSecurity, it must call either CoSetProxyBlanket or IClientSecurity::SetBlanket with parameters similar to those used in the call to CoInitializeSecurity, with the following differences:

  • pServerPrincName indicates the principal name of the server, in either msstd or fullsic format. For information on these formats, see Principal Names. If the client has the server's X.509 certificate, it can find the principal name by calling RpcCertGeneratePrincipalName.
  • pAuthInfo is a pointer to a CERT_CONTEXT, cast as a pointer to RPC_AUTH_IDENTITY_HANDLE, which represents the client's X.509 certificate. If the client does not have a certificate or does not wish to present its certificate to the server, pAuthInfo must be NULL and an anonymous connection will be attempted with the server.
  • dwCapabilities consists of flags that indicate additional client capabilities. Only four flags can be used to change security blanket settings: EOAC_DEFAULT, EOAC_MUTUAL_AUTH, EOAC_ANY_AUTHORITY, and EOAC_MAKE_FULLSIC. For more information, see CoSetProxyBlanket.

For more information about using CoSetProxyBlanket, see Setting Security at the Interface Proxy Level.

Example—A Client Changes the Security Blanket

The following example demonstrates how a client can change the security blanket to accommodate a request from the server for the client to provide its X.509 certificate.
Error handling code is omitted for brevity.

void ClientChangesSecurity ()
{
  HCRYPTPROV                   provider           = 0;
  HCERTSTORE                   cert_store         = NULL;
  PCCERT_CONTEXT               client_cert        = NULL;
  PCCERT_CONTEXT               server_cert        = NULL;
  WCHAR                        *server_princ_name = NULL;
  ISecret                      *pSecret           = NULL;
  MULTI_QI                     server_instance;
  COSERVERINFO                 server_machine;
  SOLE_AUTHENTICATION_LIST     auth_list;
  SOLE_AUTHENTICATION_INFO     auth_info[1];

  // Specify all the authentication info.
  // The client is willing to connect using SChannel,
  //   with no client certificate.
  auth_list.cAuthInfo     = 1;
  auth_list.aAuthInfo     = auth_info;
  auth_info[0].dwAuthnSvc = RPC_C_AUTHN_GSS_SCHANNEL;
  auth_info[0].dwAuthzSvc = RPC_C_AUTHZ_NONE;
  auth_info[0].pAuthInfo  = NULL;  // No certificate

  // Initialize client security with no client certificate.
  CoInitializeSecurity( NULL, 0, NULL, NULL,
                        RPC_C_AUTHN_LEVEL_PKT,
                        RPC_C_IMP_LEVEL_IMPERSONATE, &auth_list,
                        EOAC_NONE, NULL );

  // Specify info for the proxy.
  server_instance = {&IID_ISecret, NULL, S_OK};
  server_machine  = {0, L"ServerMachineName", NULL, 0};

  // Create a proxy.
  CoCreateInstanceEx( CLSID_Secret, NULL, CLSCTX_REMOTE_SERVER,
                      &server_machine, 1, &server_instance);
  pSecret = (ISecret *) server_instance.pItf;

  /* The client obtained the server's certificate during the handshake. */
  /* The server requests a certificate from the client. */

  // Get the default certificate provider.
  CryptAcquireContext( &provider, NULL, NULL, PROV_RSA_SCHANNEL, 0 );

  // Open the certificate store.
  cert_store = CertOpenSystemStore( provider, L"my" );

  // Find the client's certificate.
  client_cert =
    CertFindCertificateInStore( cert_store,
                                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                                0,
                                CERT_FIND_SUBJECT_STR,
                                L"ClientName",  // Use the principal name
                                NULL );

  // Find the fullsic principal name of the server.
  RpcCertGeneratePrincipalName( server_cert, RPC_C_FULL_CERT_CHAIN,
                                &server_princ_name );

  // Change the client's security:
  // Increase the authentication level and attach a certificate.
  CoSetProxyBlanket( pSecret, RPC_C_AUTHN_GSS_SCHANNEL,
                     RPC_C_AUTHZ_NONE,
                     server_princ_name, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                     RPC_C_IMP_LEVEL_IMPERSONATE,
                     (RPC_AUTH_IDENTITY_HANDLE *) client_cert,
                     EOAC_NONE );

  cleanup:
  if (server_princ_name != NULL)
    RpcStringFree( &server_princ_name );
  if (client_cert != NULL)
    CertFreeCertificateContext(client_cert);
  if (server_cert != NULL)
    CertFreeCertificateContext(server_cert);
  if (cert_store != NULL)
    CertCloseStore( cert_store, CERT_CLOSE_STORE_CHECK_FLAG );
  if (provider != 0 )
    CryptReleaseContext( provider, 0 );
  if (pSecret != NULL)
    pSecret->Release();
  CoUninitialize();
}

See Also

Microsoft Secure Channel in the Security node of the Platform SDK.

NTLMSSP

Kerberos v5 Protocol

Snego


Snego

Snego, whose authentication service identifier is RPC_C_AUTHN_GSS_NEGOTIATE, does not actually provide authentication services itself. Instead, it takes a list of authentication services and negotiates a service that will work between the client and server. The authentication parameters are not used by Snego but are passed to the chosen authentication service, which does the actual authentication. Snego was standardized by the Internet Engineering Task Force (IETF) in December 1998, in document RFC 2478.

Snego is useful when you don't know what authentication services the remote machine can provide. By specifying NTLMSSP and the Kerberos protocol as authentication services under Snego, you will be able to use the Kerberos protocol to connect to a remote machine that is running Windows 2000 or later, while maintaining the ability to connect to remote machines running Windows NT by using NTLMSSP.

To use Snego, both the client and the server must specify Snego as the authentication service. The server specifies RPC_C_AUTHN_GSS_NEGOTIATE as the dwAuthnSvc member of one of the SOLE_AUTHENTICATION_SERVICE structures in the asAuthSvc array parameter that is passed to CoInitializeSecurity. The client can specify Snego by calling CoSetProxyBlanket and passing RPC_C_AUTHN_GSS_NEGOTIATE as the dwAuthnSvc parameter. The client should also provide a list of possible authentication services for Snego through the PackageList member of the SEC_WINNT_AUTH_IDENTITY_EX structure that is passed to the pAuthInfo parameter in the call to CoSetProxyBlanket. If pAuthInfo is NULL, Snego composes a list of authentication services from the security packages installed on the machine. Then Snego sends the list of authentication services to the server, compares the list to the server's available authentication services, and picks an authentication service to use for the connection.

Note SChannel cannot be on the list of authentication services that Snego uses.

Clients can also specify Snego when they call CoInitializeSecurity. The dwAuthnSvc and pAuthInfo parameters of CoSetProxyBlanket become members of a SOLE_AUTHENTICATION_INFO structure that is passed to CoInitializeSecurity through its pAuthList parameter. The details of the values of those members are the same as described in the preceding paragraph.

If Snego is used, calls to CoQueryProxyBlanket or CoQueryClientBlanket will return Snego as the authentication service, rather than the actual authentication service that Snego picked for establishing the connection.

See Also

NTLMSSP

Kerberos v5 Protocol

SChannel

CoInitializeSecurity

IClientSecurity::SetBlanket


COM Security, Windows 95, and Windows 98

The DCOM-enabled versions of COM for Microsoft® Windows® 95 and Microsoft Windows 98 include a subset of the COM security found on Microsoft Windows NT® and Microsoft Windows 2000. For more information, see the following topics:

  • Call-Level Security in Windows 95 and Windows 98
  • Remote Connections and Activation in Windows 95 and Windows 98
  • Security on Windows 95 and Windows 98-Only Networks


Call-Level Security in Windows 95 and Windows 98

The current release of DCOM for Microsoft® Windows® 95 and Microsoft Windows 98 has the same default access permission as Microsoft Windows NT® and Microsoft Windows 2000. Dcomcnfg.exe can be used to change the default on both operating systems. Programmatic security is supported as follows:

  • CoInitializeSecurity, IClientSecurity, CoGetCallContext, and the related helper functions are all fully supported. However, CoInitializeSecurity returns E_NOTIMPL if you specify a security descriptor for the pVoid parameter.
  • IServerSecurity and its related helper functions are all fully supported except for IServerSecurity::ImpersonateClient and its helper function CoImpersonateClient. However, IAccessControl is also supported on DCOM for Windows 95 and Windows 98, which helps a server do access checking (without impersonation).
  • Windows 95 and Windows 98 machines can make calls at any authentication level, but incoming calls will arrive at an authentication level of none or connect (RPC_C_AUTHN_LEVEL_NONE or RPC_C_AUTHN_LEVEL_CONNECT).

In Windows 95 (with dcom951.1 and later) and in Windows 98, you can set the authentication level for an application using the AuthenticationLevel named value under the AppID key in the registry. This setting is used when the application does not call CoInitializeSecurity and when the application calls CoInitializeSecurity and specifies an AppID. For more information, see Setting Processwide Security Through the Registry.

See Also

Remote Connections and Activation in Windows 95 and Windows 98

Security on Windows 95 and Windows 98-Only Networks


Remote Connections and Activation in Windows 95 and Windows 98

By default, DCOM for Windows 95 and Windows 98 does not allow remote machines to connect to running objects. To enable remote connections, you must change the EnableRemoteConnect named value to "Y". (It is set to "N" by default.) The OLE/COM Object Viewer provides a user interface for this task in the File.System Configuration... dialog box. Changing this value requires a reboot. You can also use dcomcnfg.exe to set EnableRemoteConnect.

If EnableRemoteConnect is not set to "Y", new connections from remote clients will fail. The caller will, typically, get an RPC_S_SERVER_UNAVAILABLE return code. However, remote calls from the machine and callbacks to the machine will still work (assuming the EnableDCOM registry named value is set to "Y").

All processes on Windows 95 and Windows 98 run with the same security settings—those belonging to the currently logged on user. Therefore, because it would not be possible to securely launch a process from another machine, DCOM on Windows 95 and Windows 98 does not support remote server launch and will never launch a process. However, remote clients can still make connections to COM servers running under Windows 95 or Windows 98, as long as the process is already running, CoRegisterClassObject has been called, and the EnableRemoteConnect registry key has been set to "Y". Also, remote connects are allowed on file moniker binds.

The DefaultLaunchPermission and LaunchPermission registry values have no effect on Windows 95 and Windowds 98.

See Also

Call-Level Security in Windows 95 and Windows 98

Security on Windows 95 and Windows 98-Only Networks


Security on Windows 95 and Windows 98-Only Networks

If you have a network of machines running Microsoft® Windows® 95 or Microsoft Windows 98, it is important to know whether there is a Microsoft Windows NT® or Microsoft Windows 2000 domain in the network. If there is a Windows NT or Windows 2000 domain, both Windows 95 and Windows 98 can provide authentication and authorization using a pass-through security mechanism. However, if there is no Windows NT or Windows 2000 domain in the network, only unsecure calls can be made.

Because the default behavior of COM is to try to make a secure call and then default to an unsecure call, you need to change COM's default behavior before a COM client can successfully call a COM server over a Windows 95 and Windows 98-only network. To change this behavior, the following tasks must be completed:

  • Both the client and server must set the authentication level for call security to be RPC_C_AUTHN_LEVEL_NONE.
  • On activation, the client must specify an authentication level of RPC_C_AUTHN_LEVEL_NONE. (For more information, see COAUTHINFO and COSERVERINFO.)
  • Reference tracking must be disabled.

For a detailed explanation of these issues, see the MSDN Knowledge Base article Q174024 entitled FAQ: DCOM95 Frequently Asked Questions.

See Also

Call-Level Security in Windows 95 and Windows 98

Remote Connections and Activation in Windows 95 and Windows 98


Error Handling

Almost all COM functions and interface methods return a value of the type HRESULT. The HRESULT (for result handle) is a way of returning success, warning, and error values. HRESULTs are really not handles to anything; they are only 32-bit values with several fields encoded in the value. A zero result indicates success, and a nonzero result indicates failure.

HRESULTs work differently depending on the platform you are using. On 16-bit platforms, an HRESULT is generated from a 32-bit value known as a status code, or SCODE. On 32-bit platforms, an HRESULT is identical to an SCODE. COM uses only HRESULTs.

At the source code level, all error values consist of three parts, separated by underscores. The first part is the prefix that identifies the facility associated with the error, the second part is E for error, and the third part is a string that describes the actual condition. For example, STG_E_MEDIUMFULL is returned when there is no space left on a hard disk. The STG prefix indicates the storage facility, the E indicates that the status code represents an error, and the MEDIUMFULL provides specific information about the error. Many of the values that you might want to return from an interface method or function are defined in winerror.h.

For more information on error handling, see the following sections:

  • Structure of COM Error Codes
  • Codes in FACILITY_ITF
  • Using Macros for Error Handling
  • COM Error Handling in Java and Visual Basic
  • Error Handling Strategies
  • Handling Unknown Errors


Structure of COM Error Codes

SCODEs on 16-bit platforms are divided into four fields: a severity code, a context field, a facility field, and an error code. The following illustration shows the format of an SCODE on a 16-bit platform; the numbers indicate bit positions:

HRESULTs on 32-bit platforms have the following format:

The severity code in the 16-bit SCODE and the high-order bit in the HRESULT indicate whether the return value represents success or failure. If set to zero, SEVERITY_SUCCESS, the value indicates success. If set to 1, SEVERITY_ERROR, it indicates failure.

In the 16-bit version of the SCODE, the context field is reserved; this field does not exist in the 32-bit version. The R, C, N, and r bits are reserved in both.

The facility field in both versions indicates the system service responsible for the error. There are currently five facilities: FACILITY_NULL, FACILITY_ITF, FACILITY_DISPATCH, FACILITY_RPC, and FACILITY_STORAGE. Microsoft allocates new facility codes as they become necessary. Most SCODEs and HRESULTs set the facility field to FACILITY_ITF, indicating an interface method error.

The various facility fields are described in the following table.

Facility | Description
FACILITY_NULL | For broadly applicable common status codes such as S_OK. This facility code has a value of zero.
FACILITY_ITF | For most status codes returned from interface methods, value is defined by the interface. That is, two SCODEs or HRESULTs with exactly the same 32-bit value returned from two different interfaces might have different meanings. This facility has a value of 4.
FACILITY_DISPATCH | For late-binding IDispatch interface errors. This facility has a value of 2.
FACILITY_RPC | For status codes returned from remote procedure calls. This facility has a value of 1.
FACILITY_STORAGE | For status codes returned from IStorage or IStream method calls relating to structured storage. Status codes whose code (lower 16 bits) value is in the range of DOS error codes (that is, less than 256) have the same meaning as the corresponding DOS error. This facility has a value of 3.
FACILITY_WINDOWS | Used for additional error codes from Microsoft-defined interfaces.
FACILITY_WIN32 | Used to provide a means of handling error codes from functions in the Win32® API as an HRESULT. Error codes in 16-bit OLE that duplicated Win32 error codes have also been changed to FACILITY_WIN32.

The code field is a unique number that is assigned to represent the error or warning.

By convention, HRESULTs generally have names in the following format:

Facility_Severity_Reason

Facility is either the facility name or some other distinguishing identifier; Severity is a single letter, S or E, that indicates whether the function call succeeded (S) or failed (E); and Reason is an identifier that describes the meaning of the code. For example, the status code STG_E_FILENOTFOUND indicates a storage-related error has occurred; specifically, a requested file does not exist. Status codes from FACILITY_NULL omit the Facility_ prefix.

Error codes are defined within the context of an interface implementation. Once defined, success codes cannot be changed or new success codes added. However, new failure codes can be written. Microsoft reserves the right to define new failure codes (but not success codes) for the interfaces described in FACILITY_ITF or in new facilities.


Codes in FACILITY_ITF

HRESULTs with facilities such as FACILITY_NULL and FACILITY_RPC have universal meaning because they are defined at a single source: Microsoft. However, HRESULTs in FACILITY_ITF are determined by the function or interface method from which they are returned. This means that the same 32-bit value in FACILITY_ITF returned from two different interface methods might have different meanings.

The reason HRESULTs in FACILITY_ITF can have different meanings in different interfaces is that HRESULTs are kept to an efficient data type size of 32 bits. Unfortunately, 32 bits is not large enough for the development of an error code allocation system that avoids conflicting codes allocated by different programmers at different times in different places (unlike the handling of interface identifiers and CLSIDs). As a result, the 32-bit HRESULT is structured such that Microsoft can define several universal error codes, while allowing other programmers to define new error codes without fear of conflict. The status code convention is as follows:

  1. Status codes in facilities other than FACILITY_ITF can be defined only by Microsoft.
  2. Status codes in facility FACILITY_ITF are defined solely by the developer of the interface or function that returns the status code. To avoid conflicting error codes, whoever defines the interface is responsible for coordinating and publishing the FACILITY_ITF status codes associated with that interface.

All the COM-defined FACILITY_ITF codes have a code value in the range of 0x0000–0x01FF. While it is legal to use any codes in FACILITY_ITF, it is recommended that only code values in the range of 0x0200–0xFFFF be used. This recommendation is made as a means of reducing confusion with any COM-defined errors.

It is also recommended that developers define new functions and interfaces to return error codes as defined by COM and in facilities other than FACILITY_ITF. In particular, interfaces that have any chance of being remoted using RPC in the future should define the FACILITY_RPC codes as legal. E_UNEXPECTED is a specific error code that most developers will want to make universally legal.


Using Macros for Error Handling

COM defines a number of macros that make it easier to work with SCODEs on 16-bit platforms and HRESULTs on both platforms. Some of the macros and functions below convert return values of different data types and are quite useful in code that runs only on 16-bit platforms, code that runs both on 16-bit and on 32-bit platforms, and 16-bit code that is being ported to a 32-bit platform. These same macros are meaningless in 32-bit environments and are available to provide compatibility and make porting easier. Newly written code should use the HRESULT macros and functions.

The error handling macros are described in the following table.

Macro | Description
GetScode | (Obsolete) Returns an SCODE given an HRESULT.
ResultFromScode | (Obsolete) Returns an HRESULT given an SCODE.
PropagateResult | (Obsolete) Generates an HRESULT to return to a function in cases where an error is being returned from an internally called function.
MAKE_HRESULT | Returns an HRESULT given an SCODE that represents an error.
MAKE_SCODE | Returns an SCODE given an HRESULT.
HRESULT_CODE | Extracts the error code part of the HRESULT.
HRESULT_FACILITY | Extracts the facility from the HRESULT.
HRESULT_SEVERITY | Extracts the severity bit from the SEVERITY.
SCODE_CODE | Extracts the error code part of the SCODE.
SCODE_FACILITY | Extracts the facility from the SCODE.
SCODE_SEVERITY | Extracts the severity field from the SCODE.
SUCCEEDED | Tests the severity of the SCODE or HRESULT—returns TRUE if the severity is zero and FALSE if it is one.
FAILED | Tests the severity of the SCODE or HRESULT—returns TRUE if the severity is one and FALSE if it is zero.
IS_ERROR | Provides a generic test for errors on any status value.
FACILITY_NT_BIT | Defines bits so macros are guaranteed to work.
HRESULT_FROM_WIN32 | Maps a Win32® error value into an HRESULT. This assumes that Win32 errors fall in the range -32K to 32K.
HRESULT_FROM_NT | Maps an NT status value into an HRESULT.

Note Calling MAKE_HRESULT for S_OK verification carries a performance penalty. You should not routinely use MAKE_HRESULT for successful results.


COM Error Handling in Java and Visual Basic

There are three interfaces and three functions that can be used in COM to provide error handling when programming in Java or Microsoft® Visual Basic®. In Java and Visual Basic, the method call does not return an HRESULT as the return value. Instead, these languages use the COM interfaces and functions to obtain HRESULTs and to handle errors or exceptions. (Exceptions are events beyond the program's control, such as file problems or invalid parameters.)

The three interfaces that provide support for HRESULTs are listed and described briefly in the following table.

ICreateErrorInfo | Sets error information.
IErrorInfo | Returns information from an error object.
ISupportErrorInfo | Identifies the object as supporting the IErrorInfo interface.

The three functions that provide support for HRESULTs are listed and described briefly in the following table.

CreateErrorInfo | Creates an instance of a generic error object.
GetErrorInfo | Obtains the error information pointer set by the previous call to SetErrorInfo in the current logical thread.
SetErrorInfo | Sets the error information object for the current thread of execution.

Initially, these error-handling interfaces and functions applied exclusively to COM Automation objects. For this reason, they are documented under Automation in the COM and ActiveX Object Services branch of the Platform SDK.


Returning Error Information

Using the COM error-handling interfaces and functions, you can return error information by performing the following the steps:

  1. Implement the ISupportErrorInfo interface.
  2. To create an instance of the generic error object, call the CreateErrorInfo function.
  3. To set its contents, use the ICreateErrorInfo methods.
  4. To associate the error object with the current logical thread, call the SetErrorInfo function.

The error handling interfaces create and manage an error object, which provides information about the error. The error object is not the same as the object that encountered the error. It is a separate object associated with the current thread of execution.


Retrieving Error Information

Using the COM error-handling interfaces and functions, you can retrieve error information as follows:

  1. Check whether the returned value represents an error that the object is prepared to handle.
  2. Call IUnknown::QueryInterface to get a pointer to the ISupportErrorInfo interface. Then call InterfaceSupportsErrorInfo to verify that the error was raised by the object that returned it and that the error object pertains to the current error and not to a previous call.
  3. To get a pointer to the error object, call the GetErrorInfo function.
  4. To retrieve information from the error object, use the IErrorInfo methods.

If the object is not prepared to handle the error but needs to propagate the error information further down the call chain, it should simply pass the return value to its caller. Because the GetErrorInfo function clears the error information and passes ownership of the error object to the caller, the function should be called only by the object that handles the error.


Error Handling Strategies

Because interface methods are virtual, it is not possible for a caller to know the full set of values that may be returned from any one call. One implementation of a method may return five values; another may return eight.

The documentation lists common values that may be returned for each method; these are the values that you must check for and handle in your code because they have special meanings. Other values may be returned, but because they are not meaningful, you do not need to write special code to handle them. A simple check for zero or nonzero is adequate.


HRESULTs

The values of some HRESULTs have been changed in COM to eliminate all duplication and overlapping with Win32 error codes. Those that duplicate Win32 error codes have been changed to FACILITY_WIN32, and those that overlap remain in FACILITY_NULL. Their 32-bit values are listed in the following table.

HRESULT | Value
E_UNEXPECTED | 0x8000FFFF
E_NOTIMPL | 0x80004001
E_OUTOFMEMORY | 0x8007000E
E_INVALIDARG | 0x80070057
E_NOINTERFACE | 0x80004002
E_POINTER | 0x80004003
E_HANDLE | 0x80070006
E_ABORT | 0x80004004
E_FAIL | 0x80004005
E_ACCESSDENIED | 0x80070005

See Also

HRESULT Definitions


HRESULT Definitions

The return value of COM functions and methods is an HRESULT. The following table lists the standard HRESULT definitions.

HRESULTs | Definitions
E_NOINTERFACE | The QueryInterface function did not recognize the requested interface. The interface is not supported.
E_NOTIMPL | The function contains no implementation.
E_FAIL | An unspecified failure has occurred.
E_OUTOFMEMORY | The function failed to allocate necessary memory.
E_POINTER | Invalid pointer.
E_INVALIDARG | One or more arguments are invalid.
E_UNEXPECTED | A catastrophic failure has occurred.
E_HANDLE | Invalid handle.
E_ABORT | Operation aborted.

Requirements

To use the return values, you must include winerror.h in your project.


Win32 and Network Errors

If the first four digits of the error code are 8007, this indicates a Win32 or network error. You can use the net command to decode these types of errors. To decode the error, first convert the last four digits of the hexadecimal error code to decimal. Then, at the command prompt, type the following, where decimal code is replaced with the return value you want to decode:

    net helpmsg <decimal code>

The net command returns a description of the error. For example, if COM returns the error 8007054B, convert the 054B to decimal (1355). Then type the following:

    net helpmsg 1355

The net command returns the error description:

    The specified domain did not exist.


Handling Unknown Errors

It is legal to return a status code only from the implementation of an interface method sanctioned as legally returnable. Failure to observe this rule invites the possibility of conflict between returned error-code values and those sanctioned by the application. Pay particular attention to this potential problem when propagating error codes from functions that are called internally.

Applications that call interfaces should treat any unknown returned error code (as opposed to a success code) as synonymous with E_UNEXPECTED. This practice of handling unknown error codes is required by clients of the COM-defined interfaces and functions. Because typical programming practice is to handle a few specific error codes in detail and treat the rest generically, this requirement of handling unexpected or unknown error codes is easily met.

The following code sample shows the recommended way of handling unknown errors:

HRESULT hrErr;
hrErr = xxMethod();

switch (GetScode(hrErr))  {
    case NOERROR:
      //success
      break;

    case x1:
      .
      .
      break;

    case x2:
      .
      .
      break;

    case E_UNEXPECTED:
    default:
      //general failure
      break;
}

The following error check is often used with those routines that do not return anything special (other than S_OK or some unexpected error):

if (xxMethod() == NOERROR)
    //success
else
    //general failure;


COM Handlers

The purpose of COM handlers is to provide some "smart" code in the client address space that can optimize calls between a client and server. Handlers can override some interfaces while delegating others directly to the server (through the proxy).

There are two types of COM handlers:

  • The OLE handler is a heavyweight DLL that provides important services for linking and embedding. It is usually created before the server is launched and is initialized so that the server is activated when the embedded object enters the running state.
  • The lightweight client-side handler can be created for any purpose, can be very small, and cannot be created before the server.


The OLE Handler

An OLE handler is a DLL containing several interacting components used for linking and embedding. To avoid the expense of constant interprocess communication between a container and its server, the handler is loaded into the container's process space to act on behalf of a server as a sort of surrogate process. The OLE handler manages container requests that do not require the attention of the server application, such as requests for drawing. When a container requests something that the object handler cannot provide, the handler communicates with the server application using the COM out-of-process communication mechanism.

The OLE handler components include remoting pieces to manage communication between the handler and its server, a cache for storing an object's data (along with information on how that data should be formatted and displayed), and a controlling object that coordinates the activities of the DLL's other components. In addition, if an object is a link, the DLL also includes a linking component, or linked object, which keeps track of the name and location of the link source.

OLE provides a default handler that most applications use for linking and embedding. If the default does not match the requirements of your server, you can either completely replace the default handler or use parts of the functionality it provides where appropriate. In the latter case, the application handler is implemented as an aggregate object composed of a new control object and the default handler. Combination application/default handlers are also known as in-process handlers. The remoting handler is used for objects that are not assigned a CLSID in the system registry or that have no specified handler. All that is required from a handler for these types of objects is that they pass information across the process boundary. To create a new instance of the default handler, call OleCreateDefaultHandler. For some special circumstances, call OleCreateEmbeddingHelper.

When you create an instance of a handler for one class, you cannot use it for another. When used for a compound document, the OLE handler implements the container-side data structures when objects of a particular class are accessed remotely.

OLE defined the default handler for clients of compound document local servers. The default handler implemented a number of interfaces that the typical server did not. When OLE subsequently allowed in-process servers for compound documents, they had to create an embedding helper that implemented these extra interfaces so that clients could seamlessly work with them.

Framework designers that define and implement a client-side handler should consider this issue in their design and should provide an equivalent in-process helper for the same reasons. Even if the designers currently don't implement interfaces on the handler that the servers don't expose, they may want to define a helper now so that they can add them in the future. Alternatively, one could implement the extra interfaces on the server object itself.

See Also

The Lightweight Client-Side Handler


The Lightweight Client-Side Handler

Lightweight client-side handlers allow you to create general client-side handlers of any size, to help you do any kind of standard task. As handlers, these are usable by more than one client. They differ from OLE handlers in that they cannot be created before the server is launched, and their lifetime is tied to that of the proxy manager, preventing a possible race condition in which the handler could otherwise be prematurely released.

The proxy manager is the system-created object that implements the IMarshal interface. If you use standard marshaling, the system creates it for you when you call CoGetStandardMarshal (or CoGetStdMarshalEx, for creating an aggregated marshaler for lightweight handlers) and also implements the IClientSecurity and IMultiQI interfaces on the object. On the server side, there is a corresponding system object that also implements IMarshal. These objects handle marshaling for you transparently.

There are two general types of these handlers that you may want to implement:

  • A handler that performs a service that requires no extra data from the server
  • A handler that uses extra data from the server

Some potential uses of the extra data in the stream supplied by the server are as follows:

  • Static data from server—Regardless of the particular interface being marshaled, the server writes the same data into the stream.
  • Per-interface data from server—Depending on which particular interface is being marshaled, the server may write different data into the stream.
  • Per-interface helpers—Per-interface COM components aggregated into the client handler and delegating to the standard proxy. For example, to improve network performance, a COM component for IStream could do client-side caching of data, read-ahead, write-behind, op-locking, and so forth.
  • Network version of an interface—The network version of the interface is different from the interface exposed by the client and server application code. It is possible, for example, to multiplex exposed interfaces IA and IB over the same network interface INetAB, the way the embedding server handler does. For example, one could convert a data transfer interface into a network interface that uses pipes for efficient data transfer.

Down-level clients may not have the capability of unmarshaling interfaces that have custom handlers, for two reasons: First, they may not understand the CLSID used in the custom marshaled packet when the server handler is aggregated and the object wants a handler. Second, the handler code may not even run on the client side if it requires new functionality from COM to create the aggregated standard marshaler and to do remote QueryInterface calls.

See Also

The OLE Handler


Implementing and Activating a Handler with No Extra Server Data

To create an instance of your handler, if it is one that gets no extra server data, the server must implement IStdMarshalInfo but not IMarshal. IStdMarshalInfo has one method, GetClassForHandler, which retrieves the CLSID of the object handler to be used in the destination process. COM calls this when it calls CoMarshalInterface for you and activates the handler on the client side.

Next, both the server and handler implementations must call a function new in Microsoft® Windows® 2000, CoGetStdMarshalEx. This function creates a standard marshaler on each side (called a proxy manager on the client side and a stub manager on the server side).

The server calls CoGetStdMarshalEx, passing in the flag SMEXF_SERVER. This creates a server-side standard marshaler (stub manager). The server-side structure is shown in the following illustration:

Server-Side Structure

The handler calls CoGetStdMarshalEx, passing in the flag SMEXF_HANDLER. This creates a client-side standard marshaler (proxy manager) and aggregates it with the handler on the client side. The lifetime of both are managed by the controlling identity object (implementing IUnknown) that the system implements when the handler calls CoGetStdMarshalEx. The client-side structure is shown in the following illustration.

Client-Side Structure

As shown in the preceding illustration, the handler is actually sandwiched between the proxy manager and the identity/controlling unknown. This gives the system control over the lifetime of the object while giving the handler control over the exposed interfaces. The dashed line between the Identity and Proxy Manager indicates that the two share tight integration through internal private interfaces.

When COM calls CoUnmarshalInterface for the client, it creates the handler instance, aggregating it with the Identity. The handler will create the standard marshaler (through the call to CoGetStdMarshalEx, passing in the controlling unknown it received when it was created). The handler does not implement IMarshal but just returns IMarshal from the standard marshaler. Even if the handler does implement IMarshal, it will not get called during an unmarshal.

If two threads simultaneously unmarshal the same object for the first time, it is possible for two handlers to get created temporarily. One will subsequently be released.


Implementing and Activating a Handler with Extra Data Supplied by Server

If the server wants to include some extra data in the packet for the handler to use, the server must implement both the IMarshal and the IStdMarshalInfo interfaces. The server must aggregate the standard marshaler and must delegate the first part of the marshaling to the aggregated standard marshaler, including IMarshal::GetUnmarshalClass, and must add its own data size to the size returned by the standard marshaler's IMarshal::GetMarshalSizeMax. The standard marshaler calls IStdMarshalInfo::GetClassForHandler to get the CLSID of the handler to be created. After the standard marshaler has done its marshaling, the server then writes its own extra data into the stream. The resulting structures, with extra data in the stream, are shown in the following illustration:

Server-Side Structures with Extra Data in Stream

This allows the call from COM to CoUnmarshalInterface on the client side the ability to skip over any unread data and leave the stream in the appropriate position following all the marshaled interface data if the handler cannot be created.

As in the case where there is no extra server data in the stream, the client-side COM call to CoUnmarshalInterface will create the identity and handler. The handler must implement IMarshal and must delegate the IMarshal calls to the aggregated standard marshaler first and then marshal or unmarshal any extra data that the server provided. The handler's UnmarshalInterface will be called for every unmarshal, regardless of whether it has unmarshaled the interface before or not. In this case, the server does not call CoGetStdMarshalEx but the handler must. The resulting client-side structure is shown in the following illustration.

Client-Side Structures with Extra Data in Stream


Delegation of QueryInterface

Handlers that require access to some of the internal interfaces on the proxy manager have to go through the IInternalUnknown interface. This prevents handlers from blindly delegating and exposing the aggregatee's internal interfaces outside of the aggregate. These interfaces include IProxyManager, IClientSecurity, and IMultiQI. If the handler wants to expose IClientSecurity or IMultiQI, it should implement them itself.

In the case of IClientSecurity, if the client tries to set the security on an interface that the handler has exposed, the handler should set the security on the underlying network interface proxy.

In the case of IMultiQI, the handler should fill in the interfaces it knows about and then forward the call to the proxy manager to get the rest of the interfaces filled in.


DLL Surrogates

COM now makes it possible to create DLL servers that can be loaded into a surrogate EXE process. This combines the ease of writing DLL servers with the benefits of executable implementation. Standard development tools like Microsoft® Visual Basic®, Microsoft Visual J++®, and Microsoft Visual Studio® facilitate the writing of DLL servers, but a DLL server in itself has limits. Running the DLL server in a surrogate process offers several possible benefits:

  • Fault isolation and the ability to service multiple clients simultaneously.
  • In a distributed environment, a DLL server implementation could be used to service remote clients.
  • It could permit clients to protect themselves from untrusted server code while allowing them access to the services the DLL server provides.
  • Running a DLL server in a surrogate provides the DLL with the surrogate's security.

COM provides a default surrogate process, or you can write a custom surrogate if you have special needs. The following topics in this section provide more information on DLL surrogates:

  • DLL Server Requirements
  • Registering the DLL Server for Surrogate Activation
  • Using the System-Supplied Surrogate
  • Writing a Custom Surrogate


DLL Server Requirements

While most DLLs can run in a surrogate, some DLLs cannot. 16-bit DLLs are not supported; only 32-bit DLLs can be loaded into surrogate processes.

Further, the DLL must be well-behaved if you want to use the system-supplied surrogate. For example, a DLL that calls methods that register callbacks from the client would try to invoke those callbacks as if the function pointers it received were for instructions in its address space, which is not the case. Similarly, a DLL that uses a global variable that it expects the client to access would not work. In general, parameters that cannot be properly marshaled will prevent the DLL server from running outside the client process. In many cases, you can write a custom surrogate specifically designed to compensate for "bad" behavior. (For more information, see Writing a Custom Surrogate.)

If the DLL server uses custom interfaces, you would have to ensure that marshaling code is available for those interfaces. For example, you could build and register a proxy DLL or provide and register a type library that would allow the server to function correctly while running in a surrogate.

DLL servers will be loaded only into a surrogate process running in the proper security context. The security context for the DLL server surrogate is determined in the same way as for EXE servers. The DLL server surrogate runs in the same security context as the client, unless a RunAs named-value, which determines the security context, is set in the AppID registry section for the server.

See Also

Surrogate Sharing

Registering the DLL Server for Surrogate Activation


Surrogate Sharing

DLL servers will share a surrogate if they have matching security identities and share the same AppID value.

DLL servers are loaded, by default, into their own surrogate process. To load other DLL servers into an existing surrogate so that it supports more than one DLL server, there are two requirements:

  1. The DLL servers must have the same AppID value.
  2. The security context of the DLL servers must be the same.

If two DLL servers are to be launched under different security identities, they must be in different surrogates, whether or not their AppIDs match.

Following is an example of administering surrogate sharing with AppIDs:

    AppID
        {12345678-0000-0000-0000-abcdabcdabcd}
            @DllSurrogate    REG_SZ
    CLSID
        {12345678-0000-0000-0000-000000000001}
            @AppId    REG_SZ    {12345678-0000-0000-0000-abcdabcdabcd}
            InProcServer32
    @    REG_SZ    c:\myapp\comp1.dll
        {12345678-0000-0000-0000-000000000002}
            @AppId    REG_SZ    {12345678-0000-0000-0000-abcdabcdabcd}
            InProcServer32
    @    REG_SZ    c:\myapp\comp2.dll

The two CLSIDs for DLL components comp1.dll and comp2.dll have been configured to share an AppID. The AppID specifies that the DLL server can be loaded in a surrogate by specifying the DllSurrogate named-value. In this example, the DllSurrogate named-value is an empty string, indicating that the default system implementation of the DLL surrogate should be used.

See Also

Registering the DLL Server for Surrogate Activation


Registering the DLL Server for Surrogate Activation

A DLL server will be loaded into a surrogate process under the following conditions:

  1. There must be an AppID value specified under the CLSID key in the registry, and a corresponding AppID key.
  2. In an activation call, the CLSCTX_LOCAL_SERVER bit is set and the CLSID key does not specify LocalServer32, LocalServer, or LocalService.
  3. The CLSID key contains the InProcServer32 subkey.
  4. The proxy/stub DLL specified in the InProcServer32 key exists.
  5. The DllSurrogate value exists under the AppID key.

If there is a LocalServer, LocalServer32, or LocalService, indicating the existence of an EXE, the EXE server or service will always be launched in preference to loading a DLL server into a surrogate process.

The DllSurrogate named-value must be specified for surrogate activation to occur. Activation refers to calls to any of the following activation functions:

  • CoGetClassObject
  • CoCreateInstanceEx
  • CoGetInstanceFromFile
  • CoGetInstanceFromIStorage
  • IMoniker::BindToObject

To launch an instance of the system-supplied surrogate, set the value of DllSurrogate either to an empty string or to NULL. To specify the launch of a custom surrogate, set the value to the path of the surrogate.

If both RemoteServerName and DllSurrogate are specified for the same AppID, the RemoteServerName value is ignored and the DllSurrogate named-value causes an activation on the local machine. For remote surrogate activation, specify RemoteServerName but not DllSurrogate on the client, and specify DllSurrogate on the server.

A DLL server that is designed to always run alone in its own surrogate process is best configured with an AppID equal its CLSID. Under the AppID, simply specify a DllSurrogate named-value with an empty string value.

It is best to configure a DLL server that is designed to run alone in its own surrogate process and to service multiple clients across a network with a RunAs value specified under the AppID registry key. Whether the RunAs specifies "Interactive User" or a specific user identity depends upon the user interface, security, and other server requirements. Specifying a RunAs value ensures that only one instance of the server is loaded to service all of the clients, regardless of the identity of the client. On the other hand, do not configure the server with RunAs if the intention is to have one instance of the DLL server running in surrogate to service each remote client identity.

See Also

Surrogate Sharing


Using the System-Supplied Surrogate

To use the system-supplied surrogate for your DLL server, register the DLL specifying an empty string or NULL for the DllSurrogate named-value in the registry. When an activation request for a DLL server so designated comes to COM, COM launches the default surrogate process and the requested DLL (by specifying the CLSID on the launch command line internally) at the same time to avoid a separate call. (For information on running more than one DLL server in a surrogate process, see Surrogate Sharing.)

The default implementation of the surrogate process is a mixed-threading model style pseudo-COM server. When multiple DLL servers are loaded into a single surrogate process, this process ensures that each DLL server is instantiated using the threading model specified in the registry for that server. All loaded free-threaded servers will live together in the multithreaded apartment, while each apartment-threaded server will reside in a single-threaded apartment. If a DLL server supports both threading models, COM will choose multithreading.

This surrogate process is written so that COM handles both the unloading of DLL servers and the terminating of the surrogate process.

The system-provided surrogate will work very well for most developers, as well as being very easy to use. However, those developers with special considerations may decide that a custom surrogate is necessary. For more information, see Writing a Custom Surrogate.


Writing a Custom Surrogate

While the system-supplied surrogate will be more than adequate for most situations, there are some cases where writing a custom surrogate could be worthwhile. Following are some examples:

  • A custom surrogate could provide some optimizations or semantics not present in the system surrogate.
  • If an in-process DLL contains code that depends on being in the same process as the client, the DLL server will not function correctly if it is running in the system surrogate. A custom surrogate could be tailored to a specific DLL to deal with this.
  • The system surrogate supports a mixed-threading model so that it can load both free and apartment model DLLs. A custom surrogate might be tailored to load only apartment DLLs for reasons of efficiency or to accept a command-line argument for the type of DLL it is allowed to load.
  • A custom surrogate could take extra command-line parameters that the system surrogate does not.
  • The system surrogate calls CoInitializeSecurity and tells it to use any existing security settings found under the AppID key in the registry. A custom surrogate could use another security context.
  • Interfaces that aren't remotable (such as those for recent OCXs) will not work with the system surrogate. A custom surrogate could wrap the DLL's interfaces with its own implementation and use proxy/stub DLLs with a remotable IDL definition that would allow the interface to be remoted.

The main surrogate thread should typically perform the following setup steps:

  1. Call CoInitializeEx to initialize the thread and set the threading model.
  2. If you want the DLL servers that are to run in the server to be able to use the security settings in the AppID registry key, call CoInitializeSecurity with the EOAC_APPID capability. Otherwise, legacy security settings will be used.
  3. Call CoRegisterSurrogate to register the surrogate interface to COM.
  4. Call ISurrogate::LoadDllServer for the requested CLSID.
  5. Put main thread in a loop to call CoFreeUnusedLibraries periodically.
  6. When COM calls ISurrogate::FreeSurrogate, revoke all class factories and exit.

A surrogate process must implement the ISurrogate interface. This interface should be registered when a new surrogate is started and after calling CoInitializeEx. As indicated in the preceding steps, the ISurrogate interface has two methods that COM calls: LoadDllServer, to dynamically load new DLL servers into existing surrogates; and FreeSurrogate, to free the surrogate.

The implementation of ISurrogate::LoadDllServer, which COM calls with a load request, must first create a class factory object that supports IUnknown, IClassFactory, and IMarshal, and then call CoRegisterClassObject to register the object as the class factory for the requested CLSID.

The class factory registered by the surrogate process is not the actual class factory implemented by the DLL server but is a generic class factory implemented by the surrogate process that supports IClassFactory and IMarshal. Because it is the surrogate's class factory, rather than that of the DLL server that is being registered, the surrogate's class factory will need to use the real class factory to create an instance of the object for the registered CLSID. The surrogate's IClassFactory::CreateInstance should look something like the following example:

STDMETHODIMP CSurrogateFactory::CreateInstance(
IUnknown* pUnkOuter,
REFIID iid,
void** ppv)
{
    void* pcf;
    HRESULT hr;

    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, &pcf);
    if ( FAILED(hr) )
        return hr;
    hr = ((IClassFactory*)pcf)->CreateInstance(pUnkOuter, iid, ppv);
    ((IClassFactory*)pcf)->Release();
    return hr;
}

The surrogate's class factory must also support IMarshal because a call to CoGetClassObject can request any interface from the registered class factory, not just IClassFactory. Further, since the generic class factory supports only IUnknown and IClassFactory, requests for other interfaces must be directed to the real object. Thus, there should be a MarshalInterface method which should be similar to the following:

STDMETHODIMP CSurrogateFactory::MarshalInterface(
IStream *pStm,
REFIID riid, void *pv,
WORD dwDestContext,
void *pvDestContext,
DWORD mshlflags )
{
    void * pCF = NULL;
    HRESULT hr;

    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, riid, &pCF);
    if ( FAILED(hr) )
        return hr;
    hr = CoMarshalInterface(pStm, riid, (IUnknown*)pCF, dwDestContext,
pvDestContext,  mshlflags);
    ((IUnknown*)pCF)->Release();
    return S_OK;

The surrogate that houses a DLL server must publish the DLL server's class object(s) with a call to CoRegisterClassObject. All class factories for DLL surrogates should be registered as REGCLS_SURROGATE. REGCLS_SINGLUSE and REGCLS_MULTIPLEUSE should not be used for DLL servers loaded into surrogates.

Following these guidelines for creating a surrogate process when it is necessary to do so should ensure proper behavior.


Monikers

A moniker in COM is not only a way to identify an object—a moniker is also implemented as an object. This object provides services allowing a component to obtain a pointer to the object identified by the moniker. This process is referred to as binding.

Monikers are objects that implement the IMoniker interface and are generally implemented in DLLs as component objects. There are two ways of viewing the use of monikers: as a moniker client, a component that uses a moniker to get a pointer to another object; and as a moniker provider, a component that supplies monikers identifying its objects to moniker clients.

OLE uses monikers to connect to and activate objects, whether they are in the same machine or across a network. A very important use is for network connections. They are also used to identify, connect to, and run OLE compound document link objects. In this case, the link source acts as the moniker provider and the container holding the link object acts as the moniker client.

This section includes the following topics:

  • Moniker Clients
  • Moniker Providers
  • OLE Moniker Implementations

See Also

The Component Object Model


Moniker Clients

Moniker clients must start by getting a moniker, and there are several ways for a moniker client to get a moniker. For example, in OLE compound documents, when the end user creates a linked item (either using Insert Object dialog, the clipboard, or drag-and drop), a moniker is embedded as part of the linked item. In that case, the programmer has minimal contact with monikers. Programmatically, if you have an interface pointer to an object that implements the IMoniker interface, you can use that to get a moniker, and there are methods on other interfaces that are defined to return monikers.

There are different kinds of monikers, which are used to identify different kinds of objects, but to a moniker client, all monikers look the same. A moniker client simply calls IMoniker::BindToObject on a moniker and gets an interface pointer to the object that the moniker identifies. Whether the moniker identifies an object as large as an entire spreadsheet or as small as a single cell within a spreadsheet, calling IMoniker::BindToObject will return a pointer to that object. If the object is already running, IMoniker::BindToObject will find it in memory. If the object is stored passively on disk, IMoniker::BindToObject will locate a server for that object, run the server, and have the server bring the object into the running state. All the details of the binding process are hidden from the moniker client. Thus, for a moniker client, using the moniker is very simple.

See Also

Moniker Providers

OLE Moniker Implementations


Moniker Providers

In general, a component should be a moniker provider when it allows access to one of its objects, while still controlling the object's storage. If a component is going to hand out monikers that identify its objects, it must be capable of performing the following tasks:

  • On request, create a moniker that identifies an object.
  • Enable the moniker to be bound when a client calls IMoniker::BindToObject on it.

A moniker provider must create a moniker of an appropriate moniker class to identify an object. The moniker class refers to a specific implementation of the IMoniker interface that defines the type of moniker created. While you can implement IMoniker to create a new moniker class, it is frequently unnecessary because OLE provides implementations of several different moniker classes, each with its own CLSID. Refer to OLE Moniker Implementations for descriptions of moniker classes that OLE provides.

See Also

Moniker Clients

OLE Moniker Implementations


OLE Moniker Implementations

OLE provides implementations of several monikers for different situations, as described in the following topics:

  • File Monikers
  • Composite Monikers
  • Item Monikers
  • Anti-Monikers
  • Pointer Monikers
  • Class Monikers

The file, composite, and item monikers are the most frequently used monikers, because they can be used to make nearly any object in any location. Anti-moniker and pointer monikers are primarily used inside OLE but have some application in implementing custom monikers.

See Also

Moniker Clients

Moniker Providers


File Monikers

File monikers are the simplest moniker class. File monikers can be used to identify any object that is stored in its own file. A file moniker acts as a wrapper for the path name the native file system assigns to the file. Calling IMoniker::BindToObject for this moniker would cause this object to be activated and then would return an interface pointer to the object. The source of the object named by the moniker must provide an implementation of the IPersistFile interface to support binding a file moniker. File monikers can represent either a complete or a relative path.

For example, the file moniker for a spreadsheet object stored as the file C:\WORK\MYSHEET.XLS would contain information equivalent to that path name. The moniker would not necessarily consist of the same string, however. The string is just its display name, a representation of the moniker's contents that is meaningful to an end user. The display name, which is available through the IMoniker::GetDisplayName method, is used only when displaying a moniker to an end user. This method gets the display name for any of the moniker classes. Internally, the moniker may store the same information in a format that is more efficient for performing moniker operations but isn't meaningful to users. Then, when this same object is bound through a call to the BindToObject method, the object would be activated, probably by loading the file into the spreadsheet.

OLE offers moniker providers the helper API CreateFileMoniker that creates a file moniker object and returns its pointer to the provider.

See Also

Composite Monikers

Item Monikers

Anti-Monikers

Pointer Monikers

Class Monikers


Composite Monikers

One of the most useful features of monikers is that you can concatenate or compose monikers together. A composite moniker is a moniker that is a composition of other monikers and can determine the relation between the parts. This lets you assemble the complete path to an object given two or more monikers that are the equivalent of partial paths. You can compose monikers of the same class (such as two file monikers) or of different classes (such as a file moniker and an item moniker). If you were to write your own moniker class, you could also compose your monikers with file or item monikers. The basic advantage of a composite is that it gives you one piece of code to implement every possible moniker that is a combination of simpler monikers. That reduces tremendously the need for specific custom moniker classes.

Because monikers of different classes can be composed with one another, monikers provide the ability to join multiple namespaces. The file system defines a common namespace for objects stored as files because all applications understand a file system path name. Similarly, a container object also defines a private namespace for the objects that it contains because no container understands the names generated by another container. Monikers allow these name spaces to be joined because file monikers and item monikers can be composed. A moniker client can search the namespace for all objects using a single mechanism. The client simply calls IMoniker::BindToObject on the moniker, and the moniker code handles the rest. A call to IMoniker::GetDisplayName on a composite creates a name using the concatenation of all the individual monikers' display names.

Furthermore, because you can write your own moniker class, moniker composition allows you to add customized extensions to the namespace for objects.

Sometimes two monikers of specific classes can be combined in a special way. For example, a file moniker representing an incomplete path and another file moniker representing a relative path can be combined to form a single file moniker representing the complete path. For example, the file monikers c:\work\art could be composed with the relative file moniker ..\backup\myfile.doc to equal c:\work\backup\myfile.doc. This is an example of "nongeneric" composition.

"Generic" composition, on the other hand, permits the connection of any two monikers, no matter what their classes. For example, you could compose an item moniker onto a file moniker, although not, of course, the other way around.

Because a nongeneric composition depends on the class of the monikers involved, its details are defined by the implementation of a particular moniker class. You can define new types of nongeneric compositions if you write a new moniker class. By contrast, generic compositions are defined by OLE. Monikers created as a result of generic composition are called generic composite monikers.

These three classes—file monikers, item monikers, and generic composite monikers—all work together, and they are the most commonly used classes of monikers.

Moniker clients should call IMoniker::ComposeWith to create a composite on moniker with another. The moniker it is called on internally decides whether it can do a generic or nongeneric composition. If the moniker implementation determines that a generic composition is usable, OLE provides the CreateGenericComposite API function to facilitate this.

See Also

File Monikers

Item Monikers

Anti-Monikers

Pointer Monikers

Class Monikers


Item Monikers

Another OLE-implemented moniker class is the item moniker, which can be used to identify an object contained in another object. One type of contained object is an OLE object embedded in a compound document. A compound document could identify the embedded objects it contains by assigning each one an arbitrary name, such as "embedobj1," "embedobj2," and so forth. Another type of contained object is a user selection in a document, such as a range of cells in a spreadsheet or a range of characters in a text document. An object that consists of a selection is called a pseudo-object because it isn't treated as a distinct object until a user marks the selection. A spreadsheet might identify a cell range using a name such as "1A:7F," while a word processing document might identify a range of characters using the name of a bookmark.

An item moniker is useful primarily when concatenated—or "composed"—with another moniker, one that identifies the container. An item moniker is usually created and then composed onto (for example) a file moniker to create the equivalent of a complete path to the object. For example, you can compose the file moniker "C:\work\report.doc" (which identifies the container object) with the item moniker "embedobj1" (which identifies an object within the container) to form the moniker "C:\work\report.doc\embedobj1," which uniquely identifies a particular object within a particular file. You can also concatenate additional item monikers to identify deeply nested objects. For example, if "embedobj1" is the name of a spreadsheet object, to identify a certain range of cells in that spreadsheet object you could append another item moniker to create a moniker that would be the equivalent of "C:\work\report.doc\embedobj1\1A:7F."

When combined with a file moniker, an item moniker forms a complete path. Item monikers thus extend the notion of path names beyond the file system, defining path names to identify individual objects, not just files.

There is a significant difference between an item moniker and a file moniker. The path contained in a file moniker is meaningful to anyone who understands the file system, while the partial path contained in an item moniker is meaningful only to a particular container. Everyone knows what "c:\work\report.doc" refers to, but only one particular container object knows what "1A:7F" refers to. One container cannot interpret an item moniker created by another application; the only container that knows which object is referred to by an item moniker is the container that assigned the item moniker to the object in the first place. For this reason, the source of the object named by the combination of a file and item moniker must not only implement IPersistFile, to facilitate binding the file moniker, but also IOleItemContainer to facilitate resolving the name of the item moniker into the appropriate object, in the context of a file.

The advantage of monikers is that someone using a moniker to locate an object doesn't need to understand the name contained within the item moniker, as long as the item moniker is part of a composite. Generally, it would not make sense for an item moniker to exist on its own. Instead, you would compose an item moniker onto a file moniker. You would then call IMoniker::BindToObject on the composite, which binds the individual monikers within it, interpreting the names.

To create an item moniker object and return its pointer to the moniker provider, OLE provides the helper API CreateItemMoniker. This function creates an item moniker object and returns its pointer to the provider.

See Also

File Monikers

Composite Monikers

Anti-Monikers

Pointer Monikers

Class Monikers


Anti-Monikers

OLE provides an implementation of a special type of moniker called an anti-moniker. You use this moniker in the creation of new moniker classes. You use it as the inverse of the moniker that it is composed onto, effectively canceling that moniker, in much the same way that the ".." operator moves up a directory level in a file system command.

It is necessary to have an anti-moniker available, because once a composite moniker is created, it is not possible to delete parts of the moniker if, for example, an object moves. Instead, you use an anti-moniker to remove one or more entries from a composite moniker.

Anti-monikers are a moniker class explicitly intended for use as an inverse. COM defines an API function named CreateAntiMoniker, which returns an anti-moniker. You generally use this function to implement the IMoniker::Inverse method.

An anti-moniker is only an inverse for those types of monikers that are implemented to treat anti-monikers as an inverse. For example, if you want to remove the last piece of a composite moniker, you should not create an anti-moniker and compose it to the end of the composite. You cannot be sure that the last piece of the composite considers an anti-moniker to be its inverse. Instead, you should call IMoniker::Enum on the composite moniker, specifying FALSE as the first parameter. This creates an enumerator that returns the component monikers in reverse order. Use the enumerator to retrieve the last piece of the composite, and call IMoniker::Inverse on that moniker. The moniker returned by IMoniker::Inverse is what you need to remove the last piece of the composite.

See Also

File Monikers

Composite Monikers

Item Monikers

Pointer Monikers

Class Monikers


Pointer Monikers

A pointer moniker identifies an object that can exist only in the active or running state. This differs from other classes of monikers, which identify objects that can exist in either the passive or the active state.

Suppose, for example, an application has an object that has no persistent representation. Normally, if a client of your application needs access to that object, you could simply pass the client a pointer to the object. However, suppose your client is expecting a moniker. The object cannot be identified with a file moniker, because it isn't stored in a file, nor with an item moniker, because it isn't contained in another object.

Instead, your application can create a pointer moniker, which is a moniker that simply contains a pointer internally, and pass that to the client. The client can treat this moniker like any other. However, when the client calls IMoniker::BindToObject on the pointer moniker, the moniker code does not check the Running Object Table (ROT) or load anything from storage. Instead, the moniker code simply calls IUnknown::QueryInterface on the pointer stored inside the moniker.

Pointer monikers allow objects that exist only in the active or running state to participate in moniker operations and be used by moniker clients. One important difference between pointer monikers and other classes of monikers is that pointer monikers cannot be saved to persistent storage. If you do, calling the IMoniker::Save method returns an error. This means that pointer monikers are useful only in specialized situations. You can use the CreatePointerMoniker API function if you need to use a pointer moniker.

See Also

File Monikers

Composite Monikers

Item Monikers

Anti-Monikers

Class Monikers


Class Monikers

Although classes are typically identified directly with CLSIDs to APIs such as CoCreateInstance or CoGetClassObject, classes may also now be identified with a moniker called a class moniker. Class monikers bind to the class object of the class for which they are created.

The ability to identify classes with a moniker supports useful operations that are otherwise unwieldy. For example, file monikers traditionally supported rich binding only to the class associated with the class of file they referred to—a moniker to an Excel file would bind to an instance of an Excel object, and a moniker to a GIF image would bind to an instance of the currently registered GIF handler. A class moniker allows you to indicate the class you want to use to manipulate a file through composition with a file moniker. A class moniker for a 3D charting class composed with a moniker to an Excel file yields a moniker that binds to an instance of the 3D charting object and initializes the object with the contents of the Excel file.

Class monikers are therefore most useful in composition with other types of monikers, such as file monikers or item monikers.

Class monikers may also be composed to the right of monikers supporting binding to the IClassActivator interface. When composed in this manner, IClassActivator simply gives access to the class object and instances of the class through IClassActivator::GetClassObject. Class monikers may be identified through IMoniker::IsSystemMoniker, which returns MKSYS_CLASSMONIKER in pdwMkSys.

Programmers typically create class monikers using the CreateClassMoniker function or through MkParseDisplayName. (See IMoniker—Class Moniker Implementation about IMoniker::ParseDisplayName for details.)

See Also

File Monikers

Composite Monikers

Item Monikers

Anti-Monikers

Pointer Monikers


Asynchronous Monikers

The OLE moniker architecture provides a consistent, extensible programming model for working with Internet objects, providing methods for parsing names, representing Universal Resource Locators (URLs) as printable names, and locating and binding to the objects represented by URL strings. (Also see URL Monikers.) Standard OLE monikers (notably, item, file, and pointer monikers), however, are inappropriate for the Internet because they are synchronous, returning a pointer to an object or its storage only at such time as all data is available. Depending on the amount of data to be downloaded, binding synchronously can tie up the client's user interface for prolonged periods.

The Internet requires new approaches to application design. Applications should be able to perform all expensive network operations asynchronously to avoid stalling the user interface. An application should be able to trigger an operation and receive notification on full or partial completion. At that point, the application should have the choice either of proceeding with the next step of the operation or providing additional information as needed. As a download proceeds, an application should also be able to provide users with progress information and the opportunity to cancel the operation at any time.

Asynchronous monikers provide these capabilities, as well as various levels of asynchronous binding behavior, while providing backwards compatibility for applications that are either unaware of or do not require asynchronous behavior. Another OLE technology, Asynchronous Storage, works with asynchronous monikers to provide asynchronous downloading of an Internet object's persistent state. The asynchronous moniker triggers the bind operation and sets up the necessary components, including storage and stream objects, byte-array objects, and notification sinks. Once the components are connected, the moniker gets out of the way and the rest of the bind is executed mainly between the components implementing the asynchronous storage components and the object.

See Also

Asynchronous vs Synchronous Monikers

Asynchronous vs. Synchronous Binding

Asynchronous vs. Synchronous Storage

Data-Pull Model vs. Data-Push Model

URL Monikers


Asynchronous vs. Synchronous Monikers

A client of a stadard, synchronous OLE moniker typically creates and holds a reference to the moniker, as well as the bind-context to be used during binding. The components involved in using traditional monikers are shown in the following diagram.

Clients typically create standard monikers by calling API functions such as CreateFileMoniker, CreateItemMoniker, or CreatePointerMoniker or, because they are can be saved to persistent storage, through OleSaveToStream and OleLoadFromStream. Monikers may also be obtained from a container object by calling IBindHost::CreateMoniker Method. Clients create bind contexts by calling the CreateBindCtx API function and then pass the bind context to the moniker with calls to IMoniker::BindToStorage or IMoniker::BindToObject.

As shown in the following diagram, a client of an asynchronous moniker also creates and holds a reference to the moniker and bind context to be used during binding.

To get asynchronous behavior, the client implements the IBindStatusCallback interface on a bind-status-callback object and calls either the RegisterBindStatusCallback function or the CreateAsyncBindCtx API function to register this interface with the bind context. The moniker passes a pointer to its IBinding interface in a call to the IBindStatusCallback::OnStartBinding method. The client tells the asynchronous moniker how it wants to bind on return from the moniker's call to IBindStatusCallback::GetBindInfo method.


Asynchronous vs. Synchronous Binding

The client may check to see whether the moniker is asynchronous by calling the IsAsyncMoniker Function API function. If the client returns the BINDF_ASYNCHRONOUS flag, rather than returning an object pointer or a storage pointer from subsequent calls to IMoniker::BindToStorage or IMoniker::BindToObject, the moniker returns MK_S_ASYNCHRONOUS in place of the object pointer and NULL in place of the storage pointer. In response, the client should wait to receive the requested object or storage during implementation of the IBindStatusCallback::OnDataAvailable method and the IBindStatusCallBack::OnObjectAvailable method.

The callback object also receives progress notification through the IBindStatusCallback::OnProgress method, data availability notification through IBindStatusCallback::OnDataAvailable, and various other notifications from the moniker about the status of the binding operation.

If the client does not return the BINDF_ASYNCHRONOUS flag from the moniker's call to GetBindInfo, the bind operation will proceed synchronously and the desired object or storage will be returned from subsequent calls to IMoniker::BindToObject or IMoniker::BindToStorage. Similarly, if the client desires synchronous operation and does not wish to receive any progress notifications or callbacks, it can request an asynchronous moniker to behave synchronously by not implementing IBindStatusCallback. In such cases, the asynchronous moniker will behave like a standard synchronous moniker.


Asynchronous vs. Synchronous Storage

Asynchronous monikers may also return an Asynchronous Storage object in the IBindStatusCallback::OnDataAvailable notification. This storage object may allow access to some of the object's persistent data while the binding is still in progress. A client can choose between two modes for the storage: blocking and nonblocking. In blocking mode, which is compatible with current implementations of storage objects, if data is unavailable, the call blocks until data arrives. In nonblocking mode, rather than blocking the call, the storage object returns a new error E_PENDING when data is unavailable. A client aware of asynchronous binding and storage notes this error and waits for further notifications (IBindStatusCallback::OnDataAvailable) to retry the operation. A client can choose between a synchronous (blocking) and asynchronous (nonblocking) storage by choosing whether or not to set the BINDF_ASYNCSTORAGE flag in the pgrfBINDF value returned to IBindStatusCallback::GetBindInfo.


Data-Pull Model vs. Data-Push Model

A client of an asynchronous moniker can choose between a data-pull and data-push model for driving an asynchronous IMoniker::BindToStorage operation and receiving asynchronous notifications. In the data-pull model, the client drives the bind operation and the moniker provides data to the client only as it is read. In other words, after the first call to IBindStatusCallback::OnDataAvailable, the moniker does not provide any data to the client unless the client has consumed all of the data that is already available.

Because data is downloaded only as it is requested, clients that choose the data-pull model must make sure to read this data in a timely manner. In the case of Internet downloads with URL Monikers, the bind operation may fail if a client waits too long before requesting more data.

In the data-push model, the moniker drives the asynchronous bind operation and continuously notifies the client whenever new data is available. The client may choose whether or not to read the data at any point during the bind operation, but the moniker will drive the bind operation to completion regardless.

Also, you need to remember to follow the COM rules for memory allocation when using asynchronous monikers, specifically the following:

  • Whenever a COM interface or function call returns a buffer (string or other) to its client, the client is responsible for freeing the memory by calling CoTaskMemFree.
  • Whenever a COM interface or function requires a buffer from its client, the client must allocate that buffer using CoTaskMemAlloc and the callee must free it.

Be sure to follow these rules when allocating strings or buffers that are passed to asynchronous monikers, and remember to free memory returned by asynchronous monikers. See Managing Memory Allocation and related topics for complete details.


URL Monikers

The OLE moniker architecture provides a convenient programming model for working with URLs. The moniker architecture supports extensible and complete name parsing through the MkParseDisplayName(Ex) function and the IParseDisplayName and IMoniker interfaces, as well as printable names through the IMoniker::GetDisplayName method. The IMoniker interface is the way you actually use URLs you encounter, and building components that fit into the moniker architecture is the way to actually extend URL namespaces in practice.

A new system-provided moniker class, the URL moniker, provides a framework for building and using certain URLs. Because URLs frequently refer to resources across high-latency networks, the URL Moniker supports asynchronous as well as synchronous binding. The URL Moniker does not currently support Asynchronous Storage.

The following diagram shows the components involved in using URL monikers. All these components should be familiar. (See Asynchronous Monikers.)

Like all moniker clients, a user of URL Monikers typically creates and holds a reference to the moniker as well as to the bind context to be used during binding (IMoniker::BindToStorage or IMoniker::BindToObject). To support asynchronous binding, the client can implement a bind-status-callback object, which implements the IBindStatusCallback interface, and register it with the bind context using the RegisterBindStatusCallback API function. This object will receive the transport's IBinding interface during calls to IBindStatusCallback::OnStartBinding.

The URL Moniker identifies the protocol being used by parsing the URL prefix and then retrieves the IBinding interface from the transport layer. The client uses IBinding to support pausing, cancellation, and prioritization of the binding operation. The callback object also receives progress notification through IBindStatusCallback::OnProgress, data availability notification through IBindStatusCallback::OnDataAvailable, and various, other transport-layer notifications about the status of the binding. The URL moniker or specific transport layers may also request extended information from the client via IBindStatusCallback::QueryInterface, allowing the client to provide protocol-specific information that will affect the bind operation.

See Also

Callback Synchronization

Media-Type Negotiation

URL Moniker API Functions

Asynchronous Monikers


Callback Synchronization

The asynchronous WinInet API (used for the most common protocols) leaves the synchronization of the callback mechanism and the calling application as an exercise for the client. This is intentional because it allows the greatest degree of flexibility. The default protocols and the URL moniker implementation perform this synchronization and guarantee that single-threaded and apartment-threaded applications never have to deal with free-thread-style contention. That is, the client's IEnumFORMATETC and IBindStatusCallback interfaces are called only on their proper threads. This feature is transparent to the user of the URL mMoniker as long each thread that calls IMoniker::BindToStorage and IMoniker::BindToObject has a message queue.

The aynchronous moniker specification requires more precise control over the prioritization and management of downloads than is allowed for by either WinSock or WinInet. Accordingly, a URL moniker manages all the downloads for any given caller's thread, using—as part of its synchronization—a priority scheme based on the IBinding specification.


Media-Type Negotiation

Many application-layer Internet protocols are based on the exchange of messages in a simple, flexible format called Multipurpose Internet Mail Extensions (MIME). Although MIME originated as a standard for exchanging electronic mail messages, it is used today by a wide variety of applications to specify mutually understood data formats as MIME, or media, types. The process is called media-type negotiation.

Media types are simple strings that denote a type and subtype (such as "text/plain" or "text/HTML"). They are used to label data or qualify a request. A Web browser, for example, as part of an HTTP request-for-data or request-for-info, specifies that it is requesting "image/gif" or "image/jpeg" Media Types, to which a Web server responds by returning the appropriate media type and, if the call was a request-for-data, the data itself in the requested format.

Media-type negotiation is often similar to how existing desktop applications negotiate with the system clipboard to determine which data format to paste when a user chooses Edit/Paste or queries for formats when receiving an IDataObject pointer during a drag-and-drop operation. The subtle difference in HTTP media-type negotiation is that the client does not know ahead of time which formats the server has available. Therefore, the client specifies up-front the media types it supports, in order of greatest fidelity, and the server responds with the best available format.

URL Monikers support media-type negotiation as a way for Internet clients and servers to agree upon formats to be used when downloading data in BindToStorage operations. To support media-type negotiation, a client implements the IEnumFORMATETC interface and calls the RegisterFormatEnumerator API function to register it with the bind context. The format enumerator lists the formats the client can accept. A URL moniker translates these formats into media types when binding to HTTP URLs.

The possible media types requested by the client are represented to URL monikers through FORMATETC structures available from the IEnumFORMATETC enumerator registered by the caller on the bind context: Each FORMATETC specifies a clipboard format identifying the media type. For example, the following code fragment specifies that the media type is PostScript®.

FORMATETC fmtetc;
fmtetc.cfFormat = RegisterClipboardFormat(CF_MIME_POSTSCRIPT);
. . .

A client can set the clipboard format to the special media type CF_NULL to indicate that the default media type of the resource pointed to by the URL should be retrieved. This format is usually the last one in which the client is interested. When no enumerator is registered with the bind context, a URL Moniker works as if an enumerator containing a single FORMATETC with cfFormat=CF_NULL is available, automatically downloading the default media-type.

Whatever media type is to be used, the client is notified of the choice by means of the pformatetc argument on its IBindStatusCallback::OnDataAvailable method. The callback occurs within the context of the client's call to IMoniker::BindToStorage.

Note If received content is of an unrecognized media-type, the client automatically calls RegisterMediaTypes to register the new type.


URL Moniker API Functions

URL moniker API functions insulate developers from the complexities of creating, managing, and using URL monikers. These monikers, which are fully described in the OLE Programmer's Reference for the ActiveX® Development Kit, are as follows:

  • CreateURLMoniker Function
  • IsValidURL
  • RegisterMediaTypes Function
  • CreateFormatEnumerator
  • RegisterFormatEnumerator Function
  • RevokeFormatEnumerator Function
  • RegisterMediaTypeClass Function
  • FindMediaTypeClass Function
  • GetClassFileOrMime
  • UrlMkSetSessionOption

Your familiarity with these API can be as follows:

  • Be familiar with CreateURLMoniker and IsValidURL if you will be using URL monikers in stand-alone applications. If you are authoring an ActiveX control, you should use IBindHost::CreateMoniker Method instead of CreateURLMoniker.
  • Be familiar with RegisterMediaTypes, CreateFormatEnumerator, RegisterFormatEnumerator, and RevokeFormatEnumerator if you will be performing any MIME negotiation with URL monikers.
  • Be familiar with RegisterMediaTypeClass, FindMediaTypeClass, GetClassFileOrMime, and UrlMkSetSessionsOption only if you have significant experience both with URL monikers and with COM.


Events in COM and Connectable Objects

When a program detects something that has happened, it can notify its clients. For example, if a stock ticker program detects a change in the price of a stock, it can notify all clients of the change. This notification process is referred to as firing an event.

With COM, server objects can use COM events to fire events without any information about what objects will be notified. Objects can also use connectable objects to maintain detailed information about clients who have requested notifications.

COM connectable objects provide outgoing interfaces to their clients in addition to their incoming interfaces. As a result, objects and their clients can engage in bidirectional communication. Incoming interfaces are implemented on an object and receive calls from external clients of an object, while outgoing interfaces are implemented on the client's sink and receive calls from the object. The object defines an interface it would like to use, and the client implements it.

An object defines its incoming interfaces and provides implementations of these interfaces. Incoming interfaces are available to clients through the object's IUnknown::QueryInterface method. Clients call the methods of an incoming interface on the object, and the object performs desired actions on behalf of the client.

Outgoing interfaces are also defined by an object, but the client provides the implementations of the outgoing interfaces on a sink object that the client creates. The object then calls methods of the outgoing interface on the sink object to notify the client of changes in the object, to trigger events in the client, to request something from the client, or, in fact, for any purpose the object creator comes up with.

An example of an outgoing interface is an IButtonSink interface defined by a push button control to notify its clients of its events. For example, the button object calls IButtonSink::OnClick on the client's sink object when the user clicks the button on the screen. The button control defines the outgoing interface. For a client of the button to handle the event, the client must implement that outgoing interface on a sink object and then connect that sink to the button control. Then, when events occur in the button, the button will call the sink, at which time the client can execute whatever action it wishes to assign to that button click.

Connectable objects provide a general mechanism for object-to-client communication. Any object that wishes to expose events or notifications of any kind can use this technology. In addition to the general connectable object technology, COM provides many special purpose sink and site interfaces used by objects to notify clients of specific events of interest to the client. For example, IAdviseSink may be used by objects to notify clients of data and view changes in the object.

See Also

Architecture of Connectable Objects

Connectable Object Interfaces


Architecture of Connectable Objects

The connectable object is only one piece of the overall architecture of connectable objects. This technology includes the following elements:

  • Connectable object Implements the IConnectionPointContainer interface; creates at least one connection point object; defines an outgoing interface for the client.
  • Client Queries the object for IConnectionPointContainer to determine whether the object is connectable; creates a sink object to implement the outgoing interface defined by the connectable object.
  • Sink object Implements the outgoing interface; used to establish a connection to the connectable object.
  • Connection point object Implements the IConnectionPoint interface and manages connection with the client's sink.

The relationships between client, connectable object, a connection point, and a sink are illustrated in the following diagram:

Before the connection point object calls methods in the sink interface in step 3 in the preceding diagram, it must QueryInterface for the specific interface required, even if the pointer was already passed in the step 2 call to the Advise method.

Two enumerator objects are also involved in this architecture though not shown in the illustration. One is created by a method in IConnectionPointContainer to enumerate the connection points within the connectable object. The other is created by a method in IConnectionPoint to enumerate the connections currently established to that connection point. One connection point can support multiple connected sink interfaces, and it should iterate through the list of connections each time it makes a method call on that interface. This process is known as multicasting.

When working with connectable objects, it is important to understand that the connectable object, each connection point, each sink, and all enumerators are separate objects with separate IUnknown implementations, separate reference counts, and separate lifetimes. A client using these objects is always responsible for releasing all reference counts it owns.

Note A connectable object can support more than one client and can support multiple sinks within a client. Likewise, a sink can be connected to more than one connectable object.

The steps for establishing a connection between a client and a connectable object are as follows:

  1. The client queries for IConnectionPointContainer on the object to determine whether the object is connectable. If this call is successful, the client holds a pointer to the IConnectionPointContainer interface on the connectable object and the connectable object reference counter has been incremented. Otherwise, the object is not connectable and does not support outgoing interfaces.
  2. If the object is connectable, the client next tries to obtain a pointer to the IConnectionPoint interface on a connection point within the connectable object. There are two methods for obtaining this pointer, both in IConnectionPointContainer::FindConnectionPoint and in IConnectionPointContainer::EnumConnectionPoints. There are a few additional steps needed if EnumConnectionPoints is used. (See Using IConnectionPointContainer for more information.) If successful, the connectable object and the client both support the same outgoing interface. The connectable object defines it and calls it, and the client implements it. The client can then communicate through the connection point within the connectable object.
  3. The client then calls IConnectionPoint::Advise on the connection point to establish a connection between its sink interface and the object's connection point. After this call, the object's connection point holds a pointer to the outgoing interface on the sink.
  4. The code inside IConnectionPoint::Advise calls QueryInterface on the interface pointer that is passed in, asking for the specific interface identifier to which it connects.
  5. The object calls methods on the sink's interface as needed, using the pointer held by its connection point.
  6. The client calls IConnectionPoint::Unadvise to terminate the connection. Then the client calls IConnectionPoint::Release to free its hold on the connection point and, therefore, the main connectable object also. The client must also call IConnectionPointContainer::Release to free its hold on the main connectable object.

See Also

Connectable Object Interfaces


Connectable Object Interfaces

Support for connectable objects requires support for four interfaces:

  • IConnectionPointContainer on the connectable object
  • IConnectionPoint on the connection point object
  • IEnumConnectionPoints on an enumerator object
  • IEnumConnections on an enumerator object

The latter two are defined as standard enumerators for the types IConnectionPoint * and CONNECTDATA. See IEnumXxxx for more information on enumerators.

Additionally, the connectable object can optionally support IProvideClassInfo and IProvideClassInfo2 to provide enough information to a client so that the client can provide support for the outgoing interface at run time.

Finally, the client must provide a sink object that implements the outgoing interface, which is a custom COM interface defined by the connectable object.

The IConnectionPointContainer, IConnectionPoint, IProvideClassInfo, and IProvideClassInfo2 interfaces are defined as follows:

interface IConnectionPointContainer : IUnknown
    {
    HRESULT EnumConnectionPoints([out] IEnumConnectionPoints
        **ppEnum);
    HRESULT FindConnectionPoint([in] REFIID riid
        , [out] IConnectionPoint **ppCP);
    }

interface IConnectionPoint : IUnknown
    {
    HRESULT GetConnectionInterface([out] IID *pIID);
    HRESULT GetConnectionPointContainer([out]
        IConnectionPointContainer **ppCPC);
    HRESULT Advise([in] IUnknown *pUnk, [out] DWORD *pdwCookie);
    HRESULT Unadvise([in] DWORD dwCookie);
    HRESULT EnumConnections([out] IEnumConnections **ppEnum);
    }

interface IProvideClassInfo : IUnknown
    {
    HRESULT GetClassInfo([out] ITypeInfo **ppTI);
    }

interface IProvideClassInfo2 : IProvideClassInfo
    {
    HRESULT GetGUID( );
    }

See Also

Architecture of Connectable Objects

Using IConnectionPointContainer

Using IConnectionPoint

Using IProvideClassInfo


Using IConnectionPointContainer

A connectable object implements IConnectionPointContainer (and exposes it through QueryInterface) to indicate the existence of outgoing interfaces. For each outgoing interface, the connectable object manages a connection point sub-object, which itself implements IConnectionPoint. The connectable object therefore contains the connection points, hence the naming of IConnectionPointContainer and IConnectionPoint.

Through IConnectionPointContainer, a client can perform two operations. First, if the client already has the IID for an outgoing interface that it supports, it can locate the corresponding connection point for the IID using IConnectionPointContainer::FindConnectionPoint. The client cannot query for the connection point directly because of the container/contained relationship between the connectable object and its contained connection points. Basically, FindConnectionPoint is the QueryInterface for outgoing interfaces when the IID is known to the client.

Second, the client can enumerate all connection points within the connectable object through IConnectionPointContainer::EnumConnectionPoints. This method returns an IEnumConnectionPoints interface pointer for a separate enumerator object. Through IEnumConnectionPoints::Next, the client can obtain IConnectionPoint interface pointers to each connection point.

After the client obtains the IConnectionPoint interface, it must call IConnectionPoint::GetConnectionInterface to determine the IID of the outgoing interface supported by each connection point. If the client already supports that outgoing interface, it can establish a connection. Otherwise, it may still be able to support the outgoing interface by using information from the connectable object's type library to provide support at run time. This technique requires that the connectable object support the IProvideClassInfo interface. (See Using IProvideClassInfo.)

Because the enumerator is a separate object, the client must call IEnumConnectionPoints::Release when the enumerator is no longer needed. In addition, each connection point is an object with a separate reference count from the containing connectable object. Therefore, the client must also call IConnectionPoint::Release for each connection point accessed either through the enumerator or through FindConnectionPoint.

See Also

Connectable Object Interfaces

Using IConnectionPoint

Using IProvideClassInfo


Using IConnectionPoint

When the client has a pointer to a connection point, it can perform the following operations as expressed through IConnectionPoint:

  • First, IConnectionPoint::GetConnectionInterface retrieves the outgoing interface IID supported by the connection point. When used in conjunction with IEnumConnectionPoints, this method allows the client to examine the IIDs of all outgoing interfaces supported on the connectable object.
  • Second, a client can navigate from the connection point back to the connectable object's IConnectionPointContainer interface through the IConnectionPoint::GetConnectionPointContainer method.
  • Third, the most interesting methods for the client are IConnectionPoint::Advise and IConnectionPoint::Unadvise. When a client wishes to connect its own sink object to the connectable object, the client passes the sink's IUnknown pointer (or any other interface pointer on the same object) to Advise. The connection point queries the sink for the specific outgoing interface that is expected. If that interface is available on the sink, the connection point then stores the interface pointer. From this point until Unadvise is called, the connectable object will make calls to the sink through this interface when events occur. To disconnect the sink from the connection point, the client passes a key returned from Advise to the Unadvise method. Unadvise must call Release on the sink interface.
  • Finally, a client can ask a connection point to enumerate all the connections to it that exist through IConnectionPoint::EnumConnections. This method creates an enumerator object (with a separate reference count) returning an IEnumConnections pointer to it. The client must call Release when the enumerator is no longer needed. Additionally, the enumerator returns a series of CONNECTDATA structures, one for each connection. Each structure describes one connection using the IUnknown pointer of the sink as well as the connection key originally returned from Advise. When done with these sink interface pointers, the client must call IUnknown::Release on each pointer returned in a CONNECTDATA structure.

See Also

Connectable Object Interfaces

Using IConnectionPointContainer

Using IProvideClassInfo


Using IProvideClassInfo

A connectable object can offer the IProvideClassInfo and IProvideClassInfo2 interfaces so that its clients can easily examine its type information. This capability is important when dealing with outgoing interfaces, which, by definition, are defined by an object but implemented by a client on its own sink object. In some cases, an outgoing interface is known at compile time to both the connectable object and the sink object; such is the case with IPropertyNotifySink.

In other cases, however, only the connectable object knows its outgoing interface definitions at compile time. In these cases, the client must obtain the type information for the outgoing interface so that it can dynamically provide a sink supporting the right entry points, as follows:

  1. The client enumerates the connection points and then, to obtain the IIDs of outgoing interfaces supported by the connectable object, calls IConnectionPoint::GetConnectionInterface for each connection point.
  2. The client queries the connectable object for one of the IProvideClassInfo interfaces.
  3. The client calls methods in the IProvideClassInfo interfaces to get the type information for the outgoing interface.
  4. The client creates a sink object supporting the outgoing interface.
  5. The process continues, and the client calls IConnectionPoint::Advise to connect its sink to the connection point.

In the type information, the attribute source marks an interface or dispinterface listed under a coclass as an outgoing interface. Those listed without this attribute are considered incoming interfaces.

See Also

Connectable Object Interfaces

Providing Class Information

Using IConnectionPointContainer

Using IConnectionPoint


Component Categories Manager Implementation

As the number of available components grows, it becomes increasingly difficult to manage these components. In terms of the interfaces they expose as well as the tasks they perform, many components offer similar functionality, .

It is often necessary to enumerate the components that can be used in a certain context. Examples of this are the Insert Object dialog box used in OLE compound documents and the Insert Control dialog box used in OLE controls. These dialog boxes list all components that fulfill (or claim to fulfill) the interface contracts for compound documents or controls. These existing categories (OLE document, OLE control) do not imply an exact interface signature. OLE documents have to expose a certain set of core interfaces (for example, IOleObject or IPersistStorage) but can also expose additional interfaces such as IOleLink.

In the past, components have been tagged by adding a human-readable name ("Insertable", "Control", and so on) as a subkey to the HKEY_CLASSES_ROOT\CLSID\{...} registry key corresponding to the component. This works well for a central definition of categories but risks name collisions when many independent parties define new categories. As in other areas of COM, the solution to providing an extensible namespace lies in the use of globally unique identifiers (GUIDs). Instead of using a human-readable name, a unique number (CATID) is assigned to each category.

Another limitation with the existing categorization is that it is limited to expressing the capabilities of the component itself. Many components require certain capabilities from the containers. When such a component is inserted into a container, the insertion can fail or behave unexpectedly, even though the component fulfills the contracts implied by one of its categories. To enumerate the components that can be used successfully in certain situations, the capabilities of both the component and the container must be considered.

Because of these considerations, the following changes were made to the existing categorization:

  • Categories are indicated by using CATIDs that are globally unique identifiers.
  • Under the Components subkey of the HKEY_CLASSES_ROOT\CLSID registry key, two separate subkeys, "Implemented Categories" and "Required Categories", were developed. These subkeys contain the lists of CATIDs that are provided by the component or that the container of the component must provide.

To ease managing the component categories, categories are listed in a central place in the registry: HKEY_CLASSES_ROOT\Component Categories. This key lists the installed categories both with their CATID and with localized, human-readable names.

See Also

Categorizing by Component Capabilities

Categorizing by Container Capabilities

The Component Categories Manager

Default Classes and Associations

Defining Component Categories

Associating Icons with a Category


Categorizing by Component Capabilities

Component categories can be used to display a subset of all of the installed components. Each component category is identified by a GUID, referred to as a Category ID (CATID). Each CATID has a list of locale-tagged, human-readable names associated with it. A listing of the CATIDs and the human-readable names is stored in a well-known location in the registry.

For example, all components that implement the functionality for OLE document embedding can be classified within a component category. In the past, these objects would have been identified by the "Insertable" key in the registry. To use component categories instead, the following information would be added to the registry:

HKEY_CLASSES_ROOT\Component Categories\{40FC6ED3-2438-11cf-A3DB-080036F12502}
                    = (default) - ""
                    = 409 - "OLE Document Embeddings"

Each class that implements the functionality corresponding to a component category lists the category ID for that category within the CLSID key in the registry. Because a single component can support a wide range of functionality, components can belong to multiple component categories. For example, a particular OLE control might support all of the functionality required to participate as OLE document embedding, Microsoft® Visual Basic® data binding, and Internet functionality. Such a control would have the following information stored in its CLSID key in the registry:

;The CLSID for "My Super OLE Control" is {12345678-ABCD-4321-0101-00000000000C}HKEY_CLASSES_ROOT\CLSID\{12345678-ABCD-4321-0101-00000000000C}\Implemented Categories
;The CATID for "Insertable" is {40FC6ED3-2438-11cf-A3DB-080036F12502} HKEY_CLASSES_ROOT\CLSID\{12345678-ABCD-4321-0101-00000000000C}Implemented Categories\{40FC6ED3-2438-11cf-A3DB-080036F12502}
;The CATID for "Control" is {40FC6ED4-2438-11cf-A3DB-080036F12502} HKEY_CLASSES_ROOT\CLSID\{12345678-ABCD-4321-0101-00000000000C}Implemented Categories\{40FC6ED4-2438-11cf-A3DB-080036F12502}
;The CATID for an internet aware control is {...CATID_InternetAware...} HKEY_CLASSES_ROOT\CLSID\{12345678-ABCD-4321-0101-00000000000C}Implemented Categories\{...CATID_InternetAware...}

With this information, a container can enumerate the controls installed on a system and display only those controls that support the functionality required by the container. The use of component categories provides a way to categorize components by the implemented functionality of the component.

See Also

Categorizing by Container Capabilities

The Component Categories Manager

Default Classes and Associations

Defining Component Categories

Associating Icons with a Category


Categorizing by Container Capabilities

Components often require certain functionality from the container and will not work with a container that does not provide the support. The user interface should filter out components that require functionality the container does not support. To accomplish this, components can be categorized by the required container functionality.

An example of components that require functionality from the container and do not work in containers that do not support that functionality are simple frame OLE controls. Categorizing by container capabilities is accomplished by an additional registry key within the component's CLSID key:

;The CLSID for "Simple Frame Control" is {123456FF-ABCD-4321-0101-00000000000C}HKEY_CASSES_ROOT\CLSID\{12346FF-ABCD-4321-0101-00000000000C}\Implemented Categories
;The CATID for "Control" is {40FC6ED4-2438-11cf-A3DB-080036F12502} HKEY_CLASSES_ROOT\CLSID\{123456FF-ABCD-4321-0101-00000000000C}Implemented Categories\{40FC6ED4-2438-11cf-A3DB-080036F12502}
;The CATID for simple frame controls is {...CATID_SimpleFrameControl...} HKEY_CLASSES_ROOT\CLSID\{123456FF-ABCD-4321-0101-00000000000C}Implemented Categories\{...CATID_SimpleFrameControl...}
HKEY_CLASSES_ROOT\CLSID\{123456FF-ABCD-4321-0101-00000000000C}Required Categories\{...CATID_SimpleFrameControl...}

As shown in this example, a component can belong to component categories that indicate supported functionality as well as to component categories that indicate required functionality.

In the following example, the button control is a generic OLE control that supports no additional functionality. It will work in any OLE control container.

HKEY_CLASSES_ROOT\CLSID\{...CLSID_Button...}\Implemented Categories
HKEY_CLASSES_ROOT\CLSID\{...CLSID_Button...}\Implemented Categories\{...CATID_Control...}

Compare the preceding example with the next example in which the MyDBControl can use Visual Basic data binding if the container supports it. However, it has been defined so that it will work in containers that do not support Visual Basic data binding (perhaps by a different database API):

HKEY_CLASSES_ROOT\CLSID\{...CLSID_MyDBControl...}\Implemented Categories
HKEY_CLASSES_ROOT\CLSID\{...CLSID_MyDBControl...}\Implemented Categories\{...CATID_Control...}
HKEY_CLASSES_ROOT\CLSID\{...CLSID_MyDBControl...}\Implemented Categories\{...CATID_VBDatabound...}

The GroupBox control is a simple frame control. It relies on the container implementing the ISimpleFrameSite interface and will work correctly only in such containers:

HKEY_CLASSES_ROOT\CLSID\{...CLSID_GroupBox...}\Implemented Categories
HKEY_CLASSES_ROOT\CLSID\{...CLSID_GroupBox...}\Implemented Categories\{...CATID_Control...}
HKEY_CLASSES_ROOT\CLSID\{...CLSID_GroupBox...}\Implemented Categories\{...CATID_SimpleFrame...}
HKEY_CLASSES_ROOT\CLSID\{...CLSID_GroupBox...}\Required Categories\{...CATID_SimpleFrame...}

A container that supports Visual Basic data bound controls but does not support simple frame controls would specify CATID_Control and CATID_VBDatabound to the insert control user interface. The list of controls displayed to the user would contain the CLSID_Button and CLSID_MyDBControl. CLSID_GroupBox would not be displayed.

See Also

Categorizing by Component Capabilities

The Component Categories Manager

Default Classes and Associations

Defining Component Categories

Associating Icons with a Category


The Component Categories Manager

To facilitate the handling of component categories and to guarantee consistency of the registry, the system provides the Component Categories Manager, a COM object with a CLSID of CLSID_StdComponentCategoriesMgr. This COM object provides the following interfaces:

  • ICatInformation
  • ICatRegister

ICatInformation provides methods for obtaining information about the categories implemented or required by a certain class and provides information about the categories registered on a given machine.

ICatRegister provides methods for registering and unregistering component category information in the registry. This includes both the human-readable names of categories and the categories implemented or required by a given component or class.

See Also

Categorizing by Component Capabilities

Categorizing by Container Capabilities

Default Classes and Associations

Defining Component Categories

Associating Icons with a Category


Default Classes and Associations

For certain categories, a single class can be associated as the default class. The default class is selected whenever that particular category of object is required. While this may not be useful for all component categories, establishing a default class can be helpful when a particular class must be loaded from a list of possible classes without user intervention. Administrators define which class can be used by manipulating the registry.

To associate a default class with a category, introduce a CLSID key with the same CLSID as the CATID of the component category chosen as the default. Then add a TreatAs key to this key, using the value for the CLSID of the default class for the category. To use the default class for a component category, use CoCreateInstance or CoGetClassObject, specifying the CATID for the CLSID parameter. This automatically redirects to the CLSID established as the default for this category. The registry entry is as follows:

HKEY_CLASSES_ROOT\CLSID\{...catid...}\TreatAs = {...default clsid...}

During installation, a component can check for the existence of any default class keys for its categories and present the user with options for overriding the current settings.

See Also

Categorizing by Component Capabilities

Categorizing by Container Capabilities

The Component Categories Manager

Defining Component Categories

Associating Icons with a Category


Defining Component Categories

The author of a component category definition creates a unique GUID (the CATID) that is published along with the definition. Other parties know the definition of this type and can make use of its supported classes accordingly. Like the method signature of an interface, a category's semantics should not be modified after being installed. It is better to maintain backward compatibility of the category by introducing a new category identifier with revised semantics.

Because interface identifiers (IID) and component category identifiers (CATID) exist in different namespaces, it seems as if it would be possible to use the same GUID for both an IID and a CATID. However, since IIDs are often used for the CLSID of the interface's proxy/stub server, there is the potential for conflict. Therefore, do not use the same GUID for an IID and CATID.

See Also

Categorizing by Component Capabilities

Categorizing by Container Capabilities

The Component Categories Manager

Default Classes and Associations

Associating Icons with a Category


Associating Icons with a Category

Building a user interface that allows the user to select component categories within a category requires the ability to display a meaningful image for a particular category. To associate an icon with a component category, create a key for the category's CATID and populate that key with a DefaultIcon subkey. The registry entry is as follows:

HKEY_CLASSES_ROOT\CLSID\{...catid...}\DefaultIcon = "c:\hello\icons.dll,1"

The filename referenced by the DefaultIcon key can be either an EXE or a DLL file (resource-only DLL).

To associate a small 16x16 "toolbox bitmap" with a component category, create a key in HKEY_CLASSES_ROOT\CLSID for the category's CATID and populate that key with a TolboxBitmap32 subkey, as shown in the following example:

HKEY_CLASSES_ROOT\CLSID\{...catid...}\ToolboxBitmap32 = "c:\goodbye\mycomponent.dll,42"

The filename referenced by the ToolboxBitmap32 key can also be an EXE or DLL file (resource-only DLL).

See Also

Categorizing by Component Capabilities

Categorizing by Container Capabilities

The Component Categories Manager

Default Classes and Associations

Defining Component Categories

EnumCategories, EnumClassesOfCategories, EnumImplCategoriesOfClass, EnumReqCategoriesOfClass, GetCategoryDesc, ICatInformation, IsClassOfCategories, RegisterCategories, RegisterClassImplCategories, RegisterClassReqCategories, UnRegisterCategories, UnRegisterClassImplCategories, UnRegisterClassReqCategories


Compatibility with OLE 1 and 16:32-Bit Interoperability

Compatibility implies that an OLE 1 client application can contain OLE 2 embedded and linked objects and that an OLE 1 server application can create objects to be embedded in and linked to by OLE 2 containers. OLE provides these capabilities by means of a built-in compatibility layer in the core code, which includes a set of functions for conversion.

Note In moving from OLE 1 to OLE 2, the following changes in terminology were made:

  • The OLE 1 term "server application" has been changed to "object application."
  • The OLE 1 term "client application" has been changed to "container application."

Interoperability is not the same as OLE 1 to OLE 2 compatibility. Interoperability implies that a 16-bit OLE application can interact with a 32-bit application running on the same system. Interoperability between 16-bit and 32-bit implementations means that 16-bit and 32-bit implementations can call each other.


Compatibility with OLE 1

Compatibility between OLE 2 and OLE 1 applications is achieved through the implementation of two types of special remoting objects, called stubs and proxies. The stub is instantiated on the object, or server, application's side of the process; the proxy is instantiated on the container, or client, application's side. These special stubs and proxies use DDE to communicate rather than LRPC. When an OLE 2 object makes a call to a function in an OLE 1 client application, for example, the stub intercepts the call and responds appropriately. For the most part, this response simulates the response that an OLE 2 object or container would make. However, in a few cases the behavior is different or special HRESULT (SCODE) values are returned.

The following topics in this section discuss the issues that affect applications that must be compatible with an earlier or more recent version of OLE and describes the functions that promote this compatibility:

  • Working with OLE 1 Clients
  • Working with OLE 1 Servers


Working with OLE 1 Clients

This section describes some of the idiosyncrasies of working with OLE 1 clients.

A successful call to the IOleClientSite::GetContainer method returns a pointer to the container's IOleContainer interface. If the container does not support the IOleContainer interface, OLE_E_NOT_SUPPORTED is returned. All OLE 1 clients fall in this category, as do OLE 2 containers that do not support linking to their embedded objects.

The IOleClientSite::ShowObject method, a request to make the embedded or linked object visible, always returns OLE_E_NOT_SUPPORTED when called on an OLE 1 client. The purpose of this method is to help make the user model work smoothly; its failure does not affect OLE functionality.

When an OLE 1 client contains an OLE 2 object and the object is activated or the OleUpdate function is called, the aspect of the data returned will always be DVASPECT_CONTENT. This is because OLE 1 clients have no concept of a FORMATETC data structure. This situation may occur when an iconic OLE 2 object is pasted from an OLE 2 container into an OLE 1 container. When the object is first pasted, its presentation remains iconic. With the next update, however, the object's content picture is returned.

OLE 1 clients can link to OLE 2 objects only if the following is true:

  • The link source is represented by a file moniker or a generic composite moniker consisting of a file moniker and one item moniker.
  • The link source is not an embedded OLE 2 object.

An OLE 1 client can contain an incompatible link when a linked object is pasted from an OLE 2 container into the OLE 1 client or when an OLE 2 container saves the data to an OLE 1 file to allow the OLE 1 version of the application access to its data. When the OLE 1 client loads the incompatible link, the link is converted to an embedded object and assigned the class name "Ole2Link." The OLE 1 client cannot connect to the link source. However, if the newly embedded object is then pasted into an OLE 2 container using the Clipboard or is converted to an OLE 2 object using the OleConvertOLESTREAMToIStorage function, it will be converted back to its original state as an OLE 2 linked object.

When the link source for an OLE 1 linked object changes its name, the link can remain intact only if the file moniker for the link source has changed. That is, if the link source is a range of cells within an OLE 2 spreadsheet application and the name of the file that contains the cell range changes, OLE will track the link. However, if the name of the cell range changes, the link will break.

Pasting an OLE 2 linked object into an OLE 1 client document and then calling the OleCopyFromLink function to convert it to an embedded object will fail if the data transfer object provided by the link source does not support the IPersistStorage interface. Creating an embedded object always requires native data, and the IPersistStorage interface provides access to native data.

See Also

Working with OLE 1 Servers


Working with OLE 1 Servers

This section describes some of the known idiosyncrasies of embedding or linking OLE 1 objects.

As with OLE 2 objects, either the IPersistStorage::InitNew method or the IPersistStorage::Load method must be called to properly initialize a newly instantiated OLE 1 object before any other OLE calls are made. The InitNew method should be called to initialize a newly created object; the Load method should be called for existing objects. If one of the OleCreate helper functions or the OleLoad function is being used, these functions make the IPersistStorage call, eliminating the need to make the call directly. When an OLE 2 container with an OLE 1 embedded or linked object calls the IDataObject::GetData method or the IDataObject::GetDataHere method, the container can anticipate support for a smaller set of formats and storage mediums than would be supported for an OLE 2 object. The following table lists the combinations that can be supported.

Tymed Formats | Data Formats
TYMED_MFPICT | CF_METAFILEPICT
TYMED_GDI | CF_BITMAP
TYMED_HGLOBAL | cfNative, CF_DIB, and other OLE 1 server formats

For the aspect value of DVASPECT_ICON, only TYMED_MFPICT with CF_METAFILEPICT is supported. The icon returned from the IDataObject::GetData or IDataObject::GetDataHere call will always be the first icon (index 0) in the executable object application.

Several methods typically called by containers have unique implementations for OLE 1. The IPersistStorage::IsDirty method is defined to return S_OK if the object has changed since its last save to persistent storage; the method returns S_FALSE if it has not changed. When an OLE 2 container with an OLE 1 embedded object calls the IPersistStorage::IsDirty method, the compatibility code always returns S_OK when the server is running because there is no way to determine whether the object has in fact changed until the File Close or File Update command is selected. S_FALSE is returned when the server is not running.

An OLE 2 implementation of IOleObject::IsUpToDate can return S_OK if the object is up-to-date, S_FALSE if it is not up-to-date, or OLE_E_UNAVAILABLE if the object cannot determine whether it is up-to-date. The OLE 1 implementation always returns either E_NOT_RUNNING, if the object is in the loaded state, or S_FALSE, if the server is running.

The OLE 1 implementation of the IOleItemContainer::EnumObjects method always returns OLE_E_NOTSUPPORTED because it is not possible for an OLE 1 server to enumerate its objects.

The IOleObject::Close method takes a save option as a parameter that indicates whether the object should be saved before the close occurs. For OLE 2 objects, there are three possible save options: OLECLOSE_SAVEIFDIRTY, OLECLOSE_NOSAVE, and OLECLOSE_PROMPTSAVE. The OLE 1 implementation of the IOleObject::Close method treats OLECLOSE_PROMPTSAVE as equivalent to OLECLOSE_SAVEIFDIRTY because it is not possible to require an OLE 1 server to prompt the user.

OLE 2 containers cannot expect an OLE 1 object to activate in-place; all OLE 1 objects support activation in a separate, open window.

OLE 1 servers do not support linking to their embedded objects. It is up to OLE 2 containers with OLE 1 embedded objects to prevent a possible link from occurring. Containers can call the CoIsOle1Class function to determine at Clipboard copy time whether a data selection being copied is an OLE 1 object. If the CoIsOle1Class function returns TRUE, indicating that the selection is an OLE 1 object, the container should not offer the link source format. Link source must be available for a linked object to be created.

OLE 2 containers can store multiple presentations for an OLE 1 object. However, only the first presentation format is sent to the container when the OLE 1 server closes. After that, the server is in the process of closing down and cannot honor requests for anymore formats. Therefore, only the first presentation cache will be updated. The rest will be out of date (perhaps blank) if the object has changed since the last update.

Because OLE 1 servers do not update the cache for every change to an embedded object until the user selects the File Update command, an OLE 2 container may not be obtaining the latest data from the server. By calling the IOleObject::Update method, the container can obtain the latest object data.

An OLE 1 embedded (not linked) object does not notify its container that its data has changed until the user chooses File Update or File Close. Therefore, if an OLE 2 container registers for a data-change notification on an OLE 2 object in a particular format, it should be aware that it will not be notified immediately when the data changes.

When an OLE 1 object is inserted into a container document and then closed without an update being invoked, the container document is not saved. Neither are the correct streams for the object written into storage. Any subsequent loading of the object by the container will fail. To protect against this, containers can keep data available after the object closes without updating by implementing the following:

OleCreate();             \\ to insert the object
OleRun();                \\ if OLERENDER_NONE was specified
IOleObject::Update();    \\ to get snapshot of data
OleSave();
IOleObject::DoVerb();

See Also

Working with OLE 1 Clients


OLE 1/Registry Compatibility Information

The following topics in this section discuss the registry and how to handle compatibility issues between OLE 1 and related to OLE 2:

  • OLE 1 Compatibility Subkeys
  • OLE 1 Application Entries
  • ProgID and OLE 1 Compatibility
  • Specifying Unregistered Verbs


OLE 1 Compatibility Subkeys

To handle two-way compatibility, the OLE 2 compatibility layer creates OLE 2-style entries for OLE 1 classes it discovers and places them under the CLSID key. When installing an OLE 2 object application on a system that contains an OLE 1 version of the same application, it might be necessary to add "AutoConvertTo" or "TreatAs" subkeys to the original OLE 1 application portion of the registry.

See Also

Information for OLE 1 Applications Subkey Entries

When the OLE 1 Version Is Overwritten


Information for OLE 1 Applications Subkey Entries

To maintain compatibility with OLE 1, you must include specific OLE 1 information. The "server" key entry should contain a full path to the application. The entries for verbs must start with 0 as the primary verb and be consecutively numbered.

Hkey_Classes_Root\Ole2isvrotl\Protocol\Stdfileediting\Server =
    C:\Samp\Isvrotl.Exe
Hkey_Classes_Root\Ole2isvrotl\Protocol\Stdfileediting\Verb\0 = &Edit
Hkey_Classes_Root\Ole2isvrotl\Protocol\Stdfileediting\Verb\1 = &Open


OLE 1 Application Entries

When an OLE 1 class is inserted into an OLE 2 container for the first time, a new subkey, CLSID, is added to the original OLE 1 registration information by the OLE 2 compatibility layer. The value given to this key is a CLSID assigned by OLE 2 to this OLE 1 class as shown in the following portion of the registry:

<OLE1ClassName> = <OLE1UserTypeName>
Protocol
StdFileEditing
Verb
CLSID = <CLSID>


ProgID and OLE 1 Compatibility

Programmatic identifiers are not guaranteed to be unique, so they can be used only where name collisions are manageable, such as in achieving compatibility with OLE 1. Also, the ProgID is the "class name" used for an OLE 2 class when it is placed in a server application (OLE 1 server).

Note Because OLE 2 provides a built-in OLE 1/OLE 2 compatibility layer, rarely will an OLE 2 class that is insertable in an OLE 2 container not be insertable in an OLE 1 container.

See Also

Version-Dependent Identifiers

Inserting an OLE 2 Object in an OLE 1 Application


Version-Dependent Identifiers

The <VersionDependentProgID> is the string to use when OLE 1 needs to contact OLE 2 using DDE. Version-dependent ProgID-to-CLSID conversions must be specific, well defined, and one-to-one.


Inserting an OLE 2 Object in an OLE 1 Application

If a particular class is insertable in an OLE 1 container, the "ProgID" root key will contain a "Protocol\StdFileEditing" subkey with appropriate subkeys "Verb", "Server", and so on, as in OLE 1. The server that should be registered here is the full path to the executable file of the OLE 2 object application. An OLE 1 container uses the path and executable file names to launch the OLE 2 object application. The initialization of this application, in turn, loads the OLE 2 compatibility layer. This layer handles subsequent interactions with the OLE 1 container (client), turning them into OLE 2–like requests to the OLE 2 application. An OLE 2 object application doesn't have to take any special action beyond setting up these registry entries to make objects insertable into an OLE 1 container.

The ProgID key and its subkeys appear in the registry as shown in the following example, where "<Progid>" is the key, and "Insertable," "Protocol," "StdFileEditing," "Verb," and so on are subkeys:

<ProgId> = <MainUserTypeName>
Insertable // Class is insertable in OLE 2 containers
Protocol
 StdFileEditing // OLE 1 compatibility info; present if, and only if,
// objects of this class are insertable in OLE 1 containers.
Server = <full path to the OLE 2 object application>
Verb
0 = <verb 0> // Verb entries for the OLE 2 application must start with zero
1 = <verb 1> // as the primary verb and run consecutively.
CLSID = <CLSID> // The corresponding CLSID. Needed by GetClassFile.
Shell // Windows 3.1 File Manager Info
Print
 Open
 Command = <appname.exe> %1

To summarize, any root key that has either an "Insertable" or a "Protocol\StdFileEditing" subkey is the ProgID (or OLE 1 class name) of a class that should appear in the Insert Object dialog box. The value of that root key is the name displayed in the Insert Object dialog box.

The values of each key in the example below are used for registering the "Ole 2 In-Place Server Outline" sample application. Set these values as required and used by your application.


Specifying Unregistered Verbs

It is possible to register OLE 1 object applications (servers) with no specified verbs. In such cases, the application has a single implied verb that is understood to be "edit" by OLE 1 containers. When an OLE 2 application calls the IOleObject::EnumVerbs method (or the OleRegEnumVerbs function) on an object of this class, one verb is enumerated. By default, the name of the verb is "Edit". To avoid having the string "Edit" as the enumerated verb, the following key can be included in the registry (where verbname is the value that will be returned from the enumeration):

\Software\Microsoft\OLE1\UnregisteredVerb = <verbname>

This key allows for localization of the default verb to a specified string to accommodate a specific language.

Note Do not attempt to register a verb (under ProgID\Protocol\StdFileEditing) for an application that did not register the verb itself.


Accommodating OLE 1 Versions of the Object Application

When the OLE 1 version of an object application is superseded by an OLE 2 version and the OLE 2 version is to be installed in the user's system, such as when upgrading, the following two situations are possible:

  • An OLE 1 version of the application is present on the user's system, and the installation process overwrites the OLE 1 executable with the OLE 2 version.
  • An OLE 1 version of the application is present on the user's system, and the user chooses not to overwrite it with the OLE 2 version.

Note Even if the OLE 1 object application is not on the user's system, the install/setup program for the OLE 2 object application should register the application as capable of servicing its OLE 1 objects. To do this, follow the guidelines presented in When the OLE 1 Version Is Overwritten, and add the following entry to the CLSID root key:

<OLE 1 class name>/CLSID = <CLSID of OLE 1 application>


When the OLE 1 Version Is Overwritten

When the OLE 1 object application is replaced by the OLE 2 version, do the following. (For the purpose of illustration, the OLE 1 object application is referred to as "Ole 1 In-Place Server Outline" while the OLE 2 version is "Ole 2 In-Place Server Outline," as shown in the dialog box illustrations that follow.)

  1. Register Ole 2 In-Place Server Outline.
  2. Modify (with your install/setup program) the original registry entries of "Ole 1 In-Place Server Outline" by changing the executable path to point to the Ole 2 In-Place Server Outline executable.

    For example, the Server subkey for the OLE 1 executable, named svrapp.exe in this example, changes from

    OLE1ISvrOtl\Protocol\StdFileEditing\Server = svrapp.exe

    to

    OLE1ISvrOtl\Protocol\StdFileEditing\Server = isvrotl.exe

    where isvrotl.exe is the name of the OLE 2 object application.

    Next, proceed to either step 3 or step 4, depending on whether or not you want OLE 1 objects converted automatically to the OLE 2 format.

  3. If Ole 1 In-Place Server Outline objects will be converted automatically to the Ole 2 In-Place Server Outline format when the application is saved, create or modify the following registration database entries:
    1. Modify the original registry entry of "Ole 1 In-Place Server Outline" by changing the "Value of the ProgID = Main User Type Name" key of the entry to "Ole 2 In-Place Server Outline".

      For example, where OLE1ISvrOtl is the ProgID of the OLE 1 application,

      OLE1ISvrOtl = Ole 1 In-Place Server Outline

      becomes

      OLE1ISvrOtl = Ole 2 In-Place Server Outline

    2. Modify the original registry entry of "Ole 1 In-Place Server Outline" by adding a NotInsertable subkey under the ProgID key. For example:
      HKEY_CLASSES_ROOT\OLE1SvrOtl\NotInsertable

      Note Because the original registry entries for the OLE 1 server application remain (but with a pointer to the OLE 2 object application as shown in the preceding step 3a), the OLE 1 ProgID will appear in the Insert Object dialog box of any OLE 2 container application installed on the system. The NotInsertable subkey mentioned in step 3b prevents the ProgID of the OLE 1 application from appearing in the Insert Object dialog box of OLE 2 containers. The NotInsertable subkey overrides any Insertable subkey entries for that ProgID key.

    3. Set the "AutoConvertTo = CLSID" subkey entry for Ole 1 In-Place Server Outline under the CLSID key to the CLSID of Ole 2 In-Place Server Outline. (See also OLE 1 Compatibility Subkeys.)

      CLSID\{CLSID of OLE 1 app.}\AutoConvertTo = {CLSID of OLE 2 app.}

      Note You can obtain the CLSID of the OLE 1 object application for inclusion in the registration entry file by calling CLSIDFromProgID.

    4. Modify the original registry entry of Ole 1 In-Place Server Outline by setting the verbs to those of Ole 2 In-Place Server Outline.

      For example, change

      OLE1ISvrOtl\Protocol\StdFileEditing\Verb\0 = &Edit

      to

      OLE1ISvrOtl\Protocol\StdFileEditing\Verb\0 = &Edit

      OLE1ISvrOtl\Protocol\StdFileEditing\Verb\1 = &Open

  4. If the user is allowed to open Acme Draw 1.0 objects and save them back to disk in the Acme Draw 1.0 format, do the following:
    1. Set the "TreatAs = CLSID" entry to the CLSID of Ole 2 In-Place Server Outline using the following form.

      CLSID\{CLSID of OLE 1 app.}\TreatAs = {CLSID of OLE 2 app.}

    2. Set the Ole 1 In-Place Server Outline verbs to those of Ole 2 In-Place Server Outline as described in the preceding step 3.


When the OLE 1 Version Is Not Overwritten

You can install your program so the OLE 1 object application (Ole 1 In-Place Server Outline) is not replaced by the OLE 2 version (Ole 2 In-Place Server Outline). Instead, the user is allowed to open Ole 1 In-Place Server Outline objects with the Ole 2 In-Place Server Outline and save them back to disk in the Ole 1 In-Place Server Outline format. To do this, set the "TreatAs = CLSID" entry (Ole 1 In-Place Server Outline's portion of the registry) to the CLSID of Ole 2 In-Place Server Outline (as in step 4 in When the OLE 1 Version Is Overwritten).

If the OLE 1 version of the application is not overwritten or if the user does not want to set the "Treat As" option, register the OLE 2 version as a separate and new application.


Upgrading Applications

When an OLE 1 server is upgraded to an OLE 2 object application, several issues arise. A primary issue is whether the OLE 2 application will replace the OLE 1 application or whether both versions will coexist. If only the newer version will be available to the user, it is best to convert objects from the older version of the application automatically to the new version format. Objects can be converted on a global basis, where all objects of a specific class are converted or, on a more selective basis, where only some objects are converted. Conversion can be automatic, under programmatic control, or under the control of a user.

The ability to detect whether an object is from an OLE 1 server is helpful for implementing conversion functionality. The OLE 2 implementation of the IPersistStorage::Load method can check for a stream named "\1Ole10Native." The "\1Ole10Native" stream contains a DWORD header whose value is the length of the native data that follows. The existence of this stream indicates that the data is coming from an OLE 1 server. Applications can check whether a storage object contains an object in an OLE 1 format by calling the ReadFmtUserTypeStg method and examining the contents of pcfFormat. This is where the OLE 1 class name would appear.

In the IPersistStorage::Save method, objects that are being permanently converted should be written back to storage in the new format and the "\1Ole10Native" stream should be deleted. The conversion bit in the storage should also be cleared when the conversion to the new format is complete.

To allow manual conversion of an old OLE 1 object to the new OLE 2 version, the OLE 2 object application must put the OLE 1 server's ProgID (OLE 1 server class name) into the registry under the CLSID\{...}\Conversion\Readable\Main entry. This entry indicates that the OLE 2 application can read its OLE 1 data format; the 'Clipboard format' of the OLE 1 data is the ProgID (that is, the class name) of the OLE 1 object.

To get a CLSID for an OLE 1 server, the CLSIDFromProgId function or the CLSIDFromString function must be called. That is, an OLE 1 application cannot be assigned a CLSID from an OLE 2 application with uuidgen.exe, CoCreateGuid, or by using a GUID from a range assigned by Microsoft. Because all OLE 1 CLSIDs are expected to fall in a specific range, OLE 1 CLSIDs are assigned with the CLSIDFromProgId function.


Functions to Support Compatibility

The functions described in the following table enable applications both to determine whether an object class is from OLE 1 and to support conversion between OLE 1 and OLE 2 storage formats.

OLE 1 Compatibility Functions | Description
CoIsOle1Class | Determines whether a given CLSID represents an OLE 1 object.
OleConvertIStorageToOLESTREAM | Converts the specified storage object from OLE 2 structured storage to the OLE 1 storage model.
OleConvertIStorageToOLESTREAMEx | Converts the specified storage object from OLE 2 structured storage to the OLE 1 storage model.
OleConvertOLESTREAMToIStorage | Converts the specified object from the OLE 1 storage model to an OLE 2 structured storage object.
OleConvertOLESTREAMToIStorageEx | Converts the specified object from the OLE 1 storage model to an OLE 2 structured storage object.


16:32 Bit Interoperability

Interoperability is achieved by converting calls made on objects in one model into calls suitable in another model. For example, if a call is made by a 16-bit application to an object that is a 32-bit implementation, the OLE 16:32 interoperability (thunk) layer takes care of marshaling and converting parameters between the two models.

See Also

Thunk Layer Operation


Thunk Layer Operation

The thunk layer replaces the 16-bit implementations of OLE 2 with a new set of binaries that allow the 16-bit implementations to call the updated 32-bit implementation of OLE 2. These new binaries, collectively known as the thunk layer, forward all OLE 2 method and function calls to the 32-bit implementation of OLE. The OLE thunk layer inserts itself between implementations of different models. Whenever an interface is passed between implementation types, the system provides a thunk proxy and stub.

For example, a call to the CreateFileMoniker function returns a pointer to a moniker. When a 16-bit implementation calls the CreateFileMoniker function, the system thunks the call to the 32-bit OLE 2 implementation and creates a file moniker using a 32-bit implementation. When the call returns through the OLE thunk layer, the moniker pointer is translated into a 16/32 proxy.

A 16/32 proxy sets up an object in the 16-bit address space that, when called, converts each parameter for the specific method into the appropriate values for calling the 32-bit implementation. It then performs a model switch from 16-bit to 32-bit. The call is made on the 32-bit implementation, and then another model switch is made from the 32-bit to 16-bit implementation. In the process of switching back, any output parameters are converted into their 16-bit counterparts.

The system keeps track of the model of each pointer. For example, if a 16-bit application calls a 32-bit application, it passes a 16-bit pointer to an object. The thunk layer will create a 32/16 proxy to let the 32-bit application call the 16-bit application's object. In response, the 32-bit application calls back to the 16-bit application, passing the pointer it received. In this direction, the pointer being held by the 32-bit application holds is to a 32/16 proxy. The thunk layer also detects that the 32-bit pointer being passed in is a proxy and passes the original pointer it started with to the 16-bit application. Therefore, the 16-bit application sees the same pointer it passed in. This works when passing from 16-bit to 32-bit to 16-bit, or from 32-bit to 16-bit to 32-bit.

See Also

Interoperability Using Standard Interfaces (OLE 2 Defined)

Interoperability Using Custom Interfaces (Either MIDL or Manually Written Marshaling)


Interoperability Using Standard Interfaces (OLE 2 Defined)

In its 32-bit implementation, OLE 2 is provided by the operating system and can translate OLE-defined interfaces and methods. Existing OLE 2 applications will continue to work as before. The following combinations of interoperability using the standard interfaces defined by OLE 2 are supported:

  • 16-bit to 16-bit (in-process server)
  • 16-bit to 16-bit (local server)
  • 16-bit to 32-bit (local server)
  • 32-bit to 32-bit (in-process server)
  • 32-bit to 32-bit (local server)
  • 32-bit to 16-bit (local server)

32-bit to 16-bit (in-process server) interoperability using the standard interfaces is not supported.

See Also

Interoperability Using Custom Interfaces (Either MIDL or Manually Written Marshaling)


Interoperability Using Custom Interfaces (Either MIDL or Manually Written Marshaling)

Due to major architectural changes in the OLE communication layer, 16-bit applications that require remoting of custom interfaces are not supported at this time. However, 16-bit applications that use custom interfaces to an in-process server should still function correctly. The following combinations of interoperability using custom interfaces are supported:

  • 16-bit to 16-bit (in-process server)
  • 32-bit to 32-bit (in-process server)
  • 32-bit to 32-bit (local server using MIDL-generated stubs)

The following combinations of interoperability using custom interfaces are not supported:

  • 16-bit to 16-bit (local server)
  • 16-bit to 32-bit (all combinations)
  • 32-bit to 16-bit (all combinations)

Programmers who have written applications that provide custom interfaces must rewrite the custom interface marshaling code to conform to the RPC-based communication layer. However, information about writing a 16-bit custom-marshaling library is not available at this time. For more information about RPC, see the RPC Programmer's Guide and Reference.

See Also

Interoperability Using Standard Interfaces (OLE 2 Defined)


C and C++ Design Considerations

Component objects contain data specific to the object and one or more interface implementations. The data is private and inaccessible from outside the object, while the interface implementations are public and you can access them through pointers.

Because interfaces are a binary standard, interface implementation is language independent. However, Microsoft® Visual C++® is the preferred language because it supports many of the object-oriented concepts inherent in OLE. Using a procedural language such as C involves extra work, which can be summarized as follows:

  • You must explicitly initialize VTBLs either at compile-time or run- time, and you should not change them later. Once initialized with function pointers, those pointers should remain unchanged until the application shuts down. VTBLs in C++ are declared as constants to prevent them from being modified inadvertently. However, in C there is no way to ensure that a VTBL will remain unchanged.

    To overcome this difference, OLE provides a mechanism that lets C developers declare VTBLs as constants. To do so, place the following statement before the #include statement for the ole2.h header file:

    #define CONST_VTABLE
  • Each method requires a pointer to the object that owns the method. In C++, all members are implicitly dereferenced off the this pointer. In C, there must be an additional first parameter passed to each method that is a pointer to the interface in which the method is declared.
  • Methods in C++ can have identical names because methods are actually known by a name that is the result of concatenating the method name to the class name. Methods in C must have a unique name to designate the object with which they are associated.

    For example, the following C++ code sample defines an implementation of IUnknown::QueryInterface. The method name is QueryInterface and there are two parameters: a REFIID and a pointer to where to return the requested interface instance.

    CUnknown::QueryInterface (REFIID riid, LPVOID * ppvObj);

A similar C implementation would require a more complex name and an additional first parameter to indicate the object owning the method, as follows:

IUnknown_Doc_QueryInterface (LPUNKNOWN pUnk, REFIID riid,
    LPVOID * ppvObj);

The following topics in this section demonstrate how to declare a component object in a few typical ways:

  • Component Objects: C Nested Structures
  • Component Objects: C++ Nested Classes
  • C++ Multiple Inheritance

Note The demonstration object, named CObj, that is used in the preceding topics derives from IUnknown and supports two interfaces that also derive from Iunknown: InterfaceA and InterfaceB. The CObj object's private data includes a pointer to another component object in the application (m_pCDoc), a count of all the external references to the object (m_ObjRefCount), and pointers to two interfaces implemented by other component objects and used by CObj (m_pOleObj and m_pStg). All object members use the m_ prefix to make it easy to distinguish between member variables and other variables.


Component Objects: C Nested Structures

C interface implementations comprise data structures nested within the object's data structure. Each interface structure contains a VTBL pointer as its first member (pVtbl), a pointer to the object (pCObj), and a count of the external references to the interface (m_RefCount). To facilitate code sharing, the order of the members in the interface structures is identical.

typedef struct CObj {
  ULONG            m_ObjRefCount;
  LPSTORAGE        m_pStg;
  LPOLEOBJECT      m_pOleObj;
  struct CDOC    *  m_pCDoc;

  struct InterfaceA  {
    LPVTBL        pVtbl;
    struct CObj  *  pCObj;
    ULONG        m_RefCount;
  } m_InterfaceA;

  struct InterfaceB {
    LPVTBL       pVtbl;
    struct Obj  *    pCObj;
    ULONG        m_RefCount;
  } m_InterfaceB;

} COBJ;

See Also

Component Objects: C++ Nested Classes

C++ Multiple Inheritance


Component Objects: C++ Nested Classes

This example shows how the same object is declared and initialized using the Visual C++ nested class approach. As in the example in Component Objects: C Nested Structures, the nested class declaration includes one data structure for each interface and four private data members: an object-level reference count, two interface pointers, and a pointer to the enclosing object.

The private implementations of the IUnknown methods are called by the implementations declared for the derived interfaces. For each interface implementation, there is a structure containing a public constructor and destructor, private declarations of the interface methods, a private pointer to CObj, and an interface-level reference counter for debugging purposes. To allow the nested interface classes to access the private members of the outer class, each interface class is made a friend of the outer class.

The benefits of implementing with C++ nested classes lies in the ability to include initialization code and method implementation inline. However, inline declaration is for the convenience of illustration and is not required.

class  CObj  {

private:
  ULONG          m_ObjRefCount;
  LPSTORAGE      m_pStg;
  LPOLEOBJECT    m_pOleObj;
  CDOC    *      m_pCDoc;

public:
  CObj();
  ~CObj();

  struct CUnknown : IUnknown
{
  private:
  ULONG          m_RefCount;
  CObj  *        m_pCObj;

  public:
  CUnknown(CObj (pCObj)
  { m_pCObj = pCObj; m_RefCount = 0; }
  HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj)
  ULONG AddRef(void) { return ++m_ObjRefCount; }
  ULONG Release(void);
}
friend CUnknown;
CUnknown m_Unknown;

struct InterfaceA : InterfaceA
{
  private:
  ULONG          m_RefCount;
  CObj  *        m_pCObj;

  public:
  CInterfaceA(CObj *pCObj)
  { m_pCObj = pCObj; m_RefCount = 0; }
  HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj)
  ULONG AddRef(void) { return ++m_ObjRefCount; }
  ULONG Release(void);
  HRESULT MethodA1(LPVOID  * ppvObj);
  HRESULT MethodA2(DWORD dwArg);
}
friend CInterfaceA;
CInterfaceA m_InterfaceA;

struct InterfaceB : InterfaceB
{
  private:
  ULONG          m_RefCount;
  CObj  *        m_pCObj;

  public:
  CInterfaceB(CObj *pCObj)
  { m_pCObj = pCObj; m_RefCount = 0; }
  HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj)
  ULONG AddRef(void) { return ++m_ObjRefCount; }
  ULONG Release(void);
  HRESULT MethodB1(void);
  HRESULT MethodB2(DWORD dwArg1, DWORD dwArg2);
}
friend CInterfaceB;
CInterfaceB m_InterfaceB;

See Also

Component Objects: C Nested Structures

C++ Multiple Inheritance


C++ Multiple Inheritance

This example illustrates the use of Visual C++ multiple inheritance. There are two disadvantages to using multiple inheritance with OLE. First, it is not possible to have an interface-level reference count. (For more information about reference counting, see The Component Object Model.) Second, there is the potential for confusion over the interpretation of the class statement. A standard C++ multiple inheritance declaration implies the "is a" relationship where an object inherits implementations. In OLE, however, interfaces are attributes of the object and implementations are not inherited.

The main advantage to using multiple inheritance lies in its simplicity. Only the prototypes for each of the interface methods are listed; no interface data structures or class definitions are necessary.

Because both InterfaceA and InterfaceB inherit from IUnknown, it is not necessary to list IUnknown explicitly in the class statement. A single implementation of the IUnknown methods (QueryInterface, AddRef, and Release) is sufficient.

class  CObj : public InterfaceA, public InterfaceB
{
private:
  ULONG          m_ObjRefCount;
  LPSTORAGE      m_pStg;
  LPOLEOBJECT    m_pOleObj;
  CDOC  *        m_pCDoc;

public:
  CObj();
  ~CObj();

  HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj)
  ULONG AddRef(void) { return ++m_ObjRefCount; }
  ULONG Release(void);

  HRESULT MethodA1(LPVOID  * ppvObj);
  HRESULT MethodA2(DWORD dwArg);

  HRESULT MethodB1(void);
  HRESULT MethodB2(DWORD dwArg1, DWORD dwArg2);
};

See Also

Component Objects: C Nested Structures

Component Objects: C++ Nested Classes


IAccessControl

The IAccessControl interface enables the management of access to objects and properties on the objects.

When to Implement

Distributed COM provides an implementation of the IAccessControl interface. COM servers can use this implementation to protect their objects from unauthorized access in a manner that is supported by Microsoft® Windows® 95, Microsoft Windows 98, Microsoft Windows NT®, and Microsoft Windows 2000. To get a pointer to this implementation, call CoCreateInstance, specifying CLSID_DCOMAccessControl as the CLSID. This implementation supports the IPersist interface to save the state of the access control object.

The implementation of IAccessControl provided by COM calls built-in access control functions such as OpenThreadToken and AccessCheck. If you decide to implement IAccessControl yourself, you can also call these access control functions. However, because IAccessControl methods take access information in a different format than the built-in access control functions do, your implementation must be able to convert from one format to the other as necessary.

Note If you decide to implement IAccessControl and pass your implementation to CoInitializeSecurity, you must ensure that it is completely thread-safe, because COM can call it on any thread, at any time.

In addition to the COM implementation of IAccessControl, another implementation is supplied for storage and Directory Service objects.

When to Use

Call methods of the IAccessControl interface to manage access to objects and properties on the objects and to obtain access information. This interface is primarily used to set processwide security with a call to CoInitializeSecurity, specifying EOAC_ACCESS_CONTROL as the capability flag, and providing a pointer to an instance of IAccessControl as the first (pVoid) parameter. COM then calls IAccessControl methods to determine access rights.

IAccessControl should be used only to manage access rights. To manage launch permissions, use DCOMCNFG or set the LaunchPermission named value under the APPID registry key. For details on using DCOMCNFG, see the DCOMCNFG online help.

COM formats all character strings as UNICODE. For computers running Windows 95, you must convert all ANSI strings to Unicode before passing them to methods of an implementation of IAccessControl.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IAccessControl Methods | Description
GrantAccessRights | Merges the new ACTRL_ACCESS list with the existing access rights on the object.
SetAccessRights | Replaces existing access rights with the specified list.
SetOwner | Sets an item's owner or group.
RevokeAccessRights | Removes explicit entries for the list of trustees.
GetAllAccessRights | Gets the entire list of access rights and/or the owner and group for the object.
IsAccessAllowed | Determines whether the trustee has access to the object/property.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.

See Also

CoInitializeSecurity, Setting Processwide Security with CoInitializeSecurity


IAccessControl::GetAllAccessRights

Gets the entire list of access rights and/or the owner and group for the specified object.

HRESULT GetAllAccessRights(
  LPWSTR lpProperty,  //Name of the property
  PACTRL_ACCESSW  * ppAccessList,
                      //Address of output variable that receives a
                      // pointer to the ACTRL_ACCESSW structure
  PTRUSTEEW * ppOwner, //Address of output variable that receives a
                      // pointer to the owner's TRUSTEEW structure
  PTRUSTEEW * ppGroup  //Address of output variable that receives a
                      // pointer to the group's TRUSTEEW structure
);

Parameters

lpProperty
[in] Name of the property. If you are using the COM implementation of IAccessControl, this parameter must be NULL.
ppAccessList
[out] Address of PACTRL_ACCESSW pointer variable that receives a pointer to the access list structure. May not be NULL. If the call succeeds, the caller must free the allocated memory with CoTaskMemFree. Note that the memory is allocate(all_nodes), which means that all the substructures are allocated in one block. Therefore, the entire data structure must be freed by a single call to CoTaskMemFree.
ppOwner
[out] Address of PTRUSTEEW pointer variable that receives a pointer to the owner's TRUSTEEW structure. If this parameter is not NULL and the function succeeds, the caller must free the memory with CoTaskMemFree.
ppGroup
[out] Address of PTRUSTEEW pointer variable that receives a pointer to the group's TRUSTEEW structure. If this parameter is not NULL and the function succeeds, the caller must free the memory with CoTaskMemFree.

Remarks

The ACTRL_ACCESSW structure is the Unicode-compatible version of the ACTRL_ACCESS structure.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.

See Also

IAccessControl::GrantAccessRights, IAccessControl::SetAccessRights, ACTRL_ACCESS


IAccessControl::GrantAccessRights

Merges the new ACTRL_ACCESS list with the existing access rights on the object.

HRESULT GrantAccessRights(
  PACTRL_ACCESSW pAccessList  //Pointer to the access list
);

Parameters

pAccessList
[in] Pointer to the ACTRL_ACCESSW list, which contains an array of access lists for the object.

Remarks

Merging the new ACTRL_ACCESSW list with the existing access rights ensures that the object will have at least the indicated access rights. This merge process consists of adding the new denied access rights before the old denied access rights, and the new allowed access rights before the existing allowed rights. None of the existing rights are removed.

Following a merge, the access rights on an object are ordered as follows:

[New Access Denied]

[Old Access Denied]

[New Access Allowed]

[Old Access Allowed]

The system-supplied implementation of GrantAccessRights (CLSID_DCOMAccessControl) requires that the cEntries member of the ACTRL_ACCESSW structure be set to exactly 1. In addition, the lpProperty member of the ACTRL_PROPERTY_ENTRYW structure must be NULL to indicate that the access entry list applies to the object itself.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.

See Also

IAccessControl::GetAllAccessRights, IAccessControl::SetAccessRights


IAccessControl::IsAccessAllowed

Determines whether the given trustee has access rights to the object or property.

HRESULT IsAccessAllowed(
  PTRUSTEEW pTrustee,          //Pointer to the trustee
  LPWSTR lpProperty,           //Name of the property
  ACCESS_RIGHTS AccessRights,  //Access rights on the object
  BOOL * pfAccessAllowed       //Pointer to value indicating
                               //  whether access is allowed

Parameters

pTrustee
[in] Pointer to the trustee.
lpProperty
[in] Name of the property. If you are using the COM implementation of IAccessControl, this parameter must be NULL.
AccessRights
[in] Access rights on the object. ACCESS_RIGHTS is defined as a ULONG. If you are using the COM implementation of IAccessControl, this value must be either 0 or 1 (COM_RIGHTS_EXECUTE).
pfAccessAllowed
[out] Pointer to a BOOL variable that is set to TRUE if access is allowed; otherwise, it is set to FALSE.

Remarks

In the system-supplied implementation of IAccessControl (CLSID_DCOMAccessControl), IsAccessAllowed can be called only during a distributed COM call, and the only valid trustee name is the name of the client.

The following table lists the object-specific access permissions used with the Directory Service and storage implementation of IAccessControl.

Access Permission | Object-type | Description
ACTRL_DS_OPEN | Ds | Open of a DS object
ACTRL_DS_CREATE_CHILD | Ds | Creation of a child object
ACTRL_DS_DELETE_CHILD | Ds | Deletion of a child object
ACTRL_DS_LIST | Ds | Enumerate an object
ACTRL_DS_SELF | Ds | Member list updates involving the trustee
ACTRL_DS_READ_PROP | Ds | Read properties
ACTRL_DS_WRITE_PROP | Ds | Write properties
ACTRL_FILE-READ | File | Read from a file
ACTRL_FILE_WRITE | File | Write to a file
ACTRL_FILE_APPEND | File | Append to a file
ACTRL_FILE_READ_PROP | File | Read file properties / Extended attributes
ACTRL_FILE_WRITE_PROP | File | Write file properties / Extended attributes
ACTRL_FILE_EXECUTE | File | Execute the file
ACTRL_FILE_READ_ATTRIB | File | Read the file attributes
ACTRL_FILE_WRITE_ATTRIB | File | Write the file attributes
ACTRL_DIR_LIST | Directory | List the contents of a directory
ACTRL_DIR_CREATE_OBJECT | Directory | Create a child object (file) in a directory
ACTRL_DIR_CREATE_CHILD | Directory | Create a subdirectory
ACTRL_DIR_DELETE_CHILD | Directory | Delete a subdirectory
ACTRL_DIR_TRAVERSE | Directory | Traverse through the directory
ACTRL_KERNEL_KILL | Kernel | Kill a kernel item (process / thread)
ACTRL_KERNEL_THREAD | Kernel | Allow for the creation of a thread
ACTRL_KERNEL_VM | Kernel | Allow for address space operations
ACTRL_KERNEL_VM_READ | Kernel | Read memory
ACTRL_KERNEL_VM_WRITE | Kernel | Write memory
ACTRL_KERNEL_DUP_HANDLE | Kernel | Duplicate a handle
ACTRL_KERNEL_PROCESS | Kernel | Create a process
ACTRL_KERNEL_SET_INFO | Kernel | Get kernel object information/state
ACTRL_KERNEL_GET_INFO | Kernel | Set kernel object information/state
ACTRL_KERNEL_CONTROL | Kernel | Allow for the control of a kernel object (such as suspending a thread)
ACTRL_KERNEL_ALERT | Kernel | TBD
ACTRL_KERNEL_GET_CONTEXT | Kernel | Get the threads context
ACTRL_KERNEL_SET_CONTEXT | Kernel | Set the threads context
ACTRL_KERNEL_TOKEN | Kernel | Set the threads token
ACTRL_KERNEL_IMPERSONATE | Kernel | Client impersonation
ACTRL_KERNEL_DIMPERSONATE | Kernel | Direct client impersonation
ACTRL_PRINT_SADMIN | Printer | Print server administration
ACTRL_PRINT_SLIST | Printer | Print server enumeration
ACTRL_PRINT_PADMIN | Printer | Printer administration
ACTRL_PRINT_PUSE | Printer | Can the printer be used
ACTRL_PRINT_JADMIN | Printer | Print job administration
ACTRL_SVC_GET_INFO | Service | Get the info on a service
ACTRL_SVC_SET_INFO | Service | Set the info on a service
ACTRL_SVC_STATUS | Service | Get the status of a service
ACTRL_SVC_LIST | Service | Enumerate the services
ACTRL_SVC_START | Service | Start a service
ACTRL_SVC_STOP | Service | Stop a service
ACTRL_SVC_PAUSE | Service | Pause a service
ACTRL_SVC_INTERROGATE | Service | Query the service for the current status
ACTRL_SVC_UCONTROL | Service | User-defined control
ACTRL_REG_QUERY | Registry | Read a registry subkey
ACTRL_REG_SET | Registry | Write a registry subkey
ACTRL_REG_CREATE_CHILD | Registry | Create a registry subkey
ACTRL_REG_LIST | Registry | Enumerate a registry subkey
ACTRL_REG_NOTIFY | Registry | Create a registry notification
ACTRL_REG_LINK | Registry | Create a symbolic link
ACTRL_WIN_CLIPBRD | Window | Allow access to the clipboard
ACTRL_WIN_GLOBAL_ATOMS | Window | Allow global-atom access
ACTRL_WIN_CREATE | Window | Create desktop access
ACTRL_WIN_LIST_DESK | Window | Enumerate the desktops
ACTRL_WIN_LIST | Window | Enumerate the window station
ACTRL_WIN_READ_ATTRIBS | Window | Read the attributes
ACTRL_WIN_WRITE_ATTRIBS | Window | Write the attributes
ACTRL_WIN_SCREEN | Window | Allow access to the screen
ACTRL_WIN_EXIT | Window | Call ExitWindows* functions

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.


IAccessControl::RevokeAccessRights

Removes any explicit entries for the list of trustees.

HRESULT RevokeAccessRights(
  LPWSTR lpProperty,     //Name of the property
  ULONG cTrustees,       //Number of trustees in the list
  TRUSTEEW prgTrustees[]  //Pointer to an array of trustee names
);

Parameters

lpProperty
[in] Name of the property. If you are using the COM implementation of IAccessControl, this parameter must be NULL.
cTrustees
[in] Number of trustees in the list. This value must not be zero.
prgTrustees[]
[in] Pointer to an array of trustee names.

Remarks

Even after removing explicit entries, the trustees might still have access entries due to group inclusion.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.

See Also

IAccessControl::GrantAccessRights, TRUSTEE


IAccessControl::SetAccessRights

Replaces the existing access rights on an object with the specified list.

HRESULT SetAccessRights(
  PACTRL_ACCESSW pAccessList  //Pointer to the access list
);

Parameters

pAccessList
[in] Pointer to the ACTRL_ACCESS list, which contains an array of access lists to write to the object.

Remarks

After its access rights are replaced, an object has only the access rights specified in the ACTRL_ACCESSW list.

Following SetAccessRights, the access rights on an object are as follows:

[New Access Denied] [New Access Allowed]

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.

See Also

IAccessControl::GrantAccessRights, IAccessControl::GetAllAccessRights


IAccessControl::SetOwner

Sets the owner or the group of an item.

HRESULT SetOwner(
  PTRUSTEEW  pOwner, //Address of item owner's TRUSTEEW structure
  PTRUSTEEW  pGroup  //Address of the item group's TRUSTEEW structure
);

Parameters

pOwner
[in] Address of PTRUSTEEW variable that contains a pointer to the owner's TRUSTEE structure for the item.
pGroup
[in] Address of PTRUSTEEW variable that contains a pointer to the group's TRUSTEEW structure for the item.

Remarks

The SetOwner method is not implemented by CLSID_DCOMAccessControl.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in oleext.h.


IBindCtx

The IBindCtx interface provides access to a bind context, which is an object that stores information about a particular moniker binding operation. You pass a bind context as a parameter when calling many methods of IMoniker and in certain functions related to monikers.

A bind context includes the following information:

  • A BIND_OPTS structure containing a set of parameters that do not change during the binding operation. When a composite moniker is bound, each component uses the same bind context, so it acts as a mechanism for passing the same parameters to each component of a composite moniker.
  • A set of pointers to objects that the binding operation has activated. The bind context holds pointers to these bound objects, keeping them loaded and thus eliminating redundant activations if the objects are needed again during subsequent binding operations.
  • A pointer to the Running Object Table on the machine of the process that started the bind operation. Moniker implementations that need to access the Running Object Table should use the IBindCtx::GetRunningObjectTable method rather than using the GetRunningObjectTable function. This allows future enhancements to the system's IBindCtx implementation to modify binding behavior.
  • A table of interface pointers, each associated with a string key. This capability enables moniker implementations to store interface pointers under a well-known string so that they can later be retrieved from the bind context. For example, OLE defines several string keys ("ExceededDeadline", "ConnectManually", and so on) that can be used to store a pointer to the object that caused an error during a binding operation.

When to Implement

You do not need to implement this interface. The system provides an IBindCtx implementation, accessible though a call to the CreateBindCtx function, that is suitable for all situations.

When to Use

Anyone writing a new moniker class by implementing the IMoniker interface must call IBindCtx methods in the implementation of several IMoniker methods. Moniker providers (servers that hand out monikers to identify their objects) may also need to call IBindCtx methods from their implementations of the IOleItemContainer or IParseDisplayName interface.

Moniker clients (objects that use monikers to acquire interface pointers to other objects) typically don't call many IBindCtx methods. Instead, they simply pass a bind context as a parameter in a call to an IMoniker method. To acquire an interface pointer and activate the indicated object (called binding to an object), moniker clients typically do the following:

  1. Call the CreateBindCtx function to create a bind context and get a pointer to the IBindCtx interface on the bind context object.
  2. If desired (although this is rarely necessary), the moniker client can call IBindCtx::SetBindOptions to specify the bind options.
  3. Pass the bind context as a parameter to the desired IMoniker method (usually IMoniker::BindToObject).
  4. Call IUnknown::Release on the bind context to release it.

Although applications that act as link containers (container applications that allow their documents to contain linked objects) are moniker clients, they rarely call IMoniker methods directly. Generally, they manipulate linked objects through the system implementation (in the default handler) of the IOleLink interface. This implementation calls the appropriate IMoniker methods as needed and, in doing so, passes pointers to IBindCtx interfaces on the proper bind context objects.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IBindCtx Methods | Description
RegisterObjectBound | Registers an object with the bind context.
RevokeObjectBound | Revokes an object's registration.
ReleaseBoundObjects | Releases all registered objects.
SetBindOptions | Sets the binding options.
GetBindOptions | Retrieves the binding options.
GetRunningObjectTable | Retrieves a pointer to the Running Object Table.
RegisterObjectParam | Associates an object with a string key.
GetObjectParam | Returns the object associated with a given string key.
EnumObjectParam | Enumerates all the string keys in the table.
RevokeObjectParam | Revokes association between an object and a string key.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CreateBindCtx, IMoniker, IOleItemContainer, IParseDisplayName


IBindCtx::EnumObjectParam

Supplies a pointer to an IEnumString interface on an enumerator that can return the keys of the bind context's string-keyed table of pointers. In the system implementation of the IBindCtx interface, the IBindCtx::EnumObjectParam method is not currently implemented. Calling this function results in a return value of E_NOTIMPL.

HRESULT EnumObjectParam(
  IEnumString **ppenum  //Address of output variable that receives
                        // the IEnumString interface pointer
);

Parameter

ppenum
[out] Address of IEnumString* pointer variable that receives the interface pointer to the enumerator. If an error occurs, *ppenum is set to NULL. If *ppenum is non-NULL, the implementation calls IUnknown::AddRef on the *ppenum; it is the caller's responsibility to call IUnknown::Release.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
An enumerator was successfully created and the pointer supplied.

Remarks

This method provides an IEnumString pointer to an enumerator that can return the keys of the bind context's string-keyed table of pointers. The keys returned are the ones previously specified in calls to IBindCtx::RegisterObjectParam.

Notes to Callers

A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a string known to both parties so that the other party can later retrieve it from the bind context.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::RegisterObjectParam, IEnumString


IBindCtx::GetBindOptions

Returns the binding options stored in this bind context.

HRESULT GetBindOptions(
  BIND_OPTS *pbindopts  //Pointer to a structure
);

Parameter

pbindopts
[in, out] Pointer to an initialized BIND_OPTS or BIND_OPTS2 structure on entry that receives the current binding parameters on return.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The stored binding options were successfully returned.

Remarks

A bind context contains a block of parameters, stored in a BIND_OPTS structure, that are common to most IMoniker operations and that do not change as the operation moves from piece to piece of a composite moniker.

Notes to Callers

You typically call this method if you are writing your own moniker class. (This requires that you implement the IMoniker interface.) You call this method to retrieve the parameters specified by the moniker client.

You must initialize the BIND_OPTS structure that is filled in by this method. Before calling this method, you must initialize the cbStruct field of the structure to the size of the BIND_OPTS structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::SetBindOptions


IBindCtx::GetObjectParam

Retrieves the pointer associated with the specified key in the bind context's string-keyed table of pointers.

HRESULT GetObjectParam(
  LPOLESTR pszKey,  //Pointer to the key to be used
  IUnknown **ppunk  //Address of output variable that receives the
                    // IUnknown interface pointer
);

Parameters

pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the key to search for. Key string comparison is case-sensitive.
ppunk
[out] Address of IUnknown* pointer variable that receives the interface pointer to the object associated with pszKey. When successful, the implementation calls IUnknown::AddRef on *ppunk. It is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation sets *ppunk to NULL.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The pointer associated with the specified key was successfully returned.

Remarks

A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a string known to both parties so that the other party can later retrieve it from the bind context.

The pointer this method retrieves must have previously been inserted into the table using the IBindCtx::RegisterObjectParam method.

Notes to Callers

Those writing a new moniker class (through an implementation of IMoniker) and some moniker clients (objects using a moniker to bind to an object) can call IBindCtx::GetObjectParam.

Objects using monikers to locate other objects can call this method when a binding operation fails to get specific information about the error that occurred. Depending on the error, it may be possible to correct the situation and retry the binding operation. See IBindCtx::RegisterObjectParam for more information.

Moniker implementations can call this method to deal with situations where a caller initiates a binding operation and requests specific information. By convention, the implementer should use key names that begin with the string form of the CLSID of a moniker class. (See the StringFromCLSID function.)

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::RegisterObjectParam, IBindCtx::EnumObjectParam


IBindCtx::GetRunningObjectTable

Provides an interface pointer to the Running Object Table (ROT) for the machine on which this bind context is running.

HRESULT GetRunningObjectTable(
  IRunningObjectTable **pprot  //Address of output variable that
                               // receives the IRunningObjectTable
                               // interface pointer
);

Parameter

pprot
[out] Address of IRunningObjectTable* pointer variable that receives the interface pointer to the Running Object Table. If an error occurs, *pprot is set to NULL. If *pprot is non-NULL, the implementation calls IUnknown::AddRef on the running table object; it is the caller's responsibility to call IUnknown::Release.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
A pointer to the ROT was returned successfully.

Remarks

The Running Object Table is a globally accessible table on each machine. It keeps track of all the objects that are currently running on the machine.

Notes to Callers

Typically, those implementing a new moniker class (through an implementation of IMoniker interface) call IBindCtx::GetRunningObjectTable. It is useful to call this method in an implementation of IMoniker::BindToObject or IMoniker::IsRunning to check whether a given object is currently running. You can also call this method in the implementation of IMoniker::GetTimeOfLastChange to learn when a running object was last modified.

Moniker implementations should call this method instead of using the GetRunningObjectTable function. This makes it possible for future implementations of IBindCtx to modify binding behavior.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker, IRunningObjectTable


IBindCtx::RegisterObjectBound

Calls IUnknown::AddRef on the specified object to ensure that the object remains active until the bind context is released. The method stores a pointer to the object in the bind context's internal list of pointers.

HRESULT RegisterObjectBound(
  IUnknown *punk  //Pointer to the object being registered
);

Parameter

punk
[in] Pointer to the IUnknown interface on the object that is being registered as bound.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The object was successfully registered.

Remarks

Notes to Callers

Those writing a new moniker class (through an implementation of the IMoniker interface) should call this method whenever the implementation activates an object. This happens most often in the course of binding a moniker, but it can also happen while retrieving a moniker's display name, parsing a display name into a moniker, or retrieving the time that an object was last modified.

IBindCtx::RegisterObjectBound calls IUnknown::AddRef to create an additional reference to the object. You must, however, still release your own copy of the pointer. Calling this method twice for the same object creates two references to that object. You can release a reference obtained through a call to this method by calling IBindCtx::RevokeObjectBound. All references held by the bind context are released when the bind context itself is released.

Calling IBindCtx::RegisterObjectBound to register an object with a bind context keeps the object active until the bind context is released. Reusing a bind context in a subsequent binding operation (either for another piece of the same composite moniker or for a different moniker) can make the subsequent binding operation more efficient because it doesn't have to reload that object. This, however, improves performance only if the subsequent binding operation requires some of the same objects as the original one, so you need to balance the possible performance improvement of reusing a bind context against the costs of keeping objects activated unnecessarily.

IBindCtx does not provide a method to retrieve a pointer to an object registered using IBindCtx::RegisterObjectBound. Assuming the object has registered itself with the Running Object Table, moniker implementations can call IRunningObjectTable::GetObject to retrieve a pointer to the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::ReleaseBoundObjects, IBindCtx::RevokeObjectBound, IRunningObjectTable::GetObject


IBindCtx::RegisterObjectParam

Stores an IUnknown pointer on the specified object under the specified key in the bind context's string-keyed table of pointers. The method must call IUnknown::AddRef on the stored pointer.

HRESULT RegisterObjectParam(
  LPOLESTR pszKey,  //Pointer to the key to be used
  IUnknown *punk    //Pointer to the object to be associated with
                    // the key
);

Parameters

pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the key under which the object is being registered. Key string comparison is case-sensitive.
punk
[in] Pointer to the IUnknown interface on the object that is to be registered.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The pointer was successfully registered under the specified string.

Remarks

A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a string known to both parties so that the other party can later retrieve it from the bind context.

Binding operations subsequent to the use of this method can use IBindCtx::GetObjectParam to retrieve the stored pointer.

Notes to Callers

IBindCtx::RegisterObjectParam is useful to those implementing a new moniker class (through an implementation of IMoniker) and to moniker clients (those who use monikers to bind to objects).

In implementing a new moniker class, you call this method when an error occurs during moniker binding to inform the caller of the cause of the error. The key that you would obtain with a call to this method would depend on the error condition. Following is a list of common moniker binding errors, describing for each the keys that would be appropriate:

  • MK_E_EXCEEDEDDEADLINE—If a binding operation exceeds its deadline because a given object is not running, you should register the object's moniker using the first unused key from the list: "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and so on. If the caller later finds the moniker in the Running Object Table, the caller can retry the binding operation.
  • MK_E_CONNECTMANUALLY—The "ConnectManually" key indicates a moniker whose binding requires assistance from the end user. To request that the end user manually connect to the object, the caller can retry the binding operation after showing the moniker's display name. Common reasons for this error are that a password is needed or that a floppy needs to be mounted.
  • E_CLASSNOTFOUND—The "ClassNotFound" key indicates a moniker whose class could not be found. (The server for the object identified by this moniker could not be located.) If this key is used for an OLE compound-document object, the caller can use IMoniker::BindToStorage to bind to the object and then try to carry out a Treat As... or Convert To... operation to associate the object with a different server. If this is successful, the caller can retry the binding operation.

If you're a moniker client with detailed knowledge of the implementation of the moniker you're using, you can also call this method to pass private information to that implementation.

You can define new strings as keys for storing pointers. By convention, you should use key names that begin with the string form of the CLSID of the moniker class. (See the StringFromCLSID function.)

If the pszKey parameter matches the name of an existing key in the bind context's table, the new object replaces the existing object in the table.

When you register an object using this method, the object is not released until one of the following occurs:

  • It is replaced in the table by another object with the same key.
  • It is removed from the table by a call to IBindCtx::RevokeObjectParam.
  • The bind context is released. All registered objects are released when the bind context is released.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::GetObjectParam, IBindCtx::RevokeObjectParam, IBindCtx::EnumObjectParam


IBindCtx::ReleaseBoundObjects

Releases all pointers to all objects that were previously registered by calls to IBindCtx::RegisterObjectBound.

HRESULT ReleaseBoundObjects(void);

Return Value

S_OK
The objects were released successfully.

Remarks

You rarely call this method directly. The system's IBindCtx implementation calls this method when the pointer to the IBindCtx interface on the bind context is released (the bind context is released). If a bind context is not released, all of the registered objects remain active.

If the same object has been registered more than once, this method calls the IUnknown::Release method on the object the number of times it was registered.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::RegisterObjectBound


IBindCtx::RevokeObjectBound

Releases the IUnknown pointer to the specified object and removes that pointer from the bind context's internal list of pointers. This undoes a previous call to IBindCtx::RegisterObjectBound for the same object.

HRESULT RevokeObjectBound(
  IUnknown *punk  //Pointer to the object whose registration is
                  // being revoked
);

Parameter

punk
[in] Pointer to the IUnknown interface on the object to be released.

Return Values

S_OK
The object was released successfully.
MK_E_NOTBOUND
Indicates that punk was not previously registered with a call to IBindCtx::RegisterObjectBound.

Remarks

You rarely call this method. This method is included for completeness.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::RegisterObjectBound


IBindCtx::RevokeObjectParam

Removes the specified key and its associated pointer from the bind context's string-keyed table of objects. The key must have previously been inserted into the table with a call to IBindCtx::RegisterObjectParam.

HRESULT RevokeObjectParam(
  LPOLESTR pszKey  //Pointer to the key to be revoked
);

Parameter

pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the key to remove. Key string comparison is case-sensitive.

Return Values

S_OK
The specified key was successfully removed from the table.
S_FALSE
No object has been registered with the specified key.

Remarks

A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a string known to both parties so that the other party can later retrieve it from the bind context.

This method is used to remove an entry from the table. If the specified key is found, the bind context also releases its reference to the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::RegisterObjectParam


IBindCtx::SetBindOptions

Specifies new values for the binding parameters stored in the bind context. Subsequent binding operations can call IBindCtx::GetBindOptions to retrieve the parameters.

HRESULT SetBindOptions(
  BIND_OPTS *pbindopts  //Pointer to a structure
);

Parameter

pbindopts
[in] Pointer to a BIND_OPTS2 or a BIND_OPTS structure containing the binding parameters.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The parameters were stored successfully.

Remarks

A bind context contains a block of parameters, stored in a BIND_OPTS2 or a BIND_OPTS structure, that are common to most IMoniker operations. These parameters do not change as the operation moves from piece to piece of a composite moniker.

Notes to Callers

This method can be called by moniker clients (those who use monikers to acquire interface pointers to objects).

When you first create a bind context by using the CreateBindCtx function, the fields of the BIND_OPTS structure are initialized to the following values:

    cbStruct = sizeof(BIND_OPTS);
    grfFlags = 0;
    grfMode = STGM_READWRITE;
    dwTickCountDeadline = 0;

You can use the IBindCtx::SetBindOptions method to modify these values before using the bind context, if you want values other than the defaults. See BIND_OPTS for more information.

SetBindOptions copies only the struct members of BIND_OPTS2, but not the COSERVERINFO structure and the pointers it contains. Callers may not free any of these pointers until the bind context is released.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

Bind_OPTS2, IBindCtx::GetBindOptions


ICallFactory

The ICallFactory interface creates a call object for processing calls to the methods of an asynchronous interface.

When to Implement

Implement ICallFactory on an object that implements one or more interfaces that support asynchronous calling. For objects that also use custom marshaling, all proxies for interfaces that support asynchronous behavior must implement ICallFactory.

When to Use

Clients call the method of the ICallFactory interface to create an instance of a call object. The call object implements an asynchronous interface that corresponds to a synchronous interface implemented on the server object itself. For details about asynchronous interfaces, see Making and Processing Asynchronous Calls.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
ICallFactory Method | Description
CreateCall | Creates an instance of the call object that corresponds to a specified asynchronous interface.


ICallFactory::CreateCall

Creates an instance of the call object that corresponds to a specified asynchronous interface.

HRESULT CreateCall(
  REFIID riid1,         // IID of asynchronous interface
  IUnknown* pUnk,     // Controlling IUnknown
  REFIID riid2,       // IID of requested interface
  IUnknown** ppObj    // Pointer to interface specified by riid2
);

Parameters

riid1
[in] Reference to the identifier for the asynchronous interface.
pUnk
[in] Pointer to the controlling IUnknown of the call object. If NULL, the call object is not aggregated.
riid2
[in] Identifier of an interface on the call object. Typical values are IID_IUnknown and IID_ISynchronize.
ppObj
[out, iid_is(riid2)] Address of a pointer to the interface specified by riid2. Cannot be NULL.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The call object was created.
E_NOINTERFACE
Riid1 does not reference the identifier for the asynchronous interface, such as IID_AsyncIEventSourceCallback.

Remarks

If pUnk is not NULL, the call object will be aggregated in the specified object.


ICallFrame Interface

The ICallFrame interface is used to manipulate call frames such as stack frames. The call frame is the body of information that a procedure must save to allow it to properly return to its caller. A call frame may exist on the stack or in registers. A stack frame maintains its caller's context information on the stack.

An instance of the ICallFrame interface can perform various transformations on a call frame. The call can be marshaled or persisted. The instance of this interface is bound and has an associated method number.

When to Implement

You do not have to implement this interface. However to receive an instance of ICallFrame the client must create an interceptor. To create an interceptor for a given interface or to find its unmarshaler, instantiate the proxy-stub class for the object using CoCreateInstance and set the appropriate IID for the interface. Then query for ICallInterceptor or ICallUnmarshal interface. Calling ICallInterceptor creates an instance of ICallFrame object. When the user calls ICallUnmarshal an instance of an unmarshaled class is created for the specified interface and method number

When to Use

You can use ICallFrame for marshaling, call frame persistence, and interface-pointer swizzling. For example, you can construct a generic implementation of interfaced proxies and stubs that operate by dispatching operations to an instance of ICallFrame. The interface proxy or stub would contain generic logic to drive the marshalling process and therefore use the services from ICallFrame to carry out the interface-specific work.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICallFrame Methods | Description
GetInfo | Gets the information about the call frame in the CALLFRAMEINFO structure.
GetIIDAndMethod | Gets the interface ID or the method number.
GetNames | Gets the method or interface name of this call.
GetStackLocation | Gets the stack location onto which this call frame is bound.
SetStackLocation | Sets the stack location onto which this call frame is bound.
SetReturnValue | Sets the return value stored within this call frame.
GetReturnValue | Gets the return value stored within this call frame.
GetParamInfo | Gets the information for the specified parameter in the CALLFRAMEPARAMINFO structure.
SetParam | Sets the value of the specified parameter in this frame.
GetParam | Gets the value of the specified parameter in this frame.

Note When a sting or buffer is return from these methods, the string or buffer needs to be freed by calling CoTaskMemFree.

See Also

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallFrame::GetInfo

Platform SDK: COM

ICallFrame::GetInfo [C/C++]

CallFrame.GetInfo Method [Visual Basic]

Gets the information about the call frame in the CALLFRAMEINFO structure.

[C/C++]
HRESULT GetInfo(
CALLFRAMEINFO * pInfo
) = 0;

[Visual Basic]
object.GetInfo(pInfo)

Parameters [C/C++]

pInfo
[out] Points to the CALLFRAMEINFO structure.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pInfo
Points to the CALLFRAMEINFO structure.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetIIDAndMethod

Platform SDK: COM

ICallFrame::GetIIDAndMethod [C/C++]

CallFrame.GetIIDAndMethod Method [Visual Basic]

Gets the interface ID or the method number.

[C/C++]
HRESULT GetIIDAndMethod(
IID * pIID
ULONG * piMethod
) = 0;
[Visual Basic]
object.GetIIDAndMethod(pIID, piMethod)

Parameters [C/C++]

pIID
[out] Points to the interface ID.
piMethod
[out] Points to the method number.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pIID
Points to the interface ID.
piMethod
Points to the method number.

Return Values [C/C++]

S_OK
The process is paused successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetNames

Platform SDK: COM

ICallFrame::GetNames [C/C++]

CallFrame.GetNames Method [Visual Basic]

Gets the method or interface name of this call.

[C/C++]
HRESULT GetNames(
LPWSTR * pwszInterface,
LPWSTR * pwszMethod
) = 0;
[Visual Basic]
object.GetNames(pwszInterface, pwszMethod)

Parameters [C/C++]

pwszInterface
[out] Points to the interface name.
pwszMethod
[out] Points to the method name.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pwszInterface
Points to the interface name.
pwszMethod
Points to the method name.

Return Values [C/C++]

If a requested name is not available then a Null string is returned. The following HRESULT values are also returned:
S_OK
The process is resumed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetStackLocation

Platform SDK: COM

ICallFrame::GetStackLocation [C/C++]

CallFrame.GetStackLocation Method [Visual Basic]

Gets the stack location onto which this call frame is bound.

 [C/C++]
void GetStackLocation(
) = 0;
[Visual Basic]
GetStackLocation()

Return Value

Returns the requested stack location.


ICallFrame::SetStackLocation

Platform SDK: COM

ICallFrame::SetStackLocation [C/C++]

CallFrame.SetStackLocation Method [Visual Basic]

Sets the stack location onto which this call frame is bound.

[C/C++];
HRESULT SetStackLocation(
PVOID pvStack
) = 0;
[Visual Basic]
object.SetStackLocation(pvStack)

Parameters [C/C++]

pvStack
[in] Points to the stack location.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pvStack
Points to the stack location.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::SetReturnValue

Platform SDK: COM

ICallFrame::SetReturnValue [C/C++]

CallFrame.SetReturnValue Method [Visual Basic]

Sets the return value within the call frame

[C/C++]
HRESULT SetReturnValue(
   HRESULT hr
) = 0;
[Visual Basic]
object.SetReturnValue(hr)

Parameters [C/C++]

hr
The new return value.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
hr
The new return value.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetReturnValue

Platform SDK: COM

ICallFrame::GetReturnValue [C/C++]

CallFrame.GetReturnValue Method [Visual Basic]

Gets the return value stored in the call frame.

[C/C++]
HRESULT GetReturnValue(
) = 0;
[Visual Basic]
object.GetReturnValue()

Return Value [C/C++]

Returns the HRESULT value stored in the call frame.


ICallFrame::GetParamInfo

Platform SDK: COM

ICallFrame::GetParamInfo [C/C++]

CallFrame.GetParamInfo Method [Visual Basic]

Gets the information for the specified parameter from the CALLFRAMEPARAMINFO structure.

[C/C++]
HRESULT GetParamInfo(
ULONG iparam,
CALLFRAMEPARAMINFO * pInfo
) = 0;
[Visual Basic]
object.GetRunningApps(iparam, pInfo)

Parameters [C/C++]

iparam
[in] The parameter number.
pInfo
[out] Points to the CALLFRAMEPARAMINFO structure.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iparam
The parameter number.
pInfo
Points to the CALLFRAMEPARAMINFO structure.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::SetParam

Platform SDK: COM

ICallFrame::SetParam [C/C++]

CallFrame.SetParam Method [Visual Basic]

Sets the value of a specified parameter in the call frame.

[C/C++]
HRESULT SetParam(
ULONG iparam,
VARIANT * pvar
) = 0;
[Visual Basic]
object.SetParam(iparam, pvar)

Parameters [C/C++]

iparam
[in] The parameter number.
pvar
[in] The new value for the iparam parameter.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iparam
The parameter number.
pvar
The new value for the iparam parameter.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetParam

Platform SDK: COM

ICallFrame::GetParam [C/C++]

CallFrame.GetParam Method [Visual Basic]

Gets the value of a specified parameter in the call frame.

[C/C++]
HRESULT GetParam(
ULONG iparam,
VARIANT * pvar
) = 0;
[Visual Basic]
object.GetParam(iparam, pvar)

Parameters [C/C++]

iparam
[in] The parameter number.
pvar
[out] The value of the iparam parameter.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iparam
The parameter number.
pvar
The value of the iparam parameter.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::Copy

Platform SDK: COM

ICallFrame::Copy [C/C++]

CallFrame.Copy Method [Visual Basic]

Creates a copy of this call frame and all of the associated data. Copying a frame is like unmarshalling a marshaled frame. The call frame can only be copied if it has in-parameters. If the call frame is invoked it cannot be copied. The copy method copies interface pointers as binary values and no referenced count adjustments are performed. But if this behavior is desired then a pointer to ICallFrameWalker can be used.

Warning

The call frame does not clean up the copied data automatically. Therefore once the copy is returned then the user is responsible for Free on the frame copy. This must be done to avoid memory leakage.

[C/C++]
HRESULT Copy(
CALLFRAME_COPY copyControl,
ICallFrameWalker * pWalker,
ICallFrame ** ppFrame
) = 0;
[Visual Basic]
object.Copy(copyContorl, pWalker, ppFrame)

Parameters [C/C++]

copyControl
[in] Determines if the copied call frame data can be shared with data in the parent frame by determining its lifetime dependency on the parent frame. If the CALLFRAME_COPY_NESTED flag is set then the client will be responsible for using the copied call frame in a manner that its lifetime is nested in the lifetime of its parent frame making the data shareable. If the CALLFRAME_COPY_INDEPENDENT is set then the lifetime of the copied frame will be independent of the parents. For more information on this enumeration see CALLFRAME_COPY enumeration.
pWalker
[in] Optional. Points to an instance of the ICallFrameWalker interface. The OnWalkerInterface method will be called for each interface pointer that is copied. If this parameter is not provided then any interface pointer that is copied will be AddRef’d.
ppFrame
[out] Points to a pointer to a copy of the call frame.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
copyControl
Determines if the copied call frame data can be shared with data in the parent frame by determining its lifetime dependency on the parent frame. If the CALLFRAME_COPY_NESTED flag is set then the client will be responsible for using the copied call frame in a manner that its lifetime is nested in the lifetime of its parent frame making the data shareable. If the CALLFRAME_COPY_INDEPENDENT is set then the lifetime of the copied frame will be independent of the parents. For more information on this enumeration see CALLFRAME_COPY enumeration.
pWalker
Optional. Points to an instance of the ICallFrameWalker interface. The OnWalkerInterface method will be called for each interface pointer that is copied. If this parameter is not provided then any interface pointer that is copied will be AddRef’d.
ppFrame
Points to a pointer to a copy of the call frame.

Return Value [C/C++]

S_OK
Method completed successfully.
HRESULT_FROM_WIN32
An attempt was made to copy from a user mode stack frame data not in the user space.


ICallFrame::Free

Platform SDK: COM

ICallFrame::Free [C/C++]

CallFrame.Free Method [Visual Basic]

When a copy of a call frame is made with the Copy method, the call frame does not clean up the copied data automatically. Therefore once a copy of the call frame is returned the user is responsible for calling the Free method to free the frame copy and avoid memory leakage.

[C/C++]
HRESULT Free(
ICallFrame * pframeArgsDest,
ICallFrameWalker * pWalkerDestFree,
ICallFrameWalker * pWalkerCopy,
DWORD freeFlags,
ICallFrameWalker * pWalkerFree,
DWORD nullFlags
) = 0;
[Visual Basic]
object.Free(pframeArgsDest, pWalkerDestFree, pWalkerCopy, freeFlags, pWalkerFree, nullFlags)

Parameters [C/C++]

pframeArgsDest
[in] Optional. Points to an instance of the ICallFrame interface. Represents the stack pointer of the call frame to which the out parameters should be copied before freeing is carried out. The destination frame is usually the parent frame from which the frame was originally copied.
pWalkerDestFree
[in] Optional. Points to an instance of the ICallFrameWalker interface. If there exist in and out parameters in pframeArgsDest then the propagation requires that those values be freed. By providing the pWalkerDestFree object interface pointers will be freed. If pframeArgsDest is Null then this parameter is also Null and the interface pointers are released by calling the Release method.

Note When freeing a frame and specifying a pframeArgsDest parameter which is in the same memory space as that of the receiver frame, then only the pWalkerCopy parameter is called on each interface pointer.

pWalkerCopy
[in] Optional. Points to an instance of the ICallFrameWalker interface. When the in and out parameters in pframeArgsDest are freed then the propagation of out parameters is carried out. If this parameter is not specified then the interface pointers are AddRef’d.
freeFlags
[in] Represents flags from the CALLFRAME_FREE enumeration.
pWalkerFree
[in] Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered while freeing occurs. If this parameter is not specified then the interface pointers are freed by the Release method.
nullFlags
[in] Represents flags from the CALLFRAME_NULL enumeration.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pframeArgsDest
Optional. Points to an instance of the ICallFrame interface. Represents the stack pointer of the call frame to which the out parameters should be copied before freeing is carried out. The destination frame is usually the parent frame from which the frame was originally copied.
pWalkerDestFree
Optional. Points to an instance of the ICallFrameWalker interface. If there exist in and out parameters in pframeArgsDest then the propagation requires that those values be freed. By providing the pWalkerDestFree object interface pointers will be freed. If pframeArgsDest is Null then this parameter is also Null and the interface pointers are released by calling the Release method.

Note When freeing a frame and specifying a pframeArgsDest parameter which is in the same memory space as that of the receiver frame, then only the pWalkerCopy parameter is called on each interface pointer.

pWalkerCopy
Optional. Points to an instance of the ICallFrameWalker interface. When the in and out parameters in pframeArgsDest are freed then the propagation of out parameters is carried out. If this parameter is not specified then the interface pointers are AddRef’d.
freeFlags
Represents flags from the CALLFRAME_FREE enumeration.
pWalkerFree
Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered while freeing occurs. If this parameter is not specified then the interface pointers are freed by the Release method.
nullFlags
Represents flags from the CALLFRAME_NULL enumeration.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::FreeParam

Platform SDK: COM

ICallFrame::FreeParam [C/C++]

CallFrame.FreeParam Method [Visual Basic]

The FreeParam method frees the specified parameter in the frame.

[C/C++]
HRESULT Free(
ULONG iparam,
DWORD freeFlags,
ICallFrameWalker * pWalkerFree,
DWORD nullFlags
) = 0;
[Visual Basic]
object.Free(iparam, freeFlags, pWalkerFree, nullFlags)

Parameters [C/C++]

iparam
[in] The parameter number to be freed.
freeFlags
[in] Represents flags from the CALLFRAME_FREE enumeration.
pWalkerFree
[in] Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered while freeing occurs. If this parameter is not specified then the interface pointers are freed by the Release method.
nullFlags
[in] Represents flags from the CALLFRAME_NULL enumeration.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iparam
The parameter number to be freed.
freeFlags
Represents flags from the CALLFRAME_FREE enumeration.
pWalkerFree
Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered while freeing occurs. If this parameter is not specified then the interface pointers are freed by the Release method.
nullFlags
Represents flags from the CALLFRAME_NULL enumeration.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::WalkFrame

Platform SDK: COM

ICallFrame::WalkFrame [C/C++]

CallFrame.WalkFrame Method [Visual Basic]

The WalkFrame method looks for interface pointers that are reachable from [in], [in, out], or [out] parameters of the frame.

[C/C++]
HRESULT Free(
DWORD walkwhat,
ICallFrameWalker * pWalker
) = 0;
[Visual Basic]
object.Free(walkwhat, pWalkerFree)

Parameters [C/C++]

walkwhat
[in] Represents flags from the CALLFRAME_WALK enumeration.
pWalker
[in] Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
walkwhat
Represents flags from the CALLFRAME_WALK enumeration.
pWalker
Optional. Points to an instance of the ICallFrameWalker interface. When specified, a call back is made for each interface pointer encountered.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::GetMarshalSizeMax

Platform SDK: COM

ICallFrame::GetMarshalSizeMax [C/C++]

CallFrame.GetMarshalSizeMax Method [Visual Basic]

Returns an upper bound on the number of bytes needed to marshal the call frame. Usually an interface proxy calls this method to learn how big a buffer is needed, allocates the buffer and then calls the Marshal method to carry out the marshalling.

[C/C++]
HRESULT GetMarshalSizeMax(
CALLFRAME_MARSHALCONTEXT * pmshlContext,
MSHLFLAGS * mshlflags,
ULONG * pcbBufferNeeded
) = 0;
[Visual Basic]
object.GetMarshalSizeMax(pmshlContext, mshlflags, pcbBufferNeeded)

Parameters [C/C++]

pmshlContext
[in] Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how marshalling is carried out.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back to the client process — the normal case — or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pcbBufferNeeded
[out] Points to the size of the buffer in bytes that will be needed to marshal the call frame.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pmshlContext
Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how marshalling is carried out.
mshlflags
Flag indicating whether the data to be marshaled is to be transmitted back to the client process — the normal case — or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pcbBufferNeeded
Points to the size of the buffer in bytes that will be needed to marshal the call frame.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::Marshal

Platform SDK: COM

ICallFrame::Marshal [C/C++]

CallFrame.Marshal Method [Visual Basic]

Marshals the call frame by turning its reachable data into a flat buffer without disturbing the frame. When marshalling the [In] versions of [in, out] parameters are present, and the [out] versions are undefined. When marshalling [out] parameters the values are valid.

[C/C++]
HRESULT Marshal(
CALLFRAME_MARSHALCONTEXT * pmshlContext,
MSHLFLAGS * mshlflags,
PVOID pBuffer,
ULONG cbBuffer,
ULONG * pcBufferUsed,
RPCOLEDATAREP * pdataRep,
ULONG * prpcFlags
) = 0;
[Visual Basic]
object.Marshal(pmshlContext, mshlflags, pBuffer, cbBuffer, pcBufferUsed, pdataRep, prpcFlags)

Parameters [C/C++]

pmshlContext
[in] Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how marshalling is carried out.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back to the client process — the normal case — or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pBuffer
[in, size_is(cbBuffer)] Points to the buffer into to place the marshaled data.
cbBuffer
[in] The size of the buffer in bytes.
pcBufferUsed
[out] Optional. Points to the size of the buffer that was actually used.
pdataRep
[out] Optional. Returns the NDR data representation with which the data was marshaled. For Win32 computers, this is always 0X00000010L. For more information, see IRpcChannelBuffer::GetBuffer.
prpcFlags
[out] Optional. Returns an RPC flag associated with the call. For more information, see IRpcChannelBuffer::GetBuffer.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pmshlContext
Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how marshalling is carried out.
mshlflags
Flag indicating whether the data to be marshaled is to be transmitted back to the client process — the normal case — or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pBuffer
Points to the buffer into to place the marshaled data.
cbBuffer
The size of the buffer in bytes.
pcBufferUsed
Optional. Points to the size of the buffer that was actually used.
pdataRep
Optional. Returns the NDR data representation with which the data was marshaled. For Win32 computers, this is always 0X00000010L. For more information, see IRpcChannelBuffer::GetBuffer.
prpcFlags
Optional. Returns an RPC flag associated with the call. For more information, see IRpcChannelBuffer::GetBuffer.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Comments

If this method returns an error then the caller will not be able to clean it up. Resources such as memory transiently allocated during the attempted marshalling have been freed, and so on.


ICallFrame::UnMarshal

Platform SDK: COM

ICallFrame::UnMarshal [C/C++]

CallFrame.UnMarshal Method [Visual Basic]

Un-marshals a packet of data containing the previously marshaled [out] parameters of a call into this already existing activation record.

When un-marshalling, the [in] versions of [in, out] parameters are freed and interface pointers are released and replaced with there [out] versions. All the [in, out] and [out] parameters will always be set to reasonable [in], [in, out] values, [out] values successfully un-marshaled from the returned data, or a value explicitly initialized to Null. On failure return, the caller will typically want to call ICallFrame::Free in order to clean up the values that are not Null.

[C/C++]

HRESULT UnMarshal(
PVOID pBuffer,
ULONG cbBuffer,
RPCOLEDATAREP pdataRep,
CALLFRAME_MARSHALCONTEXT * pcontext,
ULONG * pcbUnmarshaled
) = 0;
[Visual Basic]
object.UnMarshal(pBuffer, cbBuffer, pcBufferUsed, pdataRep, pcontext, pcbUnmarshaled)

Parameters [C/C++]

pBuffer
[in,size_is(cbBuffer)] Points to the buffer containing the marshaled [out] values.
cbBuffer
[in] The size of the buffer in bytes.
pdataRep
[in] Points to the data representation with which the data was marshaled. For Win32 computers, this is always 0X00000010L. For more information, see IRpcChannelBuffer::GetBuffer.
pcontext
[in] Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how un-marshalling is carried out.
pcbUnmarshaled
[out] Optional. Returns the number of bytes that were successfully un-marshaled. This parameter is returned even in error situations.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pBuffer
Points to the buffer containing the marshaled [out] values.
cbBuffer
The size of the buffer in bytes.
pdataRep
Points to the data representation with which the data was marshaled. For Win32 computers, this is always 0X00000010L. For more information, see IRpcChannelBuffer::GetBuffer.
pcontext
Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how un-marshalling is carried out.
pcbUnmarshaled
Optional. Returns the number of bytes that were successfully un-marshaled. This parameter is returned even in error situations.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::ReleaseMarshalData

Platform SDK: COM

ICallFrame::ReleaseMarshalData [C/C++]

CallFrame.ReleaseMarshalData Method [Visual Basic]

Releases resources that are held by interface pointers residing in a packet of marshaled data. This method finds all interface pointers in the packet, and calls CoReleaseMarshalData on each one.

The ReleaseMarshalData method must be called exactly once to clean up the resources held in a marshaled buffer. However when the MRSHLFLAGES enumeration is set to NORMAL then this is done automatically during un-marshaling and so need not be carried out explicitly.

This method can function correctly on both marshaled [in] and [out] parameters.

[C/C++]

HRESULT ReleaseMarshalData(
PVOID pBuffer,
ULONG cbBuffer,
ULONG ibFirstRelease,
RPCOLEDATAREP dataRep,
CALLFRAME_MARSHALCONTEXT * pcontext
) = 0;
[Visual Basic]
object.ReleaseMarshalData(pBuffer, cbBuffer, ibFirstRelease, dataRep, pcontext)

Parameters [C/C++]

pBuffer
[in,size_is(cbBuffer)] Points to the buffer containing the marshaled [out] values.
cbBuffer
[in] The size of the buffer in bytes.
ibFirstRelease
[in] The fist byte in the buffer, which is to be released. A value of zero implies that the interface pointers in the whole buffer are to be released. The marshaled interface pointers are assumed to have been released by some other mechanism.
dataRep
[in] The data representation with which the data was marshaled.
pcontext
[in] Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how un-marshalling is carried out.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pBuffer
Points to the buffer containing the marshaled [out] values.
cbBuffer
The size of the buffer in bytes.
ibFirstRelease
The fist byte in the buffer, which is to be released. A value of zero implies that the interface pointers in the whole buffer are to be released. The marshaled interface pointers are assumed to have been released by some other mechanism.
dataRep
The data representation with which the data was marshaled.
pcontext
Points the CALLFRAME_MARSHALCONTEXT structure containing context information about how un-marshalling is carried out.

Return Value [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrame::Invoke

Platform SDK: COM

ICallFrame::Invoke [C/C++]

CallFrame.Invoke Method [Visual Basic]

Apply this activation record to an object. In a marshalling situation, typically this is carried out on the server side, and is the means by which the work of the actual object is accomplished.

Generally speaking, carrying out the invocation involves allocating a new stack frame, shallow-copying down the data in the original frame, then calling the appropriate method in the indicated object. The object invoked may then choose to modify [out] parameters, which are reachable from the copied frame, according to the appropriate semantics of the invocation. When the invocation returns from the object, the call frame automatically captures the return value from ICallFrame::SetReturnValue.

[C/C++]

HRESULT Invoke(
VOID * pvReceiver,
) = 0;
[Visual Basic]
object.Invoke(pvReceiver)

Parameters [C/C++]

pvReceiver
[in] The interface on which the invocation is to occur. Caller is responsible for ensuring that this interface is in fact of the appropriate IID; the ::Invoke implementation will simply do a cast and assume that that is the case.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pvReceiver
The interface on which the invocation is to occur. Caller is responsible for ensuring that this interface is in fact of the appropriate IID; the Invoke method implementation will simply do a cast and assume that that is the case.

Return Value [C/C++]

S_OK
Method completed successfully.
CALLFRAME_E_ALREADYINVOKED
An invocation has already been made from this frame.
E_UNEXPECTED
An unexpected error occurred.

Comments

This is a VARARGS function. Additional parameters, if any, are used to fill unbound values in the activation record other than the receiver, if any should be present. This is optional.


ICallFrameEvents Interface

The ICallFrameEvents interface delivers method call notifications.

When to Implement

You do not have to implement this interface.

When to Use

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICallFrameEvents Methods | Description
OnCall | Informs the event sink if it receives a method call on the interceptor.

See Also

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallFrameEvents::OnCall

Platform SDK: COM

ICallFrameEvents::OnCall [C/C++]

CallFrameEvents.OnCall Method [Visual Basic]

Informs the sink of the receipt of a method call on the interceptor. The sink is provided with an ICallFrame instance which is bound to the intercepted incoming method invocation. Through that sink the call frame can be manipulated in various ways.

On return from OnCall, the interceptor assumes that by some means the out-values of the method have been appropriately initialized as needed, if any; the interceptor does not itself manipulate the call frame further in any way. Typically, the OnCall implementation will have set the out-values by some means, either by invoking the call frame on an object, successfully unmarshalling some previously marshaled out-values, or NULLing them with ICallFrame::Free.

The return value should also have been appropriately set during the call in a similar manner. See also ICallFrame::SetReturnValue.

[C/C++]
HRESULT OnCall(
ICallFrame * pFrame
) = 0;

[Visual Basic]
object.OnCall(pFrame)

Parameters [C/C++]

pFrame
[in] A call frame bound to the just-received invocation.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
pFrame
A call frame bound to the just-received invocation .

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallFrameWalker Interface

The ICallFrameWalker interface is the callback interface used to walk a stack frame, looking for interesting values. For more information, see ICallFrame::WalkFrame.

When to Implement

You do not have to implement this interface.

When to Use

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICallFrameWalker Methods | Description
OnWalkInterface | The walker has discovered an interface in the call frame. We are informed of its IID and its location; we can manipulate the interface, and we may replace it if desired, being careful to get the reference counting right.

See Also

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallFrameWalker::OnWalkInterface

Platform SDK: COM

ICallFrameWalker::OnWalkInterface [C/C++]

CallFrameWalker.OnWalkInterface Method [Visual Basic]

This method walks through a call frame to look for the specified interface in the call frame. The IID and location of the interface is specified. The interface can be manipulated or replaced by paying close attention to the reference count.

[C/C++]
HRESULT OnWalkInterface(
REFIID iid,
PVOID * ppvInterface,
BOOL fIn,
BOOL fOut
) = 0;

[Visual Basic]
object. OnWalkInterface(iid, ppvInterface, fIn, fOut)

Parameters [C/C++]

iid
[in] The IID of the interface to be found.
ppvInterface
[in] Points to the buffer from which the activation record is to be reconstituted
fIn
[in] This parameter is True if an interface is inside an in or in, out parameter.
fOut
[in] This parameter is True if an interface is inside an out or in, out parameter.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iid
The IID of the interface to be found.
ppvInterface
Points to the buffer from which the activation record is to be reconstituted
fIn
This parameter is True if an interface is inside an in or in, out parameter.
fOut
This parameter is True if an interface is inside an out or in, out parameter.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallIndirect Interface

The ICallIndirect interface invokes an object with an indirect reference to the invocation’s arguments, rather than the traditional direct call. An instance of ICallIndirect supports indirect invocations for only one interface ID.

The actual detailed semantics of how to carry out an indirect call are independent of the ICallIndirect interface itself; they are instead specific to the implementation of the interface. For example, implementations of ICallIndirect found in call interceptors (see below) carry out the call by constructing and appropriate ICallFrame instance and then invoking ICallFrameEvents::OnCall in the registered sink. Other implementations might do some appropriate munging of the invocation’s arguments, then forward the call on to some actual specific object, presumably one previously registered with the ICallIndirect using some implementation-specific means.

When to Implement

When to Use

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICallIndirect Methods | Description
CallIndirect | Invokes one of the methods in the interface with an indirect reference to the arguments of the invocation.
GetMethodInfo | Returns information from CALLFRAMEINFO structure about the method in the interface.
GetStackSize | Returns the number of bytes, which should be popped from the stack in order to return from an invocation of the indicated method.
GetIID | Returns the interface id supported by this ICallIndirect implementation.

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallIndirect::CallIndirect

Platform SDK: COM

ICallIndirect::CallIndirect[C/C++]

CallIndirect.CallIndirect Method [Visual Basic]

Invokes one of the methods in the interface with an indirect reference to the arguments of the invocation.

[C/C++]
HRESULT CallIndirect(
HRESULT * phrReturn,
ULONG  iMethod,
void * pvArgs,
ULONG * cbArgs
) = 0;

[Visual Basic]
object.CallIndirect(phrReturn, iMethod, pvArgs, cbArgs)

Parameters [C/C++]

phrReturn
[out] The value returned from the invocation of the method.
iMethod
[in] The method number to be invoked.
pvArgs
[in] Points to the stack frame with which to make the invocation. Details of the exact representation of this stack frame are processor-architecture specific.
cbArgs
[out] The number of bytes, which should be popped from the stack in order, to clear the stack of arguments to this invocation.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
phrReturn
The value returned from the invocation of the method.
iMethod
The method number to be invoked.
pvArgs
Points to the stack frame with which to make the invocation. Details of the exact representation of this stack frame are processor-architecture specific.
cbArgs
The number of bytes, which should be popped from the stack in order, to clear the stack of arguments to this invocation.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallIndirect::GetMethodInfo

Platform SDK: COM

ICallIndirect::GetMethodInfo [C/C++]

CallIndirect.GetMethodInfo Method [Visual Basic]

Returns information from CALLFRAMEINFO structure about the method in the interface. The information returned is a static analysis of the method, not a dynamic one, in that it is based on an analysis of the method signature only, not the actual current contents of the call frame. For example, the static analysis might indicate that this method has the potential of having an in-interface, but because of, say, a union switch, a given call might not actually have any such interfaces. This method is equivalent to the GetInfo and GetNames methods in ICallFrame, but avoids the need to actually make any invocation to get the information.

[C/C++]
HRESULT GetMethodInfo(
ULONG  iMethod,
CALLFRAMEINFO * pInfo,
LPWSTR * pwszMethod
) = 0;
[Visual Basic]
object.GetMethodInfo(iMethod, pInfo, pwszMethod)

Parameters [C/C++]

iMethod
[in] The method number to consider.
pInfo
[out] Points to the CALLFRAMEINFO structure containing information about the specified method.
pwszMethod
[out] Optional. String representing the method name.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iMethod
The method number to consider.
pInfo
Points to the CALLFRAMEINFO structure containing information about the specified method.
pwszMethod
Optional. String representing the method name.

Return Values [C/C++]

S_OK
The process is paused successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallIndirect::GetStackSize

Platform SDK: COM

ICallIndirect::GetStackSize[C/C++]

CallIndirect.GetStackSize Method [Visual Basic]

Returns the number of bytes, which should be popped from the stack in order without making any actual invocation.

[C/C++]
HRESULT GetStackSize(
ULONG  iMethod,
ULONG * cbArgs
) = 0;
[Visual Basic]
object.GetStackSize(iMethod, cbArgs)

Parameters [C/C++]

iMethod
[in] The method number to consider.
cbArgs
[out] The number of bytes, which should be popped from the stack in order, to clear the stack of arguments to this invocation.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iMethod
The method number to consider.
cbArgs
The number of bytes, which should be popped from the stack in order, to clear the stack of arguments to this invocation.

Return Values [C/C++]

S_OK
The process is paused successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallIndirect::GetIID

Platform SDK: COM

ICallIndirect::GetIID[C/C++]

CallIndirect.GetIID Method [Visual Basic]

Returns the interface id supported by this ICallIndirect implementation.

[C/C++]
HRESULT GetStackSize(
IID * piid,
BOOL * pfDerivesFromIDispatch,
ULONG * pcMethod,
LPWSTR * pwszInterface
) = 0;
[Visual Basic]
object.GetStackSize(piid, pfDerivesFromIDispatch, pcMethod, pwszInterface)

Parameters [C/C++]

piid
[out] Optional. Points to the interface.
pfDerivesFromIDispatch
[out] Optional. Returns True if the interface is derived from IDispatch. If not returns False.
pcMethod
[out] Optional. Points to the number of methods in the inferface.
pwszInterface
[out] Optional. Points to the interface name if it is available.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
piid
Optional. Points to the interface.
pfDerivesFromIDispatch
Optional. Returns True if the interface is derived from IDispatch. If not returns False.
pcMethod
Optional. Points to the number of methods in the inferface.
pwszInterface
Optional. Points to the interface name if it is available.

Return Values [C/C++]

S_OK
The process is paused successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallInterceptor Interface

The ICallInterceptor interface supports the registration and un-registering of event sinks wishing to be notified of calls made directly on the interface. In addition, this interface provides a means by which an invocation can be carried out with an indirect reference to the invocation’s arguments.

The ICallInterceptor interface is derived from the ICallIndirect interface.

When to Implement

All implementations of ICallInterceptor also implement the interface that they intercept.

When to Use

Methods in Vtable Order

ICallIndirect Methods | Description
CallIndirect | Invokes one of the methods in the interface with an indirect reference to the arguments of the invocation.
GetMethodInfo | Returns information from CALLFRAMEINFO structure about the method in the interface.
GetStackSize | Returns the number of bytes, which should be popped from the stack in order to return from an invocation of the indicated method.
GetIID | Returns the interface id supported by this ICallIndirect implementation.
ICallInterceptor Methods | Description
RegisterSink | Registers an event sink for receiving notifications of method calls.
GetRegisteredSink | Gets the registered event sink.

See Also

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallInterceptor::RegisterSink

Platform SDK: COM

ICallInterceptor::RegisterSink [C/C++]

CallInterceptor.RegisterSink Method [Visual Basic]

Registers an event sink for receiving notifications of method calls. Only a single event sink may be registered with an interceptor at any given time. Registering a sink of Null is legal, and causes the interceptor to release any previously registered sink that it might be holding on to.

[C/C++]
HRESULT RegisterSink(
ICallFrameEvents * psink
) = 0;

[Visual Basic]
object.RegisterSink(psink)

Parameters [C/C++]

psink
[in] Points to the event sink.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
psink
Points to the event sink.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallInterceptor::GetRegisteredSink

Platform SDK: COM

ICallInterceptor::GetRegisteredSink [C/C++]

CallInterceptor.GetRegisteredSink Method [Visual Basic]

Gets the registered event sink.

[C/C++]
HRESULT GetRegisteredSink(
ICallFrameEvents ** ppsink
) = 0;
[Visual Basic]
object.GetRegisteredSink(ppsink)

Parameters [C/C++]

ppsink
[in] Points to a pointer to the event sink.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
ppsink
Points to a pointer to the event sink.

Return Values [C/C++]

S_OK
The process is paused successfully.
CO_E_OBJNOTREG
No event sink is presently registered with this interceptor.
E_UNEXPECTED
An unexpected error occurred.


ICallUnmarshal Interface

The ICallUnmarshal interface used on the server (receiving) side of a remote invocation. An appropriate instance of ICallUnmarshal can be used to transform back into an call frame a method invocation previously marshaled by a call to ICallFrame::Marshal on the client (sending) side. Once such a reconstituted call frame is obtained, the call can be (e.g.) carried out on an actual object using ICallFrame::Invoke.

When to Implement

The ICallUnmarshal interface is usually implemented by the interceptor, for a given interface.

When to Use

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICallUnmarshal Methods | Description
Unmarshal | Turns a marshaled packet of data into an activation record.
ReleaseMarhsalData | Releases resources that may be held by interface pointers residing in a packet of marshaled data.

See Also

Requirements

To use the methods of this interface, you must include oaidl.h in your project.


ICallUnmarshal::Unmarhsal

Platform SDK: COM

ICallUnmarshal::Unmarhsal [C/C++]

CallUnmarshal.Unmarshal Method [Visual Basic]

Turns a marshaled packet of data back into an activation record that can then be invoked or manipulated in some other way. Only [in] parameters must be in the data to be un-marshaled. To unmarshal data with [out] parameters see the ICallFrame::Unmarshal method.

[C/C++]
HRESULT Unmarshal(
ULONG  iMethod,
PVOID pBuffer,
ULONG cbBuffer,
BOOL fForceBufferCopy,
RPCOLEDATAREP dataRep,
CALLFRAME_MARSHALCONTEXT * pcontext,
ULONG * pcbUnmarshalled,
ICallFrame ** ppFrame
) = 0;

[Visual Basic]
object.Unmarshal(iMethod, pBuffer, cbBuffer, fForceBufferCopy, dataRep, pcontext, pcbUnmarshalled, ppFrame)

Parameters [C/C++]

iMethod
[in] The method number to be unmarshaled. If the value is –1 then the method number will be determined from the data to be unmarshaled.
pBuffer
[in, size_is(cbBuffer)] Points to the buffer from which the activation record is to be created.
cbBuffer
[in] The size of the buffer in bytes.
fForceBufferCopy
[in] If this parameter is True, indicates that the buffer should be copied and retained. If this parameter is False, indicates that the buffer will remain valid.
dataRep
[in] The data representation with which the data was marshaled.
pcontext
[in] Points to the CALLFRAME_MARSHALCONTEXT structure, containing information about the context in which unmarshaling is to be carried out.
pcbUnmarshalled
[out] Optional. Points to the number of bytes that were successfully unmarshaled.
ppFrame
[in] A call frame bound to the umarshaled invocation.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iMethod
The method number to be unmarshaled. If the value is –1 then the method number will be determined from the data to be unmarshaled.
pBuffer
Points to the buffer from which the activation record is to be created.
cbBuffer
The size of the buffer in bytes.
fForceBufferCopy
If this parameter is True, indicates that the buffer should be copied and retained. If this parameter is False, indicates that the buffer will remain valid.
dataRep
The data representation with which the data was marshaled.
pcontext
Points to the CALLFRAME_MARSHALCONTEXT structure, containing information about the context in which unmarshaling is to be carried out.
pcbUnmarshalled
Optional. Points to the number of bytes that were successfully unmarshaled.
ppFrame
A call frame bound to the umarshaled invocation.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICallUnmarshal::ReleaseMarshalData

Platform SDK: COM

ICallUnmarshal::ReleaseMarshalData [C/C++]

CallUnmarshal.ReleaseMarshalData Method [Visual Basic]

Releases resources that may be being held by interface pointers residing in a packet of marshaled data. This method finds all interface pointers in the packet and calls CoReleaseMarshalData on each interface pointer.

To clean up resources held in the marshaled buffer, the ReleaseMarshalData method must. However when the MRSHLFLAGS enumeration is set to normal then this is automatically when unmarshaling.

The ReleaseMarshalData method can be used on both marshaled in and out parameters.

[C/C++]
HRESULT ReleaseMarshalData(
ULONG  iMethod,
PVOID pBuffer,
ULONG cbBuffer,
ULONG ibFirstRelease,
RPCOLEDATAREP dataRep,
CALLFRAME_MARSHALCONTEXT * pcontext,
) = 0;

[Visual Basic]
object. ReleaseMarshalData(iMethod, pBuffer, cbBuffer, ibFirstRelease, dataRep, pcontext)

Parameters [C/C++]

iMethod
[in] The method number to be unmarshaled.
pBuffer
[in, size_is(cbBuffer)] Points to the buffer containing the marshaled out parameters.
cbBuffer
[in] The size of the buffer in bytes.
ibFirstRelease
[in] The first byte in the buffer to be released. A value of zero implies that the interface pointers in the whole buffer are to be released. The idea is that marshaled interface pointers prior to the indicated byte are assumed to have already been released by some other mechanism.
dataRep
[in] The data representation with which the data was marshaled.
pcontext
[in] Points to the CALLFRAME_MARSHALCONTEXT structure, containing information about the context in which unmarshaling is to be carried out.

Parameters [Visual Basic]

object
Required object that references a CallFrame object.
iMethod
[in] The method number to be unmarshaled.
pBuffer
[in, size_is(cbBuffer)] Points to the buffer containing the marshaled out parameters.
cbBuffer
[in] The size of the buffer in bytes.
ibFirstRelease
[in] The first byte in the buffer to be released. A value of zero implies that the interface pointers in the whole buffer are to be released. The idea is that marshaled interface pointers prior to the indicated byte are assumed to have already been released by some other mechanism.
dataRep
[in] The data representation with which the data was marshaled.
pcontext
[in] Points to the CALLFRAME_MARSHALCONTEXT structure, containing information about the context in which unmarshaling is to be carried out.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.


ICancelMethodCalls

The ICancelMethodCalls interface manages cancellation requests on an outbound method call and monitors the current state of that method call on the server thread.

When to Implement

For objects that use standard marshaling, COM automatically creates cancel objects and implements ICancelMethodCalls on them. For objects that use custom marshaling, you must implement your own corresponding cancel object and implement ICancelMethodCalls on that object.

When to Use

A thread that is blocked because an outbound synchronous method call is pending calls the methods of ICancelMethodCalls on its corresponding cancel object to determine the current state of the pending call or to request that the call be canceled.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICancelMethodCalls Methods | Description
Cancel | Requests that a method call be canceled.
TestCancel | Determines if a call has been canceled.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

IMessageFilter


ICancelMethodCalls::Cancel

Requests that the associated method call be canceled.

HRESULT Cancel(
  ULONG ulTimeout   //Timeout period
);

Parameters

ulTimeout
Number of seconds Cancel waits for the server to complete the outbound call after the client requests cancellation.

Return Values

This method supports the standard return values E_FAIL, E_NOTIMPL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The cancellation request was made.
RPC_S_CALL_CANCELLED
The call was already cancelled.
CO_E_CANCEL_DISABLED
Call cancellation is not enabled on the thread that is processing the call.
RPC_E_CALL_COMPLETE
The call was completed during the timeout interval.

Remarks

The Cancel method only issues a cancel request. A return value of S_OK does not mean that the call was canceled, only that an attempt was made to cancel the call. The behavior of the cancel object on receiving a cancel request is entirely at the discretion of the implementer.

If a method call that returns an HRESULT is actually canceled, it will return RPC_S_CALL_CANCELED.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.


ICancelMethodCalls::TestCancel

Determines if a call has been canceled.

HRESULT TestCancel(void);

Return Values

RPC_S_CALLPENDING
The call has not been canceled.
RPC_E_CALL_CANCELED
The call was canceled.

Remarks

The server object calls TestCancel to determine if the call has been canceled. If the call has been canceled, the server should clean up the necessary resources and return control to the client.

This method can be called from both client and server.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

CoTestCancel


ICatInformation

The ICatInformation interface provides methods for obtaining information about the categories implemented or required by a certain class, as well as information about the categories registered on a given machine.

When to Implement

There is no need to implement this interface. The Component Category Manager, a system-provided COM object that can be instantiated by using CoCreateInstance, implements ICatInformation.

When to Use

Call the methods of ICatInformation to obtain a listing of available categories, enumerate classes that belong to a particular category, and determine if a class belongs to a specific category.

Information on using component categories can be found in Component Categories Manager Implementation.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICatInformation Methods | Description
EnumCategories | Returns an enumerator for the component categories registered on the system.
GetCategoryDesc | Retrieves the localized description string for a specific category ID.
EnumClassesOfCategories | Returns an enumerator over the classes that implement/require a certain set of categories.
IsClassOfCategories | Determines if a class implements/requires the specified categories.
EnumImplCategoriesOfClass | Returns an enumerator over the CATIDs implemented by the specified class.
EnumReqCategoriesOfClass | Returns an enumerator over the CATIDs required by the specified class.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister


ICatInformation::EnumCategories

Returns an enumerator for the component categories registered on the system.

HRESULT EnumCategories(
  LCID lcid,  //Requested locale for returned szDescription of
              // enumerated CATEGORYINFOs
  IEnumCATEGORYINFO** ppenumCatInfo
              //Location in which to return an IEnumCATEGORYINFO
              // interface
);

Parameters

lcid
[in] Identifies the requested locale for any return szDescription of the enumerated CATEGORYINFOs. Typically, the caller specifies GetUserDefaultLCID for this parameter.
ppenumCatInfo
[out] Specifies the location to return an IEnumCATEGORYINFO interface. This can be used to enumerate the CATIDs and localized description strings of the component categories registered with the system.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumClassesOfCategories, ICatInformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInformation::IsClassOfCategories


ICatInformation::EnumClassesOfCategories

Returns an enumerator over the classes that implement one or more of rgcatidImpl. If a class requires a category not listed in rgcatidReq, it is not included in the enumeration.

HRESULT EnumClassesOfCategories(
  ULONG cImplemented,       //Number of category IDs in the
                            // rgcatidImpl array
  CATID rgcatidImpl,        //Array of category identifiers
  ULONG cRequired,          //Number of category IDs in the
                            // rgcatidReq array
  CATID rgcatidReq,         //Array of category identifiers
  IEnumCLSID** ppenumCLSID  //Location in which to return an
                            // IEnumCLSID interface
);

Parameters

cImplemented
[in] The number of category IDs in the rgcatidImpl array. This value cannot be zero. If this value is ((ULONG) -1), classes are included in the enumeration, regardless of the categories they implement.
rgcatidImpl
[in] An array of category identifiers.
cRequired
[in] The number of category IDs in the rgcatidReq array. This value can be zero. If this value is ((ULONG) -1), classes are included in the enumeration, regardless of the categories they require.
rgcatidReq
[in] An array of category identifiers.
ppenumCLSID
[out] The location in which to return an IEnumCLSID interface that can be used to enumerate the CLSIDs of the classes that implement category rcatid.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumCategories, ICatInformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInformation::IsClassOfCategories


ICatInformation::EnumImplCategoriesOfClass

Returns an enumerator for the CATIDs implemented by the specified class.

HRESULT EnumImplCategoriesOfClass(
  REFCLSID rclsid,         //Class ID
  IEnumCATID** ppenumCATD  //Location in which to return an
                           // IEnumCATID interface
);

Parameters

rclsid
[in] Specifies the class ID.
ppenumCATD
[out] Specifies the location to return an IEnumCATID interface. This can be used to enumerate the CATIDs that are implemented by rclsid.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInformation::IsClassOfCategories


ICatInformation::EnumReqCategoriesOfClass

Returns an enumerator for the CATIDs required by the specified class.

HRESULT EnumReqCategoriesOfClass(
  REFCLSID rclsid,         //Class ID
  IEnumCATID** ppenumCATD  //Location in which to return an
                           // IEnumCATID interface
);

Parameters

rclsid
[in] Specifies the class ID.
ppenumCATD
[out] Specifies the location to return an IEnumCATID interface. This can be used to enumerate the CATIDs that are required by rclsid.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatInformation::EnumImplCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInformation::IsClassOfCategories


ICatInformation::GetCategoryDesc

Retrieves the localized description string for a specific category ID.

HRESULT GetCategoryDesc(
  REFCATID rcatid,
              //Category for which the string is to be returned
  LCID lcid,  //Locale in which the resulting string is returned
  PWCHAR* ppszDesc
              //Pointer to the string pointer that contains the
              // description
);

Parameters

rcatid
[in] Identifies the category for which the description string is to be returned.
lcid
[in] Specifies the locale in which the resulting string is returned.
ppszDesc
[out] A pointer to the string pointer that contains the description. This must be released by the caller using CoMemTaskFree.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
CAT_E_CATIDNOEXIST
The category ID rcatid is not registered.
CAT_E_NODESCRIPTION
There is no description string for rcatid with the specified locale.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatInformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::IsClassOfCategories


ICatInformation::IsClassOfCategories

Determines if a class implements one or more categories. If the class requires a category not listed in rgcatidReq, it is not included in the enumeration.

HRESULT IsClassOfCategories(
  REFCLSID rclsid,    //Class ID of the class to query
  ULONG cImplemented, //Number of category IDs in the rgcatidImpl
                      // array
  CATID rgcatidImpl,  //Array of category identifiers
  ULONG cRequired,    //Number of category IDs in the rgcatidReq
                      // array
  CATID rgcatidReq[]    //Array of category identifiers
);

Parameters

rclsid
[in] The class ID of the relevent class to query.
cImplemented
[in] The number of category IDs in the rgcatidImpl array. This value cannot be zero. If this value is ((ULONG) -1), the implemented categories are not tested.
rgcatidImpl
[in] An array of category identifiers.
cRequired
[in] The number of category IDs in the rgcatidReq array. This value can be zero. If this value is ((ULONG) -1), the required categories are not tested.
rgcatidReq[]
[in] An array of category identifiers.

Return Values

S_OK
rclsid is of category rcatid.
S_FALSE
rclsid is not of category rcatid.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatInformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc


ICatRegister

The ICatRegister interface provides methods for registering and unregistering component category information in the Registry. This includes both the human-readable names of categories and the categories implemented/required by a given component or class.

When to Implement

There is no need to implement this interface. The Component Category Manager, a system-provided COM object that can be instantiated by using CoCreateInstance, implements ICatRegister.

When to Use

The owner of a category uses this interface to register or unregister the human-readable names. The owner of a component uses this interface to add or remove categories implemented or required by this component.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ICatRegister Methods | Description
RegisterCategories | Registers one or more component categories.
UnRegisterCategories | Removes the registration of one or more component categories.
RegisterClassImplCategories | Registers the class as implementing one or more component categories.
UnRegisterClassImplCategories | Removes one or more implemented category identifiers from a class.
RegisterClassReqCategories | Registers the class as requiring one or more component categories.
UnRegisterClassReqCategories | Removes one or more required category identifiers from a class.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

CATEGORYINFO Structure


ICatRegister::RegisterCategories

Registers one or more component categories. Each component category consists of a CATID and a list of locale-dependent description strings.

HRESULT RegisterCategories(
  ULONG cCategories,            //Number of component categories
  CATEGORYINFO rgCategoryInfo[]  //Array of cCategories CATEGORYINFO
                                // structures
);

Parameters

cCategories
[in] The number of component categories to register.
rgCategoryInfo[]
[in] The array of cCategories CATEGORYINFO structures. By providing the same CATID for multiple CATEGORYINFO structures, multiple locales can be registered for the same component category.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

This function can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterClassImplCategories, ICatRegister::RegisterClassReqCategories, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, ICatRegister::UnRegisterClassReqCategories


ICatRegister::RegisterClassImplCategories

Registers the class as implementing one or more component categories.

HRESULT RegisterClassImplCategories(
  REFCLSID rclsid,    //Class ID of the relevent class
  ULONG cCategories,  //Number of category CATIDs
  CATID rgcatid[]      //Array of cCategories CATID
);

Parameters

rclsid
[in] The class ID of the relevent class for which category information will be set.
cCategories
[in] The number of categories to associate as category identifiers for the class.
rgcatid[]
[in] The array of cCategories CATIDs to associate as category identifiers for the class.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterCategories, ICatRegister::RegisterClassReqCategories, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, ICatRegister::UnRegisterClassReqCategories


ICatRegister::RegisterClassReqCategories

Registers the class as requiring one or more component categories.

HRESULT RegisterClassReqCategories(
  REFCLSID rclsid,    //Class ID of the relevent class
  ULONG cCategories,  //Number of category CATIDs
  CATID rgcatid[]      //Array of cCategories CATID
);

Parameters

rclsid
[in] The class ID of the relevent class for which category information will be set.
cCategories
[in] The number of category CATIDs to associate as category identifiers for the class.
rgcatid[]
[in] The array of cCategories CATID to associate as category identifiers for the class.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, ICatRegister::UnRegisterClassReqCategories


ICatRegister::UnRegisterCategories

Removes the registration of one or more component categories. Each component category consists of a CATID and a list of locale-dependent description strings.

HRESULT UnRegisterCategories(
  ULONG cCategories,  //Number of cCategories CATIDs to be removed
  REFCATID rgcatid[]    //Array of cCategories CATIDs
);

Parameters

cCategories
[in] The number of cCategories CATIDs to be removed.
rgcatid[]
[in] Identifies the categories to be removed.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

This function will be successful even if one or more of the category IDs specified are not registered. This function can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.

Note This method does not remove the component category tags from individual classes. To do this, use the ICatRegister::UnRegisterClassCategories method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICatRegister::RegisterClassReqCategories, ICatRegister::UnRegisterClassImplCategories, ICatRegister::UnRegisterClassReqCategories


ICatRegister::UnRegisterClassImplCategories

Removes one or more implemented category identifiers from a class.

HRESULT UnRegisterClassImplCategories(
  REFCLSID rclsid,    //Class ID of the relevent class
  ULONG cCategories,  //Number of category CATIDs
  CATID rgcatid[]      //Array of cCategories CATID
);

Parameters

rclsid
[in] The class ID of the relevant class to be manipulated.
cCategories
[in] The number of category CATIDs to remove.
rgcatid[]
[in] The array of cCategories CATID that are to be removed. Only the category IDs specified in this array are removed.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. The call will be successful even if one or more of the category IDs specified are not registered for the class. This function can only be called by the owner of a class, usually as part of the de-installation of the component.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICatRegister::RegisterClassReqCategories, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassReqCategories


ICatRegister::UnRegisterClassReqCategories

Removes one or more required category identifiers from a class.

HRESULT UnRegisterClassReqCategories(
  REFCLSID rclsid,    //Class ID of the relevent class
  ULONG cCategories,  //Number of category CATIDs
  CATID rgcatid[]     //Array of cCategories CATID
);

Parameters

rclsid
[in] The class ID of the relevent class to be manipulated.
cCategories
[in] The number of category CATIDs to remove.
rgcatid[]
[in] The array of cCategories CATID that are to be removed. Only the category IDs specified in this array are removed.

Return Values

S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.

Remarks

In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. The call will be successful even if one or more of the category IDs specified are not registered for the class.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in comcat.h.

See Also

ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, ICatRegister::RegisterClassReqCategories


IClassActivator

Specifies a method that retrieves a class object.

When to Implement

No implementation of a moniker or an object supporting IClassActivator currently exists within the system, however future versions of the operating system may contain such implementations. Implement the IClassActivator interface if you are writing a custom moniker type which you want to be able to compose to the left of a class moniker or any other moniker that supports binding to IClassActivator.

When to Use

Use IClassActivator if you write a custom moniker class that should behave similarly to class monikers when composed to the right of other monikers. File monikers also use this interface.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IClassActivator Method | Description
GetClassObject | Retrieves a class object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IClassActivator::GetClassObject

Retrieves a class object. Similar to CoGetClassObject.

HRESULT GetClassObject(
  REFCLSID *pClassID,  //CLSID of class object desired
  DWORD dwClsContext,  //Values from CLSCTX
  LCID locale,         //LCID constant
  REFIID riid,         //IID of requested interface
  void ** ppv          //Address of output variable that receives
                       // the interface pointer requested in riid
);

Parameter

pClassID
[in] Points to the CLSID that Identifies the class whose class object is to be retrieved.
dwClsContext
[in] The context in which the class is expected to run; values are taken from the CLSCTX enumeration.
locale
[in] Any LCID constant as defined in WINNLS.H.
riid
[in] IID of the interface on the object to which a pointer is desired.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The CLSID was successfully returned.

Remarks

This method returns the class identifier (CLSID) for an object, used in later operations to load object-specific code into the caller's context.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoGetClassObject


IClassFactory

The IClassFactory interface contains two methods intended to deal with an entire class of objects, and so is implemented on the class object for a specific class of objects (identified by a CLSID). The first method, CreateInstance, creates an uninitialized object of a specified CLSID, and the second, LockServer, locks the object's server in memory, allowing new objects to be created more quickly.

When to Implement

You must implement this interface for every class that you register in the system registry and to which you assign a CLSID, so objects of that class can be created.

When to Use

After calling the CoGetClassObject function to get an IClassFactory interface pointer to the class object, call the CreateInstance method of this interface to create a new uninitialized object.

It is not, however, always necessary to go through this process to create an object. To create a single uninitialized object, you can, instead, just call CoCreateInstance. OLE also provides numerous helper functions (with names of the form OleCreate Xxx) to create compound document objects.

Call the LockServer method to keep the object server in memory and enhance performance only if you intend to create more than one object of the specified class.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IClassFactory Methods | Description
CreateInstance | Creates an uninitialized object.
LockServer | Locks object application open in memory.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.

See Also

CoGetClassObject, CoCreateInstance, OleCreate


IClassFactory::CreateInstance

Creates an uninitialized object.

HRESULT CreateInstance(
  IUnknown * pUnkOuter,  //Pointer to whether object is or isn't part of
                         // an aggregate
  REFIID riid,           //Reference to the identifier of the interface
  void ** ppvObject      //Address of output variable that receives the
                         // interface pointer requested in riid
);

Parameters

pUnkOuter
[in] If the object is being created as part of an aggregate, pointer to the controlling IUnknown interface of the aggregate. Otherwise, pUnkOuter must be NULL.
riid
[in] Reference to the identifier of the interface to be used to communicate with the newly created object. If pUnkOuter is NULL, this parameter is frequently the IID of the initializing interface; if pUnkOuter is non-NULL, riid must be IID_IUnknown (defined in the header as the IID for IUnknown).
ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer. If the object does not support the interface specified in riid, the implementation must set *ppvObject to NULL.

Return Values

This method supports the standard return values E_UNEXPECTED, E_OUTOFMEMORY, and E_INVALIDARG, as well as the following:

S_OK
The specified object was created.
CLASS_E_NOAGGREGATION
The pUnkOuter parameter was non-NULL and the object does not support aggregation.
E_NOINTERFACE
The object that ppvObject points to does not support the interface identified by riid.

Remarks

The IClassFactory interface is always on a class object. The CreateInstance method creates an uninitialized object of the class identified with the specified CLSID. When an object is created in this way, the CLSID must be registered in the system registry with CoRegisterClassObject.

The pUnkOuter parameter indicates whether the object is being created as part of an aggregate. Object definitions are not required to support aggregation — they must be specifically designed and implemented to support it.

The riid parameter specifies the IID (interface identifier) of the interface through which you will communicate with the new object. If pUnkOuter is non-NULL (indicating aggregation), the value of the riid parameter must be IID_IUnknown. If the object is not part of an aggregate, riid often specifies the interface though which the object will be initialized.

For OLE embeddings, the initialization interface is IPersistStorage, but in other situations, other interfaces are used. To initialize the object, there must be a subsequent call to an appropriate method in the initializing interface. Common initialization functions include IPersistStorage::InitNew (for new, blank embeddable components), IPersistStorage::Load (for reloaded embeddable components), IPersistStream::Load, (for objects stored in a stream object) or IPersistFile::Load (for objects stored in a file).

In general, if an application supports only one class of objects, and the class object is registered for single use, only one object can be created. The application must not create other objects, and a request to do so should return an error from IClassFactory::CreateInstance. The same is true for applications that support multiple classes, each with a class object registered for single use; a CreateInstance for one class followed by a CreateInstance for any of the classes should return an error.

To avoid returning an error, applications that support multiple classes with single-use class objects can revoke the registered class object of the first class by calling CoRevokeClassObject when a request for instantiating a second is received. For example, suppose there are two classes, A and B. When IClassFactory::CreateInstance is called for class A, revoke the class object for B. When B is created, revoke the class object for A. This solution complicates shutdown because one of the class objects might have already been revoked (and cannot be revoked twice).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.

See Also

CoRegisterClassObject, CoRevokeClassObject, CoCreateInstance, CoGetClassObject


IClassFactory::LockServer

Called by the client of a class object to keep a server open in memory, allowing instances to be created more quickly.

HRESULT LockServer(
  BOOL fLock  //Increments or decrements the lock count
);

Parameter

fLock
[in] If TRUE, increments the lock count; if FALSE, decrements the lock count.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The specified object was either locked ( fLock = TRUE) or unlocked from memory ( fLock = FALSE).

Remarks

IClassFactory::LockServer controls whether an object's server is kept in memory. Keeping the application alive in memory allows instances to be created more quickly.

Notes to Callers

Most clients do not need to call this function. It is provided only for those clients that require special performance in creating multiple instances of their objects.

Notes to Implementers

If the lock count is zero, there are no more objects in use, and the application is not under user control, the server can be closed. One way to implement IClassFactory::LockServer is to call CoLockObjectExternal.

The process that locks the object application is responsible for unlocking it. Once the class object is released, there is no mechanism that guarantees the caller connection to the same class later (as in the case where a class object is registered as single-use). It is important to count all calls, not just the last one, to IClassFactory::LockServer, because calls must be balanced before attempting to release the pointer to the IClassFactory interface on the class object or an error results. For every call to LockServer with fLock set to TRUE, there must be a call to LockServer with fLock set to FALSE. When the lock count and the class object reference count are both zero, the class object can be freed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.

See Also

CoLockObjectExternal


IClassFactory2

The IClassFactory2 interface enables a class factory object, in any sort of object server, to control object creation through licensing. This interface is an extension to IClassFactory. This extension enables a class factory executing on a licensed machine to provide a license key that can be used later to create an object instance on an unlicensed machine. Such considerations are important for objects like controls that are used to build applications on a licensed machine. Subsequently, the application built must be able to run on an unlicensed machine. The license key gives only that one client application the right to instantiate objects through IClassFactory2 when a full machine license does not exist.

When to Implement

Implement this interface on a class factory object if you need to control object creation through a license. A class that supports licensing should be marked in an object's type information with the [licensed] attribute on the object's coclass entry.

The CreateInstance method inherited from IClassFactory is allowed to return CLASS_E_NOTLICENSED to indicate that object creation is controlled through licensing. The caller can create an instance of this object only through IClassFactory2::CreateInstanceLic if the caller has a license key obtained from IClassFactory2::RequestLicKey. Otherwise, no object creation is allowed.

When to Use

Use this interface to create licensed objects or to obtain a license key that can be used in later creations.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IClassFactory Methods | Description
CreateInstance | Creates an uninitialized object.
LockServer | Locks object application open in memory.
IClassFactory2 Methods | Description
GetLicInfo | Fills a LICINFO structure with information on the licensing capabilities of this class factory.
RequestLicKey | Creates and returns a license key that the caller can save and use later in calls to IClassFactory2::CreateInstanceLic.
CreateInstanceLic | Creates an instance of the licensed object given a license key from IClassFactory2::RequestLicKey.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IClassFactory


IClassFactory2::CreateInstanceLic

Creates an instance of the object class supported by this class factory, given a license key previously obtained from IClassFactory2::RequestLicKey. This method is the only possible means to create an object on an otherwise unlicensed machine.

HRESULT CreateInstanceLic(
  IUnknown* pUnkOuter,  //Pointer to controlling unknown of
                        // aggregated object
  IUnknown* pUnkReserved,  //Unused. Must be NULL.
  REFIID riid,          //Reference to the identifier of the
                        // interface
  BSTR bstrKey,         //License key provided by
                        // IClassFactory2::RequestLicKey
  void** ppvObject      //Address of output variable that receives
                        // the interface pointer requested in riid
);

Parameters

pUnkOuter
[in] Pointer to the controlling IUnknown interface on the outer unknown if this object is being created as part of an aggregate. If the object is not part of an aggregate, this parameter must be NULL.
pUnkReserved
[in] Unused. Must be NULL.
riid
[in] Reference to the identifier of the interface to be used to communicate with the newly created object.
bstrKey
[in] Run-time license key previously obtained from IClassFactory2::RequestLicKey that is required to create an object.
ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer. If an error occurs, the implementation must set *ppvObject to NULL.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The license was successfully created.
E_NOTIMPL
This method is not implemented because objects can only be created on fully licensed machines through IClassFactory::CreateInstance.
E_POINTER
The pointers passed in bstrKey or ppvObject are not valid. For example, it may be NULL.
E_NOINTERFACE
The object can be created (and the license key is valid) except the object does not support the interface specified by riid.
CLASS_E_NOAGGREGATION
The pUnkOuter parameter is non-NULL, but this object class does not support aggregation.
CLASS_E_NOTLICENSED
The key provided in bstrKey is not a valid license key.

Remarks

Notes to Implementers

If the class factory does not provide a license key (that is, IClassFactory2::RequestLicKey returns E_NOTIMPL and the fRuntimeKeyAvail field in LICINFO is set to FALSE in IClassFactory2::GetLicInfo), then this method can also return E_NOTIMPL. In such cases, the class factory is implementing IClassFactory2 simply to specify whether or not the machine is licensed at all through the fLicVerified field of LICINFO.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IClassFactory2::GetLicInfo, IClassFactory2::RequestLicKey, LICINFO


IClassFactory2::GetLicInfo

Fills a caller-allocated LICINFO structure with information describing the licensing capabilities of this class factory.

HRESULT GetLicInfo(
  LICINFO* pLicInfo  //Pointer to the structure
);

Parameters

pLicInfo
[out] Pointer to the caller-allocated LICINFO structure to be filled on output.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The LICINFO structure was successfully filled in.
E_POINTER
The address in pLicInfo is not valid. For example, it may be NULL.

Remarks

Notes to Implementers

E_NOTIMPL is not allowed as a return value since this method provides critical information for the client of a licensed class factory.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IClassFactory2::CreateInstanceLic, IClassFactory2::RequestLicKey, LICINFO


IClassFactory2::RequestLicKey

If the fRuntimeKeyAvail field in LICINFO has been returned as TRUE from IClassFactory2::GetLicInfo, then this method creates and returns a license key. The caller can save the license key persistently and use it later in calls to IClassFactory2::CreateInstanceLic.

HRESULT RequestLicKey(
  DWORD dwReserved,  //Unused. Must be zero.
  BSTR* pbstrKey     //Pointer to the license key
);

Parameters

dwReserved
[in] Unused. Must be zero.
pbstrKey
[out] Pointer to the caller-allocated BSTR variable that receives the callee-allocated license key on successful return from this method. This parameter is set to NULL on any failure.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The license key was successfully created.
E_NOTIMPL
This class factory does not support run-time license keys.
E_POINTER
The address in pbstrKey is not valid. For example, it may be NULL.
CLASS_E_NOTLICENSED
This class factory supports run-time licensing, but the current machine itself is not licensed. Thus, a run-time key is not available on this machine.

Remarks

The caller can save the license key for subsequent calls to IClassFactory2::CreateInstanceLic to create objects on an otherwise unlicensed machine.

Notes to Callers

The caller must free the BSTR with SysFreeString when the key is no longer needed. The value of fRuntimeKeyAvail is returned through a previous call to IClassFactory2::GetLicInfo.

Notes to Implementers

This method allocates the BSTR key with SysAllocString or SysAllocString[Len], and the caller becomes responsible for this BSTR once this method returns successfully.

This method need not be implemented when a class factory does not support run-time license keys.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IClassFactory2::CreateInstanceLic, IClassFactory2::GetLicInfo, LICINFO


IClientSecurity

Gives the client control over the security settings for each individual interface proxy of an object. The methods of IClientSecurity can be used to set or query the security settings of a specific interface proxy or to copy an interface proxy.

Every object has one proxy manager, and every proxy manager exposes the IClientSecurity interface automatically. Therefore, the client can query the proxy manager of an object for IClientSecurity, using any interface pointer on the object. If the QueryInterface call succeeds, the IClientSecurity pointer can be used to call an IClientSecurity method, passing a pointer to the interface proxy that the client is interested in. If a call to QueryInterface for IClientSecurity fails, either the object is implemented in-process or it is remoted by a custom marshaler that does not support security. (A custom marshaler can support security by offering the IClientSecurity interface to the client.)

The interface proxies passed as parameters to IClientSecurity methods must be from the same object as the IClientSecurity interface. That is, each object has a distinct IClientSecurity interface: calling IClientSecurity on one object and passing a proxy to another object will not work. Also, you cannot pass an interface to an IClientSecurity method if the interface does not use a proxy. This means that interfaces implemented locally by the proxy manager cannot be passed to IClientSecurity methods, except for IUnknown, which is the exception to this rule.

For more information about proxies, see IMarshal - Default Implementation.

When to Implement

The proxy manager for each object provides an implementation of IClientSecurity, so you would typically not implement this interface. If, however, you are defining objects that support custom marshaling, you may choose to implement IClientSecurity on the objects' custom proxies to maintain a consistent programming model for the objects' client applications. You may also choose to support this interface on in-process objects.

When to Use

Call the methods of this interface to examine or modify the security settings of a particular connection to an out-of-process object. For example, you might temporarily establish a higher security level — one with complex encryption — only for the period when sensitive information or data is being sent to the object. Alternately, you might establish different proxies to the same object with different security levels. You could use these security levels to support different clients that are calling your object or to support different operations within your application.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IClientSecurity Methods | Description
QueryBlanket | Retrieves authentication information.
SetBlanket | Sets the authentication information that will be used to make calls on the specified proxy.
CopyProxy | Makes a copy of the specified proxy.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

Security in COM, Setting Process-wide Security with CoInitializeSecurity


IClientSecurity::CopyProxy

Makes a private copy of the proxy for the specified interface.

HRESULT CopyProxy(
  IUnknown *  pProxy,  //Pointer to the interface whose proxy
                       // will be copied
  IUnknown **  ppCopy  //Address of output variable that receives
                       // the IUnknown interface pointer of the
                       // proxy copy
);

Parameters

pProxy
[in] Points to the interface whose proxy will be copied. May not be NULL.
ppCopy
[out] Address of IUnknown* pointer variable that receives the interface pointer to the copy of the proxy. It may not be NULL.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.

Remarks

IClientSecurity::CopyProxy is called by the client to make a private copy of the proxy for the specified interface. The proxy copy has default values for the authentication information. Its authentication information can be changed through a call to IClientSecurity::SetBlanket without affecting any other clients of the original proxy. The copy has one reference, and the caller of CopyProxy must ensure that the proxy copy gets freed.

Local interfaces, such as IUnknown and IClientSecurity, cannot be copied. You cannot duplicate a proxy manager using CopyProxy.

Copies of the same proxy have a special relationship with respect to QueryInterface. Given a proxy, a, of the IA interface of a remote object, suppose a copy of a is created, called b. In this case, calling QueryInterface from the b proxy for IID_IA will not retrieve the IA interface on b, but the one on a, the original proxy.

Notice that anyone can query for a proxy and change security on it using SetBlanket. However, when you have made a copy of a proxy, no one can get the copy unless you give it to them. Only people who have the copy can set security on it.

The helper function CoCopyProxy encapsulates a QueryInterface call for a pointer to IClientSecurity, a call to IClientSecurity::CopyProxy with the IClientSecurity pointer, and the release of the IClientSecurity pointer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoCopyProxy


IClientSecurity::QueryBlanket

Retrieves authentication information the client uses to make calls on the specified proxy.

HRESULT QueryBlanket(
  IUnknown*  pProxy,    //Pointer to the interface whose proxy
                        // will be queried
  DWORD*  pAuthnSvc,    //Location for the current authentication
                        // service
  DWORD*  pAuthzSvc,    //Location for the current authorization
                        // service
  OLECHAR**  pServerPrincName,
                        //Location for the current principal name
  DWORD* pAuthnLevel,   //Location for the current authentication
                        // level
  DWORD* pImpLevel,     //Location for the current impersonation
                        // level
  RPC_AUTH_IDENTITY_HANDLE*  pAuthInfo,
                        //Location of a handle that indicates the
                        // client identity
  DWORD*  pCapabilities //Location for flags indicating further
                        // capabilities of the proxy
);

Parameters

pProxy
[in] Pointer indicating the proxy to query. It may not be NULL. For more information, see the Remarks section.
pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This will be a single value taken from the list of RPC_C_AUTHN_xxx constants. If the caller specifies NULL, the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. If the caller specifies NULL, the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the callee using CoTaskMemAlloc and must be freed by the caller using CoTaskMemFree when they are done with it. Notice that the actual principal name is returned. The EOAC_MAKE_FULLSIC flag is not accepted to convert the prinicpal name. If the caller specifies NULL, the current principal name is not retrieved.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This will be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. If the caller specifies NULL, the current authentication level is not retrieved.
pImpLevel
[out] Pointer to a DWORD value defining the current impersonation level. This will be a single value taken from the list of RPC_C_IMP_LEVEL_xxx constants. If the caller specifies NULL, the current impersonation level is not retrieved.
pAuthInfo
[out] Pointer to a handle indicating the identity of the client that was passed to the last IClientSecurity::SetBlanket call (or the default value). Default values are only valid until the proxy is released. If the caller specifies NULL, the client identity is not retrieved. The format of the structure that the returned handle refers to depends on the authentication service. For NTLMSSP and Kerberos, if the client specified a structure in the pAuthInfo parameter to CoInitializeSecurity, that value is returned. For SChannel, if a certificate for the client could be retrieved from the certificate manager, that value is returned here. Otherwise, NULL is returned. Because this points to the value itself and is not a copy, it should not be manipulated or freed.
pCapabilities
[out] Pointer to a DWORD of flags indicating the capabilities of the proxy. If the caller specifies NULL, the current capability flags are not retrieved.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pServerPrincName out-parameter.

Remarks

IClientSecurity::QueryBlanket is called by the client to retrieve the authentication information COM will use on calls made from the specified interface proxy. With a pointer to an interface on the proxy, the client would first call QueryInterface for a pointer to IClientSecurity; then, with this pointer, the client would call IClientSecurity::QueryBlanket, followed by releasing the pointer. This sequence of calls is encapsulated in the helper function CoQueryProxyBlanket.

In pProxy, you pass an interface pointer. However, you cannot pass a pointer to an interface that does not use a proxy. Thus you can't pass a pointer to an interface that has the local keyword in its interface definition since no proxy is created for such an interface. IUnknown is the exception to this rule.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoQueryProxyBlanket, CoSetProxyBlanket.


IClientSecurity::SetBlanket

Sets the authentication information (the security blanket) that will be used to make calls on the specified proxy. This overrides the process default settings for the specified proxy. Calling this method changes the security values for all other users of the specified proxy.

HRESULT SetBlanket(
  IUnknown * pProxy,          //Indicates the interface proxy whose
                              // authentication information will be set
  DWORD  dwAuthnSvc,          //The authentication service to use
  DWORD  dwAuthzSvc,          //The authorization service to use
  OLECHAR * pServerPrincName, //The server principal name to use with
                              // the authentication service
  DWORD  dwAuthnLevel,        //The authentication level to use
  DWORD  dwImpLevel,          //The impersonation level to use
  RPC_AUTH_IDENTITY_HANDLE  pAuthInfo,
                              //The identity of the client
  DWORD  dwCapabilities       //The capability flags
);

Parameters

pProxy
[in] Indicates the interface proxy to set.
dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants indicating the authentication service to use. It may be RPC_C_AUTHN_NONE if no authentication is required. If RPC_C_AUTHN_DEFAULT is specified, COM will pick an authentication service following its normal security blanket negotiation algorithm.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants indicating the authorization service to use. If RPC_C_AUTHZ_DEFAULT is specified, COM will pick an authorization service following its normal security blanket negotiation algorithm. RPC_C_AUTHZ_NONE should be used as the authorization service if NTLMSSP, Kerberos, or SChannel is used as the authentication service.
pServerPrincName
[in] Pointer to a WCHAR string that indicates the server principal name to use with the authentication service. If COLE_DEFAULT_PRINCIPAL is specified, DCOM will pick a principal name using its security blanket negotiation algorithm. If Kerberos is used as the authentication service, this parameter must be the correct principal name of the server or the call will fail.

If SChannel is used as the authentication service, this value must be one of the msstd or fullsic forms described in Principal Names, or NULL if you do not want mutual authentication.

Generally, specifying NULL will not reset server principal name on the proxy, rather, the previous setting will be retained. You must exercise care when using NULL as pServerPrincName when selecting a different Authentication Service for the proxy, because there is no guarantee that the previously set principal name would be valid for the newly selected Authentication Service.

dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants indicating the authentication level to use. If RPC_C_AUTHN_LEVEL_DEFAULT is specified, COM will pick an authentication level following its normal security blanket negotiation algorithm. If this value is set to RPC_C_AUTHN_LEVEL_NONE, the authentication service must be RPC_C_AUTHN_NONE. Each authentication service may choose to use a more secure authentication level than the one specified.
dwImpLevel
[in] A single DWORD value from the list of RPC_C_IMP_LEVEL_xxx constants indicating the impersonation level to use. If RPC_C_IMP_LEVEL_DEFAULT is specified, COM will pick an impersonation level following its normal security blanket negotiation algorithm. If you are using NTLMSSP remotely, this value must be RPC_C_IMP_LEVEL_IMPERSONATE or RPC_C_IMP_LEVEL_IDENTIFY. When using NTLMSSP on the same machine, RPC_C_IMP_LEVEL_DELEGATE is also supported. For Kerberos, this value can be RPC_C_IMP_LEVEL_IDENTIFY, RPC_C_IMP_LEVEL_IMPERSONATE, or RPC_C_IMP_LEVEL_DELEGATE. For SChannel, this value must be RPC_C_IMP_LEVEL_IMPERSONATE.
pAuthInfo
[in] An RPC_AUTH_IDENTITY_HANDLE value that indicates the identity of the client. This parameter is not used for calls on the same machine. If pAuthInfo is NULL, COM uses the current proxy identity (which is either the process token, the impersonation token, or the authentication identity from CoInitializeSecurity). If the handle is not NULL, that identity is used. The format of the structure referred to by the handle depends on the provider of the authentication service.

For NTLMSSP or Kerberos, the structure is a SEC_WINNT_AUTH_IDENTITY or SEC_WINNT_AUTH_IDENTITY_EX structure. The client must insure that the memory remains valid and unchanged until a different identity is set on the proxy or all proxies on the object are released. If NULL is specified, COM uses the current proxy identity (which is either the process token or the impersonation token). If the handle refers to a structure, that identity is used.

For SChannel, this parameter must either be a pointer to a CERT_CONTEXT that contains the client's X.509 certificate, or NULL if the client wishes to make an anonymous connection to the server. If a certificate is specified, the caller must not free it as long as any proxy to the object exists in the current apartment.

For Snego, this parameter is either NULL or points to a SEC_WINNT_AUTH_IDENTITY_EX structure. If it is NULL, Snego will pick a list of authentication services based on those available on the client machine. If non-NULL, the structure's PackageList member must point to a string containing a comma-separated list of authentication service names, and the PackageListLength member must give the number of bytes in the PackageList string. If PackageList is NULL, all calls using Snego will fail.

If COLE_DEFAULT_AUTHINFO is specified, COM will pick the authentication information following its normal security blanket negotiation algorithm.

SetBlanket will return an error if both pAuthInfo is set and one of the cloaking flags is set in dwCapabilities.

dwCapabilities
[in] A DWORD defining flags that specify the capabilities of this proxy. Capability flags are defined in the EOLE_AUTHENTICATION_CAPABILITIES enumeration. The only flags that can be set through this method are EOAC_MUTUAL_AUTH, EOAC_STATIC_CLOAKING, EOAC_DYNAMIC_CLOAKING, EOAC_ANY_AUTHORITY, EOAC_MAKE_FULLSIC, and EOAC_DEFAULT. Either EOAC_STATIC_CLOAKING or EOAC_DYNAMIC_CLOAKING can be set if pAuthInfo is not set and SChannel is not the authentication service. (See Cloaking for more information.) If any capability flags other than those mentioned here are indicated, SetBlanket will return an error.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments is invalid.

Remarks

IClientSecurity::SetBlanket sets the authentication information that will be used to make calls on the specified interface proxy. The values specified here override the values chosen by automatic security. Calling this method changes the security values for all other users of the specified proxy. If you want the changes to apply only to a particular instance of a proxy, call IClientSecurity::CopyProxy to make a private copy of the proxy and then call SetBlanket on the copy.

Whenever this method is called, DCOM will set the identity on the proxy, and future calls made using this proxy will use this identity. Calls in progress when SetBlanket is called will use the authentication information on the proxy at the time the call was started. If pAuthInfo is NULL, the proxy identity defaults to the current process token (unless an authentication identity was specified on a call to CoInitializeSecurity). See the Cloaking section to see how the cloaking flags affect the proxy when pAuthInfo is NULL.

By default, COM will choose the first available authentication service and authorization service available on both the client and server machines and the principal name that the server registered for that authentication service. Currently, COM will not try another authentication service if the first fails.

When the default constant for dwImpLevel is specified in SetBlanket, the parameter defaults to the value specified to CoInitializeSecurity. If CoInitializeSecurity is not called, it defaults to RPC_C_IMP_LEVEL_IDENTIFY.

The initial value for dwAuthnLevel on a proxy will be the higher of the value set on the client's call to CoInitializeSecurity and the server's call to CoInitializeSecurity. For any process that did not call CoInitializeSecurity, the default authentication level is RPC_C_AUTHN_CONNECT.

The default authentication and impersonation level for processes that do not call CoInitializeSecurity can be set with DCOMCNFG.

If EOAC_DEFAULT is specified for dwCapabilities, the valid capabilities from CoInitializeSecurity will be used. If CoInitializeSecurity was not called, EOAC_NONE will be used for the capabilities flag.

If SetBlanket is called simultaneously on two threads on the same proxy, only one set of changes will be applied. If SetBlanket and CRpcOptions::Set are called simultaneously on two threads on the same proxy, both changes may be applied or only one may be applied.

Security information cannot be set on local interfaces such as the IClientSecurity interface. However, since that interface is only supported locally, there is no need for security. IUnknown and IMultiQI are special cases. The location implementation makes remote calls to support these interfaces. SetBlanket can be used for IUnknown. IMultiQI will use the security settings on IUnknown.

To change one SetBlanket parameter without having to deal with the others, one can specify the default constants for the other parameters. For example, on Windows 2000, the authentication service will generally be NTLMSSP on machine local proxies and Kerberos on machine remote proxies. Applications can change a parameter (such as the authentication level) and ignore the other parameters, including the authentication service, by setting all other parameters to the default constants.

Note that it is important to set all unused parameters to the default constants because the default value is often not obvious. In particular, if you set the authentication service to the default, you should set the authentication information and principal name to the default. The reasons for this are twofold: First, the type of the authentication information depends on the authentication service DCOM chooses. Second, because DCOM needs to pass some complex authentication information for certain authentication services, if you set the authentication service to default and the authentication information to NULL, you might get a security setting that won’t work. The default parameters for SetBlanket when SChannel is used as the authentication service are only valid in Windows 2000 and later versions.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoSetProxyBlanket, CoQueryProxyBlanket, CoInitializeSecurity, Setting Security at the Interface Proxy Level


IConnectionPoint

The IConnectionPoint interface supports connection points for connectable objects.

Connectable objects support the following features:

  • Outgoing interfaces, such as event sets
  • The ability to enumerate the IIDs of the outgoing interfaces
  • The ability to connect and disconnect sinks to the object for those outgoing IIDs
  • The ability to enumerate the connections that exist to a particular outgoing interface

When to Implement

Implement this interface as part of support for connectable objects. To create a connectable object, you need to implement objects that provide four related interfaces:

  • IConnectionPointContainer
  • IEnumConnectionPoints
  • IConnectionPoint
  • IEnumConnections

The IConnectionPointContainer interface is implemented on the connectable object to indicate the existence of the outgoing interfaces. It provides access to an enumerator sub-object with the IEnumConnectionPoints interface. It also provides access to all the connection point sub-objects, each of which implements the IConnectionPoint interface. The IConnectionPoint interface provides access to an enumerator sub-object with the IEnumConnections interface.

Each connection point is a separate sub-object to avoid circular reference counting problems.

A connection point controls how many connections (one or more) it will allow in its implementation of IConnectionPoint::Advise. A connection point that allows only one interface can return E_NOTIMPL from the IConnectionPoint::EnumConnections method.

When to Use

A client can use the IConnectionPointContainer interface:

  • To obtain access to an enumerator sub-object with the IEnumConnectionPoints interface. The IEnumConnectionPoints interface can then be used to enumerate connection points for each outgoing IID.
  • To obtain access to connection point sub-objects with the IConnectionPoint interface for each outgoing IID. Through the IConnectionPoint interface, a client starts or terminates an advisory loop with the connectable object and the client's own sink. The client can also use the IConnectionPoint interface to obtain an enumerator object with the IEnumConnections interface to enumerate the connections that it knows about.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IConnectionPoint Methods | Description
GetConnectionInterface | Returns the IID of the outgoing interface managed by this connection point.
GetConnectionPointContainer | Returns the parent (connectable) object's IConnectionPointContainer interface pointer.
Advise | Creates a connection between a connection point and a client's sink, where the sink implements the outgoing interface supported by this connection point.
Unadvise | Terminates a notification previously set up with Advise.
EnumConnections | Returns an object to enumerate the current advisory connections for this connection point.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IConnectionPointContainer, IEnumConnectionPoints, IEnumConnections


IConnectionPoint::Advise

Establishes a connection between the connection point object and the client's sink.

HRESULT Advise(
  IUnknown *pUnk,  //Pointer to the client's advise sink
  DWORD *pdwCookie //Pointer to the connection point identifier used
                   // by Unadvise
);

Parameter

pUnk
[in] Pointer to the IUnknown interface on the client's advise sink. The client's sink receives outgoing calls from the connection point.
pdwCookie
[out] Pointer to a returned token that uniquely identifies this connection. The caller uses this token later to delete the connection by passing it to the IConnectionPoint::Unadvise method. If the connection was not successfully established, this value is zero.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The connection has been established and *pdwCookie has the connection token.
E_POINTER
The value in pUnk or pdwCookie is not valid. For example, either pointer may be NULL.
CONNECT_E_ADVISELIMIT
The connection point has already reached its limit of connections and cannot accept any more.
CONNECT_E_CANNOTCONNECT
The sink does not support the interface required by this connection point.

Remarks

Advise establishes a connection between the connection point and the caller's sink identified with pUnk.

The connection point must call pUnk->QueryInterface(iid, ...) to obtain the correct outgoing interface pointer to call when events occur, where iid is the IID for the outgoing interface managed by the connection point. When iid is passed to the IConnectionPointContainer::FindConnectionPoint method, an interface pointer to this same connection point is returned.

Notes to Implementers

The connection point must query the pUnk pointer for the correct outgoing interface. If this query fails, this method must return CONNECT_E_CANNOTCONNECT.

The pdwCookie value must be unique for each connection to any given instance of a connection point.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint::Unadvise


IConnectionPoint::EnumConnections

Creates an enumerator object to iterate through the current connections for this connection point.

HRESULT EnumConnections(
  IEnumConnections **ppEnum  //Address of output variable that
                             // receives the IEnumConnections interface
                             // pointer
);

Parameters

ppEnum
[out] Address of IEnumConnections* pointer variable that receives the interface pointer to the newly created enumerator.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The enumerator object was successfully created.
E_POINTER
The address in ppEnum is not valid. For example, it may be NULL.
E_NOTIMPL
The connection point does not support enumeration.

Remarks

Notes to Callers

The caller is responsible for calling (*ppEnum)->Release when the enumerator is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IEnumConnections


IConnectionPoint::GetConnectionInterface

Returns the IID of the outgoing interface managed by this connection point.

HRESULT GetConnectionInterface(
  IID *pIID  //Pointer to an IID variable
);

Parameters

pIID
[out] Pointer to the identifier of the outgoing interface managed by this connection point.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The caller's variable pIID contains the identifier of the outgoing interface managed by this connection point.
E_POINTER
The address in pIID is not valid. For example, it may be NULL.

Remarks

Using the IEnumConnectionPoints interface, a client can obtain a pointer to the IConnectionPoint interface. Using that pointer and the GetConnectionInterface method, the client can determine the IID of each connection point enumerated. The IID returned from this method must enable the caller to access this same connection point through IConnectionPointContainer::FindConnectionPoint.

Notes to Implementers

This method must be implemented in any connection point; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IConnectionPointContainer::FindConnectionPoint, IEnumConnectionPoints


IConnectionPoint::GetConnectionPointContainer

Retrieves the IConnectionPointContainer interface pointer for the parent connectable object.

HRESULT GetConnectionPointContainer(
  IConnectionPointContainer **ppCPC
                  //Address of output variable that receives
                  // the IConnectionPointContainer interface pointer
);

Parameters

ppCPC
[out] Address of IConnectionPointContainer* pointer variable that receives the interface pointer to the parent connectable object.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The IConnectionPointContainer pointer was successfully returned.
E_POINTER
The value in ppCPC is not a valid interface pointer. For example, it may be NULL.

Remarks

Notes to Callers

This method calls IConnectionPointContainer::AddRef. The caller is responsible for calling IConnectionPointContainer::Release to release this pointer when done.

Notes to Implementers

This method must call IConnectionPointContainer::AddRef before returning.

This method must be implemented in any connection point; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IConnectionPointContainer


IConnectionPoint::Unadvise

Terminates an advisory connection previously established through IConnectionPoint::Advise. The dwCookie parameter identifies the connection to terminate.

HRESULT Unadvise(
  DWORD dwCookie  //Connection token
);

Parameters

dwCookie
[in] Connection token previously returned from IConnectionPoint::Advise.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The connection was successfully terminated.
CONNECT_E_NOCONNECTION
The value in dwCookie does not represent a valid connection.

Remarks

When an advisory connection is terminated, the connection point calls the Release method on the pointer that was saved for the connection during the IConnectionPoint::Advise method. This Release reverses the AddRef that was performed during the IConnectionPoint::Advise when the connection point calls the advisory sink's QueryInterface.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint::Advise


IConnectionPointContainer

The IConnectionPointContainer interface supports connection points for connectable objects.

Connectable objects support the following features:

  • Outgoing interfaces, such as event sets
  • The ability to enumerate the IIDs of the outgoing interfaces
  • The ability to connect and disconnect sinks to the object for those outgoing IIDs
  • The ability to enumerate the connections that exist to a particular outgoing interface

When to Implement

Implement this interface as part of support for connectable objects. To support connectable objects, you need to provide four related interfaces:

  • IConnectionPointContainer
  • IEnumConnectionPoints
  • IConnectionPoint
  • IEnumConnections

The IConnectionPointContainer interface indicates the existence of the outgoing interfaces. It provides access to an enumerator sub-object with the IEnumConnectionPoints interface. It also provides a connection point sub-object with the IConnectionPoint interface. The IConnectionPoint interface provides access to an enumerator sub-object with the IEnumConnections interface.

The connection point is a separate sub-object to avoid circular reference counting problems.

Implement the IConnectionPointContainer interface to make a connectable object, that is, an object with outgoing interfaces.

When to Use

Through IConnectionPointContainer, you can locate a specific connection point for one IID or obtain an enumerator to enumerate the connections points.

Use the IConnectionPointContainer interface to obtain access to:

  • Enumerator sub-objects with the IEnumConnectionPoints interface. The IEnumConnectionPoints interface can then be used to enumerate connection points for each outgoing IID.
  • Connection point sub-objects with the IConnectionPoint interface for each IID. Through the IConnectionPoint interface, a client starts or terminates an advisory loop with the connectable object and the client's own sink. The client can also use the IConnectionPoint interface to obtain an enumerator object with the IEnumConnections interface to enumerate the connections it knows about.

When to Use

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IConnectionPointContainer Methods | Description
EnumConnectionPoints | Returns an object to enumerate all the connection points supported in the connectable object.
FindConnectionPoint | Returns a pointer to the IConnectionPoint interface for a specified connection point.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IEnumConnectionPoints, IEnumConnections


IConnectionPointContainer::EnumConnectionPoints

Creates an enumerator object to iterate through all the connection points supported in the connectable object, one connection point per outgoing IID.

HRESULT EnumConnectionPoints(
  IEnumConnectionPoints **ppEnum  //Address of output variable that
                                  // receives the IEnumConnectionPoints
                                  // interface pointer
);

Parameters

ppEnum
[out] Address of IEnumConnectionPoints* pointer variable that receives the interface pointer to the newly created enumerator.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The enumerator was successfully created.
E_POINTER
The value in ppEnum is not valid. For example, it may be NULL.

Remarks

Since IEnumConnectionPoints enumerates pointers to IConnectionPoint, the caller must use IConnectionPoint::GetConnectionInterface to determine the interface identifer of the outgoing interface that the connection point supports.

Notes to Callers

The caller is responsible for releasing the enumerator by calling (*ppEnum)->Release when it is no longer needed.

Notes to Implementers

Returning E_NOTIMPL is specifically disallowed because, with the exception of type information, there would be no other means through which a caller could find the IIDs of the outgoing interfaces. Since a connectable object typically has a fixed set of known outgoing interfaces, it is straightforward to implement the enumerator on top of a fixed length array of IIDs known at compile time.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IEnumConnectionPoints


IConnectionPointContainer::FindConnectionPoint

Returns a pointer to the IConnectionPoint interface of a connection point for a specified IID, if that IID describes a supported outgoing interface.

HRESULT FindConnectionPoint(
  REFIID riid,  //Requested connection point's interface identifier
  IConnectionPoint **ppCP
                //Address of output variable that receives the
                // IConnectionPoint interface pointer
);

Parameters

riid
[in] Interface identifier of the outgoing interface whose connection point object is being requested.
ppCP
[out] Address of IConnectionPoint* pointer variable that receives the interface pointer to the connection point that supports the riid interface. If an error occurs, the implementation sets *ppCP to NULL.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The ppCP pointer has a valid interface pointer.
E_POINTER
The address in ppCP is not valid. For example, it may be NULL.
CONNECT_E_NOCONNECTION
This connectable object does not support the outgoing interface specified by riid.

Remarks

This method is the QueryInterface equivalent for an object's outgoing interfaces, where the outgoing interface is specified with riid and where the interface pointer returned is always that of a connection point.

Notes to Callers

If the call is successful, the caller is responsible for releasing the connection point by calling (*ppCP)->Release when the connection point is no longer needed.

Notes to Implementers

E_NOTIMPL is not allowed as a return value for this method. Any implementation of IConnectionPointContainer must implement this method for the connectable object's outgoing interfaces.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint


IContinueCallback

The IContinueCallback interface is a generic callback mechanism for interruptible processes that should periodically ask an object whether to continue.

The FContinue method is a generic continuation request. FContinuePrinting carries extra information pertaining to a printing process and is used in the context of IPrint.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IContinueCallback Methods | Description
FContinue | Answers whether an operation should continue.
FContinuePrinting | Answers whether a printing operation should continue.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.


IContinueCallback::FContinue

Answers whether a given generic operation should continue.

HRESULT FContinue(void);

Return Values

S_OK
Continue the operation.
S_FALSE
Cancel the operation as soon as possible.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.


IContinueCallback::FContinuePrinting

Answers whether a given lengthy printing operation should continue.

HRESULT FContinuePrinting(
  LONG cPagesPrinted, // Total pages printed at time of call
  LONG nCurrentPage,  // Number of page currently being printed
  LPOLESTR pszPrintStatus
                      // Pointer to status message about print job
);

Parameters

cPagesPrinted
The total number of pages that have been printed at the time the object receives a call to IContinueCallback::FContinuePrinting.
nCurrentPage
The page number of the page being printed at the time the object receives a call to IContinueCallback::FContinuePrinting.
pszPrintStatus
Pointer to message about the current status of the print job. The object being printed may or may not display this message to the user. May be NULL.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
Continue the printing operation.
S_FALSE
Cancel the print job as soon as possible.

Remarks

Implementations of IPrint::Print call this method at periodic intervals during the printing process. The IPrint implementation should call back at least after printing each page, so that its client can, if necessary, display useful visual feedback to the user. IPrint::Print can call back multiple times with the same cPagesPrinted and nCurrentPage values, which is sometimes useful when a page being printed is complex and it is appropriate to give a user an opportunity to cancel in mid-page.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IPrint


IEnumXXXX

To allow you to enumerate the number of items of a given type that an object maintains, OLE provides a set of enumeration interfaces, one for each type of item.

To use these interfaces, the client asks an object that maintains a collection of items to create an enumerator object. The interface on the enumerator object is one of the enumeration interfaces, all of which have a name of the form IEnum Item_name. The only difference between enumeration interfaces is what they enumerate—there must be a separate enumeration interface for each type of item enumerated. All have the same set of methods and are used in the same way. For example, by repeatedly calling the Next method, the client gets successive pointers to each item in the collection.

The following table lists the set of enumeration interfaces that OLE defines, and the items enumerated.

Enumeration Interface Name | Item Enumerated
IEnumFORMATETC | An array of FORMATETC structures.
IEnumMoniker | The components of a moniker, or the monikers in a table.
IEnumOLEVERB | The different verbs available for an object, in order of ascending verb number.
IEnumSTATDATA | An array of STATDATA structures that contain advisory connection information for a data object.
IEnumSTATSTG | An array of STATSTG structures that contain statistical information about a storage, stream, or LockBytes object.
IEnumString | Strings
IEnumUnknown | Enumerates IUnknown interface pointers.
IEnumVARIANT | A collection of Variants. It allows clients to enumerate heterogeneous collections of objects and intrinsic types when the clients cannot or do not know the specific type(s) of elements in the collection.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IEnumXXXX Methods | Description
Next | Retrieves a specified number of items in the enumeration sequence.
Skip | Skips over a specified number of items in the enumeration sequence.
Reset | Resets the enumeration sequence to the beginning.
Clone | Creates another enumerator that contains the same enumeration state as the current one.


IEnumXXXX::Clone

Creates another enumerator that contains the same enumeration state as the current one. Using this function, a client can record a particular point in the enumeration sequence and then return to that point at a later time. The new enumerator supports the same interface as the original one.

HRESULT Clone(
  IEnum<ELT_T> ** ppenum  //Address of output variable that receives
                          // the IEnum<ELT_T> interface pointer
);

Parameter

ppenum
[out] Address of IEnum<ELT_T>* pointer variable that receives the interface pointer to the enumeration object. If the method is unsuccessful, the value of this output variable is undefined.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED.


IEnumXXXX::Next

Retrieves the next celt items in the enumeration sequence. If there are fewer than the requested number of elements left in the sequence, it retrieves the remaining elements. The number of elements actually retrieved is returned through pceltFetched (unless the caller passed in NULL for that parameter).

HRESULT Next(
  ULONG celt,           //Number of elements requested
  ELT_T[ ] rgelt,       //Array of the elements
  ULONG * pceltFetched  //Pointer to the number of elements actually
                        // supplied
);

Parameters

celt
[in] Number of elements being requested.
rgelt
[out] Array of size celt (or larger) of the elements of interest. The type of this parameter depends on the item being enumerated.
pceltFetched
[in, out] Pointer to the number of elements actually supplied in rgelt. Caller can pass in NULL if celt is one.

Return Value

S_OK if the number of elements supplied is celt; S_FALSE otherwise.


IEnumXXXX::Reset

Resets the enumeration sequence to the beginning.

HRESULT Reset(void);

Return Value

S_OK

Remarks

A call to this method, resetting the sequence, does not guarantee that the same set of objects will be enumerated after the reset, because it depends on the collection being enumerated. A static collection is reset to the beginning, but it can be too expensive for some collections, such as files in a directory, to guarantee this condition.


IEnumXXXX::Skip

Skips over the next specified number of elements in the enumeration sequence.

HRESULT Skip(
  ULONG celt  //Number of elements to be skipped
);

Parameter

celt
[in] Number of elements to be skipped.

Return Value

S_OK if the number of elements skipped is celt; otherwise, S_FALSE.


IEnumConnectionPoints

This interface enumerates connection points. Connectable objects support the following features:

  • Outgoing interfaces, such as event sets
  • The ability to enumerate the IIDs of the outgoing interfaces
  • The ability to connect and disconnect sinks to the object for those outgoing IIDs
  • The ability to enumerate the connections that exist to a particular outgoing interface

When to Implement

To support connectable objects, you need to provide four related interfaces:

  • IConnectionPointContainer
  • IEnumConnectionPoints
  • IConnectionPoint
  • IEnumConnections

The IConnectionPointContainer interface indicates the existence of the outgoing interfaces. It provides access to an enumerator sub-object with the IEnumConnectionPoints interface. It also provides a connection point sub-object with the IConnectionPoint interface. The IConnectionPoint interface provides access to an enumerator sub-object with the IEnumConnections interface.

The connection point is a separate sub-object to avoid circular reference counting problems.

A connectable object can be asked to enumerate its supported connection points through IConnectionPointContainer::EnumConnectionPoints. The resulting enumerator returned from this method implements the interface IEnumConnectionPoints, through which a client can access all the individual connection point sub-objects supported within the connectable object itself, where each connection point implements IConnectionPoint.

When enumerating connections through IEnumConnectionPoints, the enumerator is responsible for calling IUnknown::AddRef and the caller is responsible for calling IUnknown::Release when those pointers are no longer needed.

When to Use

Use the IEnumConnectionPoints interface to enumerate all the supported connection points for each outgoing IID.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG cConnections,  //[in]Number of IConnectionPoint values
                       // returned in rgpcn array
  IConnectionPoint **rgpcn,
                       //[out]Array to receive enumerated connection
                       // points
  ULONG *pcFetched     //[out]Pointer to the actual number of
                       // connection points returned in rgpcn array
);

HRESULT Skip(
  ULONG cConnections  //[in]Number of elements to skip
);

HRESULT Reset(void);

HRESULT Clone(
  IEnumConnectionPoints **ppEnum  //[out]Address of output variable
                                  // that receives the
                                  // IEnumConnectionPoints interface
                                  // pointer
);

Remarks

IEnumConnectionPoints::Next
Enumerates the next cConnections elements (IConnectionPoint pointers) in the enumerator's list, returning them in rgpcn along with the actual number of enumerated elements in pcFetched. The enumerator calls IConnectionPoint::AddRef for each returned pointer in rgpcn, and the caller is responsible for calling IConnectionPoint::Release through each pointer when those pointers are no longer needed.

E_NOTIMPL is not allowed as a return value. If an error value is returned, no entries in the rgpcn array are valid on exit and require no release.

IEnumConnectionPoints::Skip
Instructs the enumerator to skip the next cConnections elements in the enumeration so that the next call to IEnumConnectionPoints::Next will not return those elements.
IEnumConnectionPoints::Reset
Instructs the enumerator to position itself at the beginning of the list of elements.

There is no guarantee that the same set of elements will be enumerated on each pass through the list, nor will the elements necessarily be enumerated in the same order. The exact behavior depends on the collection being enumerated. It is too expensive for some collections, such as files in a directory, to maintain a specific state.

IEnumConnectionPoints::Clone
Creates another connection point enumerator with the same state as the current enumerator to iterate over the same list. This method makes it possible to record a point in the enumeration sequence in order to return to that point at a later time. The caller must release this new enumerator separately from the first enumerator.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IConnectionPointContainer, IEnumConnections, IEnumXxxx


IEnumConnections

This interface enumerates the current connections for a connectable object. Connectable objects support the following features:

  • Outgoing interfaces, such as event sets
  • The ability to enumerate the IIDs of the outgoing interfaces
  • The ability to connect and disconnect sinks to the object for those outgoing IIDs
  • The ability to enumerate the connections that exist to a particular outgoing interface

When to Implement

To support connectable objects, you need to provide four related interfaces:

  • IConnectionPointContainer
  • IEnumConnectionPoints
  • IConnectionPoint
  • IEnumConnections

The IConnectionPointContainer interface indicates the existence of the outgoing interfaces. It provides access to an enumerator sub-object with the IEnumConnectionPoints interface. It also provides a connection point sub-object with the IConnectionPoint interface. The IConnectionPoint interface provides access to an enumerator sub-object with the IEnumConnections interface.

To avoid circular reference counting problems, the connection point is a separate sub-object.

Any individual connection point can support enumeration of its currently known connections through IConnectionPoint::EnumConnections. The enumerator created by this method implements the interface IEnumConnections, which deals with the type CONNECTDATA. Each CONNECTDATA structure contains the IUnknown of a connected sink and the dwConnection that was returned by IConnectionPoint::Advise when that sink was connected. When enumerating connections through IEnumConnections, the enumerator is responsible for calling IUnknown::AddRef through the pointer in each enumerated structure, and the caller is responsible for calling IUnknown::Release when those pointers are no longer needed.

When to Use

Use the IEnumConnectionPoints interface to enumerate all the supported connection points for each outgoing IID.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG cConnections,  //[in]Number of CONNECTDATA structures
                       // returned in rgpcd
  CONNECTDATA **rgpcd, //[out]Array to receive enumerated
                       // CONNECTDATA structures
  ULONG *pcFetched     //[out]Pointer to actual number of
                       // CONNECTDATA structures
);

HRESULT Skip(
  ULONG cConnections  //[in]Number of elements to skip
);

HRESULT Reset(void);

HRESULT Clone(
  IEnumConnections **ppEnum  //[out]Address of output variable that
                             // receives the IEnumConnections interface
                             // pointer
);

Remarks

IEnumConnections::Next
Enumerates the next cConnections elements (that is, CONNECTDATA structures) in the enumerator's list, returning them in rgpcd along with the actual number of enumerated elements in pcFetched.

After this method returns successfully, the caller is responsible for calling CONNECTDATA.pUnk->Release for each element in the array. If cConnections is greater than one, the caller must also pass a non-NULL pointer to pcFetched to get the number of pointers it has to release.

E_NOTIMPL is not allowed as a return value. If an error value is returned, no entries in the rgpcd array are valid on exit and require no release.

IEnumConnections::Skip
Instructs the enumerator to skip the next cConnections elements in the enumeration so that the next call to IEnumConnections::Next will not return those elements.
IEnumConnections::Reset
Instructs the enumerator to position itself at the beginning of the list of elements. There is no guarantee that the same set of elements will be enumerated on each pass through the list. It depends on the collection being enumerated. It is too expensive for some collections, such as files in a directory, to maintain a specific state.
IEnumConnections::Clone
Creates another connection point enumerator with the same state as the current enumerator to iterate over the same list. This method makes it possible to record a point in the enumeration sequence in order to return to that point at a later time. The caller must release this new enumerator separately from the first enumerator.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CONNECTDATA, IConnectionPoint, IConnectionPointContainer, IEnumConnectionPoints, IEnumXxxx


IEnumString

IEnumString is defined to enumerate strings. LPWSTR is the type that indicates a pointer to a zero-terminated string of wide, or Unicode, characters. IEnumString has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

It is usually not necessary to implement this interface unless you have use for a custom string enumerator. A system implementation in the bind context object that exposes the IBindCtx interface also contains an implementation of IEnumString. IBindCtx::EnumObjectParam returns a pointer to this IEnumString interface on an enumerator that can return the keys of the bind context's string-keyed table of pointers.

When to Use

Call the methods of IEnumString to enumerate through a set of strings.

The prototypes of the member functions are as follows:

HRESULT Next(
  ULONG celt,
  LPOLESTR * rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumString ** ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IEnumUnknown

This enumerator enumerates objects with the IUnknown interface. It can be used to enumerate through the objects in a component containing multiple objects. IEnumUnknown has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

You can implement this whenever you want a caller to be able to enumerate the objects contained in another object. You get a pointer to IEnumUnknown through a call to IOleContainer::EnumObjects.

When to Implement

Call the methods of IEnumUnknown to enumerate the objects in a compound document, when you get a pointer to the interface on the enumerator through a call to IOleContainer::EnumObjects.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  IUnknown ** rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumUnknown ** ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleContainer


IEventProperty

The IEventProperty interface associates a named event property with its value.

When to Implement

You do not normally implement IEventProperty. This interface is implemented on a system-supplied event object collection.

When to Use

Call the methods of IEventProperty to get or set the name or value of a property contained in a collection. You use the methods of IEventObjectCollection to position to an element of the collection and then use the methods of IEventProperty to get or set the name or value of that element.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDispatch Methods | Description
GetTypeInfoCount | Retrieves the number of type descriptions.
GetTypeInfo | Retrieves a description of object's programmable interface.
GetIDsOfNames | Maps name of method or property to DISPID.
Invoke | Calls one of the object's methods, or gets/sets one of its properties.
IEventProperties | Description
get_Name | Obtains the name of an event property.
put_Name | Writes the name of an event property.
get_Value | Obtains the value of an event property.
put_Value | Writes the value of an event property.


IEventProperty Properties

The methods of the IEventProperty interface get and put the properties described in this topic.

In Microsoft® Visual Basic® and Java, programs get and set these properties directly. In Microsoft Visual C++®, programs call methods of the IEventProperty interface. Methods that get a property are of the following form:

HRESULT get_propertyName(datatype * pProperty);

Methods that put a property are of the following form:

HRESULT put_propertyName(datatype property);

Parameters

The single parameter of each put_* method is an input variable of the data type appropriate for the property. The single argument of each get_* method is an output variable that is a pointer of the appropriate data type to the address of the property.

For example, the Name property is a BSTR variable. The methods that get and put this property have the following prototypes:

get_Name(BSTR * Name);

put_PublisherID(BSTR Name);

Properties

The following table lists the properties that are managed by the IEventProperty interface and their data types and indicates for each property whether the interface supports getting (G), putting(P), or getting and putting (GP) the property.

Property | Get/Put | Data type | Description
Name | GP | BSTR | Name of an event property.
Value | GP | BSTR | Value of an event property.

Return Values

Each property method returns the standard values E_INVALIDARG, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The property was successfully obtained or written.


IEventPublisher

The IEventPublisher interface registers, modifies, removes, and provides information about an event publisher.

When to Implement

You do not need to implement the IEventPublisher interface. A system-supplied EventPublisher object (CLSID_CEventPublisher) implements IEventPublisher.

When to Use

Call the methods of IEventPublisher to perform the following actions:

  • To register event publishers in, or remove them from, the event store
  • To modify or obtain information about event publishers registered in the event store

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDispatch Methods | Description
GetTypeInfoCount | Retrieves the number of type descriptions.
GetTypeInfo | Retrieves a description of an object's programmable interface.
GetIDsOfNames | Maps name of method or property to DISPID.
Invoke | Calls one of the object's methods, or gets/sets one of its properties.
IEventPublisher Methods | Description
get_PublisherID | Obtains the GUID that identifies the event publisher.
put_PublisherID | Writes the GUID that identifies the event publisher.
get_PublisherName | Obtains a user-readable string that identifies the event publisher.
put_PublisherName | Writes a user-readable string that identifies the event publisher.
get_PublisherType | Obtains the publisher type associated with the event publisher.
put_PublisherType | Writes the publisher type associated with the event publisher.
get_OwnerSID | Obtains the security ID of the EventPublisher object's creator.
put_OwnerSID | Writes the security ID of the EventPublisher object's creator.
get_Description | Obtains text that describes the event publisher object.
put_Description | Writes text that describes the event publisher object.
GetDefaultProperty | Obtains the value of a named property from the property bag associated with the event publisher.
PutDefaultProperty | Writes a named property and its value to the property bag associated with the event publisher.
RemoveDefaultProperty | Removes a named property and its value from the property bag associated with the event publisher.
GetDefaultPropertyCollection | Enumerates the properties in the property bag associated with the event publisher.


IEventPublisher Properties

The methods of the IEventPublisher interface get and put the properties described in this topic.

In Microsoft® Visual Basic® and Java, programs get and set these properties directly. In Microsoft Visual C++®, programs call methods of the IEventPublisher interface. Methods that get a property are of the following form:

HRESULT get_propertyName(datatype * pProperty);

Methods that put a property are of the following form:

HRESULT put_propertyName(datatype property);

Parameters

The single parameter of each put_* method is an input variable of the data type appropriate for the property. The single argument of each get_* method is an output variable that is a pointer of the appropriate data type to the address of the property.

For example, the PublisherID property is a BSTR variable. The methods that get and put this property have the following prototypes:

get_PublisherID(BSTR * pbstrPublisherID);

put_PublisherID(BSTR bstrPublisherID);

Properties

The following table lists the properties that are managed by the IEventPublisher interface and their data types and indicates for each property whether the interface supports getting (G), putting(P), or getting and putting (GP) the property.

Property | Get/Put | Data type | Description
PublisherID | GP | BSTR | GUID identifying the event publisher.
PublisherName | GP | BSTR | User-readable name identifying the event publisher.
PublisherType | GP | BSTR | Optional administrative group of which the event publisher is a member.
OwnerSID | GP | BSTR | Security ID of the creator of the event publisher.
Description | GP | BSTR | Text for display that describes the event publisher.

Return Values

Each property method returns the standard values E_INVALIDARG, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The property was successfully obtained or written.


IEventPublisher::GetDefaultProperty

Obtains a named property and its value from the property bag associated with the event publisher.

HRESULT GetDefaultProperty(
  BSTR bstrPropertyName,       //Property name
  VARIANT *pvarPropertyValue   //Current property value
);

Parameters

bstrPropertyName
[in] Name of the property whose value is requested.
pvarPropertyValue
[out] Pointer to the current value of the requested property.

Return Values

This method returns the standard values E_INVALIDARG, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The property was successfully obtained.

Remarks

An EventPublisher object includes a property bag that can contain name and value pairs. Objects in the event system, including subscribers, can add, modify, and read these properties.


IEventPublisher::GetDefaultPropertyCollection

Creates a collection object that enumerates the properties contained in the property bag associated with the event publisher object.

HRESULT GetDefaultPropertyCollection(
  IEventObjectCollection **ppCollection  //Default property collection
);

Parameters

ppCollection
[out] Address of a pointer to the IEventObjectCollection interface on an event object collection. Cannot be NULL.

Return Values

This method returns the standard values E_INVALIDARG, E_POINTER, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The enumerator object was successfully created.

Remarks

An EventPublisher object includes a property bag that can contain name and value pairs. Objects in the event system, including subscribers, can add, modify, and read these properties.


IEventPublisher::PutDefaultProperty

Writes a named property and its value to the property bag associated with the event publisher.

HRESULT PutDefaultProperty(
  BSTR bstrPropertyName,        //Property name
  VARIANT *pvarPropertyValue      //New property value
);

Parameters

bstrPropertyName
[in] Name of the property whose value is to be set.
pvarPropertyValue
[in] New value of the property.

Return Values

This method returns the standard values E_INVALIDARG, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The property was successfully set.

Remarks

An EventPublisher object includes a property bag that can contain name and value pairs. Objects in the event system, including subscribers, can add, modify, and read these properties.


IEventPublisher::RemoveDefaultProperty

Removes a named property and its value from the property bag associated with the event publisher object.

HRESULT RemoveDefaultProperty(
  BSTR bstrPropertyName      //Property to be removed
);

Parameters

bstrPropertyName
[in] Name of the property to be removed from the property bag.

Return Values

This method returns the standard values E_INVALIDARG, E_OUTOFMEMORY, E_UNEXPECTED, and E_FAIL, as well as the following:

S_OK
The property and value were successfully removed.

Remarks

An EventPublisher object includes a property bag that can contain name and value pairs. Objects in the event system, including subscribers, can add, modify, and read these properties.


IExternalConnection

The IExternalConnection interface manages a server object's count of marshaled, or external, connections. A server that maintains such a count can detect when it has no external connections and shut itself down in an orderly fashion.

IExternalConnection is most commonly implemented on server objects, to enable the safe and orderly shutdown of a link to an embedded object following a silent update. Objects that do not implement IExternalConnection risk losing data in such a situation: When the final link client releases the embedded (server) object, the last external connection on the object's stub manager is released, causing the stub manager to release its pointers to interfaces on the embedded object and initiate shutdown of the object. At this point, the server object calls IOleClientSite::SaveObject on the link container, and the link container's return call to IPersistStorage::Save fails because the stub manager no longer has a pointer to the embedded object. Any unsaved changes to the server object's data would then be lost.

If the server object implements IExternalConnection, however, its stub manager will not release its connection to the object when the last external connection is released. Instead, it will stay connected until the object is ready to destroy itself.

In standard marshaling, to increment the object's count of external connections, COM calls IExternalConnection::AddConnection on the object when the object is first marshaled. The stub manager calls the methods of IExternalConnection on the object as subsequent external connections are obtained and released. When the object's count of external connections goes to zero, the object can save its data and then revoke itself from the running object table and do whatever else is necessary to reduce its object reference count to zero.

An object that implements IExternalConnection should explicitly call CoDisconnectObject on itself when its external reference count drops to 0. This call will cause the stub manager to call Release on the object so that the object can destroy itself.

When to Implement

Implement IExternalConnection on server objects that need to detect marshaled connections to themselves from clients that are either out-of-process or residing in another apartment in the same process.

Implement IExternalConnection also on objects that need to keep the stub manager in memory independent of the proxy count. For example, as explained earlier in this topic, objects that can be linked and embedded should implement IExternalConnection to keep a container application running during a silent update.

A class factory object can implement IExternalConnection to keep running even when no clients are requesting or using the objects it creates.

An in-place container should also implement IExternalConnection to keep itself running while contained objects save their data before shutting down.

When to Use

You do not normally call the methods of IExternalConnection from your own code. In nearly every case, COM makes these calls for you. The only time to use IExternalConnection from your own code is when you write a custom marshaler that implements its own stub manager functionality.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IExternalConnection Methods | Description
AddConnection | Increments count of external locks.
ReleaseConnection | Decrements count of external locks.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleObject, CoLockObjectExternal, CoDisconnectObject


IExternalConnection::AddConnection

Increments an object's count of its strong external connections (links).

HRESULT AddConnection(
  DWORD exconn,     //Type of external connection
  DWORD dwreserved  //Used by OLE to pass connection information
);

Parameters

exconn
[in] Type of external connection to the object. The only type of external connection currently supported by this interface is strong, which means that the object must remain alive as long as this external connection exists. Strong external connections are represented by the value EXTCONN_STRONG = 0x0001, which is defined in the enumeration EXTCONN.
dwreserved
[in] Passes information about the connection. This parameter is reserved for use by OLE. Its value can be zero, but not necessarily. Therefore, implementations of AddConnection should not contain blocks of code whose execution depends on whether a zero value is returned.

Return Value

DWORD value
The number of reference counts on the object; used for debugging purposes only.

Remarks

An object created by a EXE object server relies on its stub manager to call IExternalConnection::AddConnection whenever a link client activates and therefore creates an external lock on the object. When the link client breaks the connection, the stub manager calls IExternalConnection::ReleaseConnection to release the lock.

Since DLL object applications exist in the same process space as their objects, they do not use RPC (remote procedure calls) and therefore do not have stub managers to keep track of external connections. Therefore, DLL servers that support external links to their objects must implement IExternalConnection so that link clients can directly call the interface to inform them when connections are added or released.

The following is a typical implementation for the AddConnection method:

DWORD XX::AddConnection(DWORD extconn, DWORD dwReserved)
{
    return extconn&EXTCONN_STRONG ? ++m_cStrong : 0;
}

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IExternalConnection::ReleaseConnection, IRunnableObject::LockRunning, OleLockRunning


IExternalConnection::ReleaseConnection

Decrements an object's count of its strong external connections (references).

HRESULT ReleaseConnection(
  DWORD extconn,     //Type of external connection
  DWORD dwreserved,  //Used by OLE to pass connection information
  BOOL fLastReleaseCloses
                  //Indicates whether connection is last one or not
);

Parameters

extconn
[in] Type of external connection to the object. The only type of external connection currently supported by this interface is strong, which means that the object must remain alive as long as this external connection exists. Strong external connections are represented by the value EXTCONN_STRONG = 0x0001, which is defined in the enumeration EXTCONN.
dwreserved
[in] Passes information about the connection. This parameter is reserved for use by OLE. Its value can be zero, but not necessarily. Therefore, implementations of ReleaseConnection should not contain blocks of code whose execution depends on whether a zero value is returned.
fLastReleaseCloses
[in] TRUE specifies that if the connection being released is the last external lock on the object, the object should close. FALSE specifies that the object should remain open until closed by the user or another process.

Return Value

DWORD value
The number of connections to the object; used for debugging purposes only.

Remarks

If fLastReleaseCloses equals TRUE, calling ReleaseConnection causes the object to shut itself down. Calling this method is the only way in which a DLL object, running in the same process space as the container application, will know when to close following a silent update.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IExternalConnection::AddConnection


IForegroundTransfer

Transfers the foreground window to the process hosting the COM server.

When to Implement

You do not have to implement this interface. The transfer of the foreground window to the COM server process is automatically done when IOleObject::DoVerb and IAdviseSink::OnClose are called. However, during an OLE drag and drop operation, the window corresponding to a drop target is allowed to take focus.

When to Use

Yielding the foreground window is useful when the client and server interact visually. CoAllowSetForegroundWindow is useful when a client and server communicate using an interface other than IOleObject or IAdviseSink and want the server application to take focus.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IForegroundTransfer Methods | Description
AllowForegroundTransfer | Yields the foreground window to the COM server process.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.


IForegroundTransfer::AllowForegroundTransfer

Yields the foreground window to the COM server process.

HRESULT AllowForegroundTransfer(
   void *lpvReserved
);

Parameters

lpvReserved
[in] Reserved for future use; must be NULL.

Return Values

This method returns the following values:

S_OK
The method was successful.
E_INVALIDARG
The lpvReserved parameter is not NULL, or this interface is on a proxy that doesn't support foreground control.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

IForegroundTransfer, CoAllowSetForegroundWindow


IGlobalInterfaceTable

Allows any apartment in a process to get access to an interface implemented on an object in any other apartment in the process. The three methods allow you to do the following:

  • Register an interface as a global (processwide) interface.
  • Get a pointer to that interface from any other apartment through a cookie.
  • Revoke the global registration of an interface.

The IGlobalInterfaceTable interface is an efficient way for a process to store an interface pointer in a memory location that can be accessed from multiple apartments within the process, such as processwide variables and agile (free-threaded marshaled) objects containing interface pointers to other objects.

An agile object is unaware of the underlying COM infrastructure in which it runs—in other words, what apartment, context, and thread it is executing on. The object may be holding on to interfaces that are specific to an apartment or context. For this reason, calling these interfaces from wherever the agile component is executing may not always work properly. The global interface table avoids this problem by guaranteeing that a valid proxy (or direct pointer) to the object is used, based on where the agile object is executing.

The global interface table is not portable across process or machine boundaries, so it cannot be used in place of the normal parameter-passing mechanism.

Note IGlobalInterfaceTable is available on Microsoft® Windows NT® 4.0, Service Pack 3 and later. It is also available on Microsoft Windows® 95 with DCOM version 1.1 and later.

When to Implement

You should not implement this interface. The standard implementation provides complete thread-safe functionality.

When to Use

Use this interface when an interface implemented on an object in one apartment must be stored in memory accessible for use by other apartments. To create the global interface table object and get a pointer to this interface, make the following call:

CoCreateInstance(CLSID_StdGlobalInterfaceTable,
                            NULL,
                            CLSCTX_INPROC_SERVER,
                            IID_IGlobalInterfaceTable,
                            (void **)&gpGIT)

Note When creating the global interface table object using the above call, you must link to the library Uuid.lib. This will resolve the external symbols CLSID_StdGlobalInterfaceTable and IID_IGlobalInterfaceTable.

There is a single instance of the global interface table per process, so all calls to this function in a process return the same instance.

After calling the CoCreateInstance function, register the interface you want to make available processwide from the apartment in which it resides with a call to the RegisterInterfaceInGlobal method. This supplies a pointer to a "cookie" (through the pdwCookie parameter) that identifies the interface and its location. An apartment that wants a pointer to this interface then calls the GetInterfaceFromGlobal method with this cookie, and the implementation then supplies an interface pointer to the calling apartment. To revoke the interface's global registration, any apartment may call the RevokeInterfaceFromGlobal method.

A simple example of its use would be when you want to pass an interface pointer on an object in a single-threaded apartment (STA) to a worker thread in another apartment. Rather than having to marshal it into a stream and pass the stream to the worker thread as a thread parameter, this interface allows you simply to pass a cookie.

When you register the interface in the global interface table, you get a cookie that you can use instead of passing the actual pointer whenever you need to pass the pointer either to a nonmethod parameter that is going to another apartment (as a parameter to ThreadProc via CreateThread) or to in-process memory accessible outside your apartment.

Care is required because using globals places the extra burden on the programmer of managing problems such as race conditions and mutual exclusion, which are associated with accessing global state from multiple threads simultaneously.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IGlobalInterfaceTable Methods | Description
RegisterInterfaceInGlobal | Permits access to a given interface from any apartment in a process.
RevokeInterfaceFromGlobal | Revokes the registration of a global (processwide) interface.
GetInterfaceFromGlobal | Gives the calling apartment access to an interface registered as global.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM 1.2).
Header: Declared in objidl.h.


IGlobalInterfaceTable::GetInterfaceFromGlobal

Supplies a pointer to an interface on an object that is usable by the calling apartment. This interface must be currently registered in the global interface table.

HRESULT GetInterfaceFromGlobal(
  DWORD  dwCookie,  //Cookie identifying the desired global
                    //interface and its object
  REFIID  riid,     //IID of the registered global interface
  void **ppv        //Indirect pointer to the desired interface
);

Parameters

dwCookie
[in] DWORD cookie identifying the interface (and its object), retrieved through a call to IGlobalInterfaceTable::RegisterInterfaceInGlobal.
riid
[in] IID of the desired interface. This parameter must match the IID passed in IGlobalInterfaceTable::RegisterInterfaceInGlobal.
ppv
[out, iid_is(riid)] Pointer to the requested interface pointer.

Return Values

S_OK
The operation was successful.
E_INVALIDARG
One or more parameters are invalid.

Remarks

After an interface has been registered in the global interface table, an apartment can get a pointer to this interface by calling the GetInterfaceFromGlobal method with the supplied cookie. This pointer to the interface can be used in the calling apartment but not by other apartments in the process.

The application is responsible for coordinating access to the global variable during calls to IGlobalInterfaceTable::RevokeInterfaceFromGlobal. That is, the application should ensure that one thread does not call RevokeInterfaceFromGlobal while another thread is calling GetInterfaceFromGlobal with the same cookie. Multiple calls to GetInterfaceFromGlobal for the same cookie are permitted.

The GetInterfaceFromGlobal method calls IUnknown::AddRef on the pointer obtained in the ppv parameter. It is the caller's responsibility to call Release on this pointer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM 1.2).
Header: Declared in objidl.h.

See Also

IGlobalInterfaceTable::RegisterInterfaceInGlobal, IGlobalInterfaceTable::RevokeInterfaceFromGlobal


IGlobalInterfaceTable::RegisterInterfaceInGlobal

Registers a specified interface on an object residing in one apartment of a process as a global (processwide) interface, allowing other apartments access to that interface.

HRESULT RegisterInterfaceInGlobal(
  IUnknown *pUnk,  //Pointer to interface of type riid of object
                   //containing global interface
  REFIID  riid,    //IID of the interface to be registered
  DWORD *pdwCookie //Supplies a pointer to the cookie that provides
                   //a caller in another apartment access to the
                   //interface pointer
);

Parameters

pUnk
[in] Interface pointer of type riid on the object on which the interface to be registered as global is implemented.
riid
[in] IID of the interface to be registered as global.
pdwCookie
[out] Pointer to a DWORD cookie that can be used by another apartment to get access to a pointer to the interface being registered.

Return Values

S_OK
The operation was successful.
E_INVALIDARG
One or more parameters are invalid.

Remarks

Called in the apartment in which an object resides to register one of the object's interfaces as a global (processwide) interface. This method supplies a pointer to a cookie that other apartments can use in a call to the GetInterfaceFromGlobal method to get a pointer to that interface.

The interface pointer may be a pointer to an in-process object, or it may be a pointer to a proxy for an object residing in another apartment, in another process, or on another machine.

The apartment that calls this method must remain alive until the corresponding call to RevokeInterfaceFromGlobal.

Because the IGlobalInterfaceTable interface is not available on Microsoft® Windows® 95 (without DCOM version 1.1 or later) or versions of Microsoft Windows NT® earlier than version 4.0, Service Pack 3, apartments should call the CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream functions to marshal the pointers on failure of this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM 1.2).
Header: Declared in objidl.h.

See Also

IGlobalInterfaceTable::RevokeInterfaceFromGlobal, IGlobalInterfaceTable::GetInterfaceFromGlobal


IGlobalInterfaceTable::RevokeInterfaceFromGlobal

Revokes the registration of an interface in the global (processwide) interface table.

HRESULT RevokeInterfaceFromGlobal(
  DWORD  dwCookie,  //Cookie that was returned from
                    //RegisterInterfaceInGlobal
);

Parameters

dwCookie
[in] DWORD cookie identifying the interface whose global registration is to be revoked.

Return Values

S_OK
The operation was successful.
E_INVALIDARG
The parameter is invalid.

Remarks

Call this method when an interface registered in the global interface table object no longer needs to be accessed by other apartments in the same process. This method can be called by any apartment in the process, including apartments other than the one that registered the interface in the global interface table.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM 1.2).
Header: Declared in objidl.h.

See Also

IGlobalInterfaceTable::RegisterInterfaceInGlobal, IGlobalInterfaceTable::GetInterfaceFromGlobal


IInternalUnknown

The IInternalUnknown interface is used exclusively in lightweight client-side handlers that require access to some of the internal interfaces on the proxy. It gives clients pointers to internal interfaces on a given object through the QueryInternalInterface method. For more information, see Lightweight Client-side Handlers.

Handlers that want access to some of the internal interfaces on the Proxy Manager have to go through the IInternalUnknown interface. This prevents the handlers from blindly delegating and exposing the aggregatee's internal interfaces outside of the aggregate. These interfaces include IClientSecurity and IMultiQI. If the handler wants to expose IClientSecurity or IMultiQI, the handler should implement these interfaces itself and delegate to the Proxy Manager's implementation of these interfaces when appropriate.

For the IClientSecurity interface, if the client tries to set the security on an interface that the handler has exposed, the handler should set the security on the underlying network interface proxy.

For the IMultiQI interface, the handler should fill in the interfaces it knows about and then forward the call to the proxy manager to fill in the rest of the interfaces.

When to Implement

You do not need to implement this interface. The COM Proxy Manager implements IInternalUnknown when it is aggregated into a handler.

When to Use

Use the IInternalUnknown interface to gain access, through a handler, to internal interfaces on the Proxy Manager.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IInternalUnknown Method | Description
QueryInternalInterface | Returns pointers to internal interfaces.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

IUnknown, IClientSecurity, IMultiQI


IInternalUnknown::QueryInternalInterface

Returns a pointer to a specified internal interface. This function must call IUnknown::AddRef on the pointer it returns.

HRESULT QueryInternalInterface(
  REFIID riid,           //Identifier of the requested internal interface
  void **ppv            //Address of output variable that receives
                       //the interface pointer requested in riid
);

Parameters

riid
[in] Identifier of the internal interface being requested.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer to the object. If the object does not support the interface specified in riid, *ppv is set to NULL.

Return Values

S_OK if the interface is supported, E_NOINTERFACE if not.

Remarks

The IUnknown::QueryInternalInterface method is similar to the IUnknown::QueryInterface method, except that the COM Proxy Manager, when aggregated, will not expose some interfaces through IUnknown::QueryInterface. Instead, those internal interfaces must be exposed through IUnknown::QueryInternalInterface.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Unsupported.
Header: Declared in urlmon.h.

See Also

IInternalUnknown, IUnknown::QueryInterface


IMalloc

The IMalloc interface allocates, frees, and manages memory.

When to Implement

In general, you should not implement IMalloc, instead using the COM implementation, which is guaranteed to be thread-safe in managing task memory. You get a pointer to the COM task allocator object's IMalloc through a call to the CoGetMalloc function.

When to Use

Call the methods of IMalloc to allocate and manage memory. The COM libraries and object handlers also call the IMalloc methods to manage memory. Object handlers should call CoGetMalloc to get a pointer to the IMalloc implementation on the task allocator object and use the implementation of those methods to manage task memory.

The IMalloc methods Alloc, Free, and Realloc are similar to the C library functions malloc, free, and realloc. For debugging, refer to the functions CoRegisterMallocSpy and CoRevokeMallocSpy.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IMalloc Methods | Description
Alloc | Allocates a block of memory.
Realloc | Changes the size of a previously allocated block of memory.
Free | Frees a previously allocated block of memory.
GetSize | Returns the size in bytes of a previously allocated block of memory.
DidAlloc | Determines whether this instance of IMalloc was used to allocate the specified block of memory.
HeapMinimize | Minimizes the heap by releasing unused memory to the operating system.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoGetMalloc, IMallocSpy, CoRegisterMallocSpy, CoRevokeMallocSpy


IMalloc::Alloc

Allocates a block of memory.

void * Alloc(
  ULONG cb  //Size of the requested memory block in bytes
);

Parameter

cb
[in] Size, in bytes, of the memory block to be allocated.

Return Values

If successful, Alloc returns a pointer to the allocated memory block.

NULL
If insufficient memory is available, Alloc returns NULL.

Remarks

The IMalloc::Alloc method allocates a memory block in essentially the same way that the C Library malloc function does.

The initial contents of the returned memory block are undefined—there is no guarantee that the block has been initialized, so you should initialize it in your code. The allocated block may be larger than cb bytes because of the space required for alignment and for maintenance information.

If cb is zero, IMalloc::Alloc allocates a zero-length item and returns a valid pointer to that item. If there is insufficient memory available, IMalloc::Alloc returns NULL.

Note Applications should always check the return value from this method, even when requesting small amounts of memory, because there is no guarantee the memory will be allocated.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Free, IMalloc::Realloc, CoTaskMemAlloc


IMalloc::DidAlloc

Determines whether this allocator was used to allocate the specified block of memory.

int DidAlloc(
  void *pv  //Pointer to the memory block
);

Parameter

pv
[in] Pointer to the memory block; can be a NULL pointer, in which case, -1 is returned.

Return Values

1
The memory block was allocated by this IMalloc instance.
0
The memory block was not allocated by this IMalloc instance.
-1
DidAlloc is unable to determine whether or not it allocated the memory block.

Remarks

Calling IMalloc::DidAlloc is useful if a application is using multiple allocations and needs to know whether a previously allocated block of memory was allocated by a particular allocation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMalloc::HeapMinimize, IMalloc::Realloc


IMalloc::Free

Frees a previously allocated block of memory.

void Free(
  void * pv  //Pointer to the memory block to be freed
);

Parameter

pv
[in] Pointer to the memory block to be freed.

Remarks

IMalloc::Free frees a block of memory previously allocated through a call to IMalloc::Alloc or IMalloc::Realloc. The number of bytes freed equals the number of bytes that were allocated. After the call, the memory block pointed to by pv is invalid and can no longer be used.

Note The pv parameter can be NULL. If so, this method has no effect.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMalloc::Realloc, CoTaskMemFree


IMalloc::GetSize

Returns the size (in bytes) of a memory block previously allocated with IMalloc::Alloc or IMalloc::Realloc.

ULONG GetSize(
  void *pv  //Pointer to the memory block for which the size is
            //requested
);

Parameter

pv
[in] Pointer to the memory block for which the size is requested.

Return Value

The size of the allocated memory block in bytes or, if pv is a NULL pointer, -1.

Remarks

To get the size in bytes of a memory block, the block must have been previously allocated with IMalloc::Alloc or IMalloc::Realloc. The size returned is the actual size of the allocation, which may be greater than the size requested when the allocation was made.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMalloc::Realloc


IMalloc::HeapMinimize

Minimizes the heap as much as possible by releasing unused memory to the operating system, coalescing adjacent free blocks and committing free pages.

void HeapMinimize();

Remarks

Calling IMalloc::HeapMinimize is useful when an application has been running for some time and the heap may be fragmented.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMalloc::Free, IMalloc::Realloc


IMalloc::Realloc

Changes the size of a previously allocated memory block.

void *Realloc(
  void *pv, //Pointer to memory block to be reallocated
  ULONG cb  //Size of the memory block in bytes
);

Parameters

pv
[in] Pointer to the memory block to be reallocated. The pointer can have a NULL value, as discussed in the Remarks section below.
cb
[in] Size of the memory block (in bytes) to be reallocated. It can be zero, as discussed in the Remarks section below.

Return Values

Reallocated memory block
Memory block successfully reallocated.
NULL
Insufficient memory, or cb is zero and pv is not NULL.

Remarks

IMalloc::Realloc reallocates a block of memory but does guarantee that the contents of the returned memory block are initialized. Therefore, the caller is responsible for intializing it in code, subsequent to the reallocation. The allocated block may be larger than cb bytes because of the space required for alignment and for maintenance information.

The pv argument points to the beginning of the memory block. If pv is NULL, IMalloc::Realloc allocates a new memory block in the same way that IMalloc::Alloc does. If pv is not NULL, it should be a pointer returned by a prior call to IMalloc::Alloc.

The cb argument specifies the size (in bytes) of the new block. The contents of the block are unchanged up to the shorter of the new and old sizes, although the new block can be in a different location. Because the new block can be in a different memory location, the pointer returned by IMalloc::Realloc is not guaranteed to be the pointer passed through the pv argument. If pv is not NULL and cb is zero, the memory pointed to by pv is freed.

IMalloc::Realloc returns a void pointer to the reallocated (and possibly moved) memory block. The return value is NULL if the size is zero and the buffer argument is not NULL, or if there is not enough memory available to expand the block to the given size. In the first case, the original block is freed; in the second, the original block is unchanged.

The storage space pointed to by the return value is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMalloc::Free


IMallocSpy

The IMallocSpy interface is a debugging interface that allows application developers to monitor (spy on) memory allocation, detect memory leaks, and simulate memory failure in calls to IMalloc methods.

Caution The IMallocSpy interface is intended only for use in debugging application code under development. Do not ship this interface to retail customers of your application because it causes severe performance degradation and could conflict with user-installed software to produce unpredictable results.

When to Implement

Implement this interface to debug memory allocation during application development.

When to Use

When an implementation of IMallocSpy is registered with CoRegisterMallocSpy, COM calls the pair of IMallocSpy methods around the corresponding IMalloc method. You would not make direct calls to IMallocSpy methods. The COM SDK contains a sample implementation of IMallocSpy. The call to the pre-method through the return from the corresponding post-method is guaranteed to be thread-safe in multithreaded operations.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IMallocSpy Methods | Description
PreAlloc | Called before invoking IMalloc::Alloc, and may extend or modify the allocation to store debug information.
PostAlloc | Called after invoking IMalloc::Alloc.
PreFree | Called before invoking IMalloc::Free.
PostFree | Called after invoking IMalloc::Free.
PreRealloc | Called before invoking IMalloc::Realloc.
PostRealloc | Called after invoking IMalloc::Realloc.
PreGetSize | Called before invoking IMalloc::GetSize.
PostGetSize | Called after invoking IMalloc::GetSize.
PreDidAlloc | Called before invoking IMalloc::DidAlloc.
PostDidAlloc | Called after invoking IMalloc::DidAlloc.
PreHeapMinimize | Called before invoking IMalloc::HeapMinimize.
PostHeapMinimize | Called after invoking IMalloc::HeapMinimize.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc, CoGetMalloc, CoRegisterMallocSpy


IMallocSpy::PreAlloc

Called just prior to invoking IMalloc::Alloc.

ULONG PreAlloc(
  ULONG cbRequest  //Byte count passed to IMalloc::Alloc
);

Parameter

cbRequest
[in] Number of bytes specified in the allocation request the caller is passing to IMalloc::Alloc.

Return Value

The byte count actually passed to IMalloc::Alloc, which should be greater than or equal to the value of cbRequest.

Remarks

The IMallocSpy::PreAlloc implementation may extend and/or modify the allocation to store debug-specific information with the allocation.

PreAlloc can force memory allocation failure by returning 0, allowing testing to ensure that the application handles allocation failure gracefully in all cases. In this case, IMallocSpy::PostAlloc is not called and Alloc returns NULL. Forcing allocation failure is effective only if cbRequest is not equal to 0. If PreAlloc is forcing failure by returning NULL, PostAlloc is not called. However, if IMalloc::Alloc encounters a real memory failure and returns NULL, PostAlloc is called.

The call to PreAlloc through the return from PostAlloc is guaranteed to be thread-safe.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMallocSpy::PostAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostAlloc

Called just after invoking IMalloc::Alloc, taking as input a pointer to the IMalloc::Alloc caller's allocation and returning a pointer to the actual allocation.

void * PostAlloc(
  void * pActual  //Pointer to the allocation actually done by
                  //IMalloc::Alloc
);

Parameter

pActual
[in] Pointer to the allocation done by IMalloc::Alloc.

Return Value

A pointer to the beginning of the memory block actually allocated. This pointer is also returned to the caller of IMalloc::Alloc. If debug information is written at the front of the caller's allocation, this should be a forward offset from pActual. The value is the same as pActual if debug information is appended or if no debug information is attached.

Remarks

When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy, COM calls IMallocSpy::PostAlloc after any call to IMalloc::Alloc. It takes as input a pointer to the allocation done by the call to IMalloc::Alloc, and returns a pointer to the beginning of the total allocation, which could include a forward offset from the other value if IMallocSpy::PreAlloc was implemented to attach debug information to the allocation in this way. If not, the same pointer is returned and also becomes the return value to the caller of IMalloc::Alloc.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Alloc, IMallocSpy::PreAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PreDidAlloc

Called by COM just prior to invoking IMalloc::DidAlloc.

void * PreDidAlloc(
  void * pRequest,
               //Pointer the caller is passing to IMalloc::DidAlloc
  BOOL fSpyed  //Whether pRequest was allocated while this spy was
               //active
);

Parameters

pRequest
[in] Pointer the caller is passing to IMalloc::DidAlloc.
fSpyed
[in] TRUE if the allocation was done while this spy was active.

Return Value

The pointer for which allocation status is determined. This pointer is passed to IMallocSpy::PostDidAlloc as the fActual parameter.

Remarks

When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy, COM calls this method immediately before any call to IMalloc::DidAlloc. This method is included for completeness and consistency—it is not anticipated that developers will implement significant functionality in this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::DidAlloc, IMallocSpy::PostDidAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostDidAlloc

Called just after invoking IMalloc::DidAlloc.

int PostDidAlloc(
  void * pRequest,  //Original pointer passed to IMalloc::DidAlloc
  BOOL fSpyed,      //Whether the allocation was done while this spy
                    //was active
  int fActual       //Whether pRequest was actual value used in
                    //IMalloc call
);

Parameters

pRequest
[in] Pointer specified in the original call to IMalloc::DidAlloc.
fSpyed
[in] TRUE if the allocation was done while this spy was active.
fActual
[in] Actual value returned by IMalloc::DidAlloc.

Return Value

The value returned to the caller of IMalloc::DidAlloc.

Remarks

When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy, COM calls this method immediately after any call to IMalloc::DidAlloc. This method is included for completeness and consistency—it is not anticipated that developers will implement significant functionality in this method.

For convenience, pRequest, the original pointer passed in the call to IMalloc::DidAlloc, is passed to IMallocSpy::PostDidAlloc. In addition, the parameter fActual is a Boolean that indicates whether this value was actually passed to IMalloc::DidAlloc. If not, it would indicate that IMallocSpy::PreDidAlloc was implemented to alter this pointer for some debugging purpose.

The fSpyed parameter is a Boolean that indicates whether the allocation was done while the current spy object was active.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::DidAlloc, IMallocSpy::PreDidAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PreFree

Called just before invoking IMalloc::Free to ensure that the pointer passed to IMalloc::Free points to the beginning of the actual allocation.

void * PreFree(
  void * pRequest,  //Pointer is passing to IMalloc::Free
  BOOL fSpyed       //TRUE if this memory was allocated while the
                    //spy was active
);

Parameters

pRequest
[in] Pointer to the block of memory that the caller is passing to IMalloc::Free.
fSpyed
[in] TRUE if the pRequest parameter of IMallocSpy::PreFree was allocated while the spy was installed. This value is also passed to IMallocSpy::PostFree.

Return Value

The actual pointer to pass to IMalloc::Free.

Remarks

If IMallocSpy::PreAlloc modified the original allocation request passed to IMalloc::Alloc (or IMalloc::Realloc), IMallocSpy::PreFree must supply a pointer to the actual allocation, which COM will pass to IMalloc::Free. For example, if the PreAlloc/PostAlloc pair attached a header used to store debug information to the beginning of the caller's allocation, PreFree must return a pointer to the beginning of this header so that all of the block that was allocated can be freed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Free, IMallocSpy::PostFree, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostFree

Called just after invoking IMalloc::Free.

void PostFree(
  BOOL fSpyed  //Whether the memory block to be freed was allocated
               //while the spy was active
);

Parameter

fSpyed
[in] TRUE if the memory block to be freed was allocated while the current spy was active, otherwise FALSE.

Remarks

When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy, COM calls this method immediately after any call to IMalloc::Free. This method is included for completeness and consistency—it is not anticipated that developers will implement significant functionality in this method. On return, the fSpyed parameter simply indicates whether the memory was freed while the current spy was active.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Free, IMallocSpy::PreFree, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PreGetSize

Called by COM just prior to any call to IMalloc::GetSize.

void * PreGetSize(
  void * pRequest,
               //Pointer the caller is passing to IMalloc::GetSize
  BOOL fSpyed  //TRUE if allocation was done while this spy was
               //active
);

Parameters

pRequest
[in] Pointer the caller is passing to IMalloc::GetSize.
fSpyed
[in] TRUE if the allocation was done while the spy was active.

Return Value

Pointer to the actual allocation for which the size is to be determined.

Remarks

The PreGetSize method receives as its pRequest parameter the pointer the caller is passing to IMalloc::GetSize. It must then return a pointer to the actual allocation, which may have altered pRequest in the implementation of either the PreAlloc or the PreRealloc method of IMallocSpy. The pointer to the true allocation is then passed to IMalloc::GetSize as its pv parameter.

IMalloc::GetSize then returns the size determined, and COM passes this value to IMallocSpy::PostGetSize in cbActual.

Note The size determined by IMalloc::GetSize is the value returned by the Win32® function HeapSize. On Microsoft® Windows NT® and Microsoft Windows® 2000, this is the size originally requested. On Microsoft Windows 95, memory allocations are done on 8-byte boundaries. For example, a memory allocation request of 27 bytes on Windows NT or Windows 2000 would return an allocation of 32 bytes and GetSize would return 27. On Windows 95, the same request would return an allocation of 28 bytes and GetSize would return 28. Therefore, implementers of IMallocSpy::PostGetSize cannot assume that if cbActual is sizeof(debug_header), the value is the actual size of the user's allocation.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::GetSize, IMallocSpy::PostGetSize, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostGetSize

Called just after invoking IMalloc::GetSize.

ULONG PostGetSize(
  ULONG cbActual,  //Actual size of the allocation
  BOOL fSpyed      //Whether the allocation was done while a spy was
                   //active
);

Parameters

cbActual
[in] Actual number of bytes in the allocation, as returned by IMalloc::GetSize.
fSpyed
[in] TRUE if the allocation was done while a spy was active.

Return Values

The same value returned by IMalloc::GetSize, which is the size of the allocated memory block in bytes.

Remarks

The size determined by IMalloc::GetSize is the value returned by the Win32® function HeapSize. On Microsoft® Windows NT® and Microsoft Windows® 2000, this is the size originally requested. On Microsoft Windows 95, memory allocations are done on 8-byte boundaries. For example, a memory allocation request of 27 bytes on Windows NT or Windows 2000 would return an allocation of 32 bytes and GetSize would return 27. On Windows 95, the same request would return an allocation of 28 bytes and GetSize would return 28. Therefore, implementers of IMallocSpy::PostGetSize cannot assume that if cbActual is sizeof(debug_header), the value is the actual size of the user's allocation.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::GetSize, IMallocSpy::PreGetSize, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PreHeapMinimize

Called just prior to invoking IMalloc::HeapMinimize.

void PreHeapMinimize(void);

Remarks

This method is included for completeness; it is not anticipated that developers will implement significant functionality in this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::HeapMinimize, IMallocSpy::PostHeapMinimize, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostHeapMinimize

Called just after invoking IMalloc::HeapMinimize.

void PostHeapMinimize(void);

Remarks

When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy, COM calls this method immediately after any call to IMalloc::Free. This method is included for completeness and consistency—it is not anticipated that developers will implement significant functionality in this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::HeapMinimize, IMallocSpy::PreHeapMinimize, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PreRealloc

Called just before invoking IMalloc::Alloc.

ULONG PreRealloc(
  void * pRequest,  //Pointer the caller is passing to
                    //IMalloc::Realloc
  ULONG cbRequest,  //Byte count the caller is passing to
                    //IMalloc::Realloc
  void ** ppNewRequest,
                    //Address of output variable that receives a
                    //pointer to the requested memory block to be
                    //reallocated
  BOOL fSpyed       //Whether the original allocation was "spyed"
);

Parameters

pRequest
[in] Pointer specified in the original call to IMalloc::Realloc, indicating the memory block to be reallocated.
cbRequest
[in] Memory block's byte count as specified in the original call to IMalloc::Realloc.
ppNewRequest
[out] Address of pointer variable that receives a pointer to the actual memory block to be reallocated. This may be different from the pointer in pRequest if the implementation of IMallocSpy::PreRealloc extends or modifies the reallocation. This is an out pointer and should always be stored by PreRealloc.
fSpyed
[in] TRUE if the original allocation was done while the spy was active.

Return Value

The actual byte count to be passed to IMalloc::Realloc.

Remarks

The IMallocSpy::PreRealloc implementation may extend and/or modify the allocation to store debug-specific information with the allocation. Thus, the ppNewRequest parameter may differ from pRequest, a pointer to the request specified in the original call to IMalloc::Realloc.

PreRealloc can force memory allocation failure by returning 0, allowing testing to ensure that the application handles allocation failure gracefully in all cases. In this case, PostRealloc is not called and Realloc returns NULL. However, if IMalloc::Realloc encounters a real memory failure and returns NULL, PostRealloc is called. Forcing allocation failure is effective only if cbRequest is not equal to 0.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Realloc, IMallocSpy::PostRealloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMallocSpy::PostRealloc

Called after invoking IMalloc::Realloc.

void * PostRealloc(
  void * pActual,  //Pointer returned by IMalloc::Realloc
  BOOL fSpyed      //Whether the original allocation was "spyed"
);

Parameters

pActual
[in] Pointer to the memory block reallocated by IMalloc::Realloc.
fSpyed
[in] If TRUE, the original memory allocation was done while the spy was active.

Return Values

A pointer to the beginning of the memory block actually allocated. This pointer is also returned to the caller of IMalloc::Realloc. If debug information is written at the front of the caller's allocation, it should be a forward offset from pActual. The value should be the same as pActual if debug information is appended or if no debug information is attached.

Remarks

If memory is successfully reallocated while the spy is active, fSpyed will be TRUE in subsequent calls to IMallocSpy methods that track the reallocated memory, even if fSpyed was previously FALSE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMalloc::Realloc, IMallocSpy::PreRealloc, CoRegisterMallocSpy, CoRevokeMallocSpy


IMarshal

The IMarshal interface enables a COM object to define and manage the marshaling of its interface pointers. The alternative is to use the COM default implementation, the preferred choice in all but a few special cases. (See "When to Implement.")

Marshaling is the process of packaging data into packets for transmission to a different process or machine. Unmarshaling is the process of recovering that data at the receiving end. In any given call, method arguments are marshaled and unmarshaled in one direction, while return values are marshaled and unmarshaled in the other.

Although marshaling applies to all data types, interface pointers require special handling. The fundamental problem is how client code running in one address space can correctly dereference a pointer to an interface on an object residing in a different address space. The COM solution is for a client application to communicate with the original object through a surrogate object, or proxy, which lives in the client's process. The proxy holds a reference to an interface on the original object and hands the client a pointer to an interface on itself. When the client calls an interface method on the original object, its call is actually going to the proxy. Therefore, from the client's point of view, all calls are in-process.

On receiving a call, the proxy marshals the method arguments and through some means of interprocess communication, such as RPC, passes them along to code in the server process, which unmarshals the arguments and passes them to the original object. This same code marshals return values for transmission back to the proxy, which unmarshals the values and passes them to the client application.

IMarshal provides methods for creating, initializing, and managing a proxy in a client process; it does not dictate how the proxy should communicate with the original object. The COM default implementation of IMarshal uses RPC. When you implement this interface yourself, you are free to choose any method of interprocess communication you deem to be appropriate for your application—shared memory, named pipe, window handle, RPC—in short, whatever works.

When to Implement

Implement IMarshal only when you believe that you can realize significant optimizations to the COM default implementation. In practice, this will rarely be the case. However, there are occasions, such as the following, where implementing IMarshal may be preferred:

  • The objects you are writing keep their state in shared memory. In this case, both the original process and the client process use proxies that refer to the shared memory. This type of custom marshaling is possible only if the client process is on the same machine as the original process. COM-provided implementations of IStorage and IStream are examples of this type of custom marshaling.
  • The objects you are writing are immutable—that is, their state does not change after creation. Instead of forwarding method calls to the original objects, you simply create copies of those objects in the client process. This technique avoids the cost of switching from one process to another. Some monikers are examples of immutable objects; if you are implementing your own moniker class, you should evaluate the costs and benefits of implementing IMarshal on your moniker objects.
  • Objects that themselves are proxy objects can use custom marshaling to avoid creating proxies to proxies. Instead, the existing proxy can refer new proxies back to the original object. This capability is important for the sake of both efficiency and robustness.
  • Your server application wants to manage how calls are made across the network without affecting the interface exposed to clients. For example, if an end user were making changes to a database record, the server might want to cache the changes until the user has committed them all, at which time the entire transaction would be forwarded in a single packet. Using a custom proxy would enable the caching and batching of changes in this way.

When you choose to implement IMarshal, you must do so for both your original object and the proxy you create for it. When implementing the interface on either object or proxy, you simply return E_NOTIMPL for the methods that are not implemented.

COM uses your implementation of IMarshal in the following manner: When it's necessary to create a remote interface pointer to your object (that is, when a pointer to your object is passed as an argument in a remote function call), COM queries your object for the IMarshal interface. If your object implements it, COM uses your IMarshal implementation to create the proxy object. If your object does not implement IMarshal, COM uses its default implementation.

How you choose to structure the proxy is entirely up to you. You can write the proxy to use whatever mechanisms you deem appropriate for communicating with the original object. You can also create the proxy as either a stand-alone object or as part of a larger aggregation such as a handler. However you choose to structure the proxy, it must implement IMarshal to work at all. You must also generate a CLSID for the proxy to be returned by your implementation of GetUnmarshalClass on the original object.

When to Use

COM calls this interface as part of system-provided marshaling support. The COM calls are wrapped in calls to CoMarshalInterface and CoUnmarshalInterface. Your code typically will not need to call this interface. Special circumstances where you might choose to do so are discussed in the "Notes to Callers" section for each method.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IMarshal Methods | Description
GetUnmarshalClass | Returns CLSID of unmarshaling code.
GetMarshalSizeMax | Returns size of buffer needed during marshaling.
MarshalInterface | Marshals an interface pointer.
UnmarshalInterface | Unmarshals an interface pointer.
ReleaseMarshalData | Destroys a marshaled data packet.
DisconnectObject | Releases all connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IStdMarshalInfo


IMarshal::DisconnectObject

Forcibly releases all external connections to an object. The object's server calls the object's implementation of this method prior to shutting down.

HRESULT DisconnectObject(
  DWORD dwReserved  //Reserved for future use
);

Parameter

dwReserved
[in] Reserved for future use; must be zero. To ensure compatibility with future use, DisconnectObject must not check for zero.

Return Values

The method supports the standard return value E_FAIL, as well as the following:

S_OK
The object was disconnected successfully.

Remarks

This method is implemented on the object, not the proxy.

Notes to Callers

The usual case in which this method is called occurs when an end user forcibly closes a COM server that has one or more running objects that implement IMarshal. Prior to shutting down, the server calls the CoDisconnectObject helper function to release external connections to all its running objects. For each object that implements IMarshal, however, this function calls IMarshal::DisconnectObject so that each object that manages its own marshaling can take steps to notify its proxy that it is about to shut down.

Notes to Implementers

As part of its normal shutdown code, a server should call the CoDisconnectObject function, which in turn calls IMarshal::DisconnectObject, on each of its running objects that implements IMarshal.

The outcome of any implementation of this method should be to enable a proxy to respond to all subsequent calls from its client by returning RPC_E_DISCONNECTED or CO_E_OBJECTNOTCONNECTED rather than attempting to forward the calls on to the original object. It is up to the client to destroy the proxy.

If you are implementing this method for an immutable object, such as a moniker, your implementation doesn't need to do anything because such objects are typically copied whole into the client's address space. Therefore, they have neither a proxy nor a connection to the original object. For more information on marshaling immutable objects, see the "When to Implement" section for the IMarshal interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoDisconnectObject


IMarshal::GetMarshalSizeMax

Returns an upper bound on the number of bytes needed to marshal the specified interface pointer on the specified object.

HRESULT GetMarshalSizeMax(
  REFIID riid,          //Reference to the identifier of the
                        //interface to be marshaled
  void *pv,             //Interface pointer to be marshaled
  DWORD dwDestContext,  //Destination process
  void * pvDestContext, //Reserved for future use
  DWORD mshlflags,      //Reason for marshaling
  ULONG * pSize         //Pointer to upper-bound value
);

Parameters

riid
[in] Reference to the identifier of the interface to be marshaled.
pv
[in] Interface pointer to be marshaled; can be NULL.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling can occur either in another apartment of the current process (MSHCTX_INPROC) or in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pSize
[out] Pointer to the upper bound on the amount of data to be written to the marshaling stream.

Return Values

The method supports the standard return value E_FAIL, as well as the following:

S_OK
The maximum size was returned successfully.
E_NOINTERFACE
The specified interface was not supported.

Remarks

This method is called indirectly, in a call to CoGetMarshalSizeMax, by whatever code in the server process is responsible for marshaling a pointer to an interface on an object. This marshaling code is usually a stub generated by COM for one of several interfaces that can marshal a pointer to an interface implemented on an entirely different object. Examples include the IClassFactory and IOleItemContainer interfaces. For purposes of discussion, the code responsible for marshaling a pointer is here called the "marshaling stub."

To create a proxy for an object, COM requires two pieces of information from the original object: the amount of data to be written to the marshaling stream and the proxy's CLSID.

The marshaling stub obtains these two pieces of information with successive calls to CoGetMarshalSizeMax and CoMarshalInterface.

Note to Callers

The marshaling stub, through a call to CoGetMarshalSizeMax, calls the object's implementation of this method to preallocate the stream buffer that will be passed to IMarshal::MarshalInterface.

You do not explicitly call this method if you are implementing existing COM interfaces or using the Microsoft Interface Definition Language (MIDL) to define your own custom interfaces. In either case, the MIDL-generated stub automatically makes the call.

If you are not using MIDL to define your own interface (see Defining COM Interfaces), your marshaling stub does not have to call GetMarshalSizeMax, although doing so is highly recommended. An object knows better than an interface stub what the maximum size of a marshaling data packet is likely to be. Therefore, unless you are providing an automatically growing stream that is so efficient that the overhead of expanding it is insignificant, you should call this method even when implementing your own interfaces.

The value returned by this method is guaranteed to be valid only as long as the internal state of the object being marshaled does not change. Therefore, the actual marshaling should be done immediately after this function returns, or the stub runs the risk that the object, because of some change in state, might require more memory to marshal than it originally indicated.

Notes to Implementers

Your implementation of IMarshal::MarshalInterface will use the preallocated buffer to write marshaling data into the stream. If the buffer is too small, the marshaling operation will fail. Therefore, the value returned by this method must be a conservative estimate of the amount of data that will be needed to marshal the interface. Violation of this requirement should be treated as a catastrophic error.

In a subsequent call to MarshalInterface, your IMarshal implementation cannot rely on the caller actually having called GetMarshalSizeMax beforehand. It must still be wary of STG_E_MEDIUMFULL errors returned by the stream and be prepared to handle them gracefully.

To ensure that your implementation of GetMarshalSizeMax will continue to work properly as new destination contexts are supported in the future, delegate marshaling to the COM default implementation for all dwDestContext values that your implementation does not understand. To delegate marshaling to the COM default implementation, call the CoGetStandardMarshal function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoGetMarshalSizeMax, IMarshal::MarshalInterface


IMarshal::GetUnmarshalClass

Returns the CLSID that COM uses to locate the DLL containing the code for the corresponding proxy. COM loads this DLL to create an uninitialized instance of the proxy.

HRESULT GetUnmarshalClass(
  REFIID riid,          //Reference to the identifier of the
                        //interface to be marshaled
  void * pv,            //Interface pointer being marshaled
  DWORD dwDestContext,  //Destination process
  void * pvDestContext, //Reserved for future use
  DWORD mshlflags,      //Reason for marshaling
  CLSID * pCid          //Pointer to CLSID of proxy
);

Parameters

riid
[in] Reference to the identifier of the interface to be marshaled.
pv
[in] Pointer to the interface to be marshaled; can be NULL if the caller does not have a pointer to the desired interface.
dwDestContext
[in] "Destination context" where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling can occur either in another apartment of the current process (MSHCTX_INPROC) or in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
pCid
[out] Pointer to the CLSID to be used to create a proxy in the client process.

Return Value

Returns S_OK if successful; otherwise, S_FALSE.

Remarks

This method is called by whatever code in the server process may be responsible for marshaling a pointer to an interface on an object. This marshaling code is usually a stub generated by COM for one of several interfaces that can marshal a pointer to an interface implemented on an entirely different object. Examples include the IClassFactory and IOleItemContainer interfaces. For purposes of this discussion, the code responsible for marshaling a pointer is called the marshaling stub.

To create a proxy for an object, COM requires two pieces of information from the original object: the amount of data to be written to the marshaling stream and the proxy's CLSID.

The marshaling stub obtains these two pieces of information with successive calls to CoGetMarshalSizeMax and CoMarshalInterface.

Note to Callers

The marshaling stub calls the object's implementation of this method to obtain the CLSID to be used in creating an instance of the proxy. The client, upon receiving the CLSID, loads the DLL listed for it in the system registry.

You do not explicitly call this method if you are implementing existing COM interfaces or using the Microsoft Interface Definition Language (MIDL) to define your own interfaces. In either case, the stub automatically makes the call. See Defining COM Interfaces.

If you are not using MIDL to define your own interface, your stub must call this method, either directly or indirectly, to get the CLSID that the client-side COM Library needs to create a proxy for the object implementing the interface.

If the caller has a pointer to the interface to be marshaled, it should, as a matter of efficiency, use the pv parameter to pass that pointer. In this way, an implementation that may use such a pointer to determine the appropriate CLSID for the proxy does not have to call IUnknown::QueryInterface on itself. If a caller does not have a pointer to the interface to be marshaled, it can pass NULL.

Notes to Implementers

COM calls GetUnmarshalClass to obtain the CLSID to be used for creating a proxy in the client process. The CLSID to be used for a proxy is normally not that of the original object (see "Notes to Implementers" for the exception) but one you will have generated (using the guidgen.exe tool supplied with the Platform SDK) specifically for your proxy object.

Implement this method for each object that provides marshaling for one or more of its interfaces. The code responsible for marshaling the object writes the CLSID, along with the marshaling data, to a stream; COM extracts the CLSID and data from the stream on the receiving side.

If your proxy implementation consists simply of copying the entire original object into the client process, thereby eliminating the need to forward calls to the original object, the CLSID returned would be the same as that of the original object. This strategy, of course, is advisable only for objects that are not expected to change.

If the pv parameter is NULL and your implementation needs an interface pointer, it can call IUnknown::QueryInterface on the current object to get it. The pv parameter exists merely to improve efficiency.

To ensure that your implementation of GetUnmarshalClass continues to work properly as new destination contexts are supported in the future, delegate marshaling to the COM default implementation for all dwDestContext values that your implementation does not handle. To delegate marshaling to the COM default implementation, call the CoGetStandardMarshal function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IMarshal::MarshalInterface

Writes into a stream the data required to initialize a proxy object in some client process.

HRESULT MarshalInterface(
  IStream *pStm,        //Pointer to stream used for marshaling
  REFIID riid,          //Reference to the identifier of the
                        //interface to be marshaled
  void *pv,             //Interface pointer to be marshaled
  DWORD dwDestContext,  //Destination context
  void *pvDestContext,  //Reserved for future use
  DWORD mshlflags       //Reason for marshaling
);

Parameters

pStm
[in] Pointer to the stream to be used during marshaling.
riid
[in] Reference to the identifier of the interface to be marshaled. This interface must be derived from the IUnknown interface.
pv
[in] Pointer to the interface pointer to be marshaled; can be NULL if the caller does not have a pointer to the desired interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling can occur either in another apartment of the current process (MSHCTX_INPROC) or in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be zero.
mshlflags
[in] Whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.

Return Values

The method supports the standard return value E_FAIL, as well as the following:

S_OK
The interface pointer was marshaled successfully.
E_NOINTERFACE
The specified interface is not supported.
STG_E_MEDIUMFULL
The stream is full.

Remarks

This method is called indirectly, in a call to CoMarshalInterface, by whatever code in the server process may be responsible for marshaling a pointer to an interface on an object. This marshaling code is usually a stub generated by COM for one of several interfaces that can marshal a pointer to an interface implemented on an entirely different object. Examples include the IClassFactory and IOleItemContainer interfaces. For purposes of this discussion, the code responsible for marshaling a pointer is called the marshaling stub.

Notes to Callers

Normally, rather than calling IMarshal::MarshalInterface directly, your marshaling stub instead should call the CoMarshalInterface function, which contains a call to this method. The stub makes this call to command an object to write its marshaling data into a stream. The stub then either passes the marshaling data back to the client process or writes it to a global table, where it can be unmarshaled by multiple clients. The stub's call to CoMarshalInterface is normally preceded by a call to CoGetMarshalSizeMax to get the maximum size of the stream buffer into which the marshaling data will be written.

You do not explicitly call this method if you are implementing existing COM interfaces or defining your own interfaces using the Microsoft Interface Definition Language (MIDL). In either case, the MIDL-generated stub automatically makes the call.

If you are not using MIDL to define your own interface, your marshaling stub must call this method, either directly or indirectly. Your stub implementation should call MarshalInterface immediately after its previous call to IMarshal::GetMarshalSizeMax returns. Because the value returned by GetMarshalSizeMax is guaranteed to be valid only as long as the internal state of the object being marshaled does not change, a delay in calling MarshalInterface runs the risk that the object will require a larger stream buffer than originally indicated.

If the caller has a pointer to the interface to be marshaled, it should, as a matter of efficiency, use the pv parameter to pass that pointer. In this way, an implementation that may use such a pointer to determine the appropriate CLSID for the proxy does not have to call IUnknown::QueryInterface on itself. If a caller does not have a pointer to the interface to be marshaled, it can pass NULL.

Notes to Implementers

Your implementation of IMarshal::MarshalInterface must write to the stream whatever data is needed to initialize the proxy on the receiving side. Such data would include a reference to the interface to be marshaled, a MSHLFLAGS value specifying whether the data should be returned to the client process or written to a global table, and whatever is needed to connect to the object, such as a named pipe, handle to a window, or pointer to an RPC channel.

Your implementation should not assume that the stream is large enough to hold all the data. Rather, it should gracefully handle a STG_E_MEDIUMFULL error. Just before exiting, your implementation should position the seek pointer in the stream immediately after the last byte of data written.

If the pv parameter is NULL and your implementation needs an interface pointer, it can call IUnknown::QueryInterface on the current object to get it. The pv parameter exists merely to improve efficiency.

To ensure that your implementation of MarshalInterface continues to work properly as new destination contexts are supported in the future, delegate marshaling to the COM default implementation for all dwDestContext values that your implementation does not handle. To delegate marshaling to the COM default implementation, call the CoGetStandardMarshal helper function.

Using the MSHLFLAGS enumeration, callers can specify whether an interface pointer is to be marshaled back to a single client or written to a global table, where it can be unmarshaled by multiple clients. You must make sure that your object can handle calls from the multiple proxies that might be created from the same initialization data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoGetStandardMarshal, IMarshal::GetMarshalSizeMax, IMarshal::GetUnmarshalClass, IMarshal::UnmarshalInterface, Defining COM Interfaces


IMarshal::ReleaseMarshalData

Destroys a marshaled data packet.

HRESULT ReleaseMarshalData(
  IStream * pStm  //Pointer to stream used for unmarshaling
);

Parameter

pStm
[in] Pointer to a stream that contains the data packet to be destroyed.

Return Values

The method supports the standard return value E_FAIL, as well as the following:

S_OK
The data packet was released successfully.
IStream errors
This function can also return any of the stream-access error values for the IStream interface.

Remarks

If an object's marshaled data packet does not get unmarshaled in the client process space and the packet is no longer needed, the client calls ReleaseMarshalData on the proxy's IMarshal implementation to instruct the object to destroy the data packet. The call occurs within the CoReleaseMarshalData function. The data packet serves as an additional reference on the object, and releasing the data is like releasing an interface pointer by calling IUnknown::Release.

If the marshaled data packet somehow does not arrive in the client process or if ReleaseMarshalData is not successfully re-created in the proxy, COM can call this method on the object itself.

Notes to Callers

You will rarely if ever have occasion to call this method yourself. A possible exception would be if you were to implement IMarshal on a class factory for a class object on which you are also implementing IMarshal. In this case, if you were marshaling the object to a table where it could be retrieved by multiple clients, you might, as part of your unmarshaling routine, call ReleaseMarshalData to release the data packet for each proxy.

Notes to Implementers

If your implementation stores state information about marshaled data packets, you can use this method to release the state information associated with the data packet represented by pStm. Your implementation should also position the seek pointer in the stream past the last byte of data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoUnMarshalInterface, CoReleaseMarshalData


IMarshal::UnmarshalInterface

Initializes a newly created proxy and returns an interface pointer to that proxy.

HRESULT UnmarshalInterface(
  IStream * pStm,  //Pointer to the stream to be unmarshaled
  REFIID riid,     //Reference to the identifier of the interface to
                   //be unmarshaled
  void ** ppv      //Address of output variable that receives the
                   //interface pointer requested in riid
);

Parameters

pStm
[in] Pointer to the stream from which the interface pointer is to be unmarshaled.
riid
[in] Reference to the identifier of the interface to be unmarshaled.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer of the interface to be unmarshaled.

Return Values

The method supports the standard return value E_FAIL, as well as the following:

S_OK
The interface pointer was unmarshaled successfully.
E_NOINTERFACE
The specified interface was not supported.

Remarks

The COM library in the process where unmarshaling is to occur calls the proxy's implementation of this method.

Notes to Callers

You do not call this method directly. There are, however, some situations in which you might call it indirectly through a call to CoUnmarshalInterface. For example, if you are implementing a stub, your implementation would call CoUnmarshalInterface when the stub receives an interface pointer as a parameter in a method call.

Notes to Implementers

The proxy's implementation should read the data written to the stream by the original object's implementation of IMarshal::MarshalInterface and use that data to initialize the proxy object whose CLSID was returned by the marshaling stub's call to the original object's implementation of IMarshal::GetUnmarshalClass.

To return the appropriate interface pointer, the proxy implementation can simply call IUnknown::QueryInterface on itself, passing the riid and ppv parameters. However, your implementation of UnmarshalInterface is free to create a different object and, if necessary, return a pointer to it.

Just before exiting, even if exiting with an error, your implementation should reposition the seek pointer in the stream immediately after the last byte of data read.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMarshal::GetUnmarshalClass, IMarshal::MarshalInterface


IMarshal--Default Implementation

Platform SDK: COM

IMarshal—Default Implementation

COM uses its own internal implementation of the IMarshal interface to marshal any object that does not provide its own implementation. COM makes this determination by querying the object for IMarshal. If the interface is missing, COM defaults to its internal implementation.

The COM default implementation of IMarshal uses a generic proxy for each object and creates individual stubs and proxies, as they are needed, for each interface implemented on the object. This mechanism is necessary because COM cannot know in advance what particular interfaces a given object may implement. Developers who do not use COM default marshaling, electing instead to write their own proxy and marshaling routines, know at compile time all the interfaces to be found on their objects and therefore understand exactly what marshaling code is required. COM, in providing marshaling support for all objects, must do so at run time.

The interface proxy resides in the client process; the interface stub resides in the server. Together, each pair handles all marshaling for the interface. The job of each interface proxy is to marshal arguments and unmarshal return values and out parameters that are passed back and forth in subsequent calls to its interface. The job of each interface stub is to unmarshal function arguments and pass them along to the original object and then marshal the return values and out parameters that the object returns.

Proxy and stub communicate by means of an RPC (remote procedure call) channel, which utilizes the system's RPC infrastructure for interprocess communication. The RPC channel implements a single interface, IRpcChannelBuffer, an internal interface to which both interface proxies and stubs hold a pointer. The proxy and stub call the interface to obtain a marshaling packet, send the data to their counterpart, and destroy the packet when they are done. The interface stub also holds a pointer to the original object.

For any given interface, the proxy and stub are both implemented as instances of the same class, which is listed for each interface in the system registry under the label ProxyStubClsid32 (or ProxyStubClsid on 16-bit systems). This entry maps the interface's IID to the CLSID of its proxy and stub objects. When COM needs to marshal an interface, it looks in the system registry to obtain the appropriate CLSID. The server identified by this CLSID implements both the interface proxy and interface stub.

Most often, the class to which this CLSID refers is automatically generated by a tool whose input is a description of the function signatures and semantics of a given interface, written in some interface description language. While using such a language is highly recommended and encouraged for accuracy's sake, doing so is not required. Proxies and stubs are merely COM components used by the RPC infrastructure and, as such, can be written in any manner desired, as long as the correct external contracts are upheld. The programmer who designs a new interface is responsible for ensuring that all interface proxies and stubs that ever exist agree on the representation of their marshaled data.

When created, interface proxies are always aggregated into a larger proxy, which represents the object as a whole. This object proxy also aggregates the COM generic proxy object, which is known as the proxy manager. The proxy manager implements two interfaces: IUnknown and IMarshal. All of the other interfaces that may be implemented on an object are exposed in its object proxy through the aggregation of individual interface proxies. A client holding a pointer to the object proxy "believes" it holds a pointer to the actual object.

A proxy representing the object as a whole is required in the client process so that a client can distinguish calls to the same interfaces implemented on entirely different objects. Such a requirement does not exist in the server process, however, where the object itself resides, because all interface stubs communicate only with the objects for which they were created. No other connection is possible.

Interface stubs, by contrast with interface proxies, are not aggregated because there is no need that they appear to some external client to be part of a larger whole. When connected, an interface stub is given a pointer to the server object to which it should forward method invocations that it receives. Although it is useful to refer conceptually to a stub manager, meaning whatever pieces of code and state in the server-side RPC infrastructure that service the remoting of a given object, there is no direct requirement that the code and state take any particular, well-specified form.

The first time a client requests a pointer to an interface on a particular object, COM loads an IClassFactory stub in the server process and uses it to marshal the first pointer back to the client. In the client process, COM loads the generic proxy for the class factory object and calls its implementation of IMarshal to unmarshal that first pointer. COM then creates the first interface proxy and hands it a pointer to the RPC channel. Finally, COM returns the IClassFactory pointer to the client, which uses it to call IClassFactory::CreateInstance, passing it a reference to the interface.

Back in the server process, COM now creates a new instance of the object, along with a stub for the requested interface. This stub marshals the interface pointer back to the client process, where another object proxy is created, this time for the object itself. Also created is a proxy for the requested interface, a pointer to which is returned to the client. With subsequent calls to other interfaces on the object, COM will load the appropriate interface stubs and proxies as needed.

When a new interface proxy is created, COM hands it a pointer to the proxy manager's implementation of IUnknown, to which it delegates all QueryInterface calls. Each interface proxy implements two interfaces of its own: the interface it represents and IRpcProxyBuffer. The interface proxy exposes its own interface directly to clients, which can obtain its pointer by calling QueryInterface on the proxy manager. Only COM, however, can call IRpcProxyBuffer, which is used to connect and disconnect the proxy to the RPC channel. A client cannot query an interface proxy to obtain a pointer to the IRpcProxyBuffer interface.

On the server side, each interface stub implements IRpcStubBuffer, an internal interface. The server code acting as a stub manager calls IRpcStubBuffer::Connect and passes the interface stub the IUnknown pointer of its object.

When an interface proxy receives a method invocation, it obtains a marshaling packet from its RPC channel through a call to IRpcChannelBuffer::GetBuffer. The process of marshaling the arguments will copy data into the buffer. When marshaling is complete, the interface proxy invokes IRpcChannelBuffer::SendReceive to send the marshaled packet to the corresponding interface stub. When IRpcChannelBuffer::SendReceive returns, the buffer into which the arguments were marshaled will have been replaced by a new buffer containing the return values marshaled from the interface stub. The interface proxy unmarshals the return values, invokes IRpcChannelBuffer::FreeBuffer to free the buffer, and then returns the return values to the original caller of the method.

It is the implementation of IRpcChannelBuffer::SendReceive that actually sends the request to the server process and that knows how to identify the server process and, within that process, the object to which the request should be sent. The channel implementation also knows how to forward the request on to the appropriate stub manager in that process. The interface stub unmarshals the arguments from the provided buffer, invokes the indicated method on the server object, and marshals the return values back into a new buffer allocated by a call to IRpcchannelBuffer::GetBuffer. The channel then transmits the return data packet back to the interface proxy, which is still in the middle of IRpcchannelBuffer::SendReceive, which returns to the interface proxy.

A particular instance of an interface proxy can be used to service more than one interface, as long as the following conditions are met:

  • The IIDs of the affected interfaces must be mapped to the appropriate ProxyStubClsid in the system registry.
  • The interface proxy must support calls to QueryInterface from one supported interface to the other interfaces, as usual, as well as from IUnknown and IRpcProxyBuffer.

A single instance of an interface stub can also service more than one interface, but only if that set of interfaces has a strict single-inheritance relationship. This restriction exists because the stub can direct method invocations to multiple interfaces only where it knows in advance which methods are implemented on which interfaces.

At various times, proxies and stubs will have need to allocate or free memory. Interface proxies, for example, will need to allocate memory in which to return out parameters to their caller. In this respect, interface proxies and interface stubs are just normal COM components, in that they should use the standard task allocator. (See CoGetMalloc.)

When to Use

You should use the COM default implementation of IMarshal except in those very few cases where your application has special requirements that the COM default implementation does not address, or where you can achieve optimizations over the marshaling code COM has provided. For examples of such special cases, see the "When to Implement" section for the IMarshal interface.

See Also

IMarshal


IMessageFilter

The IMessageFilter interface provides COM servers and applications with the ability to selectively handle incoming and outgoing COM messages while waiting for responses from synchronous calls. Filtering messages helps to ensure that calls are handled in a manner that improves performance and avoids deadlocks. COM messages can be synchronous, asynchronous, or input-synchronized; the majority of interface calls are synchronous.

Synchronous calls require the caller to wait for a reply before continuing. COM enters a modal loop while waiting for the reply. During this time, the caller is still able to receive and dispatch incoming messages.

Asynchronous calls allow the caller to proceed without waiting for a response from the called object. Today, in COM, the only asynchronous calls are to an object's IAdviseSink interface. While the object is processing an asynchronous call, it is prohibited from making any synchronous calls back to the calling object.

Input-synchronized calls require the called object to complete the call before relinquishing control, ensuring that behaviors such as focus management and type-ahead function correctly.

When to Implement

You will probably want to implement your own message filter. The default implementation provided by COM offers only minimal message filtering capability. Although message filtering is no longer as significant as it was with 16-bit applications because the size of the message queue is now virtually unlimited, you still should implement IMessageFilter as a way of resolving deadlocks.

COM will call your implementation of IMessageFilter to find out if an application is blocking, so you can task-switch to that application and give the user an opportunity to deal with the situation. For example, if you have Microsoft® Word talking to Microsoft Excel, with Excel running in the background in formula mode, in which formulas are being applied to data on the worksheet to compute different or what-if results, Excel won't check all calls, thereby blocking further action. IMessageFilter would put up a dialog box indicating which task was blocking and provide the user with an opportunity to deal with the deadlock.

Although it is probably obvious from the method descriptions, it may still be useful to point out that HandleIncomingCall is an object-based method and RetryRejectedCall and MessagePending are client-based methods. Clearly, the object must have some way of handling incoming calls from external clients. HandleIncomingCall provides that functionality by allowing the object to handle or defer some incoming calls and reject others. The client also needs to know how an object is going to handle its call so that it can respond appropriately. The client needs to know if a call has been rejected, or just deferred temporarily, so that it can retry rejected calls after some specified time. The client also needs to be able to respond to Windows messages while waiting for replies to pending messages.

Note If a client implements IMessageFilter and calls a server method on a remote machine, the client's RetryRejectedCall will not get called.

You will use CoRegisterMessageFilter to register your message filter. Then COM calls your message filter instead of the default implementation.

When to Use

You don't call this interface directly. It's provided by the COM server or application and called by COM.

Application Shutdown with WM_QUERYENDSESSION and WM_ENDSESSION

When a user exits Windows®, each open application receives a WM_QUERYENDSESSION message followed by a WM_ENDSESSION message, provided the exit is not canceled. These messages are invoked with SendMessage, which unfortunately restricts the initiation of all outgoing LRPC calls. This is a problem for container applications that have open embedded objects when they receive the shutdown request because LRPC is needed to close those objects.

Container and container/server applications with open documents typically display a message box on receipt of the WM_QUERYENDSESSION message that asks if the user wants to save changes before exiting. A positive response is usually the default. The recommendation for dealing with the situation described above is for the application to display an alternate message box asking if the user wants to discard changes; a negative response should be the default. If the user chooses to discard the changes, TRUE should be returned for WM_QUERYENDSESSION, which signals to Windows that it can terminate. If the user does not want to discard the changes, FALSE should be returned. No attempt should be made to close or release running embeddings.

Server applications should return TRUE for WM_QUERYENDSESSION without prompting the user. On receipt of a WM_ENDSESSION message, all COM applications should execute the normal close sequence for each application's documents and objects. At the same time, you should ignore any errors resulting from any cross-process calls or calls to IUnknown::Release. All storage pointers (IStorage and IStream interface pointers) must be released to properly flush any temporary files maintained by the compound file implementation of structured storage.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IMessageFilter Methods | Description
HandleIncomingCall | Provides a single entry point for incoming calls.
RetryRejectedCall | Provides application with opportunity to display a dialog box offering retry or cancel or task-switching options.
MessagePending | Indicates a Windows message has arrived while COM is waiting to respond to a remote call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoRegisterMessageFilter, OleUIBusy, OLEUIBUSY


IMessageFilter::HandleInComingCall

An object-based method that provides the ability to filter or reject incoming calls (or callbacks) to an object or a process. This method is called prior to each method invocation originating outside the current process.

DWORD HandleInComingCall(
  DWORD dwCallType,      //Type of incoming call
  HTASK threadIDCaller,  //Task handle calling this task
  DWORD dwTickCount,     //Elapsed tick count
  LPINTERFACEINFO lpInterfaceInfo
                         //Pointer to INTERFACEINFO structure
);

Parameters

dwCallType
[in] Kind of incoming call that has been received. Valid values are from the enumeration CALLTYPE. See the chapter on enumerations for details.
threadIDCaller
[in] Handle of the task calling this task.
dwTickCount
[in] Elapsed tick count since the outgoing call was made if dwCallType is not CALLTYPE_TOPLEVEL. If dwCallType is CALLTYPE_TOPLEVEL, dwTickCount should be ignored.
lpInterfaceInfo
[in] Pointer to an INTERFACEINFO structure, which identifies the object, the interface, and the method making the call. In the case of DDE calls, lpInterfaceInfo can be NULL because the DDE layer does not return interface information.

Return Values

SERVERCALL_ISHANDLED
The application might be able to process the call.
SERVERCALL_REJECTED
The application cannot handle the call due to an unforeseen problem, such as network unavailability, or if it is in the process of terminating.
SERVERCALL_RETRYLATER
The application cannot handle the call at this time. An application might return this value when it is in a user-controlled modal state.

Remarks

If implemented, IMessageFilter::HandleInComingCall is called by COM when an incoming COM message is received.

Depending on an application's current state, a call is either accepted and processed or rejected (permanently or temporarily). If SERVERCALL_ISHANDLED is returned, the application may be able to process the call, although success depends on the interface for which the call is destined. If the call cannot be processed, COM returns RPC_E_CALL_REJECTED.

Input-synchronized and asynchronous calls are dispatched even if the application returns SERVERCALL_REJECTED or SERVERCALL_RETRYLATER.

IMessageFilter::HandleInComingCall should not be used to hold off updates to objects during operations such as band printing. For that purpose, use IViewObject::Freeze.

You can also use IMessageFilter::HandleInComingCall to set up the application's state so that the call can be processed in the future.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IViewObject::Freeze, CALLTYPE, INTERFACEINFO


IMessageFilter::MessagePending

A client-based method called by COM when a Windows message appears in a COM application's message queue while the application is waiting for a reply to a remote call. Handling input while waiting for an outgoing call to finish can introduce complications. The application should determine whether to process the message without interrupting the call, to continue waiting, or to cancel the operation.

DWORD MessagePending(
  HTASK threadIDCallee,  //Called applications task handle
  DWORD dwTickCount,     //Elapsed tick count
  DWORD dwPendingType    //Call type
);

Parameters

threadIDCallee
[in] Task handle of the called application that has not yet responded.
dwTickCount
[in] Number of ticks since the call was made. It is calculated from the Windows GetTickCount function.
dwPendingType
[in] Type of call made during which a message or event was received. Valid values are from the enumeration PENDINGTYPE (where PENDINGTYPE_TOPLEVEL means the outgoing call was not nested within a call from another application and PENDINTGYPE_NESTED means the outgoing call was nested within a call from another application).

Return Values

PENDINGMSG_CANCELCALL
Cancel the outgoing call. This should be returned only under extreme conditions. Canceling a call that has not replied or been rejected can create orphan transactions and lose resources. COM fails the original call and returns RPC_E_CALL_CANCELLED.
PENDINGMSG_WAITNOPROCESS
Continue waiting for the reply, and do not dispatch the message unless it is a task-switching or window-activation message. A subsequent message will trigger another call to IMessageFilter::MessagePending. Leaving messages or events in the queue enables them to be processed normally, if the outgoing call is completed. Note that returning PENDINGMSG_WAITNOPROCESS can cause the message queue to fill.
PENDINGMSG_WAITDEFPROCESS
Because of the increased resources available in 32-bit systems, you are unlikely to get this return value. It now indicates the same state as PENDINGMSG_WAITNOPROCESS.

Keyboard and mouse messages are no longer dispatched, as was done with PENDINGMSG_WAITDEFPROCESS. However there are some cases where mouse and keyboard messages could cause the system to deadlock, and in these cases, mouse and keyboard messages are discarded. WM_PAINT messages are dispatched. Task-switching and activation messages are handled as before.

Remarks

COM calls IMessageFilter::MessagePending after an application has made a COM method call and a Windows message occurs before the call has returned. A Windows message is sent, for example, when the user selects a menu command or double-clicks an object. Before COM makes the IMessageFilter::MessagePending call, it calculates the elapsed time since the original COM method call was made. COM delivers the elapsed time in the dwTickCount parameter. In the meantime, COM does not remove the message from the queue.

Windows messages that appear in the caller's queue should remain in the queue until sufficient time has passed to ensure that the messages are probably not the result of typing ahead, but are instead an attempt to get attention. Set the delay with the dwTickCount parameter—a two-second or three-second delay is recommended. If that amount of time passes and the call has not been completed, the caller should flush the messages from the queue and the OLE UI busy dialog box should be displayed offering the user the choice of retrying the call (continue waiting) or switching to the task identified by the threadIDCallee parameter. This ensures the following behaviors:

  • If calls are completed in a reasonable amount of time, type ahead will be treated correctly.
  • If the callee does not respond, type ahead is not misinterpreted and the user is able to act to solve the problem. For example, OLE 1 servers can queue up requests without responding when they are in modal dialog boxes.

Handling input while waiting for an outgoing call to finish can introduce complications. The application should determine whether to process the message without interrupting the call, to continue waiting, or to cancel the operation.

When there is no response to the original COM call, the application can cancel the call and restore the COM object to a consistent state by calling IStorage::Revert on its storage. The object can be released when the container can shut down. However, canceling a call can create orphaned operations and resource leaks. Canceling should be used only as a last resort. It is strongly recommended that applications not allow such calls to be canceled.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IStorage::Revert, IStorage::Revert, OleUIBusy, OLEUIBUSY, GetTickCount


IMessageFilter::RetryRejectedCall

A client-based method that gives the application an opportunity to display a dialog box so that the user can retry or cancel the call, or switch to the task identified by threadIDCallee.

DWORD RetryRejectedCall(
  HTASK threadIDCallee,  //Server task handle
  DWORD dwTickCount,     //Elapsed tick count
  DWORD dwRejectType     //Returned rejection message
);

Parameters

threadIDCallee
[in] Handle of the server task that rejected the call.
dwTickCount
[in] Number of elapsed ticks since the call was made.
dwRejectType
[in] Specifies either SERVERCALL_REJECTED or SERVERCALL_RETRYLATER, as returned by the object application.

Return Values

-1
The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the original method call.
Value >= 0 and <100
The call is to be retried immediately.
Value >= 100
COM will wait for this many milliseconds and then retry the call.

Remarks

COM calls RetryRejectedCall on the caller's IMessageFilter interface immediately after receiving SERVERCALL_RETRYLATER or SERVERCALL_REJECTED from the IMessageFilter::HandleIncomingCall method on the callee's IMessageFilter interface.

If a called task rejects a call, the application is probably in a state where it cannot handle such calls, possibly only temporarily. When this occurs, COM returns to the caller and issues IMessageFilter::RetryRejectedCall to determine whether it should retry the rejected call.

Applications should silently retry calls that have returned with SERVERCALL_RETRYLATER. If, after a reasonable amount of time has passed, say about 30 seconds, the application should display the busy dialog box; a standard implementation of this dialog box is available in the OLEDLG library. The callee may momentarily be in a state where calls can be handled. The option to wait and retry is provided for special kinds of calling applications, such as background tasks executing macros or scripts, so that they can retry the calls in a nonintrusive way.

If, after a dialog box is displayed, the user chooses to cancel, RetryRejectedCall returns -1 and the call will appear to fail with RPC_E_CALL_REJECTED.

If a client implements IMessageFilter and calls a server method on a remote machine, RetryRejectedCall will not be called.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IMoniker

The IMoniker interface contains methods that allow you to use a moniker object, which contains information that uniquely identifies a COM object. An object that has a pointer to the moniker object's IMoniker interface can locate, activate, and get access to the identified object without having any other specific information on where the object is actually located in a distributed system.

Like a path to a file in a file system, a moniker contains information that allows a COM object to be located and activated. Monikers can identify any type of COM object, from a document object stored in a file to a selection within an embedded object. COM provides a set of moniker classes that allow you to create moniker objects identifying the objects most commonly found in the system. For example, there might be an object representing a range of cells in a spreadsheet that is itself embedded in a text document stored in a file. In a distributed system, this object's moniker would identify the location of the object's system, the file's physical location on that system, the storage of the embedded object within that file, and, finally, the location of the range of cells within the embedded object.

A moniker object supports the IMoniker interface, which is derived from the IPersistStream interface and uniquely identifies a single object in the system. Once an object providing a moniker has created the moniker object, this information cannot be changed within that object. If the moniker provider changes the information, it can do so only by creating a new moniker object, which would then uniquely identify the object in question.

Monikers have the following two important capabilities:

  • Monikers can be saved to a persistent storage. When a moniker is loaded back into memory, it still identifies the same object.
  • Monikers support an operation called binding, which is the process of locating the object named by the moniker, activating it (loading it into memory) if it is not already active, and returning a pointer to a requested interface on that object.

Monikers are used as the basis for linking in COM. A linked object contains a moniker that identifies its source. When the user activates the linked object to edit it, the moniker is bound; this loads the link source into memory.

When to Implement

Implement IMoniker only if you are writing a new moniker class. This is necessary only if you need to identify objects that cannot be identified using one of the COM-supplied moniker classes described below.

The COM-supplied moniker classes are sufficient for most situations. Before considering writing your own moniker class, you should make sure that your requirements cannot be satisfied by these classes.

If you decide you need to write your own implementation of IMoniker, you must also implement the IROTData interface on your moniker class. This interface allows your monikers to be registered with the Running Object Table (ROT).

When to Use

The following two kinds of objects call the methods of IMoniker:

  • A component that contains one or more objects to be identified with a moniker and must provide the moniker to other objects
  • A client object that must bind to the object identified by the moniker

The component providing a moniker makes it accessible to other objects. To know which are appropriate for a given object, it is important to understand the differences between the various system-supplied moniker classes. COM also provides functions for creating monikers by using the COM-supplied moniker classes.

  • File monikers Based on a path in the file system. File monikers can be used to identify objects that are saved as files. The associated creation function is CreateFileMoniker.
  • Item monikers Based on a string that identifies an object in a container. Item monikers can be used to identify objects smaller than a file, such as embedded objects in a compound document and pseudo-objects (such as a range of cells in a spreadsheet). The associated creation function is CreateItemMoniker.
  • Generic composite monikers Consist of two or more monikers of arbitrary type that have been composed together. Generic composite monikers allow monikers of different classes to be used in combination. The associated creation function is CreateGenericComposite.
  • Anti-monikers The inverse of file, item, or pointer monikers. Anti-monikers are used primarily for constructing relative monikers, which are analogous to relative path (such as "..\backup\report.old") and which specify a location of an object relative to the location of another object. The associated creation function is CreateAntiMoniker.
  • Pointer monikers A nonpersistent moniker that wraps an interface pointer to an object loaded in memory. Whereas most monikers identify objects that can be saved to persistent storage, pointer monikers identify objects that cannot. The associated creation function is CreatePointerMoniker.
  • Class monikers These represent an object class. Class monikers bind to the class object of the class for which they are created. The associated creation function is CreateClassComposite.

A moniker provider must also implement other interfaces to allow the monikers it hands out to be bound. COM objects that commonly provide monikers are link sources. These include server applications that support linking and container applications that support linking to their embedded objects.

Binding to an object means that a client uses a moniker to locate the object, activate it when necessary, and get a pointer to one of the active object's interfaces. The client of the moniker does not need to be aware of the class of the moniker—it must just get a pointer to the correct moniker's IMoniker interface. Monikers are used most often in this way by container applications that allow their documents to contain linked objects. However, link containers rarely call IMoniker methods directly. Instead, they generally manipulate linked objects through the default handler's implementation of the IOleLink interface, which calls the appropriate IMoniker methods as needed.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPersist Methods | Description
GetClassID | Returns the object's CLSID.
IPersistStream Methods | Description
IsDirty | Checks whether object has been modified.
Load | Loads the object from a stream.
Save | Saves the object to a stream.
GetSizeMax | Returns the buffer size needed to save the object.
IMoniker Methods | Description
BindToObject | Binds to the object named by the moniker.
BindToStorage | Binds to the object's storage.
Reduce | Reduces the moniker to simplest form.
ComposeWith | Composes with another moniker.
Enum | Enumerates component monikers.
IsEqual | Compares with another moniker.
Hash | Returns a hash value.
IsRunning | Checks whether object is running.
GetTimeOfLastChange | Returns time the object was last changed.
Inverse | Returns the inverse of the moniker.
CommonPrefixWith | Finds the prefix that the moniker has in common with another moniker.
RelativePathTo | Constructs a relative moniker between the moniker and another.
GetDisplayName | Returns the display name.
ParseDisplayName | Converts a display name into a moniker.
IsSystemMoniker | Checks whether moniker is one of the system-supplied types.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

BindMoniker, CreateBindCtx, CreateGenericComposite, CreateFileMoniker, CreateItemMoniker, CreateAntiMoniker, CreatePointerMoniker, IOleLink, IPersistStream, IROTData, IMoniker—AntiMoniker Implementation, IMoniker—File Moniker Implementation, IMoniker—Item Moniker Implementation, IMoniker—Generic Composite Moniker Implementation, IMoniker—Pointer Moniker Implementation


IMoniker::BindToObject

Uses the moniker to bind to the object it identifies. The binding process involves finding the object, putting it into the running state if necessary, and supplying the caller with a pointer to a specified interface on the identified object.

HRESULT BindToObject(
  IBindCtx *pbc,        //Pointer to bind context object to be used
  IMoniker *pmkToLeft,  //Pointer to moniker that precedes this one
                        //in the composite
  REFIID riidResult,    //IID of interface pointer requested
  void **ppvResult      //Address of output variable that receives
                        //the interface pointer requested in riidResult
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context object, which is used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to the left of this moniker. This parameter is primarily used by moniker implementers to enable cooperation between the various components of a composite moniker. Moniker clients should pass NULL.
riidResult
[in] IID of the interface the client wishes to use to communicate with the object that the moniker identifies.
ppvResult
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvResult contains the requested interface pointer to the object the moniker identifies. When successful, the implementation must call IUnknown::AddRef on the moniker. It is the caller's responsibility to release the object with a call to IUnknown::Release. If an error occurs, *ppvResult should be NULL.

Return Values

The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following:

S_OK
The binding operation was successful.
MK_E_NOOBJECT
The object identified by this moniker, or some object identified by the composite moniker of which this moniker is a part, could not be found.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The binding operation requires assistance from the end user. The most common reason for returning this value is that a password is needed or that a floppy needs to be mounted. When this value is returned, retrieve the moniker that caused the error with a call to IBindCtx::GetObjectParam with the key "ConnectManually". You can then call IMoniker::GetDisplayName to get the display name, display a dialog box that communicates the desired information, such as instructions to mount a floppy or a request for a password, and then retry the binding operation.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
An intermediate object was found but it did not support an interface required to complete the binding operation. For example, an item moniker returns this value if its container does not support the IOleItemContainer interface.
STG_E_ACCESSDENIED
Unable to access the storage object.
IOleItemContainer::GetObject errors
If the moniker used to bind to an object contains an item moniker, errors associated with this method can be returned.

Remarks

IMoniker::BindToObject implements the primary function of a moniker, which is to locate the object identified by the moniker and return a pointer to one of its interfaces.

Notes to Callers

If you are using a moniker as a persistent connection between two objects, you activate the connection by calling IMoniker::BindToObject.

You typically call IMoniker::BindToObject during the following process:

  1. Create a bind context object with a call to the CreateBindCtx function.
  2. Call IMoniker::BindToObject using the moniker, retrieving a pointer to a desired interface on the identified object.
  3. Release the bind context.
  4. Through the acquired interface pointer, perform the desired operations on the object.
  5. When finished with the object, release the object's interface pointer.

The following code fragment illustrates these steps:

// pMnk is an IMoniker * that points to a previously acquired moniker.
// ICellRange is a custom interface designed for an object that is a
// range of spreadsheet cells.
ICellRange *pCellRange;
IBindCtx *pbc;

CreateBindCtx( 0, &pbc );
pMnk->BindToObject( pbc, NULL, IID_ICellRange, &pCellRange );
pbc->Release();
// pCellRange now points to the object; safe to use pCellRange
pCellRange->Release();

You can also use the BindMoniker function when you intend only one binding operation and don't need to retain the bind context object. This helper function encapsulates the creation of the bind context, calling IMoniker::BindToObject and releasing the bind context.

COM containers that support links to objects use monikers to locate and get access to the linked object but typically do not call IMoniker::BindToObject directly. Instead, when a user activates a link in a container, the link container usually calls IOleObject::DoVerb, using the link handler's implementation, which calls IMoniker::BindToObject on the moniker stored in the linked object (if it cannot handle the verb).

Notes to Implementers

What your implementation does depends on whether you expect your moniker to have a prefix—that is, whether you expect the pmkToLeft parameter to be NULL or not. For example, an item moniker, which identifies an object within a container, expects that pmkToLeft identifies the container. An item moniker consequently uses pmkToLeft to request services from that container. If you expect your moniker to have a prefix, you should use the pmkToLeft parameter (for example, calling IMoniker::BindToObject on it) to request services from the object it identifies.

If you expect your moniker to have no prefix, your IMoniker::BindToObject implementation should first check the Running Object Table (ROT) to see whether the object is already running. To acquire a pointer to the ROT, your implementation should call IBindCtx::GetRunningObjectTable on the pbc parameter. You can then call the IRunningObjectTable::GetObject method to see if the current moniker has been registered in the ROT. If so, you can immediately call IUnknown::QueryInterface to get a pointer to the interface requested by the caller.

When your IMoniker::BindToObject implementation binds to some object, it should use the pbc parameter to call IBindCtx::RegisterObjectBound to store a reference to the bound object in the bind context. This ensures that the bound object remains running until the bind context is released, which can avoid the expense of having a subsequent binding operation load it again later.

If the bind context's BIND_OPTS structure specifies the BINDFLAGS_JUSTTESTEXISTENCE flag, your implementation has the option of returning NULL in ppvResult (although you can also ignore the flag and perform the complete binding operation).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

BindMoniker, IMoniker::BindToStorage


IMoniker::BindToStorage

Retrieves an interface pointer to the storage that contains the object identified by the moniker. Unlike the IMoniker::BindToObject method, this method does not activate the object identified by the moniker.

HRESULT BindToStorage(
  IBindCtx *pbc,        //Pointer to bind context to be used
  IMoniker *pmkToLeft,  //Pointer to moniker to the left of this one
                        //in the composite
  REFIID riid,          //Reference to the identifier of the storage
                        //interface requested
  void **ppvObj         //Address of output variable that receives
                        //the interface pointer requested in riid
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used during this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to the left of this moniker. This parameter is used primarily by moniker implementers to enable cooperation between the various components of a composite moniker. Moniker clients should pass NULL.
riid
[in] Reference to the identifier of the storage interface requested, whose pointer will be returned in ppvObj. Storage interfaces commonly requested include IStorage, IStream, and ILockBytes.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer to the storage of the object identified by the moniker. If ppvObj is non-NULL, the implementation must call IUnknown::AddRef on the storage; it is the caller's responsibility to call IUnknown::Release. If an error occurs, *ppvObj must be set to NULL.

Return Values

The method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The binding operation was successful.
MK_E_NOSTORAGE
The object identified by this moniker does not have its own storage.
MK_E_EXCEEDEDDEADLINE
The operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The operation was unable to connect to the storage, possibly because a network device could not be connected to. For more information, see IMoniker::BindToObject.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
An intermediate object was found, but it did not support an interface required for an operation. For example, an item moniker returns this value if its container does not support the IOleItemContainer interface.
STG_E_ACCESSDENIED
Unable to access the storage object.
IOleItemContainer::GetObject errors
Binding to a moniker containing an item moniker can return any of the errors associated with this function.

Remarks

There is an important difference between the IMoniker::BindToObject and IMoniker::BindToStorage methods. If, for example, you have a moniker that identifies a spreadsheet object, calling IMoniker::BindToObject provides access to the spreadsheet object itself, while calling IMoniker::BindToStorage provides access to the storage object in which the spreadsheet resides.

Notes to Callers

Although none of the COM moniker classes call this method in their binding operations, it might be appropriate to call it in the implementation of a new moniker class. You could call this method in an implementation of IMoniker::BindToObject that requires information from the object identified by the pmkToLeft parameter and can get it from the persistent storage of the object without activation. For example, if your monikers are used to identify objects that can be activated without activating their containers, you may find this method useful.

A client that can read the storage of the object its moniker identifies could also call this method.

Notes to Implementers

Your implementation should locate the persistent storage for the object identified by the current moniker and return the desired interface pointer. Some types of monikers represent pseudo-objects, which are objects that do not have their own persistent storage. Such objects comprise some portion of the internal state of its container—as, for example, a range of cells in a spreadsheet. If your moniker class identifies this type of object, your implementation of IMoniker::BindToStorage should return the error MK_E_NOSTORAGE.

If the bind context's BIND_OPTS structure specifies the BINDFLAGS_JUSTTESTEXISTENCE flag, your implementation has the option of returning NULL in ppvObj (although it can also ignore the flag and perform the complete binding operation).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::BindToObject


IMoniker::CommonPrefixWith

Creates a new moniker based on the common prefix that this moniker (the one comprising the data of this moniker object) shares with another moniker.

HRESULT CommonPrefixWith(
  IMoniker *pmkOther,    //Pointer to moniker to be used for
                         //comparison
  IMoniker **ppmkPrefix  //Address of output variable that receives
                         //the IMoniker interface pointer
);

Parameters

pmkOther
[in] Pointer to the IMoniker interface on another moniker to be compared with this one to determine whether there is a common prefix.
ppmkPrefix
[out] Address of IMoniker* pointer variable that receives the interface pointer to the moniker that is the common prefix of this moniker and pmkOther. When successful, the implementation must call IUnknown::AddRef on the resulting moniker; it is the caller's responsibility to call IUnknown::Release. If an error occurs or if there is no common prefix, the implementation should set *ppmkPrefix to NULL.

Return Values

The method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
A common prefix exists that is neither this moniker nor pmkOther.
MK_S_NOPREFIX
No common prefix exists.
MK_S_HIM
The entire pmkOther moniker is a prefix of this moniker.
MK_S_US
The two monikers are identical.
MK_S_ME
This moniker is a prefix of the pmkOther moniker.
MK_E_NOTBINDABLE
This method was called on a relative moniker. It is not meaningful to take the common prefix on a relative moniker.

Remarks

IMoniker::CommonPrefixWith creates a new moniker that consists of the common prefixes of the moniker on this moniker object and another moniker. For example, if one moniker represents the path "c:\projects\secret\art\pict1.bmp" and another moniker represents the path "c:\projects\secret\docs\chap1.txt", the common prefix of these two monikers would be a moniker representing the path "c:\projects\secret".

Notes to Callers

The IMoniker::CommonPrefixWith method is primarily called in the implementation of the IMoniker::RelativePathTo method. Clients using a moniker to locate an object rarely need to call this method.

Call this method only if pmkOther and this moniker are both absolute monikers. An absolute moniker is either a file moniker or a generic composite whose leftmost component is a file moniker that represents an absolute path. Do not call this method on relative monikers because it would not produce meaningful results.

Notes to Implementers

Your implementation should first determine whether pmkOther is a moniker of a class that you recognize and for which you can provide special handling (for example, if it is of the same class as this moniker). If so, your implementation should determine the common prefix of the two monikers. Otherwise, it should pass both monikers in a call to the MonikerCommonPrefixWith function, which correctly handles the generic case.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::RelativePathTo, MonikerCommonPrefixWith


IMoniker::ComposeWith

Combines the current moniker with another moniker, creating a new composite moniker.

HRESULT ComposeWith(
  IMoniker *pmkRight,  //Pointer to moniker to be composed onto this
                       //one
  BOOL fOnlyIfNotGeneric,  //Indicates if generic composition
                           //permissible
  IMoniker **ppmkComposite
                       //Address of output variable that receives
                       //the IMoniker interface pointer
);

Parameters

pmkRight
[in] Pointer to the IMoniker interface on the moniker to compose onto the end of this moniker.
fOnlyIfNotGeneric
[in] If TRUE, the caller requires a nongeneric composition, so the operation should proceed only if pmkRight is a moniker class that this moniker can compose with in some way other than forming a generic composite. If FALSE, the method can create a generic composite if necessary.
ppmkComposite
[out] Address of IMoniker* pointer variable that receives the interface pointer to the resulting composite moniker pointer. When successful, the implementation must call IUnknown::AddRef on the resulting moniker; it is the caller's responsibility to call IUnknown::Release. If an error occurs or if the monikers compose to nothing (for example, composing an anti-moniker with an item moniker or a file moniker), *ppmkComposite should be set to NULL.

Return Values

The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The monikers were successfully combined.
MK_E_NEEDGENERIC
Indicates that fOnlyIfNotGeneric was TRUE, but the monikers could not be composed together without creating a generic composite moniker.

Remarks

Joining two monikers together is called composition. Sometimes two monikers of the same class can be combined in what is called nongeneric composition. For example, a file moniker representing an incomplete path and another file moniker representing a relative path can be combined to form a single file moniker representing the complete path. Nongeneric composition for a given moniker class can be handled only in the implementation of IMoniker::ComposeWith for that moniker class.

Combining two monikers of any class is called generic composition, which can be accomplished through a call to the CreateGenericComposite function.

Composition of monikers is an associative operation. That is, if A, B, and C are monikers, then, where Comp() represents the composition operation:

Comp( Comp( A, B ), C )

is always equal to

Comp( A, Comp( B, C ) )

Notes to Callers

To combine two monikers, you should call IMoniker::ComposeWith rather than calling the CreateGenericComposite function to give the first moniker a chance to perform a nongeneric composition.

An object that provides item monikers to identify its objects would call IMoniker::ComposeWith to provide a moniker that completely identifies the location of the object. This would apply, for example, to a server that supports linking to portions of a document, or to a container that supports linking to embedded objects within its documents. In such a situation, you would do the following:

  1. Create an item moniker identifying an object.
  2. Get a moniker that identifies the object's container.
  3. Call IMoniker::ComposeWith on the moniker identifying the container, passing the item moniker as the pmkRight parameter.

Most callers of IMoniker::ComposeWith should set the fOnlyIfNotGeneric parameter to FALSE.

Notes to Implementers

You can use either nongeneric or generic composition to compose the current moniker with the moniker that pmkRight points to. If the class of the moniker indicated by pmkRight is the same as that of the current moniker, it is possible to use the contents of pmkRight to perform a more intelligent nongeneric composition.

In writing a new moniker class, you must decide if there are any kinds of monikers, whether of your own class or another class, to which you want to give special treatment. If so, implement IMoniker::ComposeWith to check whether pmkRight is a moniker of the type that should have this treatment. To do this, you can call the moniker's GetClassID method (derived from the IPersist interface), or if you have defined a moniker object that supports a custom interface, you can call IUnknown::QueryInterface on the moniker for that interface. An example of special treatment would be the nongeneric composition of an absolute file moniker with a relative file moniker. The most common case of a special moniker is the inverse for your moniker class (whatever you return from your implementation of IMoniker::Inverse).

If pmkRight completely negates the receiver so that the resulting composite is empty, you should pass back NULL in ppmkComposite and return the status code S_OK.

If the pmkRight parameter is not of a class to which you give special treatment, examine fOnlyIfNotGeneric to determine what to do next. If fOnlyIfNotGeneric is TRUE, pass back NULL through ppmkComposite and return the status code MK_E_NEEDGENERIC. If fOnlyIfNotGeneric is FALSE, call the CreateGenericComposite function to perform the composition generically.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CreateGenericComposite, IMoniker::Inverse


IMoniker::Enum

Supplies a pointer to an enumerator that can enumerate the components of a composite moniker.

HRESULT Enum(
  BOOL fForward,  //Specifies direction of enumeration
  IEnumMoniker **ppenumMoniker
                  //Address of output variable that receives the
                  //IEnumMoniker interface pointer
);

Parameters

fForward
[in] If TRUE, enumerates the monikers from left to right. If FALSE, enumerates from right to left.
ppenumMoniker
[out] Address of IEnumMoniker* pointer variable that receives the interface pointer to the enumerator object for the moniker. When successful, the implementation must call IUnknown::AddRef on the enumerator object. It is the caller's responsibility to call IUnknown::Release. If an error occurs or if the moniker has no enumerable components, the implementation sets *ppenumMoniker to NULL.

Return Values

The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
Indicates success. This value is returned even if the moniker does not provide an enumerator (if ppenumMoniker equals NULL).

Remarks

IMoniker::Enum must supply an IEnumMoniker pointer to an enumerator that can enumerate the components of a moniker. For example, the implementation of the IMoniker::Enum method for a generic composite moniker creates an enumerator that can determine the individual monikers that make up the composite, while the IMoniker::Enum method for a file moniker creates an enumerator that returns monikers representing each of the components in the path.

Notes to Callers

Call this method to examine the components that make up a composite moniker.

Notes to Implementers

If the new moniker class has no discernible internal structure, your implementation of this method can simply return S_OK and set ppenumMoniker to NULL.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumXXXX


IMoniker::GetDisplayName

Gets the display name, which is a user-readable representation of this moniker.

HRESULT GetDisplayName(
  IBindCtx *pbc,  //Pointer to bind context to be used
  IMoniker *pmkToLeft,
                  //Pointer to moniker to the left in the composite
  LPOLESTR *ppszDisplayName
                  //Address of output variable that receives a
                  //pointer to the display name string
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to the left of this moniker. This parameter is used primarily by moniker implementers to enable cooperation between the various components of a composite moniker. Moniker clients should pass NULL.
ppszDisplayName
[out] Address of LPOLESTR pointer variable that receives a pointer to the display name string. When successful, this string is a zero-terminated wide character string (two bytes per character) containing the display name of the moniker. The implementation must use IMalloc::Alloc to allocate the string returned in ppszDisplayName, and the caller is responsible for calling IMalloc::Free to free it. Both the caller and the implementation of this method use the COM task allocator returned by CoGetMalloc. If an error occurs, the implementation must set *ppszDisplayName should be set to NULL.

Return Values

The method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The display name was successfully supplied.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure.
E_NOTIMPL
There is no display name.

Remarks

IMoniker::GetDisplayName provides a string that is a displayable representation of the moniker. A display name is not a complete representation of a moniker's internal state; it is simply a form that can be read by users. As a result, it is possible (though rare) for two different monikers to have the same display name. While there is no guarantee that the display name of a moniker can be parsed back into that moniker when calling the MkParseDisplayName function with it, failure to do so is rare.

As examples, the file moniker implementation of this method supplies the path the moniker represents, and an item moniker's display name is the string identifying the item that is contained in the moniker.

Notes to Callers

It is possible that retrieving a moniker's display name may be an expensive operation. For efficiency, you may want to cache the results of the first successful call to IMoniker::GetDisplayName, rather than making repeated calls.

Notes to Implementers

If you are writing a moniker class in which the display name does not change, simply cache the display name and supply the cached name when requested. If the display name can change over time, getting the current display name might mean that the moniker has to access the object's storage or bind to the object, either of which can be expensive operations. If this is the case, your implementation of IMoniker::GetDisplayName should return MK_E_EXCEEDEDDEADLINE if the name cannot be retrieved by the time specified in the bind context's BIND_OPTS structure.

A moniker that is intended to be part of a generic composite moniker should include any preceding delimiter (such as '\') as part of its display name. For example, the display name returned by an item moniker includes the delimiter specified when it was created with the CreateItemMoniker function. The display name for a file moniker does not include a delimiter because file monikers are always expected to be the leftmost component of a composite.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::ParseDisplayName, MkParseDisplayName


IMoniker::GetTimeOfLastChange

Provides a number representing the time the object identified by this moniker was last changed. To be precise, the time returned is the earliest time COM can identify after which no change has occurred, so this time may be later than the time of the last change to the object.

HRESULT GetTimeOfLastChange(
  IBindCtx *pbc,       //Bind context to be used
  IMoniker *pmkToLeft, //Moniker to the left in the composite
  FILETIME *pFileTime  //Receives the time of last change
);

Parameters

pbc
[in] Pointer to the bind context to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to the left of this moniker. This parameter is primarily used by moniker implementers to enable cooperation between the various components of a composite moniker. Moniker clients should pass NULL.
pFileTime
[out] Pointer to the FILETIME structure receiving the time of last change. A value of {0xFFFFFFFF,0x7FFFFFFF} indicates an error (for example, exceeded time limit, information not available).

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method successfully returned a time.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The operation was unable to connect to the storage for this object, possibly because a network device could not be connected to. For more information, see IMoniker::BindToObject.
MK_E_UNAVAILABLE
The time of the change is unavailable and will not be available regardless of the deadline that is used.

Remarks

Notes to Callers

If you're caching information returned by the object identified by the moniker, you may want to ensure that your information is up-to-date. To do so, you would call IMoniker::GetTimeOfLastChange and compare the time returned with the time you last retrieved information from the object.

For the monikers stored within linked objects, IMoniker::GetTimeOfLastChange is primarily called by the default handler's implementation of IOleObject::IsUpToDate. Container applications call IOleObject::IsUpToDate to determine if a linked object (or an embedded object containing linked objects) is up-to-date without actually binding to the object. This enables an application to determine quickly which linked objects require updating when the end user opens a document. The application can then bind only those linked objects that need updating (after prompting the end user to determine whether they should be updated) instead of binding every linked object in the document.

Notes to Implementers

It is important to perform this operation quickly because, for linked objects, this method is called when a user first opens a compound document. Consequently, your IMoniker::GetTimeOfLastChange implementation should not bind to any objects. In addition, your implementation should check the deadline parameter in the bind context and return MK_E_EXCEEDEDDEADLINE if the operation cannot be completed by the specified time.

Following are some strategies you can use in your implementations:

  • For many types of monikers, the pmkToLeft parameter identifies the container of the object identified by this moniker. If this is true of your moniker class, you can simply call IMoniker::GetTimeOfLastChange on the pmkToLeft parameter, because an object cannot have changed at a date later than its container.
  • You can get a pointer to the Running Object Table (ROT) by calling IBindCtx::GetRunningObjectTable on the pbc parameter and then calling IRunningObjectTable::GetTimeOfLastChange, because the ROT generally records the time of last change.
  • You can get the storage associated with this moniker (or the pmkToLeft moniker) and return the storage's last modification time with a call to IStorage::Stat.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::GetRunningObjectTable, IRunningObjectTable::GetTimeOfLastChange


IMoniker::Hash

Calculates a 32-bit integer using the internal state of the moniker.

HRESULT Hash(
  DWORD *pdwHash  //Pointer to hash value
);

Parameter

pdwHash
[out] Pointer to the hash value.

Return Value

S_OK
Successfully received a 32-bit integer hash value.

Remarks

Notes to Callers

You can use the value returned by this method to maintain a hash table of monikers. The hash value determines a hash bucket in the table. To search such a table for a specified moniker, calculate its hash value and then compare it to the monikers in that hash bucket using IMoniker::IsEqual.

Notes to Implementers

The hash value must be constant for the lifetime of the moniker. Two monikers that compare as equal using IMoniker::IsEqual must hash to the same value.

Marshaling and then unmarshaling a moniker should have no effect on its hash value. Consequently, your implementation of IMoniker::Hash should rely only on the internal state of the moniker, not on its memory address.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::IsEqual


IMoniker::Inverse

Provides a moniker that, when composed to the right of this moniker or one of similar structure, will destroy it. (The moniker will compose to nothing.)

HRESULT Inverse(
  IMoniker **ppmk  //Address of output variable that receives the
                   //IMoniker interface pointer
);

Parameter

ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to a moniker that is the inverse of this moniker. When successful, the implementation must call IUnknown::AddRef on the new inverse moniker. It is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation should set *ppmk to NULL.

Return Values

The method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The inverse moniker has been returned successfully.
MK_E_NOINVERSE
The moniker class does not have an inverse.

Remarks

The inverse of a moniker is analogous to the ".." directory in MS-DOS file systems; the ".." directory acts as the inverse to any other directory name, because appending ".." to a directory name results in an empty path. In the same way, the inverse of a moniker typically is also the inverse of all monikers in the same class. However, it is not necessarily the inverse of a moniker of a different class.

The inverse of a composite moniker is a composite consisting of the inverses of the components of the original moniker, arranged in reverse order. For example, if the inverse of A is Inv( A ) and the composite of A, B, and C is Comp( A, B, C ), then

Inv( Comp( A, B, C ) )

is equal to

Comp( Inv( C ), Inv( B ), Inv( A ) ).

Not all monikers have inverses. Most monikers that are themselves inverses, such as anti-monikers, do not have inverses. Monikers that have no inverse cannot have relative monikers formed from inside the objects they identify to other objects outside.

Notes to Callers

An object that is using a moniker to locate another object usually does not know the class of the moniker it is using. To get the inverse of a moniker, you should always call IMoniker::Inverse rather than the CreateAntiMoniker function, because you cannot be certain that the moniker you're using considers an anti-moniker to be its inverse.

The IMoniker::Inverse method is also called by the implementation of the IMoniker::RelativePathTo method, to assist in constructing a relative moniker.

Notes to Implementers

If your monikers have no internal structure, you can call the CreateAntiMoniker function in to get an anti-moniker in your implementation of IMoniker::Inverse. In your implementation of IMoniker::ComposeWith, you need to check for the inverse you supply in the implementation of IMoniker::Inverse.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CreateAntiMoniker, IMoniker::ComposeWith, IMoniker::RelativePathTo


IMoniker::IsEqual

Compares this moniker with a specified moniker and indicates whether they are identical.

HRESULT IsEqual(
  IMoniker *pmkOtherMoniker  //Pointer to moniker to be used for
                             //comparison
);

Parameter

pmkOtherMoniker
[in] Pointer to the IMoniker interface on the moniker to be used for comparison with this one (the one from which this method is called).

Return Values

S_OK
The two monikers are identical.
S_FALSE
The two monikers are not identical.

Remarks

Previous implementations of the Running Object Table (ROT) called this method. The current implementation of the ROT uses the IROTData interface instead.

Notes to Callers

Call this method to determine whether or not two monikers are identical. The reduced form of a moniker is considered different from the unreduced form. You should call the IMoniker::Reduce method before calling IMoniker::IsEqual, because a reduced moniker is in its most specific form. IMoniker::IsEqual may return S_FALSE on two monikers before they are reduced, and S_OK after they are reduced.

Notes to Implementers

Your implementation should not reduce the current moniker before performing the comparison. It is the caller's responsibility to call IMoniker::Reduce to compare reduced monikers.

Two monikers that compare as equal must hash to the same value using IMoniker::Hash.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::Reduce, IMoniker::Hash, IROTData


IMoniker::IsRunning

Determines whether the object identified by this moniker is currently loaded and running.

HRESULT IsRunning(
  IBindCtx *pbc,  //Pointer to bind context to be used
  IMoniker *pmkToLeft,
                  //Pointer to moniker to the left in the composite
  IMoniker *pmkNewlyRunning
                  //Pointer to moniker of a newly running object
);

Parameters

pbc
[in] Pointer to theIBindCtx interface on the bind context to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] Pointer to theIMoniker interface on the moniker to the left of this moniker if this moniker is part of a composite. This parameter is used primarily by moniker implementers to enable cooperation between the various components of a composite moniker; moniker clients can usually pass NULL.
pmkNewlyRunning
[in] Pointer to theIMoniker interface on the moniker most recently added to the Running Object Table (ROT). This can be NULL. If non-NULL, the implementation can return the results of calling IMoniker::IsEqual on the pmkNewlyRunning parameter, passing the current moniker. This parameter is intended to enable IMoniker::IsRunning implementations that are more efficient than just searching the ROT, but the implementation can choose to ignore pmkNewlyRunning without causing any harm.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The moniker is running.
S_FALSE
The moniker is not running.

Remarks

Notes to Callers

If speed is important when you're requesting services from the object identified by the moniker, you may want those services only if the object is already running (because loading an object into the running state may be time-consuming). In such a situation, you'd call IMoniker::IsRunning to determine if the object is running.

For the monikers stored within linked objects, IMoniker::IsRunning is primarily called by the default handler's implementation of IOleLink::BindIfRunning.

Notes to Implementers

To get a pointer to the Running Object Table (ROT), your implementation should call IBindCtx::GetRunningObjectTable on the pbc parameter. Your implementation can then call IRunningObjectTable::IsRunning to determine whether the object identified by the moniker is running. The object identified by the moniker must have registered itself with the ROT when it first began running.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleLink::BindIfRunning, IBindCtx::GetRunningObjectTable, IRunningObjectTable::IsRunning


IMoniker::IsSystemMoniker

Indicates whether this moniker is of one of the system-supplied moniker classes.

HRESULT IsSystemMoniker(
  DWORD *pdwMksys  //Pointer to value from MKSYS enumeration
);

Parameter

pdwMksys
[out] Pointer to an integer that is one of the values from the MKSYS enumeration and refers to one of the COM moniker classes. This parameter cannot be NULL.

Return Values

S_OK
The moniker is a system moniker.
S_FALSE
The moniker is not a system moniker.

Remarks

Notes to Callers

New values of the MKSYS enumeration may be defined in the future; therefore, you should explicitly test for each value you are interested in.

Notes to Implementers

Your implementation of this method must return MKSYS_NONE. You cannot use this function to identify your own monikers (for example, in your implementation of IMoniker::ComposeWith). Instead, you should use your moniker's implementation of IPersist::GetClassID or use IUnknown::QueryInterface to test for your own private interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersist::GetClassID, MKSYS


IMoniker::ParseDisplayName

Reads as many characters of the specified display name as it understands and builds a moniker corresponding to the portion read; this procedure is known as parsing the display name.

HRESULT ParseDisplayName(
  IBindCtx *pbc,        //Pointer to bind context to be used
  IMoniker *pmkToLeft,  //Pointer to moniker to the left in the
                        //composite
  LPOLESTR pszDisplayName,  //Pointer to display name
  ULONG *pchEaten,      //Pointer to number of characters consumed
  IMoniker **ppmkOut    //Address of output variable that receives
                        //the IMoniker interface pointer
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] Pointer to the IMoniker interface on the moniker that has been built out of the display name up to this point.
pszDisplayName
[in] Pointer to a zero-terminated string containing the remaining display name to be parsed. For Win32® applications, the LPOLESTR type indicates a wide character string (two bytes per character); otherwise, the string has one byte per character.
pchEaten
[out] Pointer to the number of characters in pszDisplayName that were consumed in this step.
ppmkOut
[out] Address of IMoniker* pointer variable that receives the interface pointer to the moniker that was built from pszDisplayName. When successful, the implementation must call IUnknown::AddRef on the new moniker; it is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation sets *ppmkOut to NULL.

Return Values

The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The parsing operation was completed successfully.
MK_E_SYNTAX
An error in the syntax of the input components (pmkToLeft, this moniker, and pszDisplayName). For example, a file moniker returns this error if pmkToLeft is non-NULL, and an item moniker returns it if pmkToLeft is NULL.
IMoniker::BindToObject errors
Parsing display names may cause binding. Therefore, any error associated with this function may be returned.

Remarks

Notes to Callers

Moniker clients do not typically call IMoniker::ParseDisplayName directly. Instead, they call the MkParseDisplayName function when they want to convert a display name into a moniker (for example, in implementing the Links dialog box for a container application, or for implementing a macro language that supports references to objects outside the document). That function first parses the initial portion of the display name itself.

It then calls IMoniker::ParseDisplayName on the moniker it has just created, passing the remainder of the display name and getting a new moniker in return; this step is repeated until the entire display name has been parsed.

Notes to Implementers

Your implementation may be able to perform this parsing by itself if your moniker class is designed to designate only certain kinds of objects. Otherwise, you must get an IParseDisplayName interface pointer for the object identified by the moniker-so-far (that is, the composition of pmkToLeft and this moniker) and then return the results of calling IParseDisplayName::ParseDisplayName.

There are different strategies for getting an IParseDisplayName pointer, as follows:

  • You can try to get the object's CLSID (by calling IPersist::GetClassID on the object) and then call the CoGetClassObject function, requesting the IParseDisplayName interface on the class factory associated with that CLSID.
  • You can try to bind to the object itself to get an IParseDisplayName pointer.
  • You can try binding to the object identified by pmkToLeft to get an IOleItemContainer pointer and then call IOleItemContainer::GetObject to get an IParseDisplayName pointer for the item.

Any objects that are bound should be registered with the bind context (see IBindCtx::RegisterObjectBound) to ensure that they remain running for the duration of the parsing operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IParseDisplayName, MkParseDisplayName


IMoniker::Reduce

Returns a reduced moniker—that is, another moniker that refers to the same object as this moniker but can be bound with equal or greater efficiency.

HRESULT Reduce(
  IBindCtx *pbc,         //Pointer to bind context to be used
  DWORD dwReduceHowFar,  //How much reduction should be done
  IMoniker **ppmkToLeft, //Address of output variable that receives
                         //the IMoniker interface pointer to the
                         //moniker to the left in the composite
  IMoniker **ppmkReduced //Address of output variable that receives
                         //the IMoniker interface pointer to the
                         //reduced moniker
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the moniker implementation should retrieve information about its environment. For more information, see IBindCtx.
dwReduceHowFar
[in] DWORD that specifies how far this moniker should be reduced. This parameter must be one of the values from the MKRREDUCE enumeration.
ppmkToLeft
[in, out] On entry, address of IMoniker* pointer variable that contains the interface pointer to moniker to the left of this moniker. This parameter is used primarily by moniker implementers to enable cooperation between the various components of a composite moniker; moniker clients can usually pass NULL.

On return, *ppmkToLeft is usually set to NULL, indicating no change in the original moniker to the left. In rare situations, *ppmkToLeft indicates a moniker, indicating that the previous moniker to the left should be disregarded and the moniker returned through *ppmkToLeft is the replacement. In such a situation, the implementation must call IUnknown::Release on the old moniker to the left of this moniker and must call IUnknown::AddRef on the new returned moniker; the caller must release it later. If an error occurs, the implementation can either leave the interface pointer unchanged or set it to NULL.

ppmkReduced
[out] Address of IMoniker* pointer variable that receives the interface pointer to the reduced form of this moniker, which can be NULL if an error occurs or if this moniker is reduced to nothing. If this moniker cannot be reduced, *ppmkReduced is simply set to this moniker and the return value is MK_S_REDUCED_TO_SELF. If *ppmkReduced is non-NULL, the implementation must call IUnknown::AddRef on the new moniker; it is the caller's responsibility to call IUnknown::Release. (This is true even if *ppmkReduced is set to this moniker.)

Return Values

The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following:

S_OK
This moniker was reduced.
MK_S_REDUCED_TO_SELF
This moniker could not be reduced any further, so ppmkReduced indicates this moniker.
MK_E_EXCEEDEDDEADLINE
The operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure.

Remarks

IMoniker::Reduce is intended for the following uses:

  • It enables the construction of user-defined macros or aliases as new kinds of moniker classes. When reduced, the moniker to which the macro evaluates is returned.
  • It enables the construction of a kind of moniker that tracks data as it moves about. When reduced, the moniker of the data in its current location is returned.
  • On file systems that support an identifier-based method of accessing files that is independent of filenames; a file moniker could be reduced to a moniker which contains one of these identifiers.

The intent of the MKRREDUCE flags passed in the dwReduceHowFar parameter is to provide the ability to programmatically reduce a moniker to a form whose display name is recognizable to the user. For example, paths in the file system, bookmarks in word-processing documents, and range names in spreadsheets are all recognizable to users. In contrast, a macro or an alias encapsulated in a moniker are not recognizable to users.

Notes to Callers

The scenarios described above are not currently implemented by the system-supplied moniker classes.

You should call IMoniker::Reduce before comparing two monikers using the IMoniker::IsEqual method because a reduced moniker is in its most specific form. IMoniker::IsEqual may return S_FALSE on two monikers before they are reduced and return S_OK after they are reduced.

Notes to Implementers

If the current moniker can be reduced, your implementation must not reduce the moniker in-place. Instead, it must return a new moniker that represents the reduced state of the current one. This way, the caller still has the option of using the nonreduced moniker (for example, enumerating its components). Your implementation should reduce the moniker at least as far as is requested.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::IsEqual, MKRREDUCE


IMoniker::RelativePathTo

Supplies a moniker that, when composed onto the end of this moniker (or one with a similar structure), yields the specified moniker.

HRESULT RelativePathTo(
  IMoniker *pmkOther,     //Pointer to moniker to which a relative
                          //path should be taken
  IMoniker **ppmkRelPath  //Address of output variable that receives
                          //the IMoniker interface pointer
);

Parameters

pmkOther
[in] Pointer to the IMoniker interface on the moniker to which a relative path should be taken.
ppmkRelPath
[out] Address of IMoniker* pointer variable that receives the interface pointer to the relative moniker. When successful, the implementation must call IUnknown::AddRef on the new moniker; it is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation sets *ppmkRelPath to NULL.

Return Values

The method supports the standard return values E_OUTOFMEMORY and

E_UNEXPECTED, as well as the following:

S_OK
A meaningful relative path has been returned.
MK_S_HIM
No common prefix is shared by the two monikers and the moniker returned in ppmkRelPath is pmkOther.
MK_E_NOTBINDABLE
This moniker is a relative moniker, such as an item moniker. This moniker must be composed with the moniker of its container before a relative path can be determined.

Remarks

A relative moniker is analogous to a relative path (such as "..\backup"). For example, suppose you have one moniker that represents the path "c:\projects\secret\art\pict1.bmp" and another moniker that represents the path "c:\projects\secret\docs\chap1.txt". Calling IMoniker::RelativePathTo on the first moniker, passing the second one as the pmkOther parameter, would create a relative moniker representing the path "..\docs\chap1.txt".

Notes to Callers

Moniker clients typically do not need to call IMoniker::RelativePathTo. This method is called primarily by the default handler for linked objects. Linked objects contain both an absolute and a relative moniker to identify the link source. (This enables link tracking if the user moves a directory tree containing both the container and source files.) The default handler calls this method to create a relative moniker from the container document to the link source. (That is, it calls IMoniker::RelativePathTo on the moniker identifying the container document, passing the moniker identifying the link source as the pmkOther parameter.)

If you do call IMoniker::RelativePathTo, call it only on absolute monikers—for example, a file moniker or a composite moniker whose leftmost component is a file moniker, where the file moniker represents an absolute path. Do not call this method on relative monikers.

Notes to Implementers

Your implementation of IMoniker::RelativePathTo should first determine whether pmkOther is a moniker of a class that you recognize and for which you can provide special handling (for example, if it is of the same class as this moniker). If so, your implementation should determine the relative path. Otherwise, it should pass both monikers in a call to the MonikerRelativePathTo function, which correctly handles the generic case.

The first step in determining a relative path is determining the common prefix of this moniker and pmkOther. The next step is to break this moniker and pmkOther into two parts each, say (P, myTail) and (P, otherTail) respectively, where P is the common prefix. The correct relative path is then the inverse of myTail composed with otherTail:

Comp( Inv( myTail ), otherTail )

where Comp() represents the composition operation and Inv() represents the inverse operation.

Note For certain types of monikers, you cannot use your IMoniker::Inverse method to construct the inverse of myTail. For example, a file moniker returns an anti-moniker as an inverse, while its IMoniker::RelativePathTo method must use one or more file monikers that each represent the path ".." to construct the inverse of myTail.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::Inverse, IMoniker::CommonPrefixWith, MonikerRelativePathTo


IMoniker--Anti-Moniker Implementation

Platform SDK: COM

IMoniker—Anti-Moniker Implementation

Anti-monikers are the inverse of the COM implementations of file, item, and pointer monikers. That is, an anti-moniker composed to the right of a file moniker, item moniker, or pointer moniker composes to nothing.

When To Use

If you're a moniker client, you typically do not need to use anti-monikers. When you need the inverse of a moniker, you should call IMoniker::Inverse. For example, if you need an inverse to remove the last piece of a composite moniker, use IMoniker::Enum to enumerate the pieces of the moniker and call IMoniker::Inverse on the rightmost piece. You shouldn't use an anti-moniker for this purpose because you can't be sure that the rightmost piece of a composite considers an anti-moniker to be its inverse.

The only situation in which you should explicitly use an anti-moniker is when you are writing a new moniker class and you have no special requirements for constructing inverses to your monikers. In that situation, you can return anti-monikers from your implementation of IMoniker::Inverse. In your implementation of IMoniker::ComposeWith, you should then annihilate one of your monikers for every anti-moniker you encounter.

Remarks

IMoniker::BindToObject
This method is not implemented. It returns E_NOTIMPL.
IMoniker::BindToStorage
This method is not implemented. It returns E_NOTIMPL.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If fOnlyIfNotGeneric is TRUE, this method sets ppmkComposite to NULL moniker and returns MK_E_NEEDGENERIC; otherwise, the method returns the result of combining the two monikers into a generic composite. Note that composing a file, item, or pointer moniker to the right of an anti-moniker produces a generic composite rather than composing to nothing, as would be the case if the order of composition were reversed.
IMoniker::Enum
This method returns S_OK and sets *ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if both are anti-monikers; otherwise, it returns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
This method checks the ROT to see whether the object is running.
IMoniker::GetTimeOfLastChange
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::Inverse
This method returns MK_E_NOINVERSE and sets *ppmk to NULL.
IMoniker::CommonPrefixWith
If the other moniker is also an anti-moniker, the method returns MK_S_US and sets ppmkPrefix to this moniker. Otherwise, the method calls the MonikerCommonPrefixWith function. This function correctly handles the case where the other moniker is a generic composite.
IMoniker::RelativePathTo
This method returns MK_S_HIM and sets *ppmkRelPath to the other moniker.
IMoniker::GetDisplayName
For each anti-moniker contained in this moniker, this method return one instance of "\..".
IMoniker::ParseDisplayName
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_ANTIMONIKER.

See Also

CreateAntiMoniker, IMoniker


IMoniker--Class Moniker Implementation

Platform SDK: COM

IMoniker—Class Moniker Implementation

Class monikers are monikers that represent an object class. Class monikers bind to the class object of the class for which they are created.

Class monikers are most useful in composition with other types of monikers, such as file monikers or item monikers. Class monikers may also be composed to the right of monikers supporting binding to the IClassActivator interface. This allows IClassActivator to provide access to the class object and instances of the class.

When to Use

To use class monikers, you must use the CreateClassMoniker function to create the monikers.

Remarks

IMoniker::BindToObject
If pmkLeft is NULL, calls CoGetClassObject, using the CLSID the class moniker was initialized with (in CreateClassMoniker or through MkParseDisplayName) and the CLSCTX of the current pbc (IBindContext).

If pmkLeft is non-NULL, calls pmkLeft->BindToObject for IClassActivator and calls IClassActivator::GetClassObject with the CLSID it was initialized with and the CLSCTX and LOCALE parameters of the current pbc (IBindContext).

This process is very roughly sketched out in the following code:

    BIND_OPTS2    bindOpts;
    IClassActivator *pActivate;

    bindOpts.cbStruct = sizeof(bindOpts);
    pbc->GetBindOptions(&bindOpts);

    if (NULL == pmkToLeft)
        return CoGetClassObject(<clsid>, bindOpts.dwClassContext, NULL, riid, ppvResult);

    pmkToLeft->BindToObject(pbc, NULL, IID_IClassActivator, (void **) &pActivate);
    hr = pActivate->GetClassObject(<clsid>, bindOpts.dwClassContext, bindOpts.locale, iid, ppvResult);
    pActivate->Release();
    return hr;
IMoniker::BindToStorage
This method forwards to the class moniker's BindToObject.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
Follows the contract and behaves like an Item Moniker in that it can return E_INVALIDARG and MK_E_NEEDGENERIC, and so forth.
IMoniker::Enum
This method returns S_OK and sets ppenumMoniker to NULL. May return E_INVALIDARG if ppenumMoniker is an invalid pointer.
IMoniker::IsEqual
This method returns S_OK if pmkOther is a class moniker constructed with the same CLSID information as itself. Otherwise, the method returns S_FALSE. May return E_INVALIDARG if pmkOther is an invalid pointer.
IMoniker::Hash
This method calculates a hash value for the moniker and returns S_OK. May return E_INVALIDARG if pdwHash is an invalid pointer.
IMoniker::IsRunning
Returns E_NOTIMPL.
IMoniker::GetTimeOfLastChange
Returns MK_E_UNAVAILABLE.
IMoniker::Inverse
This method returns an anti-moniker (that is, the results of calling CreateAntiMoniker).
IMoniker::CommonPrefixWith
If pmkOther IsEqual to this moniker, retrieves a pointer to this moniker and returns MK_S_US. If pmkOther is a class moniker but is not equal to this moniker, returns MK_E_NOPREFIX. Otherwise, returns the result of calling MonikerCommonPrefixWith with itself as pmkThis, pmkOther, and ppmkPrefix, which handles the case where pmkOther is a generic composite moniker.
IMoniker::RelativePathTo
This method returns the result of calling MonikerRelativePathTo with pmkSrc equal to this moniker, pmkOther, ppmkRelPath, and TRUE as dwReserved.
IMoniker::GetDisplayName
The display name for class monikers is of the following form:

display-name = "CLSID:" string-clsid-no-curly-braces *[";" clsid-options] ":"

clsid-options = clsid-param "=" value

clsid-param = none currently defined

Example:

clsid:a7b90590-36fd-11cf-857d-00aa006d2ea4:

IMoniker::ParseDisplayName
This method parses the display name by binding to itself for IParseDisplayName and asking the bound object to parse the display name into a moniker, as follows:
    hr = BindToObject(pbc, pmkToLeft, IID_IParseDisplayName, (void**)&ppdn);
    if (SUCCEEDED(hr)) {
        hr = ppdn->ParseDisplayName(pbc, lpszDisplayName, pchEaten, ppmkOut);
        ppdn->Release();
        }
    return hr;

This method tries to acquire an IParseDisplayName pointer, first by binding to the class factory for the object identified by the moniker and then by binding to the object itself. If either of these binding operations is successful, the file moniker passes the unparsed portion of the display name to the IParseDisplayName::ParseDisplayName method.

This method returns MK_E_SYNTAX if pmkToLeft is non-NULL.

IMoniker::IsSystemMoniker
This method returns S_OK, and passes back MKSYS_CLASSMONIKER.

See Also

CreateClassMoniker, IMoniker


IMoniker--File Moniker Implementation

Platform SDK: COM

IMoniker—File Moniker Implementation

File monikers are monikers that represent a path in the file system; a file moniker can identify any object that is saved in its own file. To identify objects contained within a file, you can compose monikers of other classes (for example, item monikers) to the right of a file moniker. However, the moniker to the left of a file moniker within a composite must be another file moniker, an anti-moniker, or a class moniker. It is illegal, for example, for an item moniker to appear to the left of a file moniker in a composite.

Note that an anti-moniker is the inverse of an entire file moniker, not the inverse of a component of the path that the moniker represents; that is, when you compose an anti-moniker to the right of a file moniker, the entire file moniker is removed. If you want to remove just the rightmost component of the path represented by a file moniker, you must create a separate file moniker based on the ".." path and then compose that to the end of the file moniker.

When to Use

If you're a moniker client (that is, you're using a moniker to get an interface pointer to an object), you typically don't need to know the class of the moniker you're using; you simply call methods using an IMoniker interface pointer.

If you're a moniker provider (that is, you're handing out monikers that identify your objects to make them accessible to moniker clients), you must use file monikers if the objects you're identifying are stored in files. If each object resides in its own file, file monikers are the only type you need. If the objects you're identifying are smaller than a file, you need to use another type of moniker (for example, item monikers) in addition to file monikers.

To use file monikers, you must use the CreateFileMoniker function to create the monikers. To allow your objects to be loaded when a file moniker is bound, your objects must implement the IPersistFile interface.

The most common example of moniker providers are COM server applications that support linking. If your COM server application supports linking only to file-based documents in their entirety, file monikers are the only type of moniker you need. If your COM server application supports linking to objects smaller than a document (such as sections of a document or embedded objects), you must use item monikers as well as file monikers.

Remarks

IMoniker::BindToObject
When pmkToLeft is NULL, the method looks for the moniker in the ROT and, if found, queries the retrieved object for the requested interface pointer. If the moniker is not found in the ROT, the method loads the object from the file system and retrieves the requested interface pointer.

If pmkLeft is not NULL, instead of determining the class to instantiate and initialize with the contents of the file referred to by the file moniker using GetClassFile (or other means), call pmkLeft->BindToObject for IClassFactory and IClassActivator, retrieve this pointer in pcf.

If this fails with E_NOINTERFACE, return MK_E_INTERMEDIATEINTERFACENOTSUPPORTED.

If the IClassFactory pointer is successfully retrieved, call pcf->CreateInstance(IID_IPersistFile, (void**)&ppf) to get a fresh instance of the class to be initialized and initialize it by using IPersistFile or other appropriate means per the existing initialization paths of File moniker.

IMoniker::BindToStorage
This method opens the file specified by the path represented by the moniker and returns an IStorage pointer to that file. The method supports binding to the IStorage interface only; if IStream or ILockBytes is requested in riid, the method returns E_UNSPEC, and if other interfaces are requested, this method returns E_NOINTERFACE. IStream and ILockBytes will be supported in future releases. Unless pmkToLeft is a class moniker, pmkToLeft should be NULL, as in the implementation of IMoniker::BindToObject. For situations where pmkToLeft is non-NULL, see the above description.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL. If pmkRight is a composite whose leftmost component is an anti-moniker, the returned moniker is the composite with the leftmost anti-moniker removed. If pmkRight is a file moniker, this method collapses the two monikers into a single file moniker, if possible. If not possible (for example, if both file monikers represent absolute paths, as in d:\work and e:\reports), the returned moniker is NULL and the return value is MK_E_SYNTAX. If pmkRight is neither an anti-moniker nor a file moniker, the method checks the fOnlyIfNotGeneric parameter; if it is FALSE, the method combines the two monikers into a generic composite; if it is TRUE, the method sets *ppmkComposite to NULL and returns MK_E_NEEDGENERIC.
IMoniker::Enum
This method returns S_OK and sets ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if *pmkOther is a file moniker and the paths for both monikers are identical (using a case-insensitive comparison). Otherwise, the method returns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkNewlyRunning is non-NULL, this method returns TRUE if that moniker is equal to this moniker. Otherwise, the method asks the ROT whether this moniker is running. The method ignores pmkToLeft.
IMoniker::GetTimeOfLastChange
If this moniker is in the ROT, this method returns the last change time registered there; otherwise, it returns the last write time for the file. If the file cannot be found, this method returns MK_E_NOOBJECT.
IMoniker::Inverse
This method returns an anti-moniker (that is, the results of calling CreateAntiMoniker).
IMoniker::CommonPrefixWith
If both monikers are file monikers, this method returns a file moniker that is based on the common components at the beginning of two file monikers. Components of a file moniker can be of the following types:
  • A machine name of the form \\server\share. A machine name is treated as a single component, so two monikers representing the paths "\\myserver\public\work" and "\\myserver\private\games" do not have "\\myserver" as a common prefix.
  • A drive designation (for example, "C:").
  • A directory or filename.

If the other moniker is not a file moniker, this method passes both monikers in a call to the MonikerCommonPrefixWith function. This function correctly handles the case where the other moniker is a generic composite.

This method returns MK_E_NOPREFIX if there is no common prefix.

IMoniker::RelativePathTo
This method computes a moniker which when composed to the right of this moniker yields the other moniker. For example, if the path of this moniker is "C:\work\docs\report.doc" and if the other moniker is "C:\work\art\picture.bmp", the path of the computed moniker would be "..\..\art\picture.bmp".
IMoniker::GetDisplayName
This method returns the path that the moniker represents. If this method is called by a 16-bit application, the method checks to see whether the specified file exists and, if so, returns a short name for that file because 16-bit applications are not equipped to handle long filenames.
IMoniker::ParseDisplayName
This method tries to acquire an IParseDisplayName pointer, first by binding to the class factory for the object identified by the moniker and then by binding to the object itself. If either of these binding operations is successful, the file moniker passes the unparsed portion of the display name to the IParseDisplayName::ParseDisplayName method.

This method returns MK_E_SYNTAX if pmkToLeft is non-NULL.

IMoniker::IsSystemMoniker
This method returns S_OK, and passes back MKSYS_FILEMONIKER.

See Also

CreateFileMoniker, IMoniker, IPersistFile


IMoniker--Generic Composite Moniker Implementation

Platform SDK: COM

IMoniker—Generic Composite Moniker Implementation

A generic composite moniker is a composite moniker whose components have no special knowledge of each other.

Composition is the process of joining two monikers together. Sometimes two monikers of specific classes can be combined in a special manner; for example, a file moniker representing an incomplete path and another file moniker representing a relative path can be combined to form a single file moniker representing the complete path. This is an example of nongeneric composition. Generic composition, on the other hand, can connect any two monikers, no matter what their classes. Because a nongeneric composition depends on the class of the monikers involved, it can be performed only by a particular class's implementation of the IMoniker::ComposeWith method. You can define new types of nongeneric compositions if you write a new moniker class. By contrast, generic compositions are performed by the CreateGenericComposite function.

When to Use

If you're a moniker client (that is, you're using a moniker to get an interface pointer to an object), you typically don't need to know the class of the moniker you're using, or whether it is a generic composite or a nongeneric composite; you simply call methods using an IMoniker interface pointer.

If you're a moniker provider (that is, you're handing out monikers that identify your objects to make them accessible to moniker clients), you may have to compose two monikers together. (For example, if you are using an item moniker to identify an object, you must compose it with the moniker identifying the object's container before you hand it out.) You use the IMoniker::ComposeWith method to do this, calling the method on the first moniker and passing the second moniker as a parameter; this method may produce either a generic or a nongeneric composite.

The only time you should explicitly create a generic composite moniker is when you are writing your own moniker class. In your implementation of IMoniker::ComposeWith, you should attempt to perform a nongeneric composition whenever possible; if you cannot perform a nongeneric composition and generic composition is acceptable, you can call the CreateGenericComposite function to create a generic composite moniker.

Remarks

IMoniker::BindToObject
If pmkToLeft is NULL, this method looks for the moniker in the ROT and, if found, queries the retrieved object for the requested interface pointer. If pmkToLeft is not NULL, the method recursively calls IMoniker::BindToObject on the rightmost component of the composite, passing the rest of the composite as the pmkToLeft parameter for that call.
IMoniker::BindToStorage
This method recursively calls BindToStorage on the rightmost component of the composite, passing the rest of the composite as the pmkToLeft parameter for that call.
IMoniker::Reduce
This method recursively calls Reduce for each of its component monikers. If any of the components reduces itself, the method returns S_OK and passes back a composite of the reduced components. If no reduction occurred, the method passes back the same moniker and returns MK_S_REDUCED_TO_SELF.
IMoniker::ComposeWith
If fOnlyIfNotGeneric is TRUE, this method sets *pmkComposite to NULL and returns MK_E_NEEDGENERIC; otherwise, the method returns the result of combining the two monikers by calling the CreateGenericComposite function.
IMoniker::Enum
If successful, this method returns S_OK and passes back an enumerator that enumerates the component monikers that make up the composite; otherwise, the method returns E_OUTOFMEMORY.
IMoniker::IsEqual
This method returns S_OK if the components of both monikers are equal when compared in the left-to-right order.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkToLeft is non-NULL, this method composes pmkToLeft with this moniker and calls IsRunning on the result.

If pmkToLeft is NULL, this method returns TRUE if pmkNewlyRunning is non-NULL and is equal to this moniker.

If pmkToLeft and pmkNewlyRunning are both NULL, this method checks the ROT to see whether the moniker is running. If so, the method returns S_OK; otherwise, it recursively calls IMoniker::IsRunning on the rightmost component of the composite, passing the remainder of the composite as the pmkToLeft parameter for that call. This handles the case where the moniker identifies a pseudo-object that is not registered as running; see the Item Moniker implementation of IMoniker::IsRunning.

IMoniker::GetTimeOfLastChange
This method creates a composite of pmkToLeft (if non-NULL) and this moniker and uses the ROT to retrieve the time of last change. If the object is not in the ROT, the method recursively calls IMoniker::GetTimeOfLastChange on the rightmost component of the composite, passing the remainder of the composite as the pmkToLeft parameter for that call.
IMoniker::Inverse
This method returns a composite moniker that consists of the inverses of each of the components of the original composite, stored in reverse order. For example, if the inverse of A is Inv( A ), the inverse of the composite of A, B, and C is Comp( Inv( C ), Inv( B ), Inv( A ) ).
IMoniker::CommonPrefixWith
If the other moniker is a composite, this method compares the components of each composite from left to right. The returned common prefix moniker might also be a composite moniker, depending on how many of the leftmost components were common to both monikers. If the other moniker is not a composite, the method simply compares it to the leftmost component of this moniker.

If the monikers are equal, the method returns MK_S_US and sets ppmkPrefix to this moniker. If the other moniker is a prefix of this moniker, the method returns MK_S_HIM and sets ppmkPrefix to the other moniker. If this moniker is a prefix of the other, this method returns MK_S_ME and sets ppmkPrefix to this moniker.

If there is no common prefix, this method returns MK_E_NOPREFIX and sets ppmkPrefix to NULL.

IMoniker::RelativePathTo
This method finds the common prefix of the two monikers and creates two monikers that consist of the remainder when the common prefix is removed. Then it creates the inverse for the remainder of this moniker and composes the remainder of the other moniker on the right of it.
IMoniker::GetDisplayName
This method returns the concatenation of the display names returned by each component moniker of the composite.
IMoniker::ParseDisplayName
This method recursively calls IMoniker::ParseDisplayName on the rightmost component of the composite, passing everything else as the pmkToLeft parameter for that call.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_GENERICCOMPOSITE.

See Also

CreateGenericComposite, IMoniker


IMoniker--Item Moniker Implementation

Platform SDK: COM

IMoniker—Item Moniker Implementation

Item monikers are used to identify objects within containers, such as a portion of a document, an embedded object within a compound document, or a range of cells within a spreadsheet. Item monikers are often used in combination with file monikers; a file moniker is used to identify the container while an item moniker is used to identify the item within the container.

An item moniker contains a text string; this string is used by the container object to distinguish the contained item from the others. The container object must implement the IOleItemContainer interface; this interface enables the item moniker code to acquire a pointer to an object, given only the string that identifies the object.

When to Use

If you're a moniker client (that is, you're using a moniker to get an interface pointer to an object), you typically don't need to know the class of the moniker you're using; you simply call methods using an IMoniker interface pointer.

If you're a moniker provider (that is, you're handing out monikers that identify your objects to make them accessible to moniker clients), you must use item monikers if the objects you're identifying are contained within another object and can be individually identified using a string. You'll also need to use another type of moniker (for example, file monikers) to identify the container object.

To use item monikers, you must use the CreateItemMoniker function to create the monikers. To allow your objects to be loaded when an item moniker is bound, the container of your objects must implement the IOleItemContainer interface.

The most common example of moniker providers are COM applications that support linking. If your COM application supports linking to objects smaller than a file-based document, you need to use item monikers. For a server application that allows linking to a selection within a document, you use the item monikers to identify those objects. For a container application that allows linking to embedded objects, you use the item monikers to identify the embedded objects.

Remarks

IMoniker::BindToObject
If pmkToLeft is NULL, this method returns E_INVALIDARG. Otherwise, the method calls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemContainer interface pointer. The method then calls IOleItemContainer::GetObject, passing the string contained within the moniker, and returns the requested interface pointer.
IMoniker::BindToStorage
If pmkToLeft is NULL, this method returns E_INVALIDARG. Otherwise, the method calls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemContainer interface pointer. The method then calls IOleItemContainer::GetObjectStorage for the requested interface.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL; if pmkRight is a composite whose leftmost component is an anti-moniker, the returned moniker is the composite after the leftmost anti-moniker is removed. If pmkRight is not an anti-moniker, the method combines the two monikers into a generic composite if fOnlyIfNotGeneric is FALSE; if fOnlyIfNotGeneric is TRUE, the method returns a NULL moniker and a return value of MK_E_NEEDGENERIC.
IMoniker::Enum
This method returns S_OK and sets *ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if both monikers are item monikers and their display names are identical (using a case-insensitive comparison); otherwise, the method returns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkToLeft is NULL, this method returns TRUE if pmkNewlyRunning is non-NULL and is equal to this moniker. Otherwise, the method checks the ROT to see whether this moniker is running.

If pmkToLeft is non-NULL, the method calls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemContainer interface pointer. The method then calls IOleItemContainer::IsRunning, passing the string contained within this moniker.

IMoniker::GetTimeOfLastChange
If pmkToLeft is NULL, this method returns MK_E_NOTBINDABLE. Otherwise, the method creates a composite of pmkToLeft and this moniker and uses the ROT to access the time of last change. If the object is not in the ROT, the method calls IMoniker::GetTimeOfLastChange on the pmkToLeft parameter.
IMoniker::Inverse
This method returns an anti-moniker (that is, the results of calling CreateAntiMoniker).
IMoniker::CommonPrefixWith
If the other moniker is an item moniker that is equal to this moniker, this method sets *ppmkPrefix to this moniker and returns MK_S_US; otherwise, the method calls the MonikerCommonPrefixWith function. This function correctly handles the case where the other moniker is a generic composite.
IMoniker::RelativePathTo
This method returns MK_E_NOTBINDABLE and sets *ppmkRelPath to NULL.
IMoniker::GetDisplayName
This method returns the concatenation of the delimiter and the item name that were specified when the item moniker was created.
IMoniker::ParseDisplayName
If pmkToLeft is NULL, this method returns MK_E_SYNTAX. Otherwise, the method calls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemContainer interface pointer. The method then calls IOleItemContainer::GetObject, requesting an IParseDisplayName interface pointer to the object identified by the moniker, and passes the display name to IParseDisplayName::ParseDisplayName.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_ITEMMONIKER.

See Also

CreateItemMoniker, IMoniker, IOleItemContainer


IMoniker--OBJREF Moniker Implementation

Platform SDK: COM

IMoniker—OBJREF Moniker Implementation

OBJREF monikers represent a reference to an object instance that is running on an out-of-process server, either locally or remotely. The moniker identifies the object instance and the computer the object is running on.

An OBJREF moniker is similar in many ways to a pointer moniker, except that the running object is out-of-process. A client can call IMoniker::BindToObject on an OBJREF moniker and use the pointer it obtains to access the running object, regardless of its location.

An important distinction from a pointer moniker is that the display name of an OBJREF moniker can be embedded in an HTML page, and the running object represented by the moniker can be bound by a client script, applet, or ActiveX® control.

When to Use

The primary use for an OBJREF moniker is to obtain access to a running object instance over the Internet. An active server page or some other means of generating dynamic HTML content places the display name of an OBJREF moniker in a parameter to an applet or an ActiveX control. The code of the applet or control calls CreateObjrefMoniker to create an OBJREF moniker based on the display name, and it then calls IMoniker::BindToObject on the resulting OBJREF moniker to get access to the running object instance. The active server page then marshals a pointer to the running object back to the page's client.

Remarks

IMoniker::BindToObject
For OBJREF monikers, the pmkToLeft parameter must be NULL. Because the OBJREF moniker represents a running object, no activation takes place. If the represented object is no longer running, BindToObject fails with E_UNEXPECTED.
IMoniker::BindToStorage
This method obtains a marshaled pointer to the requested interface on the storage that contains the running object. Because the OBJREF moniker represents a running object, no activation takes place. If the represented object is no longer running, BindToStorage fails with E_UNEXPECTED.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL. If pmkRight is a composite whose leftmost component is an anti-moniker, the returned moniker is the composite with the leftmost anti-moniker removed. If pmkRight is neither an anti-moniker nor a composite moniker whose leftmost component is an anti-moniker, the method checks the fOnlyIfNotGeneric parameter. If it is FALSE, the method combines the two monikers into a generic composite; if it is TRUE, the method sets *ppmkComposite to NULL and returns MK_E_NEEDGENERIC.
IMoniker::Enum
This method returns S_OK and sets ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if *pmkOther is an OBJREF moniker and the paths for both monikers are identical (using a case-insensitive comparison). Otherwise, the method returns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
Because OBJREF monikers represent a running object instance, this method returns TRUE unless the object is known to be no longer running because a recent call failed. The method ignores pmkToLeft.
IMoniker::GetTimeOfLastChange
This method returns E_NOTIMPL.
IMoniker::Inverse
This method returns an anti-moniker (that is, the results of calling CreateAntiMoniker).
IMoniker::CommonPrefixWith
If the two monikers are equal, this method returns MK_S_US and sets *ppmkPrefix to NULL. If the other moniker is not an OBJREF moniker, this method passes both monikers to the MonikerCommonPrefixWith function. This function correctly handles the case where the other moniker is a generic composite.

If there is no common prefix, this method returns MK_E_.

IMoniker::RelativePathTo
This method returns E_NOTIMPL.
IMoniker::GetDisplayName
This method obtains the display name for the OBJREF moniker. The display name is a 64-bit encoding that encapsulates the machine location, process endpoint, and interface pointer ID (IPID) of the running object. For future compatibility, the display name is restricted to characters that can be specified as part of a URL.
IMoniker::ParseDisplayName
If pmkToLeft is not NULL, this method returns MK_E_SYNTAX.
IMoniker::IsSystemMoniker
This method returns S_OK and passes back MKSYS_OBJREFMONIKER.

See Also

CreateObjrefMoniker, IMoniker


IMoniker--Pointer Moniker Implementation

Platform SDK: COM

IMoniker—Pointer Moniker Implementation

A pointer moniker essentially wraps an interface pointer so that it looks like a moniker and can be passed to those interfaces that require monikers. Binding a pointer moniker is done by calling the pointer's QueryInterface method.

Instances of pointer monikers refuse to be serialized; that is, IPersistStream::Save will return an error. These monikers can, however, be marshaled to a different process in an RPC call; internally, the system marshals and unmarshals the pointer by using the standard paradigm for marshaling interface pointers.

When to Use

Pointer monikers are rarely needed. Use pointer monikers only if you need monikers to identify objects that have no persistent representation. Pointer monikers allow such objects to participate in a moniker-binding operation.

Remarks

IMoniker::BindToObject
This method queries the wrapped pointer for the requested interface.
IMoniker::BindToStorage
This method queries the wrapped pointer for the requested interface.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL; if pmkRight is a composite whose leftmost component is an anti-moniker, the returned moniker is the composite after the leftmost anti-moniker is removed. If fOnlyIfNotGeneric is FALSE, the returned moniker is a generic composite of the two monikers; otherwise, the method sets *ppmkComposite to NULL and returns MK_E_NEEDGENERIC.
IMoniker::Enum
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::IsEqual
This method returns S_OK only if both are pointer monikers and the interface pointers that they wrap are identical.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
This method always returns S_OK, because the object identified by a pointer moniker must always be running.
IMoniker::GetTimeOfLastChange
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::Inverse
This method returns an anti-moniker (that is, the results of calling CreateAntiMoniker).
IMoniker::CommonPrefixWith
If the two monikers are equal, this method returns MK_S_US and sets *ppmkPrefix to this moniker. Otherwise, the method returns MK_E_NOPREFIX and sets *ppmkPrefix to NULL.
IMoniker::RelativePathTo
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::GetDisplayName
This method is not implemented. (That is, it returns E_NOTIMPL.)
IMoniker::ParseDisplayName
This method queries the wrapped pointer for the IParseDisplayName interface and passes the display name to IParseDisplayName::ParseDisplayName.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_POINTERMONIKER.

See Also

IMoniker, CreatePointerMoniker


IMoniker--URL Moniker Implementation

Platform SDK: COM

IMoniker—URL Moniker Implementation

The URL moniker implementation of IMoniker is found on a URL moniker object, which also supports IUnknown and the IAsyncMoniker interface. The IMoniker interface inherits its definition from IPersistStream as well as IUnknown, and IPersistStream inherits from IPersist. Therefore, the IMoniker implementation includes support for IPersistStream and IPersist.

The IAsyncMoniker interface is simply IUnknown. (There are no additional methods.) It is is used to allow clients to determine whether a moniker supports asynchronous binding.

To get a pointer to the IMoniker interface on this object, call the CreateURLMoniker function.

Note The current URL Moniker implementation of IMoniker does not support asynchronous storage. Future implementations will do so.

When to Use

If you're a moniker client (that is, you're using a moniker to get an interface pointer to an object), you typically don't need to know the class of the moniker you're using; you simply call methods using an IMoniker interface pointer.

If you're a moniker provider (that is, you're handing out monikers that identify your objects to make them accessible to moniker clients), you must use item monikers if the objects you're identifying are contained within another object and can be individually identified using a string. You'll also need to use another type of moniker (for example, file monikers) to identify the container object.

To use item monikers, you must use the CreateItemMoniker function to create the monikers. To allow your objects to be loaded when an item moniker is bound, the container of your objects must implement the IOleItemContainer interface.

The most common example of moniker providers are COM applications that support linking. If your COM application supports linking to objects smaller than a file-based documents, you need to use item monikers. For a server application that allows linking to a selection within a document, you use the item monikers to identify those objects. For a container application that allows linking to embedded objects, you use the item monikers to identify the embedded objects.

Remarks

IMoniker::BindToObject
Since the URL Moniker supports asynchronous binding, the actual return value of its BindToObject may vary depending on the object parameters established in the bind context, however the semantics of the bind operation are identical regardless of synchronous or asynchronous usage, and are as follows:
  1. The URL Moniker pulls further information for the bind operation from the bind context. For example, the moniker can obtain pointers to the IBindStatusCallback and IEnumFORMATETC interfaces that are registered in the bind context. Further information can include additional bind options specified on the bind context through IBindCtx::SetBindOptions, such as the dwTickCountDeadline parameter or the grfFlags value of BIND_MAYBOTHERUSER.
  2. Next the moniker checks the Running Object Table of the bind context to determine whether the referenced object is already running. The moniker can obtain this information with the following calls:
    IBindCtx::GetRunningObjectTable(&prot)
prot->IsRunning(this)
  3. If the object is already running, the moniker retrieves the running object with the following call:
    prot->GetObject(this, &punk)
  4. Then the moniker calls QueryInterface for the requested interface.
  5. Otherwise, the moniker queries the client by calling IBindStatusCallback::GetBindInfo to obtain additional bind information. The moniker then initiates the bind operation and passes the resulting IBinding interface back to the client by calling IBindStatusCallback::OnStartBinding.
  6. If in step 1 it was determined that this was an asynchronous bind, BindToObject returns MK_S_ASYNCHRONOUS at this point with NULL in ppv. The caller will receive the actual object pointer during the IBindStatusCallback::OnObjectAvailable method at some later point. The following steps then occur asynchronously to the caller, typically on another thread of execution.
  7. The class of the resource designated by the URL Moniker is determined in one of the following ways:
  • The URL Moniker examines the media type of the data. If the media type is application/x-oleobject, the first 16-bytes of the actual data (Content-Body) contain the CLSID of the resource and subsequent data is to be interpreted by the class itself. For all other media types, URL Moniker looks in the system registry for the HKEY_CLASSES_ROOT\MIME\Database\Content-Type\<media-type>\CLSID key. Note that application/x-oleobject will be used until application/oleobject is approved.
  • The URL Moniker matches portions of arriving data to patterns registered in the system registry under HKEY_CLASSES_ROOT\FileTypes.
  • Finally, if all else fails, the URL Moniker correlates the trailing extension of the resource, if any, to a CLSID using the HKEY_CLASSES_ROOT\.??? keys in the system registry, as is done by GetClassFile and the shell.
  1. Having determined the class, the URL moniker creates an instance using CoCreateInstance of CLSCTX_SERVER asking for the IUnknown interface.
  2. The URL Moniker next calls the QueryInterface method of the newly created object for the IPersistMoniker interface. If the QueryInterface is successful, the URL Moniker calls IPersistMoniker::Load passing itself (this) as the moniker parameter. The object typically calls IMoniker::BindToStorage asking for the storage interface that they're interested in.
  3. Otherwise, the URL Moniker calls the QueryInterface method for IPersistStream and, if successful, calls IPersistStream::Load, passing the object an IStream pointer for a stream object that is being filled asynchronously by the transport.

    If the class being called is not marked with the category CATID_AsyncAware, calls to IStream::Read or IStream::Write that reference data not yet available block until the data becomes available. These calls block in the traditional COM sense. A message loop is entered which allows certain messages to be processed, and the IMessageFilter of the thread is called appropriately.

    If the class is marked with the category CATID_AsyncAware, calls to IStream::Read or IStream::Write that reference data not yet available return E_PENDING.

  4. Otherwise, the URL Moniker calls QueryInterface for IPersistFile and, if successful, completes the download into a temporary file. On completion, the URL Moniker calls IPersistFile::Load. The created file is cached along with other Internet downloaded data. The client must be sure not to delete this file.
  5. When the object returns from one of the various IPersistXxx::Load calls described in the previous steps, the URL Moniker calls the IBindStatusCallback::OnObjectAvailable method to return the interface pointer that the client originally requested when the client called IMoniker::BindToObject.
IMoniker::BindToStorage
The system implementation of URL Monikers supports the BindToStorage for stream objects on all URLs and for storage objects in the case where the designated resource is a compound file. Future support for ILockBytes may also be added.

Because the URL Moniker supports asynchronous binding, the actual return value of its BindToStorage may vary depending on the object parameters established in the bind context. However, the semantics of the bind operation are identical regardless of synchronous or asynchronous usage, as follows:

  1. The URL Moniker pulls further information for the bind operation from the bind context. For example, the moniker can obtain pointers to the IBindStatusCallback and IEnumFORMATETC interfaces that are registered in the bind context. Further information can include additional bind options specified on the bind context through IBindCtx::SetBindOptions, such as the dwTickCountDeadline parameter or the grfFlags value of BIND_MAYBOTHERUSER. The moniker then queries the client by calling IBindStatusCallback::GetBindInfo and initiates the bind operation with the transport and passes the resulting IBinding to the client by calling IBindStatusCallback::OnStartBinding.
  2. If the caller requested an asynchronous IStream or IStorage by specifying the BINDF_ASYNCSTORAGE flag in the BINDINFO structure retrieved from the IBindStatusCallback::GetBindInfo, method the URL moniker returns the object as soon as possible. Calls to these IStorage or IStream objects that reference data not yet available return E_PENDING.
  3. If the caller does not specify asynchronous IStream or IStorage as described above, the URL Moniker will still return an object through the IBindStatusCallback::OnDataAvailable method as soon as possible. However, calls to these objects that reference data not yet available will block until the data becomes available. For some applications, this will require the least modification of their existing I/O code yet may still result in improved performance depending on their access patterns.
IMoniker::Reduce
Returns MK_S_REDUCED_TO_SELF and itself (this) in *ppmkReduced.
IMoniker::ComposeWith
URL Monikers support composition of two URLs: a base URL composed with a relative URL. This composition is done according to the RFC on relative URLs. URL Monikers do not currently support the composition of a base URL moniker with a relative file moniker, although this may be supported in the future.

However, URL Monikers do support generic composition. If fOnlyIfNotGeneric==TRUE, the ComposeWith method returns MK_E_NEEDGENERIC. Otherwise, this method simply returns CreateGenericComposite(this, pmkRight, ppmkComposite). See the Win32® documentation for more information about IMoniker::ComposeWith.

IMoniker::Enum
Returns S_OK and sets *ppenumMoniker to NULL, indicating that the moniker does not contain sub-monikers.
IMoniker::IsEqual
Returns S_FALSE if the other moniker (pmkOtherMoniker) is not an URL moniker, which it checks using IPersist::GetClassID to see whether the CLSID is CLSID_URLMoniker. If the other moniker is an URL moniker, it compares the display names of the monikers for equality, returning S_OK if they are identical or S_FALSE if not.
IMoniker::Hash
Creates a hash value based on the URL string of the moniker. This hash value is identical when URL strings are identical, although it may also be identical for different URL strings. This method is used to speed up comparisons by reducing the amount of time that it is necessary to call IMoniker::IsEqual.
IMoniker::IsRunning
Returns S_OK if this moniker is currently running. Otherwise, it returns S_FALSE. The URL Moniker determines whether it is running by first checking whether it is equal to the newly running moniker, by making the following call:
pmkNewlyRunning->IsEqual

Typically, this call is an inexpensive operation. If this does not succeed, the moniker next checks to see whether it is registered with the Running Object Table of the passed-in bind context.

IMoniker::GetTimeOfLastChange
Returns the time of last change of an object that is registered in the Running Object Table.
IMoniker::Inverse
Returns MK_E_NOINVERSE.
IMoniker::CommonPrefixWith
Currently returns E_NOTIMPL. In the future, it may properly compute the proper common prefix of two URL monikers. See the Win32 documentation about IMoniker::CommonPrefixWith for details.
IMoniker::RelativePathTo
Currently returns E_NOTIMPL. In the future, it may properly compute the relative path between two URL monikers. See the Win32 documentation about IMoniker::RelativePathTo for details.
IMoniker::GetDisplayName
THe URL Moniker attempts to return its full URL string. If the moniker was created with a partial URL string (see CreateURLMoniker), it will first attempt to find an URL moniker in the bind context under SZ_URLCONTEXT and will next look to the moniker to its left for contextual information. If it cannot return its full URL string, it will return its partial URL string.
IMoniker::ParseDisplayName
Parses a full or partial URL string into a result moniker (ppmkOut). If szDisplayName represents a full URL string (for example, "http://foo.com/default.html"), the result is a new full URL moniker. If szDisplayName represents a partial URL string (for example, "..\default.html"), the result is a full URL that takes its context from either the bind context's SZ_URLCONTEXT object-parameter or from this URL moniker. For example, if the context moniker was "http://foo.com/pub/list.html" and szDisplayName was "..\default.html", the resulting URL moniker would represent "http://foo.com/default.html".
IMoniker::IsSystemMoniker
Returns S_TRUE and MKSYS_URLMONIKER in *pdwMksys.

See Also

CreateURLMoniker, IPersistStream - URL Moniker Implementation


IMultiQI

The IMultiQI interface enables a client to query an object proxy, or handler, for multiple interfaces by using a single RPC call. By using this interface, instead of relying on separate calls to IUnknown::QueryInterface, clients can reduce the number of RPC calls that have to cross thread, process, or machine boundaries and, therefore, the amount of time required to obtain the requested interface pointers.

When to Implement

You never have to implement this interface because there is no situation in which it is required. COM server applications that rely on COM's standard remoting support get the interface for free because COM implements it on every object proxy. The only situation in which you might want to implement this interface yourself is when you are writing a custom marshaler that handles interface remoting. Even here, implementing IMultiQI yourself is not recommended, particularly if your object is aggregatable.

When to Use

When more than one interface pointer is sought, client applications should QueryInterface for IMultiQI and use it if available.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IMultiQI Methods | Description
QueryMultipleInterfaces | Queries for multiple interfaces.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

IUnknown::QueryInterface


IMultiQI::QueryMultipleInterfaces

Fills a caller-provided array of structures with pointers to multiple interfaces. Calling this method is equivalent to issuing a series of separate QueryInterface calls except that you do not incur the overhead of a corresponding number of RPC calls. In multithreaded applications and distributed environments, keeping RPC calls to a minimum is essential for optimal performance.

HRESULT QueryMultipleInterfaces(
  ULONG cMQIs,     //Number of structures in array
  MULTI_QI *pMQIs  //Pointer to first structure in array
);

Parameters

cMQIs
[in] Pointer to the number of elements in an array of MULTI_QI structures, each of which contains the IID of a single interface.
pMQIs
[in, out] Pointer to the first MULTI_QI structure in the array.

Return Values

S_OK
Pointers were returned to all requested interfaces.
S_FALSE
Pointers were returned to some, but not all, of the requested interfaces.
E_NOINTERFACE
Pointers were returned to none of the requested interfaces.

Remarks

The QueryMultipleInterfaces method takes as input an array of MULTI_QI structures. Each structure specifies an interface IID and contains two additional blank fields for receiving an interface pointer and return value.

This method obtains as many requested interface pointers as possible directly from the object proxy. For each interface not implemented on the proxy, the method calls the server to obtain a pointer. Upon receiving an interface pointer from the server, the method builds a corresponding interface proxy and returns its pointer along with pointers to the interfaces it already implements.

Notes to Callers

A caller should begin by querying the object proxy for the IMultiQI interface. If the object proxy returns a pointer to this interface, the caller should then create a MULTI_QI structure for each interface it wants to obtain. Each structure should specify an interface IID and set its pItf member to NULL. Failure to set the pItf member to NULL will cause the object proxy to ignore the structure.

On return, QueryMultipleInterfaces writes the requested interface pointer and a return value into each MULTI_QI structure in the client's array. The pItf field receives the pointer; the hr field receives the return value.

If the value returned from a call to QueryMultipleInterfaces is S_OK, then pointers were returned for all requested interfaces. If the return value is E_NOINTERFACE, then pointers were returned for none of the requested interfaces. If the return value is S_FALSE, then pointers to one or more requested interfaces were not returned.In this event, the client should check the hr field of each MULTI_QI structure to determine which interfaces were acquired and which were not.

If a client knows ahead of time that it will be using several of an object's interfaces, it can call QueryMultipleInterfaces up front and then, later, if a QueryInterface is done for one of the interfaces already acquired through QueryMultipleInterfaces, no RPC call will be necessary.

On return, the caller should check the hr field of each MULTI_QI structure to determine which interface pointers were and were not returned.

The client is responsible for releasing each of the acquired interfaces by calling IUnknown::Release.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

IUnknown


IOleItemContainer

The IOleItemContainer interface is used by item monikers when they are bound to the objects they identify.

When any container of objects uses item monikers to identify its objects, it must define a naming scheme for those objects. The container's IOleItemContainer implementation uses knowledge of that naming scheme to retrieve an object given a particular name. Item monikers use the container's IOleItemContainer implementation during binding.

When to Implement

You must implement IOleItemContainer if you're a moniker provider handing out item monikers. Being a moniker provider means handing out monikers that identify your objects to make them accessible to moniker clients. You must use item monikers if the objects you're identifying are contained within another object and can be individually identified using a string.

The most common example of moniker providers are OLE applications that support linking. If your OLE application supports linking to objects smaller than a file-based document, you need to use item monikers. For a server application that allows linking to a portion of a document (such as selections within a document), you use the item monikers to identify those objects. For a container application that allows linking to embedded objects, you use the item monikers to identify the embedded objects.

You must define a naming scheme for identifying the objects within the container; for example, embedded objects in a document could be identified with names of the form "embedobj1," "embedobj2," and so forth, while ranges of cells in a spreadsheet could be identified with names of the form "A1:E7," "G5:M9," and so forth. (Ranges of cells in a spreadsheet are examples of "pseudo-objects" because they do not have their own persistent storage, but simply represent a portion of the container's internal state.) You create an item moniker that represents an object's name using the CreateItemMoniker function and hand it out to a moniker client. When an item moniker is bound, your implementation of IOleItemContainer must be able to take a name and retrieve the corresponding object.

When to Use

Applications typically do not call IOleItemContainer methods directly. The item moniker implementation of IMoniker is the primary caller of IOleItemContainer methods.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IParseDisplayName Method | Description
ParseDisplayName | Parses object's display name to form moniker.
IOleContainer Methods | Description
EnumObjects | Enumerates objects in a container.
LockContainer | Keeps container running until explicitly released.
IOleItemContainer Methods | Description
GetObject | Returns a pointer to a specified object.
GetObjectStorage | Returns a pointer to an object's storage.
IsRunning | Checks whether an object is running.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CreateItemMoniker, IMoniker – Item Moniker Implementation


IOleItemContainer::GetObject

Returns a pointer to the object identified by the specified name.

HRESULT GetObject(
  LPOLESTR pszItem,     //Pointer to name of the object requested
  DWORD dwSpeedNeeded,  //Speed requirements on binding
  IBindCtx *pbc,        //Pointer to bind context object to be used
  REFIID riid,          //Reference to the identifier of the
                        // interface pointer desired
  void **ppvObject      //Address of output variable that receives
                        // the interface pointer requested in riid
);

Parameters

pszItem
[in] Pointer to a zero-terminated string containing the container's name for the requested object. For Win32 applications, the LPOLESTR type indicates a wide character string (two bytes per character); otherwise, the string has one byte per character.
dwSpeedNeeded
[in] Indicates approximately how long the caller will wait to get the object. The legal values for dwSpeedNeeded are taken from the enumeration BINDSPEED. For information on the BINDSPEED enumeration, see the "Data Structures" section.
pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the binding implementation should retrieve information about its environment. For more information, see IBindCtx.
riid
[in] Reference to the identifier of the interface pointer requested.
ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer to the object named by pszItem. When successful, the implementation must call IUnknown::AddRef on the *ppvObject; it is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation sets *ppvObject to NULL.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The specified object was successfully returned.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by the bind context's BIND_OPTS structure, or with the speed indicated by the dwSpeedNeeded parameter.
MK_E_NOOBJECT
The parameter pszItem does not identify an object in this container.
E_NOINTERFACE
The requested interface was not available.

Remarks

The item moniker implementation of IMoniker::BindToObject calls this method, passing the name stored within the item moniker as the pszItem parameter.

Notes to Implementers

Your implementation of IOleItemContainer::GetObject should first determine whether pszItem is a valid name for one of the container's objects. If not, you should return MK_E_NOOBJECT.

If pszItem names an embedded or linked object, your implementation must check the value of the dwSpeedNeeded parameter. If the value is BINDSPEED_IMMEDIATE and the object is not yet loaded, you should return MK_E_EXCEEDEDDEADLINE. If the object is loaded, your implementation should determine whether the object is running (for example, by calling the OleIsRunning function). If it is not running and the dwSpeedNeeded value is BINDSPEED_MODERATE, your implementation should return MK_E_EXCEEDEDDEADLINE. If the object is not running and dwSpeedNeeded is BINDSPEED_INDEFINITE, your implementation should call the OleRun function to put the object in the running state. Then it can query the object for the requested interface. Note that it is important the object be running before you query for the interface.

If pszItem names a pseudo-object, your implementation can ignore the dwSpeedNeeded parameter because a pseudo-object is running whenever its container is running. In this case, your implementation can simply query for the requested interface.

If you want more specific information about the time limit than is given by dwSpeedNeeded, you can call IBindCtx::GetBindOptions on the pbc parameter to get the actual deadline parameter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IMoniker::BindToObject, IBindCtx::GetBindOptions, OleIsRunning, OleRun


IOleItemContainer::GetObjectStorage

Returns a pointer to the storage for the object identified by the specified name.

HRESULT GetObjectStorage(
  LPOLESTR pszItem,  //Name of the string containing the name of
                     // object whose storage is requested
  IBindCtx *pbc,     //Pointer to bind context to be used
  REFIID riid,       //Reference to the identifier of the interface
                     // pointer desired
  void **ppvStorage  //Address of output variable that receives the
                     // interface pointer requested in riid
);

Parameters

pszItem
[in] Pointer to a zero-terminated string containing the compound document's name for the object whose storage is requested. For Win32 applications, the LPOLESTR type indicates a wide character string (two bytes per character); otherwise, the string has one byte per character.
pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this binding operation. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the binding implementation should retrieve information about its environment. For more information, see IBindCtx.
riid
[in] Reference to the identifier of the interface to be used to communicate with the object, usually IStorage.
ppvStorage
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer to the storage for the object named by pszItem. When successful, the implementation must call IUnknown::AddRef on *ppvStorage; it is the caller's responsibility to call IUnknown::Release. If an error occurs, *ppvStorage is set to NULL.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The storage of the specified object was successfully returned.
MK_E_NOOBJECT
The parameter pszItem does not identify a object in this container.
MK_E_NOSTORAGE
The object does not have its own independent storage.
E_NOINTERFACE
The requested interface is not available.

Remarks

The item moniker implementation of IMoniker::BindToStorage calls this method.

Notes to Implementers

If pszItem designates a pseudo-object, your implementation should return MK_E_NOSTORAGE, because pseudo-objects do not have their own independent storage. If pszItem designates an embedded object, or a portion of the document that has its own storage, your implementation should return the specified interface pointer on the appropriate storage object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IMoniker - Item Moniker Implementation


IOleItemContainer::IsRunning

Indicates whether the object identified by the specified name is running.

HRESULT IsRunning(
  LPOLESTR pszItem  //Pointer to string containing name of object
);

Parameter

pszItem
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the container's name for the object.

Return Values

S_OK
The specified object is running.
S_FALSE
The object is not running.
MK_E_NOOBJECT
The parameter pszItem does not identify an object in this container.

Remarks

The item moniker implementation of IMoniker::IsRunning calls this method.

Notes to Implementers

Your implementation of IOleItemContainer::IsRunning should first determine whether pszItem identifies one of the container's objects. If it does not, your implementation should return MK_E_NOOBJECT. If the object is not loaded, your implementation should return S_FALSE. If it is loaded, your implementation can call the OleIsRunning function to determine whether it is running.

If pszItem names a pseudo-object, your implementation can simply return S_OK because a pseudo-object is running whenever its container is running.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IMoniker::IsRunning


IParseDisplayName

The IParseDisplayName interface parses a displayable name string to convert it into a moniker for custom moniker implementations. Display name parsing is necessary when the end user inputs a string to identify a component, as in the following situations:

  • A compound document application that supports linked components typically supports the Edit:Links... dialog box. Through this dialog box, the end user can enter a display name to specify a new link source for a specified linked component. The compound document needs to have this input string converted into a moniker.
  • A script language such as the macro language of a spreadsheet can allow textual references to a component. The language's interpreter needs to have such a reference converted into a moniker in order to execute the macro.

When to Implement

Compound document applications that support links to embedded components or to pseudo-objects within their documents must provide an implementation of the IOleItemContainer interface, which is derived indirectly from IParseDisplayName. In effect, such a compound document is providing a namespace for identifying its internal components; and its IOleItemContainer implementation (which includes the IParseDisplayName implementation) is the interface through which another application can access this namespace. Alternatively, the compound document application can implement IParseDisplayName as part of its class object, which is accessible through the CoGetClassObject function.

Monikers that support their own namespace with custom requirements for parsing names also implement this interface.

When to Use

If you are implementing your own moniker class, you might need to use this interface from your implementation of IMoniker::ParseDisplayName. If you call the MkParseDisplayName or the MkParseDisplayNameEx functions, you are indirectly using IParseDisplayName. These two functions call IParseDisplayName to parse display names for objects that provide custom moniker implementations.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IParseDisplayName Method | Description
ParseDisplayName | Parses the display name returning a moniker corresponding to it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IMoniker::ParseDisplayName, IOleItemContainer, MkParseDisplayName, MkParseDisplayNameEx


IParseDisplayName::ParseDisplayName

Parses the display name to extract a component of the string that it can convert into a moniker, using the maximum number of characters from the left side of the string.

HRESULT ParseDisplayName(
  IBindCtx *pbc,     //Pointer to bind context
  LPOLESTR pszDisplayName,
                     //Pointer to string containing display name
  ULONG *pchEaten,   //Pointer to length, in characters, of display
                     //name
  IMoniker **ppmkOut //Address of output variable that receives the
                     //resulting IMoniker interface pointer
);

Parameters

pbc
[in] Pointer to the bind context to be used in this binding operation.
pszDisplayName
[in] Pointer to a zero-terminated string containing the display name to be parsed. For Win32 applications, the LPOLESTR type indicates a wide character string (two bytes per character); otherwise, the string has one byte per character.
pchEaten
[out] Pointer to the number of characters in the display name that correspond to the ppmkOut moniker.
ppmkOut
[out] Address of IMoniker* pointer variable that receives the interface pointer to the resulting moniker. If an error occurs, the implementation sets *ppmkOut to NULL. If *ppmkOut is non-NULL, the implementation must call (*ppmkOut)-> IUnknown::AddRef; so it is the caller's responsibility to call (*ppmkOut)-> IUnknown::Release.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The parse operation was successful.
MK_E_SYNTAX
Syntax error in the display name. Parsing failed because szDisplayName could only be partially resolved into a moniker. In this case, *pchEaten has the number of characters that were successfully parsed into a moniker prefix. The parameter ppmkOut should be NULL.
MK_E_NOOBJECT
The display name does not identify a component in this namespace.
E_INVALIDARG
One or more parameters are invalid.

Remarks

In general, the maximum prefix of szDisplayName that is syntactically valid and that represents an object should be consumed by this method and converted to a moniker.

Typically, this method is called by MkParseDisplayName[Ex]. In the initial step of the parsing operation, the MkParseDisplayName[Ex] function can retrieve the IParseDisplayName interface directly from an instance of a class identified with either the "@ProgID" or "ProgID" notation. Subsequent parsing steps can query for the interface on an intermediate object.

The main loop of MkParseDisplayName[Ex] finds the next moniker piece by calling the equivalent method in the IMoniker interface, that is, IMoniker::ParseDisplayName, on the moniker that it currently holds. In this call to IMoniker::ParseDisplayName, the MkParseDisplayName[Ex] function passes NULL in the pmkToLeft parameter. If the moniker currently held by MkParseDisplayName[Ex] is a generic composite, the call to IMoniker::ParseDisplayName is forwarded by that composite onto its last piece, passing the prefix of the composite to the left of the piece in pmkToLeft.

Some moniker classes will be able to handle this parsing internally to themselves since they are designed to designate only certain kinds of objects. Others will need to bind to the object that they designate to accomplish the parsing process. As is usual, these objects should not be released by IMoniker::ParseDisplayName but instead should be transferred to the bind context via IBindCtx::RegisterObjectBound or IBindCtx::GetRunningObjectTable followed by IRunningObjectTable::Register for release at a later time.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

MkParseDisplayName, MkParseDisplayNameEx, IMoniker::ParseDisplayName


IPersist

The IPersist interface defines the single method GetClassID, which is designed to supply the CLSID of an object that can be stored persistently in the system. A call to this method can allow the object to specify which object handler to use in the client process, as it is used in the OLE default implementation of marshaling.

IPersist is the base interface for three other interfaces: IPersistStorage, IPersistStream, and IPersistFile. Each of these interfaces, therefore, includes the GetClassID method, and the appropriate one of these three interfaces is implemented on objects that can be serialized to a storage, a stream, or a file. The methods of these interfaces allow the state of these objects to be saved for later instantiations, and load the object using the saved state. Typically, the persistence interfaces are implemented by an embedded or linked object, and are called by the container application or the default object handler.

When to Implement

You must implement the single method of IPersist in implementing any one of the other persistence interfaces: IPersistStorage, IPersistStream, or IPersistFile. Typically, for example, you would implement IPersistStorage on an embedded object, IPersistFile on a linked object, and IPersistStream on a new moniker class, although their uses are not limited to these objects. You could implement IPersist in a situation where all that is required is to obtain the CLSID of a persistent object, as it is used in marshaling.

When to Use

The single method of IPersist is rarely called directly in application code. It is called by the default object handler to get the CLSID of an embedded object, or an object to be marshaled. A container application, for example, would probably not call the GetClassID method directly unless it provided object handlers for specific classes of objects.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPersist Method | Description
GetClassID | Returns the class identifier (CLSID) for the component object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IPersist::GetClassID

Retrieves the class identifier (CLSID) of an object. The CLSID is a unique value that identifies the code that can manipulate the persistent data.

HRESULT GetClassID(
  CLSID *pClassID  //Pointer to CLSID of object
);

Parameter

pClassID
[out] Points to the location of the CLSID on return. The CLSID is a globally unique identifier (GUID) that uniquely represents an object class that defines the code that can manipulate the object's data.

Return Values

S_OK
The CLSID was successfully retrieved.
E_FAIL
The CLSID could not be retrieved.

Remarks

The GetClassID method retrieves the class identifier (CLSID) for an object, used in later operations to load object-specific code into the caller's context.

Notes to Callers

A container application might call this method to retrieve the original CLSID of an object that it is treating as a different class. Such a call would be necessary if a user performed an editing operation that required the object to be saved. If the container were to save it using the treat-as CLSID, the original application would no longer be able to edit the object. Typically, in this case, the container calls the OleSave helper function, which performs all the necessary steps. For this reason, most container applications have no need to call this method directly.

The exception would be a container that provides an object handler for certain objects. In particular, a container application should not get an object's CLSID and then use it to retrieve class specific information from the registry. Instead, the container should use IOleObject and IDataObject interfaces to retrieve such class-specific information directly from the object.

Notes to Implementers

Typically, implementations of this method simply supply a constant CLSID for an object. If, however, the object's TreatAs registry key has been set by an application that supports emulation (and so is treating the object as one of a different class), a call to IPersist::GetClassID must supply the CLSID specified in the TreatAs key. For more information on emulation, refer to CoTreatAsClass.

When an object is in the running state, the default handler calls an implementation of IPersist::GetClassID that delegates the call to the implementation in the object. When the object is not running, the default handler instead calls the ReadClassStg function to read the CLSID that is saved in the object's storage.

If you are writing a custom object handler for your object, you might want to simply delegate this method to the default handler implementation (see OleCreateDefaultHandler).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistStorage, IPersistStream, IPersistFile, ReadClassStg


IPersistFile

The IPersistFile interface provides methods that permit an object to be loaded from or saved to a disk file, rather than a storage object or stream. Because the information needed to open a file varies greatly from one application to another, the implementation of IPersistFile::Load on the object must also open its disk file.

The IPersistFile interface inherits its definition from IPersist, so all implementations must also include the GetClassID method of IPersist.

When to Implement

Implement IPersistFile when you want to read or write information from a separate file, which could be of any file format.

This interface should be implemented on any objects that support linking through a file moniker, including the following:

  • Any object that supports links to its files or to pseudo-objects within its files.
  • A container application that supports links to objects within its compound file.

Typically, you implement the IPersistFile interface as part of an aggregate object that includes other interfaces that are appropriate for the type of moniker binding that is supported.

For example, in either of the cases mentioned above, the moniker for the linked object can be a composite moniker. In the first case, a composite moniker identifies the pseudo-object contained within the file. In the second case, a composite moniker identifies the embedded object contained within the compound file. In either case of composite monikers, you must implement the IPersistFile interface as part of the same object on which the IOleItemContainer interface is implemented. Then, when the application for the linked object is run, OLE queries for the IOleItemContainer interface to locate the embedded object or the pseudo-object contained in the file.

As another example, if the moniker is a simple file moniker (i.e., the link is to the entire file), OLE queries for the interface that the initiator of the bind operation requested. Typically, this is one of the compound document interfaces, such as IOleObject, IDataObject, or IPersistStorage.

When to Use

Call methods in the IPersistFile interface to load or save a linked object in a specified file.

When IPersistFile is implemented on an object that supports linking through a file moniker and the application for the linked object is run, OLE calls IPersistFile::Load. Once the file is loaded, OLE calls IPersistFile::QueryInterface to get another interface pointer to the loaded object. The IPersistFile interface is typically part of an aggregate object that offers other interfaces.

In this case, the only IPersistFile method that OLE calls is the Load method to load a file linked to a container, running the application associated with the file. It would also be unusual for applications to call other methods in this case, which support saving an object to a file. Generally, it is left to the end user and the application for the linked object to decide when to save the object. This differs from the situation for an embedded object, in which the container application uses the IPersistStorage interface to provide the storage and to tell the object when to save itself.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPersist Method | Description
GetClassID | Returns the class identifier (CLSID) for the component object.
IPersistFile Methods | Description
IsDirty | Checks an object for changes since it was last saved to its current file.
Load | Opens the specified file and initializes an object from the file contents.
Save | Saves the object into the specified file.
SaveCompleted | Notifies the object that it can revert from NoScribble mode to Normal mode.
GetCurFile | Gets the current name of the file associated with the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IPersistFile::GetCurFile

Retrieves either the absolute path to the object's current working file or, if there is no current working file, the object's default filename prompt.

HRESULT GetCurFile(
  LPOLESTR *ppszFileName  //Pointer to the path for the current file
                          //or the default save prompt
);

Parameter

ppszFileName
[out] Points to the location of a pointer to a zero-terminated string containing the path for the current file or the default filename prompt (such as *.txt). If an error occurs, ppszFileName is set to NULL.

Return Values

S_OK
A valid absolute path was successfully returned.
S_FALSE
The default save prompt was returned.
E_OUTOFMEMORY
The operation failed due to insufficient memory.
E_FAIL
The operation failed due to some reason other than insufficient memory.

Remarks

This method returns the current filename or the default save prompt for the object.

This method allocates memory for the string returned in the ppszFileName parameter using the IMalloc::Alloc method. The caller is responsible for calling the IMalloc::Free method to free the string. Both the caller and this method use the OLE task allocator provided by a call to CoGetMalloc.

The LPOLESTR type indicates a wide character string (two bytes per character); otherwise, the string has one byte per character.

The filename returned in ppszFileName is the name specified in a call to IPersistFile::Load when the document was loaded; or in IPersistFile::SaveCompleted if the document was saved to a different file.

If the object does not have a current working file, it should supply the default filename prompt that it would display in a "Save As" dialog. For example, the default save prompt for a word processor object could be:

*.txt

Notes to Callers

OLE does not call the IPersistFile::GetCurFile method. Applications would not call this method unless they are also calling the save methods of this interface.

In saving the object, you can call this method before calling IPersistFile:Save to determine whether the object has an associated file. If this method returns S_OK, you can then call IPersistFile::Save with a NULL filename and a TRUE value for the fRemember parameter to tell the object to save itself to its current file. If this method returns S_FALSE, you can use the save prompt returned in the ppszFileName parameter to ask the end user to provide a filename. Then, you can call IPersistFile::Save with the filename that the user entered to perform a "Save As" operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistFile::Load, IPersistFile::Save, IPersistFile::SaveCompleted


IPersistFile::IsDirty

Checks an object for changes since it was last saved to its current file.

HRESULT IsDirty(void);

Return Values

S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.

Remarks

This method checks whether an object has changed since it was last saved. Call it to determine whether an object should be saved before closing it. The dirty flag for an object is conditionally cleared in the IPersistFile::Save method.

Notes to Callers

OLE does not call IPersistFile::IsDirty. Applications would not call it unless they are also saving an object to a file.

You should treat any error return codes as an indication that the object has changed. Unless this method explicitly returns S_FALSE, assume that the object must be saved.

Notes to Implementers

An object with no contained objects simply checks its dirty flag to return the appropriate result.

A container with one or more contained objects must maintain an internal dirty flag that is set when any of its contained objects has changed since it was last saved. To do this, the container should maintain an advise sink by implementing the IAdviseSink interface. Then, the container can register each link or embedding for data change notifications with a call to IDataObject::DAdvise. Then, the container can set its internal dirty flag when it receives an IAdviseSink::OnDataChange notification. If the container does not register for data change notifications, the IPersistFile::IsDirty implementation would call IPersistStorage::IsDirty for each of its contained objects to determine whether they have changed.

The container can clear its dirty flag whenever it is saved, as long as the file to which the object is saved is the current working file after the save. Therefore, the dirty flag would be cleared after a successful "Save" or "Save As" operation, but not after a "Save A Copy As . . ." operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IAdviseSink::OnDataChange, IDataObject::DAdvise, IPersistStorage::IsDirty


IPersistFile::Load

Opens the specified file and initializes an object from the file contents.

HRESULT Load(
   LPCOLESTR pszFileName,  //Pointer to absolute path of the file to open
   DWORD dwMode  //Specifies the access mode from the STGM enumeration
);

Parameters

pszFileName
[in] Points to a zero-terminated string containing the absolute path of the file to open.
dwMode
[in] Specifies some combination of the values from the STGM enumeration to indicate the access mode to use when opening the file. IPersistFile::Load can treat this value as a suggestion, adding more restrictive permissions if necessary. If dwMode is zero, the implementation should open the file using whatever default permissions are used when a user opens the file.

Return Values

S_OK
The object was successfully loaded.
E_OUTOFMEMORY
The object could not be loaded due to a lack of memory.
E_FAIL
The object could not be loaded for some reason other than a lack of memory.
IPersistFile::Load STG_E_* error codes.

Remarks

IPersistFile::Load loads the object from the specified file. This method is for initialization only and does not show the object to the end user. It is not equivalent to what occurs when an end user selects the File Open command.

Notes to Callers

The BindToObject method in file monikers calls this method to load an object during a moniker binding operation (when a linked object is run). Typically, applications do not call this method directly.

Notes to Implementers

Because the information needed to open a file varies greatly from one application to another, the object on which this method is implemented must also open the file specified by the pszFileName parameter. This differs from the IPersistStorage::Load and IPersistStream::Load, in which the caller opens the storage or stream and then passes an open storage or stream pointer to the loaded object.

For an application that normally uses OLE compound files, your IPersistFile::Load implementation can simply call the StgOpenStorage function to open the storage object in the specified file. Then, you can proceed with normal initialization. Applications that do not use storage objects can perform normal file-opening procedures.

When the object has been loaded, your implementation should register the object in the Running Object Table (see IRunningObjectTable::Register).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IRunningObjectTable::Register, StgOpenStorage


IPersistFile::Save

Saves a copy of the object into the specified file.

HRESULT Save(
   LPCOLESTR pszFileName,   //Pointer to absolute path of the file
                            //where the object is saved
   BOOL fRemember           //Specifies whether the file is to be the
                            //current working file or not
);

Parameters

pszFileName
[in] Points to a zero-terminated string containing the absolute path of the file to which the object should be saved. If pszFileName is NULL, the object should save its data to the current file, if there is one.
fRemember
[in] Indicates whether the pszFileName parameter is to be used as the current working file. If TRUE, pszFileName becomes the current file and the object should clear its dirty flag after the save. If FALSE, this save operation is a "Save A Copy As ..." operation. In this case, the current file is unchanged and the object should not clear its dirty flag. If pszFileName is NULL, the implementation should ignore the fRemember flag.

Return Values

S_OK
The object was successfully saved.
E_FAIL
The file was not saved.
IPersistFile::Save STG_E_* errors.

Remarks

This method can be called to save an object to the specified file in one of three ways:

Save
Call IPersistFile::GetCurFile first to determine whether the object has an associated filename. If so, call IPersistFile::Save specifying NULL for the pszFileName parameter in this method to indicate that the object should be saved to its current file. Then call IPersistFile::SaveCompleted to indicate completion.
Save As
Call IPersistFile::Save specifying TRUE in the fRemember parameter and a non-NULL value, indicating the name of the new file the object is to be saved to, for the pszFileName parameter . Then call IPersistFile::SaveCompleted to indicate completion.
Save a Copy As
Call IPersistFile::Save specifying FALSE in the fRemember parameter and a non-NULL value, indicating the name of the new file the object is to be copied to, for the pszFileName parameter.

The implementer must detect which type of save operation the caller is requesting. If the pszFileName parameter is NULL, a "Save" is being requested. If the pszFileName parameter is not NULL, use the value of the fRemember parameter to distinguish between a "Save As" and a "Save a Copy As".

In "Save" or "Save As" operations, IPersistFile::Save clears the internal dirty flag after the save and sends IAdviseSink::OnSave notifications to any advisory connections (see also IOleAdviseHolder::SendOnSave). Also, in these operations, the object is in NoScribble mode until it receives an IPersistFile::SaveCompleted call. In NoScribble mode, the object must not write to the file.

In the "Save As" scenario, the implementation should also send IAdviseSink::OnRename notifications to any advisory connections (see also IOleAdviseHolder::SendOnRename).

In the "Save a Copy As" scenario, the implementation does not clear the internal dirty flag after the save.

Notes to Callers

OLE does not call IPersistFile::Save. Typically, applications would not call it unless they are saving an object to a file directly, which is generally left to the end-user.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleAdviseHolder::SendOnRename, IOleAdviseHolder::SendOnSave, IPersistFile::GetCurFile, IPersistFile::SaveCompleted


IPersistFile::SaveCompleted

Notifies the object that it can write to its file. It does this by notifying the object that it can revert from NoScribble mode (in which it must not write to its file), to Normal mode (in which it can). The component enters NoScribble mode when it receives an IPersistFile::Save call.

HRESULT SaveCompleted(
  LPCOLESTR pszFileName  //Pointer to absolute path of the file
                         //where the object was saved
);

Parameter

pszFileName
[in] Points to the absolute path of the file where the object was previously saved.

Return Value

S_OK
Returned in all cases.

Remarks

IPersistFile::SaveCompleted is called when a call to IPersistFile::Save is completed, and the file that was saved is now the current working file (having been saved with "Save" or "Save As" operations). The call to Save puts the object into NoScribble mode so it cannot write to its file. When SaveCompleted is called, the object reverts to Normal mode, in which it is free to write to its file.

Notes to Callers

OLE does not call the IPersistFile::SaveCompleted method. Typically, applications would not call it unless they are saving objects directly to files, an operation which is generally left to the end-user.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistFile::Save


IPersistStorage

The IPersistStorage interface defines methods that enable a container application to pass a storage object to one of its contained objects and to load and save the storage object. This interface supports the structured storage model, in which each contained object has its own storage that is nested within the container's storage.

The IPersistStorage contract inherits its definition from IPersist, so all implementations must also include the GetClassID method of IPersist.

When to Implement

Any object that can be embedded in a container must implement the IPersistStorage interface. This interface is one of the primary interfaces for a compound document object. Embeddable objects must also implement the IOleObject and IDataObject interfaces.

The OLE default handler for embedded objects provides an implementation of the IPersistStorage interface that is used when the object is in the loaded state. Similarly, the OLE default link handler provides an IPersistStorage implementation that manages storage for a linked object. These default handlers both interact with the OLE default cache implementation, which has its own IPersistStorage implementation.

If you are providing a custom embedding or link handler for your objects, the handler must include an implementation of IPersistStorage. You can delegate calls to the default handler so you can take advantage of the default cache implementation.

When to Use

When an OLE container creates a new object, loads an existing object from storage, or inserts a new object in a clipboard or a drag-and-drop operation, the container uses the IPersistStorage interface to initialize the object and put it in the loaded or running state. When an object is loaded or running, an OLE container calls other IPersistStorage methods to instruct the object to perform various save operations or to release its storage.

Typically, applications use helper functions such as OleLoad or OleCreate, rather than calling the IPersistStorage::Load or IPersistStorage::InitNew methods directly. Similarly, applications typically call the OleSave helper function rather than calling IPersistStorage::Save directly.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPersist Method | Description
GetClassID | Returns the class identifier (CLSID) for the object on which it is implemented.
IPersistStorage Methods | Description
IsDirty | Indicates whether the object has changed since it was last saved to its current storage.
InitNew | Initializes a new storage object.
Load | Initializes an object from its existing storage.
Save | Saves an object, and any nested objects that it contains, into the specified storage object. The object enters NoScribble mode.
SaveCompleted | Notifies the object that it can revert from NoScribble or HandsOff mode, in which in must not write to its storage object, to Normal mode in which it can.
HandsOffStorage | Instructs the object to release all storage objects that have been passed to it by its container and to enter HandsOffAfterSave or HandsOffFromNormal mode.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject, IOleObject, OleCreate, OleLoad, OleSave


IPersistStorage::HandsOffStorage

Instructs the object to release all storage objects that have been passed to it by its container and to enter HandsOff mode, in which the object cannot do anything and the only operation that works is a close operation.

HRESULT HandsOffStorage(void);

Return Value

S_OK
The object has successfully entered HandsOff mode.

Remarks

This method causes an object to release any storage objects that it is holding and to enter the HandsOff mode until a subsequent IPersistStorage::SaveCompleted call. In HandsOff mode, the object cannot do anything and the only operation that works is a close operation.

A container application typically calls this method during a full save or low-memory full save operation to force the object to release all pointers to its current storage. In these scenarios, the HandsOffStorage call comes after a call to either OleSave or IPersistStorage::Save, putting the object in HandsOffAfterSave mode. Calling this method is necessary so the container application can delete the current file as part of a full save, or so it can call the IRootStorage::SwitchToFile method as part of a low-memory save.

A container application also calls this method when an object is in Normal mode to put the object in HandsOffFromNormal mode.

While the component object is in either HandsOffAfterSave or HandsOffFromNormal mode, most operations on the object will fail. Thus, the container should restore the object to Normal mode as soon as possible. The container application does this by calling the IPersistStorage::SaveCompleted method, which passes a storage pointer back to the component object for the new storage object.

Notes to Implementers

This method must release all pointers to the current storage object, including pointers to any nested streams and storages. If the object contains nested objects, the container application must recursively call this method for any nested objects that are loaded or running.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

OleSave, IPersistStorage::Save, IPersistStorage::SaveCompleted, IRootStorage::SwitchToFile


IPersistStorage::InitNew

Initializes a new object, providing a pointer to the storage to be used for the object.

HRESULT InitNew(
  IStorage *pStg  //Points to the new storage object
);

Parameter

pStg
[in] IStorage pointer to the new storage object to be initialized. The container creates a nested storage object in its storage object (see IStorage::CreateStorage). Then, the container calls the WriteClassStg function to initialize the new storage object with the object class identifier (CLSID).

Return Values

S_OK
The new storage object was successfully initialized.
CO_E_ALREADYINITIALIZED
The object has already been initialized by a previous call to either the IPersistStorage::Load method or the IPersistStorage::InitNew method.
E_OUTOFMEMORY
The storage object was not initialized due to a lack of memory.
E_FAIL
The storage object was not initialized for some reason other than a lack of memory.

Remarks

A container application can call this method when it needs to initialize a new object, for example, with an InsertObject command.

An object that supports the IPersistStorage interface must have access to a valid storage object at all times while it is running. This includes the time just after the object has been created but before it has been made persistent. The object's container must provide the object with a valid IStorage pointer to the storage during this time through the call to IPersistStorage::InitNew. Depending on the container's state, a temporary file might have to be created for this purpose.

If the object wants to retain the IStorage instance, it must call IUnknown::AddRef to increment its reference count.

After the call to IPersistStorage::InitNew, the object is in either the loaded or running state. For example, if the object class has an in-process server, the object will be in the running state. However, if the object uses the default handler, the container's call to InitNew only invokes the handler's implementation which does not run the object. Later if the container runs the object, the handler calls the IPersistStorage::InitNew method for the object.

Notes to Callers

Rather than calling IPersistStorage::InitNew directly, you typically call the OleCreate helper function which does the following:

  1. Calls the CoCreateInstance function to create an instance of the object class
  2. Queries the new instance for the IPersistStorage interface
  3. Calls the IPersistStorage::InitNew method to initialize the object

The container application should cache the IPersistStorage pointer to the object for use in later operations on the object.

Notes to Implementers

An implementation of IPersistStorage::InitNew should initialize the object to its default state, taking the following steps:

  1. Pre-open and cache the pointers to any streams or storages that the object will need to save itself to this storage.
  2. Call IPersistStorage::AddRef and cache the storage pointer that is passed in.
  3. Call the WriteFmtUserTypeStg function to write the native clipboard format and user type string for the object to the storage object.
  4. Set the dirty flag for the object.

The first two steps are particularly important for ensuring that the object can save itself in low memory situations. Pre-opening and holding onto pointers to the stream and storage interfaces guarantee that a save operation to this storage will not fail due to insufficient memory.

Your implementation of this method should return the CO_E_ALREADYINITIALIZED error code if it receives a call to either the IPersistStorage::InitNew method or the IPersistStorage::Load method after it is already initialized.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistStorage::Load, OleCreate, WriteFmtUserTypeStg


IPersistStorage::IsDirty

Indicates whether the object has changed since it was last saved to its current storage.

HRESULT IsDirty(void);

Return Values

S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.

Remarks

This method checks whether an object has changed since it was last saved so you can save it before closing it. The dirty flag for an object is conditionally cleared in the IPersistStorage::Save method.

For example, you could optimize a File:Save operation by calling the IPersistStorage::IsDirty method for each object and then calling the IPersistStorage::Save method only for those objects that are dirty.

Notes to Callers

You should treat any error return codes as an indication that the object has changed. In other words, unless this method explicitly returns S_FALSE, you must assume that the object needs to be saved.

Notes to Implementers

A container with one or more contained objects must maintain an internal dirty flag that is set whenever any of its contained objects are dirty.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IAdviseSink::OnDataChange, IDataObject::DAdvise, IPersistStorage::Save


IPersistStorage::Load

Loads an object from its existing storage.

HRESULT Load(
  IStorage *pStg  //Pointer to existing storage for the object
);

Parameter

pStg
[in] IStorage pointer to the existing storage from which the object is to be loaded.

Return Values

S_OK
The object was successfully loaded.
CO_E_ALREADYINITIALIZED
The object has already been initialized by a previous call to the IPersistStorage::Load method or the IPersistStorage::InitNew method.
E_OUTOFMEMORY
The object was not loaded due to lack of memory.
E_FAIL
The object was not loaded due to some reason besides a lack of memory.

Remarks

This method initializes an object from an existing storage. The object is placed in the loaded state if this method is called by the container application. If called by the default handler, this method places the object in the running state.

Either the default handler or the object itself can hold onto the IStorage pointer while the object is loaded or running.

Notes to Callers

Rather than calling IPersistStorage::Load directly, you typically call the OleLoad helper function which does the following:

  1. Create an uninitialized instance of the object class.
  2. Query the new instance for the IPersistStorage interface.
  3. Call IPersistStorage::Load to initialize the object from the existing storage.

You also call this method indirectly when you call the OleCreateFromData function or the OleCreateFromFile function to insert an object into a compound file (as in a drag-and-drop or clipboard paste operation).

The container should cache the IPersistStorage pointer for use in later operations on the object.

Notes to Implementers

Your implementation should perform the following steps to load an object:

  1. Open the object's streams in the storage object, and read the necessary data into the object's internal data structures.
  2. Clear the object's dirty flag.
  3. Call the IPersistStorage::AddRef method and cache the passed in storage pointer.
  4. Keep open and cache the pointers to any streams or storages that the object will need to save itself to this storage.
  5. Perform any other default initialization required for the object.

Steps 3 and 4 are particularly important for ensuring that the object can save itself in low memory situations. Holding onto pointers to the storage and stream interfaces guarantees that a save operation to this storage will not fail due to insufficient memory.

Your implementation of this method should return the CO_E_ALREADYINITIALIZED error code if it receives a call to either the IPersistStorage::InitNew method or the IPersistStorage::Load method after it is already initialized.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

GetConvertStg, IPersistStorage::InitNew, OleLoad, ReadFmtUserTypeStg, SetConvertStg, WriteFmtUserTypeStg


IPersistStorage::Save

Saves an object, and any nested objects that it contains, into the specified storage. The object is placed in NoScribble mode, and it must not write to the specified storage until it receives a call to its IPersistStorage::SaveCompleted method.

HRESULT Save(
  IStorage *pStgSave,   //Pointer to storage object
  BOOL fSameAsLoad      //Indicates whether the specified storage
                        //object is the current one
);

Parameters

pStgSave
[in] IStorage pointer to the storage into which the object is to be saved.
fSameAsLoad
[in] Indicates whether the specified storage is the current one, which was passed to the object by one of the following calls:
  • IPersistStorage::InitNew when it was created.
  • IPersistStorage::Load when it was loaded.
  • IPersistStorage::SaveCompleted when it was saved to a storage different from its current storage.

This parameter is set to FALSE when performing a Save As or Save A Copy To operation or when performing a full save. In the latter case, this method saves to a temporary file, deletes the original file, and renames the temporary file.

This parameter is set to TRUE to perform a full save in a low-memory situation or to perform a fast incremental save in which only the dirty components are saved.

Return Values

S_OK
The object was successfully saved.
STG_E_MEDIUMFULL
The object was not saved because of a lack of space on the disk.
E_FAIL
The object could not be saved due to errors other than a lack of disk space.

Remarks

This method saves an object, and any nested objects it contains, into the specified storage. It also places the object into NoScribble mode. Thus, the object cannot write to its storage until a subsequent call to the IPersistStorage::SaveCompleted method returns the object to Normal mode.

If the storage object is the same as the one it was loaded or created from, the save operation may be able to write incremental changes to the storage object. Otherwise, a full save must be done.

This method recursively calls the IPersistStorage::Save method, the OleSave function, or the IStorage::CopyTo method to save its nested objects.

This method does not call the IStorage::Commit method. Nor does it write the CLSID to the storage object. Both of these tasks are the responsibilities of the caller.

Notes to Callers

Rather than calling IPersistStorage::Save directly, you typically call the OleSave helper function which performs the following steps:

  1. Call the WriteClassStg function to write the class identifier for the object to the storage.
  2. Call the IPersistStorage::Save method.
  3. If needed, call the IStorage::Commit method on the storage object.

Then, a container application performs any other operations necessary to complete the save and calls the SaveCompleted method for each object.

If an embedded object passes the IPersistStorage::Save method to its nested objects, it must receive a call to its IPersistStorage::SaveCompleted method before calling this method for its nested objects.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistStorage::InitNew, IPersistStorage::Load, IPersistStorage::SaveCompleted, OleSave, IStorage::Commit, IStorage::CopyTo, OleSave, WriteClassStg, WriteFmtUserTypeStg


IPersistStorage::SaveCompleted

Notifies the object that it can revert from NoScribble or HandsOff mode, in which it must not write to its storage object, to Normal mode, in which it can. The object enters NoScribble mode when it receives an IPersistStorage::Save call.

HRESULT SaveCompleted(
  IStorage *pStgNew  //Pointer to the current storage object
);

Parameter

pStgNew
[in] IStorage pointer to the new storage object, if different from the storage object prior to saving. This pointer can be NULL if the current storage object does not change during the save operation. If the object is in HandsOff mode, this parameter must be non-NULL.

Return Values

S_OK
The object was successfully returned to Normal mode.
E_OUTOFMEMORY
The object remained in HandsOff mode or NoScribble mode due to a lack of memory. Typically, this error occurs when the object is not able to open the necessary streams and storage objects in pStgNew.
E_INVALIDARG
The pStgNew parameter is not valid. Typically, this error occurs if pStgNew is NULL when the object is in HandsOff mode.
E_UNEXPECTED
The object is in Normal mode, and there was no previous call to IPersistStorage::Save or IPersistStorage::HandsOffStorage.

Remarks

This method notifies an object that it can revert to Normal mode and can once again write to its storage object. The object exits NoScribble mode or HandsOff mode.

If the object is reverting from HandsOff mode, the pStgNew parameter must be non-NULL. In HandsOffFromNormal mode, this parameter is the new storage object that replaces the one that was revoked by the IPersistStorage::HandsOffStorage method. The data in the storage object is a copy of the data from the revoked storage object. In HandsOffAfterSave mode, the data is the same as the data that was most recently saved. It is not the same as the data in the revoked storage object.

If the object is reverting from NoScribble mode, the pStgNew parameter can be NULL or non-NULL. If NULL, the object once again has access to its storage object. If it is not NULL, the component object should simulate receiving a call to its IPersistStorage::HandsOffStorage method. If the component object cannot simulate this call, its container must be prepared to actually call the IPersistStorage::HandsOffStorage method.

The IPersistStorage::SaveCompleted method must recursively call any nested objects that are loaded or running.

If this method returns an error code, the object is not returned to Normal mode. Thus, the container object can attempt different save strategies.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IAdviseSink::OnSave, IOleObject::Close, IPersistStorage::HandsOffStorage, IPersistStorage::Save, IRootStorage::SwitchToFile


IPersistStream

The IPersistStream interface provides methods for saving and loading objects that use a simple serial stream for their storage needs. The IPersistStream interface inherits its definition from the IPersist interface, and so the includes the GetClassID method of IPersist.

One way in which it is used is to support OLE moniker implementations. Each of the OLE-provided moniker interfaces provides an IPersistStream implementation through which the moniker saves or loads itself. An instance of the OLE generic composite moniker class calls the IPersistStream methods of its component monikers to load or save the components in the proper sequence in a single stream.

OLE containers with embedded and linked component objects do not use this interface; they use the IPersistStorage interface instead.

When to Implement

Implement the IPersistStream interface on objects that are to be saved to a simple stream. Some objects of this type are monikers and some OLE controls, although generally, controls use the IPersistStreamInit interface, which has the same methods as IPersistStream, with one added method, IPersistStreamInit::InitNew. The IMoniker interface is derived from the IPersistStream interface, so you must implement the IPersistStream interface if you are implementing a new moniker class.

When to Use

Call methods of IPersistStream from a container application to save or load objects that are contained in a simple stream. When used to save or load monikers, typical applications do not call the methods directly, but allow the default link handler to make the calls to save and load the monikers that identify the link source. These monikers are stored in a stream in the storage for the linked object. If you are writing a custom link handler for your class of objects, you would call the methods of IPersistStream to implement the link handler.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPersist Method | Description
GetClassID | Returns the class identifier (CLSID) for the component object.
IPersistStream Methods | Description
IsDirty | Checks the object for changes since it was last saved.
Load | Initializes an object from the stream where it was previously saved.
Save | Saves an object into the specified stream and indicates whether the object should reset its dirty flag.
GetSizeMax | Return the size in bytes of the stream needed to save the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker


IPersistStream::GetSizeMax

Returns the size in bytes of the stream needed to save the object.

HRESULT GetSizeMax(
  ULARGE_INTEGER *pcbSize  //Pointer to size of stream needed to
                           //save object
);

Parameter

pcbSize
[out] Points to a 64-bit unsigned integer value indicating the size in bytes of the stream needed to save this object.

Return Value

S_OK
The size was successfully returned.

Remarks

This method returns the size needed to save an object. You can call this method to determine the size and set the necessary buffers before calling the IPersistStream::Save method.

Notes to Implementers

The GetSizeMax implementation should return a conservative estimate of the necessary size because the caller might call the IPersistStream::Save method with a non-growable stream.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistStream::Save


IPersistStream::IsDirty

Checks the object for changes since it was last saved.

HRESULT IsDirty(void);

Return Values

S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.

Remarks

This method checks whether an object has changed since it was last saved so you can avoid losing information in objects that have not yet been saved. The dirty flag for an object is conditionally cleared in the IPersistStream::Save method.

Notes to Callers

You should treat any error return codes as an indication that the object has changed. In other words, unless this method explicitly returns S_FALSE, you must assume that the object needs to be saved.

Note that the OLE-provided implementations of the IPersistStream::IsDirty method in the OLE-provided moniker interfaces always return S_FALSE because their internal state never changes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersistStream::Save


IPersistStream::Load

Initializes an object from the stream where it was previously saved.

HRESULT Load(
  IStream *pStm  //Pointer to the stream from which the object
                 //should be loaded
);

Parameter

pStm
[in] IStream pointer to the stream from which the object should be loaded.

Return Values

S_OK
The object was successfully loaded.
E_OUTOFMEMORY
The object was not loaded due to a lack of memory.
E_FAIL
The object was not loaded due to some reason other than a lack of memory.

Remarks

This method loads an object from its associated stream. The seek pointer is set as it was in the most recent IPersistStream::Save method. This method can seek and read from the stream, but cannot write to it.

On exit, the seek pointer should be left in the same position as it was on exit from Save, immediately past the end of the data.

Notes to Callers

Rather than calling IPersistStream::Load directly, you typically call the OleLoadFromStream function does the following:

  1. Calls the ReadClassStm function to get the class identifier from the stream.
  2. Calls the CoCreateInstance function to create an instance of the object.
  3. Queries the instance for IPersistStream.
  4. Calls IPersistStream::Load.

The OleLoadFromStream function assumes that objects are stored in the stream with a class identifier followed by the object data. This storage pattern is used by the generic, composite-moniker implementation provided by OLE.

If the objects are not stored using this pattern, you must call the methods separately yourself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoCreateInstance, OleLoadFromStream, ReadClassStm


IPersistStream::Save

Saves an object to the specified stream.

HRESULT Save(
  IStream *pStm,   //Pointer to the stream where the object is to be
                   //saved
  BOOL fClearDirty //Specifies whether to clear the dirty flag
);

Parameters

pStm
[in] IStream pointer to the stream into which the object should be saved.
fClearDirty
[in] Indicates whether to clear the dirty flag after the save is complete. If TRUE, the flag should be cleared. If FALSE, the flag should be left unchanged.

Return Values

S_OK
The object was successfully saved to the stream.
STG_E_CANTSAVE
The object could not save itself to the stream. This error could indicate, for example, that the object contains another object that is not serializable to a stream or that an ISequentialStream::Write call returned STG_E_CANTSAVE.
STG_E_MEDIUMFULL
The object could not be saved because there is no space left on the storage device.

Remarks

IPersistStream::Save saves an object into the specified stream and indicates whether the object should reset its dirty flag.

The seek pointer is positioned at the location in the stream at which the object should begin writing its data. The object calls the ISequentialStream::Write method to write its data.

On exit, the seek pointer must be positioned immediately past the object data. The position of the seek pointer is undefined if an error returns.

Notes to Callers

Rather than calling IPersistStream::Save directly, you typically call the OleSaveToStream helper function which does the following:

  1. Calls IPersistStream::GetClassID to get the object's CLSID.
  2. Calls the WriteClassStm function to write the object's CLSID to the stream.
  3. Calls IPersistStream::Save.

If you call these methods directly, you can write other data into the stream after the CLSID before calling IPersistStream::Save.

The OLE-provided implementation of IPersistStream follows this same pattern.

Notes to Implementers

The IPersistStream::Save method does not write the CLSID to the stream. The caller is responsible for writing the CLSID.

The IPersistStream::Save method can read from, write to, and seek in the stream; but it must not seek to a location in the stream before that of the seek pointer on entry.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IPersist::GetClassID, ISequentialStream::Write, OleSaveToStream


IPersistStream - URL Moniker Implementation

The URL moniker implementation of IPersistStream is found on an URL moniker object, which supports IUnknown, IAsyncMoniker Interface, and IMoniker. The IMoniker interface inherits its definition from IPersistStream and thus, the URL moniker also provides an implementation of IPersistStream as part of its implementation of IMoniker.

The IAsyncMoniker interface on an URL moniker is simply IUnknown (there are no additional methods); it is used to allow clients to determine if a moniker supports asynchronous binding. To get a pointer to the IMoniker interface on this object, call the CreateURLMoniker function. Then, to get a pointer to IPersistStream, call the QueryInterface method.

IPersistStream, in addition to inheriting its definition from IUnknown, also inherits the single method of IPersist, GetClassID.

Note The current URL Moniker implementation of IMoniker does not support asynchronous storage. Future implementations will do so.

When to Use

Call IPersistStream to have the moniker manage its persistent storage.

Remarks

IPersistStream::GetClassID
Returns CLSID_StdURLMoniker.
IPersistStream::IsDirty
Returns S_OK if the Moniker has changed since it was last saved (IPersistStream::Save with fClearDirty==TRUE), S_FALSE otherwise.
IPersistStream::Load
Initializes an URL moniker from data within a stream, usually stored there previously using its IPersistStream::Save (via OleSaveToStream or OleSaveToStreamEx). The binary format of the URL Moniker is its URL string in Unicode™ (may be a full or partial URL string, see CreateURLMoniker for details). This is represented as a ULONG (32-bit) count of characters followed by that many Unicode characters.
IPersistStream::Save
Saves an URL moniker to a stream. The binary format of URL Moniker is its URL string in Unicode (may be a full or partial URL string, see CreateURLMoniker for details). This is represented as a ULONG (32-bit) count of characters followed by that many Unicode™ characters.
IPersistStream::GetSizeMax
Returns the maximum number of bytes in the stream that will be required by a subsequent call to IPersistStream::Save. This value is SIZEOF(ULONG)==4 plus SIZEOF(WCHAR)*n where n is the length of the full or partial URL string including the NULL terminator.

See Also

IMoniker - URL Moniker Implementation


IPersistStreamInit

The IPersistStreamInit interface is defined as a replacement for IPersistStream in order to add an initialization method, InitNew. This interface is not derived from IPersistStream; it is mutually exclusive with IPersistStream. An object chooses to support only one of the two interfaces, based on whether it requires the InitNew method. Otherwise, the signatures and semantics of the other methods are the same as the corresponding methods of IPersistStream, except as described below.

When to Implement

Implement this interface on any object that needs to support initialized stream-based persistence, regardless of whatever else the object does. The presence of the InitNew method requires some changes to other methods that are common to IPersistStream, as noted in the method descriptions.

When to Use

Use this interface to initialize a stream-based object and to save that object to a stream.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPersist Method | Description
GetClassID | Returns the class identifier (CLSID) for the component object.
IPersistStreamInit Methods | Description
IsDirty | Checks the object for changes since it was last saved.
Load | Initializes an object from the stream where it was previously saved.
Save | Saves an object into the specified stream and indicates whether the object should reset its dirty flag.
GetSizeMax | Return the size in bytes of the stream needed to save the object.
InitNew | Initializes an object to a default state.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPersistStream


IPersistStreamInit::GetSizeMax

Same as IPersistStream::GetSizeMax.

HRESULT GetSizeMax(
  ULARGE_INTEGER* pcbSize  //Receives a pointer to the size of the
                           //stream needed to save object
);

Parameters

pcbSize
[out] Receives a pointer to the size of the stream needed to save the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPersistStreamInit::InitNew

Initializes the object to a default state. This method is called instead of IPersistStreamInit::Load.

HRESULT InitNew(void);

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object successfully initialized itself.
E_NOTIMPL
The object requires no default initialization. This error code is allowed because an object may choose to implement IPersistStreamInit simply for orthogonality or in anticipation of a future need for this method.

Remarks

If the object has already been initialized with Load, then this method must return E_UNEXPECTED.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPersistStreamInit::Load


IPersistStreamInit::IsDirty

Same as IPersistStream::IsDirty.

HRESULT IsDirty(void);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPersistStreamInit::Load

Same as IPersistStream::Load.

HRESULT Load(
  LPSTREAM pStm  //Pointer to the stream from which the object
                 //should be loaded
);

Parameters

pStm
[in] Points to the stream from which the object should be loaded.

Remarks

If the object has already been initialized with InitNew, then this method must return E_UNEXPECTED.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPersistStreamInit::Save

Same as IPersistStream::Save.

HRESULT Save(
  LPSTREAM pStm ,  //Pointer to the stream where the object is to be
                   //saved
  BOOL fClearDirty //Specifies whether to clear the dirty flag
);

Parameters

pStm
[in] Points to the stream where the object is to be saved.
fClearDirty
[in] Specifies whether to clear the dirty flag.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPipeByte

The IPipeByte interface transfers data of the byte type (which is 8 bits wide). For transfering floating point doubles, use the IPipeDouble interface. For transfering long integers, use the IPipeLong interface.

The IPipeByte, IPipeDouble, and IPipeLong interfaces are similar to the standard DCE/RPC pipes. However, the COM implementation of pipes offers more flexibility. With the COM implementation, the basic idea is that the pipe is simply another interface with two methods: Pull and Push. This results in three main benefits:

  • A COM pipe is another interface, so it can be received as an out parameter from a method call and then either Pull or Push can be called.
  • There are no restrictions on when to call the Pull and Push methods, so a pipe is in reality bi-directional.
  • Pipes are interfaces, so the method calls can be asynchronous and follow those rules.

For more information, see Pipes in the Platform SDK's MIDL Programmer's Guide.

When to Implement

The provider of the pipe source is responsible for implementing this interface. COM provides only the proxy implementation, allowing it to optimize the use of asynchronous calls.

When to Use

A caller can use the IPipeByte interface to asynchronously transfer data of the byte type. This is especially helpful for transfering large quantities of data or data that is generated incrementally.

With the IPipeByte interface, you can intersperse calls to the Pull and Push methods. It is not necessary to complete all the Pull operations before calling the Push method.

One of the ways to use IPipeByte is in a consumer/provider scenario. For example, server P (the provider) receives a call to Pull from client A. If it doesn't already have the data requested by client A, server P can wait for a call to Push from client C.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPipeByte Methods | Description
Pull | Obtains data from the pipe source.
Push | Supplies data to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeDouble, IPipeLong


IPipeByte::Pull

Obtains data of the byte type from the pipe source. When the Pull method is called, data is requested from the provider of the pipe. The caller must provide a buffer that will hold at least the number of bytes specified in the cRequest parameter. The proxy will unmarshal the data into the provided buffer and set the number of bytes actually provided in pcReturned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

HRESULT Pull(
  BYTE *buffer,      //Pointer to the memory buffer that will hold the returned data.
  ULONG cRequest,  //The number of bytes requested.
  ULONG *pcReturned     //The actual number of bytes returned.
);

Parameters

buffer
[out, size_is (cRequest), length_is (*pcReturned)] A pointer to the memory buffer that will hold the returned data. The buffer must be able to hold at least the number of bytes specified in cRequest.
cRequest
[in] The number of bytes requested.
pcReturned
[out] The actual number of bytes returned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

Return Values

S_OK
The data was obtained successfully from the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeByte::Push, IPipeDouble, IPipeLong


IPipeByte::Push

Supplies data of the byte type to the pipe source. When the Push method is called, the data is being sent to the provider of the pipe. The caller fills the buffer with the data and then calls Push. The number of bytes being sent is specified in the cSent parameter. The caller is responsible for ensuring that the buffer is valid for the duration of the call.

Note When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

HRESULT Push(
  BYTE *buffer,  //Pointer to the memory buffer that holds the data to be sent.
  ULONG cSent     //The number of bytes in the buffer.
);

Parameters

buffer
[in, size_is (cSent)] A pointer to the memory buffer that holds the data to be sent. The caller is responsible for ensuring that this buffer is valid for the duration of the call.
cSent
[in] The number of bytes in the buffer. When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

Return Values

S_OK
The data was supplied successfully to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeByte::Pull, IPipeDouble, IPipeLong


IPipeDouble

The IPipeDouble interface transfers data of the double type (which is 64 bits wide). For transfering bytes, use the IPipeByte interface. For transfering long integers, use the IPipeLong interface.

The IPipeDouble, IPipeByte, and IPipeLong interfaces are similar to the standard DCE/RPC pipes. However, the COM implementation of pipes offers more flexibility. With the COM implementation, the basic idea is that the pipe is simply another interface with two methods: Pull and Push. This results in three main benefits:

  • A COM pipe is another interface, so it can be received as an out parameter from a method call and then either Pull or Push can be called.
  • There are no restrictions on when to call the Pull and Push methods, so a pipe is in reality bi-directional.
  • Pipes are interfaces, so the method calls can be asynchronous and follow those rules.

For more information, see Pipes in the Platform SDK's MIDL Programmer's Guide.

When to Implement

The provider of the pipe source is responsible for implementing this interface. COM provides only the proxy implementation, allowing it to optimize the use of asynchronous calls.

When to Use

A caller can use the IPipeDouble interface to asynchronously transfer data of the double type. This is especially helpful for transfering large quantities of data or data that is generated incrementally.

With the IPipeDouble interface, you can intersperse calls to the Pull and Push methods. It is not necessary to complete all the Pull operations before calling the Push method.

One of the ways to use IPipeDouble is in a consumer/provider scenario. For example, server P (the provider) receives a call to Pull from client A. If it doesn't already have the data requested by client A, server P can wait for a call to Push from client C.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPipeDouble Methods | Description
Pull | Obtains data from the pipe source.
Push | Supplies data to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeByte, IPipeLong


IPipeDouble::Pull

Obtains data of the double integer type from the pipe source. When the Pull method is called, data is requested from the provider of the pipe. The caller must provide a buffer that will hold at least the number of double integers specified in the cRequest parameter. The proxy will unmarshal the data into the provided buffer and set the number of double integers actually provided in pcReturned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

HRESULT Pull(
  DOUBLE *buffer,      //Pointer to the memory buffer that will hold the returned data.
  ULONG cRequest,  //The number of double integers requested.
  ULONG *pcReturned     //The actual number of double integers returned.
);

Parameters

buffer
[out, size_is (cRequest), length_is (*pcReturned)] A pointer to the memory buffer that will hold the returned data. The buffer must be able to hold at least the number of double integers specified in cRequest.
cRequest
[in] The number of double integers requested.
pcReturned
[out] The actual number of double integers returned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

Return Values

S_OK
The data was obtained successfully from the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeDouble::Push, IPipeByte, IPipeLong


IPipeDouble::Push

Supplies data of the double integer type to the pipe source. When the Push method is called, the data is being sent to the provider of the pipe. The caller fills the buffer with the data and then calls Push. The number of double integers being sent is specified in the cSent parameter. The caller is responsible for ensuring that the buffer is valid for the duration of the call.

Note When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

HRESULT Push(
  DOUBLE *buffer,  //Pointer to the memory buffer that holds the data to be sent.
  ULONG cSent     //The number of double integers in the buffer.
);

Parameters

buffer
[in, size_is (cSent)] A pointer to the memory buffer that holds the data to be sent. The caller is responsible for ensuring that this buffer is valid for the duration of the call.
cSent
[in] The number of double integers in the buffer. When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

Return Values

S_OK
The data was supplied successfully to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeDouble::Pull, IPipeByte, IPipeLong


IPipeLong

The IPipeLong interface transfers data of the long integer type (which is 32 bits wide). For transfering bytes, use the IPipeByte interface. For transfering floating point doubles, use the IPipeDouble interface.

The IPipeLong, IPipeByte, and IPipeDouble interfaces are similar to the standard DCE/RPC pipes. However, the COM implementation of pipes offers more flexibility. With the COM implementation, the basic idea is that the pipe is simply another interface with two methods: Pull and Push. This results in three main benefits:

  • A COM pipe is another interface, so it can be received as an out parameter from a method call and then either Pull or Push can be called.
  • There are no restrictions on when to call the Pull and Push methods, so a pipe is in reality bi-directional.
  • Pipes are interfaces, so the method calls can be asynchronous and follow those rules.

For more information, see Pipes in the Platform SDK's MIDL Programmer's Guide.

When to Implement

The provider of the pipe source is responsible for implementing this interface. COM provides only the proxy implementation, allowing it to optimize the use of asynchronous calls.

When to Use

A caller can use the IPipeLong interface to asynchronously transfer data of the long integer type. This is especially helpful for transfering large quantities of data or data that is generated incrementally.

With the IPipeLong interface, you can intersperse calls to the Pull and Push methods. It is not necessary to complete all the Pull operations before calling the Push method.

One of the ways to use IPipeLong is in a consumer/provider scenario. For example, server P (the provider) receives a call to Pull from client A. If it doesn't already have the data requested by client A, server P can wait for a call to Push from client C.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPipeLong Methods | Description
Pull | Obtains data from the pipe source.
Push | Supplies data to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeByte, IPipeDouble


IPipeLong::Pull

Obtains data of the long integer type from the pipe source. When the Pull method is called, data is requested from the provider of the pipe. The caller must provide a buffer that will hold at least the number of long integers specified in the cRequest parameter. The proxy will unmarshal the data into the provided buffer and set the number of long integers actually provided in pcReturned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

HRESULT Pull(
  LONG *buffer,      //Pointer to the memory buffer that will hold the returned data.
  ULONG cRequest,  //The number of long integers requested.
  ULONG *pcReturned     //The actual number of long integers returned.
);

Parameters

buffer
[out, size_is (cRequest), length_is (*pcReturned)] A pointer to the memory buffer that will hold the returned data. The buffer must be able to hold at least the number of long integers specified in cRequest.
cRequest
[in] The number of long integers requested.
pcReturned
[out] The actual number of long integers returned. The pcReturned parameter can be less than or equal to cRequest, but it will never be greater. When pcReturned is zero, it indicates that there is no more data.

Return Values

S_OK
The data was obtained successfully from the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeLong::Push, IPipeByte, IPipeDouble


IPipeLong::Push

Supplies data of the long integer type to the pipe source. When the Push method is called, the data is being sent to the provider of the pipe. The caller fills the buffer with the data and then calls Push. The number of long integers being sent is specified in the cSent parameter. The caller is responsible for ensuring that the buffer is valid for the duration of the call.

Note When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

  LONG *buffer,  //Pointer to the memory buffer that holds the data to be sent.
  ULONG cSent     //The number of long integers in the buffer.
);

Parameters

buffer
[in, size_is (cSent)] A pointer to the memory buffer that holds the data to be sent. The caller is responsible for ensuring that this buffer is valid for the duration of the call.
cSent
[in] The number of long integers in the buffer. When the last of the data has been pushed, the caller must do one last push of cSent equal to zero to indicate that the data transfer is complete.

Return Values

S_OK
The data was supplied successfully to the pipe source.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Requires Unsupported.
Header: Declared in objidl.h.

See Also

IPipeLong::Pull, IPipeByte, IPipeDouble


IProgressNotify

The IProgressNotify interface enables applications and other objects to receive notifications of changes in the progress of a downloading operation.

When to Implement

You do not need to implement IProgressNotify. The downloading code implements this interface, and the asynchronous storage implementation provides a connection point for dispatching notifications to it. An application can also register an IProgressNotify sink to receive progress notifications for individual streams.

When to Use

You do not need to call this interface. The Compound Files implementation uses IProgressNotify::OnProgress to control the blocking behavior of the asynchronous storage and to trigger additional byte range requests if applicable.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IProgressNotify Method | Description
OnProgress | Receives status information about progress of download operation.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IProgressNotify::OnProgress

Notifies registered objects and applications of the progress of a downloading operation.

HRESULT OnProgress(
  DWORD dwProgressCurrent,  //Amount of data available
  DWORD dwProgressMaximum,  //Total amount of data to be downloaded
  BOOL fAccurate,           //Reliability of notifications
  BOOL fOwner               //Ownership of blocking behavior
);

Parameters

dwProgressCurrent
[in] The amount of data available.
dwProgressMaximum
[in] The total amount of data to be downloaded.
fAccurate
[in] Values in dwProgressCurrent and dwProgressMaximum are either reliable (TRUE) or unreliable (FALSE). The FALSE value indicates that control structures for determining the actual position of, or amount of, data yet to be downloaded are not available.
fOwner
[in] Indicates whether this OnProgress call can control the blocking behavior of the operation. If TRUE, the caller can use return values from OnProgress to block (STG_S_BLOCK), retry (STG_S_RETRYNOW), or monitor (STG_S_MONITORING) the operation. If FALSE, the return value from OnProgress has no influence on blocking behavior.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as the following:

STG_S_RETRYNOW
The caller is to retry the operation immediately. (This value is most useful for applications that do blocking from within the callback routine.)
STG_S_BLOCK
The caller is to block the download and retry the call as needed to determine if additional data is available. This is the default behavior if no sinks are registered on the connection point.
STG_S_MONITORING
The callback recipient reliquishes control of the downloading process to one of the other objects or applications that have registered progress notification sinks on the same stream. This is useful if the notification sink is interested only in gathering statistics.
E_PENDING
Data is currently unavailable. The caller is to try again after some desired interval. The notification sink returns this value if the asynchronous storage is to operate in nonblocking mode.

Remarks

Sinks may be inherited by any substorage or substream of a given storage. If no sink is registered, the thread will block until the requested data becomes available, or the download is canceled by the downloader.

Where multiple objects or applications have registered progress notification sinks on a single stream, only one of them can control the behavior of a download. Ownership of the download goes to:

  1. The first sink to register with the storage or stream.
    1. Any advise skinks that may have been inherited from the parent storage (if the storage was created with ASYNC_MODE_COMPATIBILITY.

Any one of the sinks can relinquish control to the next connection point by returning STG_S_MONITORING to the connection point making the current caller. Once a connection point obtains control (through receiving STG_S_BLOCK or STG_S_RETRYNOW), all subsequent connection points calling IProgressNotify::OnProgress will set fOwner to FALSE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IProvideClassInfo

The IProvideClassInfo interface provides a single method for accessing the type information for an object's coclass entry in its type library.

When to Implement

Implement this interface on any object that can provide type information for its entire class, that is, the coclass entry in the type library.

When to Use

Use this interface to access the coclass type information for an object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IProvideClassInfo Methods | Description
GetClassInfo | Returns the ITypeInfo interface for the object's coclass type information.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IProvideClassInfo::GetClassInfo

Returns a pointer to the ITypeInfo interface for the object's type information. The type information for an object corresponds to the object's coclass entry in a type library.

HRESULT GetClassInfo(
  ITypeInfo** ppTI  //Address of output variable that receives the
                    //ITypeInfo interface pointer
);

Parameters

ppTI
[out] Address of ITypeInfo* pointer variable that receives the interface pointer to the object's type information. The caller is responsible for calling ITypeInfo::Release on the returned interface pointer if this method returns successfully.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The type information was successfully returned.
E_POINTER
The address in ppTI is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The caller is responsible for calling ITypeInfo::Release when the returned interface pointer is no longer needed.

Notes to Implementers

This method must call ITypeInfo::AddRef before returning. If the object loads the type information from a type library, the type library itself will call AddRef in creating the pointer.

Because the caller cannot specify a locale identifier (LCID) when calling this method, this method must assume the neutral language, that is, LANGID_NEUTRAL, and use this value to determine what locale-specific type information to return.

This method must be implemented; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IProvideClassInfo2

The IProvideClassInfo2 interface is a simple extension to IProvideClassInfo for the purpose of making it quick and easy to retrieve an object's outgoing interface IID for its default event set. The mechanism, the added GetGUID method, is extensible for other types of GUIDs as well.

When to Implement

An object implements this interface to provide type information for its outgoing interfaces.

When to Use

Call the method in this interface to obtain type information on an object's outgoing interfaces.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IProvideClassInfo Method | Description
GetClassInfo | Returns the ITypeInfo interface for the object's coclass type information.
IProvideClassInfo2 Method | Description
GetGUID | Returns the GUID for the object's outgoing IID for its default event set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IProvideClassInfo2::GetGUID

Returns a GUID corresponding to the specified dwGuidKind. The dwGuidKind parameter has several values defined. See GUIDKIND. Additional flags can be defined at a later time and will be recognized by an IProvideClassInfo2 implementation.

HRESULT GetGUID(
  DWORD dwGuidKind,  //Desired GUID
  GUID * pGUID       //Pointer to the desired GUID
);

Parameters

dwGuidKind
[in] Specifies the GUID desired on return. This parameter takes a value from the GUIDKIND enumeration.
pGUID
[out] Pointer to the caller's variable in which to store the GUID associated with dwGuidKind.

Return Values

S_OK
The GUID was successfully returned in *pGUID.
E_POINTER
The address in pGUID is not valid (such as NULL).
E_UNEXPECTED
An unknown error occurred.
E_INVALIDARG
The dwGuidKind value does not correspond to a supported GUID kind.

Remarks

E_NOTIMPL is not a valid return code since it would be pointless to implement this interface without implementing this method.

E_INVALIDARG is not valid when dwGuidKind is GUIDKIND_DEFAULT_SOURCE_DISP_IID.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

GUIDKIND


IProvideMultipleClassInfo

The IProvideMultipleClassInfo interface is a simple extension to IProvideClassInfo2 for the purpose of making it quick and easy to retrieve type information from a component that may have multiple coclasses that determine its behavior.

When to Implement

Implement IProvideMultipleClassInfo on an object that aggregates from another. The component that aggregates is responsible for returning type information that will describe both components. By implementing this interface and publishing the way that the type information blocks are ordered, a component can provide both type information blocks.

When to Use

Clients call the methods of the IProvedMultipleClassInfo interface to specify the kinds of information they are looking for. This allows more than one kind of information to be returned with a single call.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IProvideMultipleClassInfo Method | Description
GetMultiTypeInfoCount | Returns the number of type information blocks that this object has to provide.
GetInfoOfIndex | Gets the type information from the specified index.


IProvideMultipleClassInfo::GetMultiTypeInfoCount

Returns the number of type information blocks that this object has to provide.

HRESULT GetMultiTypeInfoCount(
   ULONG  *pcti
);

Parameters

pcti
[out] The ULONG pointed by the input parameter contains the number of type information blocks that this object has to provide.

Return Values

S_OK | Success.
E_POINTER | An invalid pointer was passed.
E_INVALIDARG | An invalid argument was passed.
E_FAIL | Unspecified error.


IProvideMultipleClassInfo::GetInfoOfIndex

Gets the type information from the specified index.

HRESULT GetInfoOfIndex{
   ULONG  iti,
   DWORD  dwMCIFlags,
   ITypeInfo   **pptiCoClass,
   DWORD  *pdwTIFlags,
   ULONG  *pcdispidReserved,
   IID  *piidPrimary,
   IID  *piidSource
};

Parameters

iti
[in] Index of the type information for which you want to obtain information. Index 0 is the default interface of the extender object; index *pcti-1 is the index of the base object.
dwMCIFlags
[in] Bit field indicating which [out] parameters are being requested. Indicating a particular flag results in the appropriate information being assigned to the associated [out] parameter. dwMCIFlags can be one of the following flags:
MULTICLASSINFO_GETTYPEINFO | Indicates a request for pptiCoClass information.
MULTICLASSINFO_GETNUMRESERVEDDISPIDS | Indicates a request for pcdispidReserved and pdwTIFlags information.
MULTICLASSINFO_GETIIDPRIMARY | Indicates a request for piidPrimary information.
MULTICLASSINFO_GETIIDSOURCE | Indicates a request for piidSource information.
pptiCoClass
[out] CoClass type information for the requested contributor.
pdwTIFlags
[out] The bit field flag.
pcdispidReserved
[out] Number of DISPIDs the [default] interface of pptiCoClass reserves for its own use. This number can be used to calculate the amount to offset DISPIDs in the reserved range implemented by the object this class is extending.
piidPrimary
[out] Interface identifier (IID) of the primary interface for the requested contributor.
piidSource
[out] Interface identifier (IID) of the default source interface for the requested contributor.

Return Values

S_OK | Success.
E_POINTER | An invalid pointer was passed.
E_INVALIDARG | An invalid argument was passed.
E_FAIL | Unspecified error.


IROTData

The IROTData interface is implemented by monikers to enable the Running Object Table (ROT) to compare monikers against each other.

The ROT uses the IROTData interface to test whether two monikers are equal. The ROT must do this when, for example, it checks whether a specified moniker is registered as running.

When to Implement

You must implement IROTData if you are writing your own moniker class (that is, writing your own implementation of the IMoniker interface), and if your monikers are meant to be registered in the ROT.

When to Use

You typically do not need to use this interface. This interface is used by the system's implementation of the ROT.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IROTData Method | Description
GetComparisonData | Retrieve data to allow moniker to be compared with another.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker, IRunningObjectTable


IROTData::GetComparisonData

Retrieves data from a moniker that can be used to test the moniker for equality against another moniker.

HRESULT GetComparisonData(
  byte *pbData,  //Pointer to a buffer that receives the comparison
                 //data
  ULONG cbMax,   //Length of buffer
  ULONG *pcbData //Pointer to the length of the comparison data
);

Parameters

pbData
[out] Pointer to a buffer that receives the comparison data.
cbMax
[in] Length of the buffer specified in ppvData.
pcbData
[out] Pointer to the length of the comparison data.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The comparison data was successfully returned.

Remarks

The IROTData::GetComparisonData method is primarily called by the Running Object Table (ROT). The comparison data returned by the method is tested for binary equality against the comparison data returned by another moniker. The pcbData parameter enables the ROT to locate the end of the data returned.

Notes to Implementers

The comparison data that you return must uniquely identify the moniker, while still being as short as possible. The comparison data should include information about the internal state of the moniker, as well as the moniker's CLSID. For example, the comparison data for a file moniker would include the path name stored within the moniker, as well as the CLSID of the file moniker implementation. This makes it possible to distinguish two monikers that happen to store similar state information but are instances of different moniker classes.

The comparison data for a moniker cannot exceed 2048 bytes in length. For composite monikers, the total length of the comparison data for all of its components cannot exceed 2048 bytes; consequently, if your moniker can be a component within a composite moniker, the comparison data you return must be significantly less than 2048 bytes.

If your comparison data is longer than the value specified by the cbMax parameter, you must return an error. Note that when IROTData::GetComparisionData is called on the components of a composite moniker, the value of cbMax becomes smaller for each moniker in sequence.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker, IRunningObjectTable


IRunnableObject

The IRunnableObject interface enables a container to control the running of its embedded objects. In the case of an object implemented with a local server, calling IRunnableObject::Run launches the server's .EXE file. In the case of an object implemented with an in-process server, calling the Run method causes the object .DLL file to transition into the running state.

When to Implement

Object handlers should implement IRunnableObject to provide their containers with a way to run them and manage their running state. DLL object applications should implement IRunnableObject to support silent updates of their objects.

When to Use

Containers call IRunnableObject to determine if an embedded object is running, to force an object to run, to lock an object into the running state, or to inform an object handler whether its object is being run as either a simple embedding or as a link source.

Methods VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IRunnableObject Methods | Description
GetRunningClass | Returns CLSID of a running object.
Run | Forces an object to run.
IsRunning | Determines if an object is running.
LockRunning | Locks an object into running state.
SetContainedObject | Indicates that an object is embedded.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IRunnableObject::GetRunningClass

Returns the CLSID of a running object.

HRESULT GetRunningClass(
  LPCLSID lpClsid  //Pointer to an object's CLSID
);

Parameter

lpClsid
[out] Pointer to the object's class identifier.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
CLSID was returned successfully.

Remarks

If an embedded document was created by an application that is not available on the user's computer, the document, by a call to CoTreatAsClass, may be able to display itself for editing by emulating a class that is supported on the user's machine. In this case, the CLSID returned by a call to IRunnableObject::GetRunningClass will be that of the class being emulated, rather than the document's native class.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoTreatAsClass


IRunnableObject::IsRunning

Determines whether an object is currently in the running state.

BOOL IsRunning();

Return Values

TRUE
The object is in the running state.
FALSE
The object is not in the running state.

Remarks

A container application could call IRunnableObject::IsRunning when it needs to know if the server is immediately available. For example, a container's implementation of the IOleItemContainer::GetObject method would return an error if the server is not running and the bindspeed parameter specifies BINDSPEED_IMMEDIATE.

An object handler could call IRunnableObject::IsRunning when it wants to avoid conflicts with a running server or when the running server might have more up-to-date information. For example, a handler's implementation of IOleObject::GetExtent would delegate to the object server if it is running, because the server's information might be more current than that in the handler's cache.

OleIsRunning is a helper function that conveniently repackages the functionality offered by IRunnableObject::IsRunning. With the release of OLE 2.01, the implementation of OleIsRunning was changed so that it calls QueryInterface, asks for IRunnableObject, and then calls IRunnableObject::IsRunning. In other words, you can use the interface and the helper function interchangeably.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

OleIsRunning


IRunnableObject::LockRunning

Locks an already running object into its running state or unlocks it from its running state.

HRESULT LockRunning(
  BOOL fLock,   //Flag indicating whether object is locked
  BOOL fLastUnlockCloses
                //Flag indicating whether to close object
);

Parameters

fLock
[in] TRUE locks the object into its running state. FALSE unlocks the object from its running state.
fLastUnlockCloses
[in] TRUE specifies that if the connection being released is the last external lock on the object, the object should close. FALSE specifies that the object should remain open until closed by the user or another process.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
If the value of fLock is TRUE, the object was successfully locked; if the value of fLock is FALSE, the object was successfully unlocked.

Remarks

Most implementations of IRunnableObject::LockRunning call CoLockObjectExternal.

OleLockRunning is a helper function that conveniently repackages the functionality offered by IRunnableObject::LockRunning. With the release of OLE 2.01, the implementation of OleLockRunning was changed to call QueryInterface, ask for IRunnableObject, and then call IRunnableObject::LockRunning. In other words, you can use the interface and the helper function interchangeably.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoLockObjectExternal


IRunnableObject::Run

Runs an object.

HRESULT Run(
  LPBC lpbc  //Pointer to binding context
);

Parameter

lpbc
[in] Pointer to the binding context of the run operation. May be NULL.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was successfully placed in the running state.

Remarks

Containers call IRunnableObject::Run to force their objects to enter the running state. If the object is not already running, calling IRunnableObject::Run can be an expensive operation, on the order of many seconds. If the object is already running, then this method has no effect on the object.

Notes to Callers

When called on a linked object that has been converted to a new class since the link was last activated, IRunnableObject::Run may return OLE_E_CLASSDIFF. In this case, the client should call IOleLink::BindToSource.

OleRun is a helper function that conveniently repackages the functionality offered by IRunnableObject::Run. With the release of OLE 2.01, the implementation of OleRun was changed so that it calls QueryInterface, asks for IRunnableObject, and then calls IRunnableObject::Run. In other words, you can use the interface and the helper function interchangeably.

Notes to Implementers

The object should register in the running object table if it has a moniker assigned. The object should not hold any strong locks on itself; instead, it should remain in the unstable, unlocked state. The object should be locked when the first external connection is made to the object.

An embedded object must hold a lock on its embedding container while it is in the running state. The Default handler provided by OLE 2 takes care of locking the embedding container on behalf of objects implemented by an EXE object application. Objects implemented by a DLL object application must explicitly put a lock on their embedding containers, which they do by first calling IOleClientSite::Getcontainer to get a pointer to the container, then calling IOleContainer::LockContainer to actually place the lock. This lock must be released when IOleObject::Close is called.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleLink::BindToSource, OleRun


IRunnableObject::SetContainedObject

Notifies an object that it is embedded in an OLE container, which ensures that reference counting is done correctly for containers that support links to embedded objects.

HRESULT SetContainedObject(
  BOOL fContained  //Flag indicating whether object is embedded
);

Parameter

fContained
[in] TRUE specifies that the object is contained in an OLE container. FALSE indicates that it is not.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY AND E_UNEXPECTED, as well as the following:

S_OK
Object has been marked as a contained embedding.

Remarks

The IRunnableObject::SetContainedObject method enables a container to inform an object handler that it is embedded in the container, rather than acting as a link. This call changes the container's reference on the object from strong, the default for external connections, to weak. When the object is running visibly, this method is of little significance because the end user has a lock on the object. During a silent update of an embedded link source, however, the container should not be able to hold an object in the running state after the link has been broken. For this reason, the container's reference to the object must be weak.

Notes to Callers

A container application must call IRunnableObject::SetContainedObject if it supports linking to embedded objects. It normally makes the call immediately after calling OleLoad or OleCreate and never calls the method again, even before it closes. Moreover, a container almost always calls this method with fContained set to TRUE. The use of this method with fContained set to FALSE is rare.

Calling IRunnableObject::SetContainedObject is optional only when you know that the embedded object will not be referenced by any client other than the container. If your container application does not support linking to embedded objects; it is preferable, but not necessary, to call IRunnableObject::SetContainedObject.

OleSetContainedObject is a helper function that conveniently repackages the functionality offered by IRunnableObject::SetContainedObject. With the release of OLE 2.01, the implementation of OleSetContainedObject was changed to call QueryInterface, ask for IRunnableObject, and then call IRunnableObject::SetContainedObject. In other words, you can use the interface and the helper function interchangeably.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

OleSetContainedObject, OleNoteObjectVisible, CoLockObjectExternal


IRunningObjectTable

The IRunningObjectTable interface manages access to the Running Object Table (ROT), a globally accessible look-up table on each workstation. A workstation's ROT keeps track of those objects that can be identified by a moniker and that are currently running on the workstation. When a client tries to bind a moniker to an object, the moniker checks the ROT to see if the object is already running; this allows the moniker to bind to the current instance instead of loading a new one.

The ROT contains entries of the form:

(pmkObjectName, pUnkObject)

The pmkObjectName element is a pointer to the moniker that identifies the running object. The pUnkObject element is a pointer to the running object itself. During the binding process, monikers consult the pmkObjectName entries in the Running Object Table to see if an object is already running.

Objects that can be named by monikers must be registered with the ROT when they are loaded and their registration must be revoked when they are no longer running.

When to Implement

You do not need to implement this interface. The system provides an implementation of the Running Object Table that is suitable for all situations.

When to Use

You typically use the ROT if you're a moniker provider (that is, you hand out monikers identifying your objects to make them accessible to others) or if you're writing your own moniker class (that is, implementing the IMoniker interface).

If you are a moniker provider, you register your objects with the ROT when they begin running and revoke their registrations when they are no longer running. This enables the monikers that you hand out to be bound to running objects. You should also use the ROT to record the object's last modification time. You can get an IRunningObjectTable interface pointer to the local ROT by calling the GetRunningObjectTable function.

The most common type of moniker provider is a compound-document link source. This includes server applications that support linking to their documents (or portions of a document) and container applications that support linking to embeddings within their documents. Server applications that do not support linking can also use the ROT to cooperate with container applications that support linking to embeddings.

If you are writing your own moniker class, you use the ROT to determine whether a object is running and to retrieve the object's last modification time. You can get an IRunningObjectTable interface pointer to the local ROT by calling the IBindCtx::GetRunningObjectTable method on the bind context for the current binding operation. Moniker implementations should always use the bind context to acquire a pointer to the ROT; this allows future implementations of IBindCtx to modify binding behavior. Note that you must also implement the IROTData interface on your moniker class in order to allow your monikers to be registered with the ROT.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IRunningObjectTable Methods | Description
Register | Registers an object with the ROT.
Revoke | Revokes an object's registration with the ROT.
IsRunning | Checks whether an object is running.
GetObject | Returns a pointer to an object given its moniker.
NoteChangeTime | Notifies the ROT that an object has changed.
GetTimeOfLastChange | Returns the time an object was last changed.
EnumRunning | Returns an enumerator for the ROT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IBindCtx::GetRunningObjectTable, IROTData, GetRunningObjectTable


IRunningObjectTable::EnumRunning

Creates and returns a pointer to an enumerator that can list the monikers of all the objects currently registered in the Running Object Table (ROT).

HRESULT EnumRunning(
  IEnumMoniker **ppenumMoniker  //Address of output variable that
                                //receives the IEnumMoniker interface
                                //pointer
);

Parameter

ppenumMoniker
[out] Address of IEnumMoniker* pointer variable that receives the interface pointer to the new enumerator for the ROT. When successful, the implementation calls IUnknown::AddRef on the the enumerator; it is the caller's responsibility to call IUnknown::Release. If an error occurs; the implementation sets *ppenumMoniker to NULL.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
An enumerator was successfully returned.

Remarks

IRunningObjectTable::EnumRunning must create and return a pointer to an IEnumMoniker interface on an enumerator object. The standard enumerator methods can then be called to enumerate the monikers currently registered in the registry. The enumerator cannot be used to enumerate monikers that are registered in the ROT after the enumerator has been created.

The EnumRunning method is intended primarily for the use by the system in implementing the Alert Object Table. Note that OLE 2 does not include an implementation of the Alert Object Table.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumXXXX, IEnumMoniker


IRunningObjectTable::GetObject

Determines whether the object identified by the specified moniker is running, and if it is, retrieves a pointer to that object. This method looks for the moniker in the Running Object Table (ROT), and retrieves the pointer registered there.

HRESULT GetObject(
  IMoniker *pmkObjectName,  //Pointer to the moniker on the object
  IUnknown **ppunkObject  //Address of output variable that receives
                          //the IUnknown interface pointer
);

Parameters

pmkObjectName
[in] Pointer to the moniker to search for in the Running Object Table.
ppunkObject
[out] Address of IUnknown* pointer variable that receives the interface pointer to the running object. When successful, the implementation calls IUnknown::AddRef on the object; it is the caller's responsibility to call IUnknown::Release. If the object is not running or if an error occurs, the implementation sets *ppunkObject to NULL.

Return Values

S_OK
Indicates that pmkObjectName was found in the ROT and a pointer was returned.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies is no longer running (in which case, the entry is revoked).

Remarks

This method checks the ROT for the moniker specified by pmkObjectName. If that moniker had previously been registered with a call to IRunningObjectTable::Register, this method returns the pointer that was registered at that time.

Notes to Callers

Generally, you call the IRunningObjectTable::GetObject method only if you are writing your own moniker class (that is, implementing the IMoniker interface). You typically call this method from your implementation of IMoniker::BindToObject.

However, note that not all implementations of IMoniker::BindToObject need to call this method. If you expect your moniker to have a prefix (indicated by a non-NULL pmkToLeft parameter to IMoniker::BindToObject), you should not check the ROT. The reason for this is that only complete monikers are registered with the ROT, and if your moniker has a prefix, your moniker is part of a composite and thus not complete. Instead, your moniker should request services from the object identified by the prefix (for example, the container of the object identified by your moniker).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::BindToObject


IRunningObjectTable::GetTimeOfLastChange

Returns the time that an object was last modified. The object must have previously been registered with the Running Object Table (ROT). This method looks for the last change time recorded in the ROT.

HRESULT GetTimeOfLastChange(
  IMoniker *pmkObjectName,  //Pointer to moniker on the object whose
                            //status is desired
  FILETIME *pfiletime       //Pointer to structure that receives
                            //object's last change time
);

Parameters

pmkObjectName
[in] Pointer to the IMoniker interface on the moniker to search for in the ROT.
pfiletime
[out] Pointer to a FILETIME structure that receives the object's last change time.

Return Values

S_OK
The last change time was successfully retrieved.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies is no longer running (in which case, the entry is revoked).

Remarks

This method returns the change time that was last reported for this object by a call to IRunningObjectTable::NoteChangeTime. If IRunningObjectTable::NoteChangeTime has not been called previously, the method returns the time that was recorded when the object was registered.

This method is provided to enable checking whether a connection between two objects (represented by one object holding a moniker that identifies the other) is up-to-date. For example, if one object is holding cached information about the other object, this method can be used to check whether the object has been modified since the cache was last updated. See IMoniker::GetTimeOfLastChange.

Notes to Callers

Generally, you call IRunningObjectTable::GetTimeOfLastChange only if you are writing your own moniker class (that is, implementing the IMoniker interface). You typically call this method from your implementation of IMoniker::GetTimeOfLastChange. However, you should do so only if the pmkToLeft parameter of IMoniker::GetTimeOfLastChange is NULL. Otherwise, you should call IMoniker::GetTimeOfLastChange on your pmkToLeft parameter instead.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::GetTimeOfLastChange, IRunningObjectTable::NoteChangeTime


IRunningObjectTable::IsRunning

Determines whether the object identified by the specified moniker is currently running. This method looks for the moniker in the Running Object Table (ROT).

HRESULT IsRunning(
  IMoniker *pmkObjectName  //Pointer to the moniker of the object
                           //whose status is desired
);

Parameter

pmkObjectName
[in] Pointer to the IMoniker interface on the moniker to search for in the Running Object Table.

Return Values

S_OK
The object identified by pmkObjectName is running.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies is no longer running (in which case, the entry is revoked).

Remarks

This method simply indicates whether a object is running. To retrieve a pointer to a running object, use the IRunningObjectTable::GetObject method.

Notes to Callers

Generally, you call the IRunningObjectTable::IsRunning method only if you are writing your own moniker class (that is, implementing the IMoniker interface). You typically call this method from your implementation of IMoniker::IsRunning. However, you should do so only if the pmkToLeft parameter of IMoniker::IsRunning is NULL. Otherwise, you should call IMoniker::IsRunning on your pmkToLeft parameter instead.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::IsRunning


IRunningObjectTable::NoteChangeTime

Records the time that a running object was last modified. The object must have previously been registered with the Running Object Table (ROT). This method stores the time of last change in the ROT.

HRESULT NoteChangeTime(
  DWORD dwRegister,  //Value identifying registration being updated
  FILETIME *pfiletime
                     //Pointer to structure containing object's last
                     //change time
);

Parameters

dwRegister
[in] Value identifying the ROT entry of the changed object. This value was previously returned by IRunningObjectTable::Register.
pfiletime
[in] Pointer to a FILETIME structure containing the object's last change time.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The change time was recorded successfully.

Remarks

The time recorded by this method can be retrieved by calling IRunningObjectTable::GetTimeOfLastChange.

This method is provided to enable a program to check whether a connection between two objects (represented by one object holding a moniker that identifies the other) is up-to-date. For example, if one object is holding cached information about the other object, this method can be used to check whether the object has been modified since the cache was last updated. See IMoniker::GetTimeOfLastChange.

Notes to Callers

If you're a moniker provider (that is, you hand out monikers identifying your objects to make them accessible to others), you must call the IRunningObjectTable::NoteChangeTime method whenever your objects are modified. You must have previously called IRunningObjectTable::Register and stored the identifier returned by that method; you use that identifier when calling IRunningObjectTable::NoteChangeTime.

The most common type of moniker provider is a compound-document link source. This includes server applications that support linking to their documents (or portions of a document) and container applications that support linking to embeddings within their documents. Server applications that do not support linking can also use the ROT to cooperate with container applications that support linking to embeddings.

When an object is first registered in the ROT, the ROT records its last change time as the value returned by calling IMoniker::GetTimeOfLastChange on the moniker being registered.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IRunningObjectTable::GetTimeOfLastChange, IMoniker::GetTimeOfLastChange


IRunningObjectTable::Register

Registers an object and its identifying moniker in the Running Object Table (ROT).

HRESULT Register(
  DWORD grfFlags,           //Registration options
  IUnknown *punkObject,     //Pointer to the object being registered
  IMoniker *pmkObjectName,  //Pointer to the moniker of the object
                            // being registered
  DWORD *pdwRegister        //Pointer to the value identifying the
                            // registration
);

Parameters

grfFlags
[in] Specifies whether the ROT’s reference to punkObject is weak or strong and controls access to the object through its entry in the ROT. For details, see the Remarks section below.
punkObject
[in] Pointer to the object that is being registered as running.
pmkObjectName
[in] Pointer to the moniker that identifies punkObject.
pdwRegister
[out] Pointer to a 32-bit value that can be used to identify this ROT entry in subsequent calls to IRunningObjectTable::Revoke or IRunningObjectTable::NoteChangeTime. The caller cannot specify NULL for this parameter. If an error occurs, *pdwRegister is set to zero.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The object was successfully registered.
MK_S_MONIKERALREADYREGISTERED
The moniker/object pair was successfully registered, but that another object (possibly the same object) has already been registered with the same moniker.

Remarks

This method registers a pointer to an object under a moniker that identifies the object. The moniker is used as the key when the table is searched with IRunningObjectTable::GetObject.

The grfFlags parameter accepts two flag settings:

  • ROTFLAGS_REGISTRATIONKEEPSALIVE. When set, indicates a strong registration for the object.
  • ROTFLAGS_ALLOWANYCLIENT. When set, any client can connect to the running object through its entry in the ROT. When not set, only clients in the window station that registered the object can connect to it.

When an object is registered, the ROT always calls AddRef on the object. For a weak registration (ROTFLAGS_REGISTRATIONKEEPSALIVE not set), the ROT will release the object whenever the last strong reference to the object is released. For a strong registration (ROTFLAGS_REGISTRATIONKEEPSALIVE set), the ROT prevents the object from being destroyed until the object’s registration is explicitly revoked.

A server registered as either LocalService or RunAs can set the ROTFLAGS_ALLOWANYCLIENT flag in its call to Register to allow any client to connect to it. A server setting this bit must have its executable name in the AppID section of the registry that refers to the AppID for the executable. An "activate as activator" server (not registered as LocalService or RunAs) must not set this flag in its call to Register. For details on installing services, see Installing as a Win32 Service or User Account.

Registering a second object with the same moniker, or re-registering the same object with the same moniker, creates a second entry in the ROT. In this case, Register returns MK_S_MONIKERALREADYREGISTERED. Each call to Register must be matched by a call to IRunningObjectTable::Revoke because even duplicate entries have different pdwRegister identifiers. A problem with duplicate registrations is that there is no way to determine which object will be returned if the moniker is specified in a subsequent call to IRunningObjectTable::IsRunning.

Notes to Callers

If you’re a moniker provider (that is, you hand out monikers identifying your objects to make them accessible to others), you must call the Register method to register your objects when they begin running. You must also call this method if you rename your objects while they are loaded.

The most common type of moniker provider is a compound-document link source. This includes server applications that support linking to their documents (or portions of a document) and container applications that support linking to embeddings within their documents. Server applications that do not support linking can also use the ROT to cooperate with container applications that support linking to embeddings.

If you’re writing a server application, you should register an object with the ROT when it begins running, typically in your implementation of IOleObject::DoVerb. The object must be registered under its full moniker, which requires getting the moniker of its container document using IOleClientSite::GetMoniker. You should also revoke and re-register the object in your implementation of IOleObject::SetMoniker, which is called if the container document is renamed.

If you’re writing a container application that supports linking to embeddings, you should register your document with the ROT when it is loaded. If your document is renamed, you should revoke and re-register it with the ROT and call IOleObject::SetMoniker for any embedded objects in the document to give them an opportunity to re-register themselves.

Objects registered in the ROT must be explicitly revoked when the object is no longer running or when its moniker changes. This revocation is important because there is no way for the system to automatically remove entries from the ROT. You must cache the identifier that is written through pdwRegister and use it in a call to IRunningObjectTable::Revoke to revoke the registration. For a strong registration, a strong reference is released when the object’s registration is revoked.

The system’s implementation of IRunningObjectTable::Register calls IMoniker::Reduce on the pmkObjectName parameter to ensure that the moniker is fully reduced before registration. If an object is known by more than one fully reduced moniker, it should be registered under all such monikers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::Reduce, IOleClientSite::GetMoniker, IOleObject::SetMoniker, IRunningObjectTable::IsRunning, IRunningObjectTable::Revoke

Installing as a Win32 Service or User Account


IRunningObjectTable::Revoke

Removes from the Running Object Table (ROT) an entry that was previously registered by a call to IRunningObjectTable::Register.

HRESULT Revoke(
  DWORD dwRegister  //Value identifying registration to be revoked
);

Parameter

dwRegister
[in] Value identifying the ROT entry to revoke. This value was previously returned by IRunningObjectTable::Register.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The object's registration was successfully revoked.

Remarks

This method undoes the effect of a call to IRunningObjectTable::Register, removing both the moniker and the pointer to the object identified by that moniker.

Notes to Callers

If you're a moniker provider (that is, you hand out monikers identifying your objects to make them accessible to others), you must call the IRunningObjectTable::Revoke method to revoke the registration of your objects when they stop running. You must have previously called IRunningObjectTable::Register and stored the identifier returned by that method; you use that identifier when calling IRunningObjectTable::Revoke.

The most common type of moniker provider is a compound-document link source. This includes server applications that support linking to their documents (or portions of a document) and container applications that support linking to embeddings within their documents. Server applications that do not support linking can also use the ROT to cooperate with container applications that support linking to embeddings.

If you're writing a container application, you must revoke a document's registration when the document is closed. You must also revoke a document's registration before re-registering it when it is renamed.

If you're writing a server application, you must revoke an object's registration when the object is closed. You must also revoke an object's registration before re-registering it when its container document is renamed (see IOleObject::SetMoniker).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IOleObject::SetMoniker, IRunningObjectTable::Register


IServerSecurity

The IServerSecurity interface is used by a server to help authenticate the client and to manage impersonation of the client.

When a client calls a server, the server can call CoGetCallContext to get a pointer to the IServerSecurity interface implemented on the call context object for the current method call. This pointer can be used to call an IServerSecurity method that allows the server to check the client’s authentication or to impersonate the client.

The pointer to the instance of IServerSecurity returned by CoGetCallContext is automatically deleted when the server sends the reply back to the client. Therefore, the IServerSecurity object is valid only for the duration of the call. The IServerSecurity pointer must be released when it is no longer needed.

When to Implement

The stub management code in the system provides an implementation of IServerSecurity for objects by default as part of each incoming call, so typically you would not implement this interface.

You may choose to implement IServerSecurity on the custom stubs of objects that support custom marshaling to maintain a consistent programming model for their objects. Before dispatching an arriving call, custom marshalers call CoSwitchCallContext, specifying a new context object (which must implement IServerSecurity). The original call context must be restored after the server object sends a reply.

When to Use

The methods of the IServerSecurity interface are called by a server object to examine the security settings of a particular call between a client and the server object (QueryBlanket) or to impersonate the client (ImpersonateClient and RevertToSelf). A server impersonates a client by running in the client's security context, which allows the server to test the privilege level of the calling client with an AccessCheck call and to access resources (such as files) as the client. For more information about how impersonation works, see Impersonation and Cloaking.

The information obtained through IServerSecurity also allows an object to perform security checks in addition to the automatic ACL checks COM performs. For example, an application may wish to allow time of day access to some objects or may have a different ACL for each method on an object.

IServerSecurity methods may be called only before the remote procedure call completes.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IServerSecurity Methods | Description
QueryBlanket | Called by the server to find out about the client that invoked one of its methods.
ImpersonateClient | Allows a server to impersonate a client for the duration of a call.
RevertToSelf | Restores the authentication information on a thread.
IsImpersonating | Indicates whether the server is currently impersonating the client.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

Security in COM, CoGetCallContext


IServerSecurity::ImpersonateClient

Allows a server to impersonate a client for the duration of a call.

HRESULT ImpersonateClient( )

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Success.

Remarks

IServerSecurity::ImpersonateClient allows a server to impersonate a client for the duration of a call. Normally, a method executes on a thread that uses the access token of the process. However, when impersonating a client, the server runs in the client's security context so that the server has access to the resources that the client has access to. When impersonation is necessary, the server calls the ImpersonateClient method to cause an access token representing the client's credentials to be assigned to the current thread. This thread token is used for access checks. RevertToSelf restores the current thread's access token.

What the server can do on behalf of the client depends on the impersonation level set by the client, which is specified using one of the RPC_C_IMP_LEVEL_xxx constants. The server may impersonate the client on a secure call at identify, impersonate, or delegate level. For information about these levels of impersonation, see Impersonation Levels.

The identity presented to a server called during impersonation depends on the type of cloaking value, if any, that is set by the client. Cloaking is only available for Windows 2000.

At the end of each method call, COM will call IServerSecurity::RevertToSelf if the application does not.

Traditionally, impersonation information is not nested – the last call to any Win32 impersonation mechanism overrides any previous impersonation. However, in the apartment model, impersonation is maintained during nested calls. Thus if the server A receives a call from B, impersonates, calls C, receives a call from D, impersonates, reverts, and receives the reply from C, the impersonation token will be set back to B, not A.

For information on using impersonation with asynchronous calls, see Impersonation and Asynchronous Calls.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoImpersonateClient


IServerSecurity::IsImpersonating

Indicates whether IServerSecurity::ImpersonateClient has been called without a matching call to IServerSecurity::RevertToSelf.

BOOL IsImpersonating( )

Return Values

TRUE
This thread has called IServerSecurity::ImpersonateClient and is currently impersonating.
FALSE
This thread is not currently impersonating.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

IServerSecurity::RevertToSelf


IServerSecurity::QueryBlanket

Called by the server to find out about the client that invoked one of its methods.

HRESULT QueryBlanket(
  DWORD *  pAuthnSvc,     //Pointer to the current authentication
                          // service
  DWORD *  pAuthzSvc,     //Pointer to the current authorization
                          // service
  OLECHAR **  pServerPrincName,
                          //Pointer to the current principal name
  DWORD * pAuthnLevel,    //Pointer to the current authentication level
  DWORD * pImpLevel,      //Reserved for future use; must be NULL
  RPC_AUTHZ_HANDLE *  pPrivs,
                          //Pointer to handle to privilege information
  DWORD *  pCapabilities  //Pointer to flags indicating further
                          // capabilities
);

Parameter

pAuthnSvc
[out] Pointer to the current authentication service. This will be a single value taken from the list of RPC_C_AUTHN_xxx constants. If the caller specifies NULL, the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to the current authorization service. This will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. If the caller specifies NULL, the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the callee using CoTaskMemAlloc, and must be freed by the caller using CoTaskMemFree when they are done with it. By default, SChannel principal names will be in the msstd form. The fullsic form will be returned if EOAC_MAKE_FULLSIC is specified in the pCapabilities parameter. For more information on the msstd and fullsic forms, see Principal Names. If the caller specifies NULL, the current principal name is not retrieved.
pAuthnLevel
[out] Pointer to the current authentication level. This will be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. If the caller specifies NULL, the current authentication level is not retrieved.
pImpLevel
[out] Must be NULL. This parameter is reserved for future use.
pPrivs
[out] Pointer to a handle that indicates the privilege information for the client application. The format of the structure that the handle refers to depends on the authentication service. The application should not write or free the memory. The information is only valid for the duration of the current call. For NTLMSSP, and Kerberos, this is a SEC_WINNT_AUTH_IDENTITY or SEC_WINNT_AUTH_IDENTITY_EX structure. For SChannel, this is a CERT_CONTEXT that represents the client's certificate. If the client has no certificate, NULL is returned. If the caller specifies NULL, the current privilege information is not retrieved.
pCapabilities
[in, out] Pointer to return flags indicating capabilities of the call. To request that the principal name be returned in fullsic form if SChannel is the authentication service, the caller can set the EOAC_MAKE_FULLSIC flag in this parameter. If the caller specifies NULL, the current capabilities are not retrieved.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
Success.

Remarks

IServerSecurity::QueryBlanket is used by the server to find out about the client that invoked one of its methods. To get a pointer to IServerSecurity for the current call on the current thread, call CoGetCallContext, specifying IID_IServerSecurity. This interface pointer may only be used in the same apartment as the call for the duration of the call.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoQueryClientBlanket, CoQueryProxyBlanket


IServerSecurity::RevertToSelf

Restores a thread's authentication information.

HRESULT RevertToSelf( )

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Success.

Remarks

In Windows NT 4, IServerSecurity::RevertToSelf sets the thread token to NULL. In Windows 2000, RevertToSelf restores the thread's authentication information. This behavior is described as follows.

RevertToSelf restores the authentication information on a thread to the authentication information on the thread before impersonation began. If the server does not call RevertToSelf before the end of the current call, it will be called automatically by COM.

When ImpersonateClient is called on a thread that is not currently impersonating, COM saves the token currently on the thread. A subsequent call to RevertToSelf restores the saved token, and IsImpersonating will then return false. This means that if a series of impersonation calls are made using different IServerSecurity objects, RevertToSelf will restore the token that was on the thread when the first call to ImpersonateClient was made. Also, only one RevertToSelf call is needed to undo any number of ImpersonateClient calls.

This method will only revert impersonation changes made by IServerSecurity::ImpersonateClient. If the thread token is modified by other means (through the SetThreadToken or RpcImpersonateClient Win32 functions) the result of this function is undefined.

RevertToSelf affects only the current method invocation. If there are nested method invocations, each invocation can have its own impersonation token and DCOM will correctly restore the impersonation token before returning to them (regardless of whether or not CoRevertToSelf or IServerSecurity::RevertToSelf was called).

It is important to understand that an instance of IServerSecurity is valid on any thread in the apartment until the call represented by IServerSecurity completes. However, impersonation is local to a particular thread for the duration of the current call on that thread. Therefore, if two threads in the same apartment use the same IServerSecurity instance to call IServerSecurity::ImpersonateClient, one thread can call RevertToSelf without affecting the other.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoRevertToSelf


IStdMarshalInfo

The IStdMarshalInfo interface returns the CLSID identifying the handler to be used in the destination process during standard marshaling.

An object that uses OLE's default implementation of IMarshal does not provide its own proxy but, by implementing IStdMarshalInfo, can nevertheless specify a handler to be loaded in the client process. Such a handler would typically handle certain requests in-process and use OLE's default marshaling to delegate others back to the original object.

To create an instance of an object in some client process, COM must first determine whether the object uses default marshaling or its own implementation. If the object uses default marshaling, COM then queries the object to determine whether it uses a special handler or, simply, OLE's default proxy. To get the CLSID of the handler to be loaded, COM queries the object for the IStdMarshalInfo interface and then the IPersist interface. If neither interface is supported, a standard handler is used.

When to Implement

If you are writing a server application that supports class emulation (that is, if your server can manipulate objects of another type in response to the Activate As option in the Convert dialog box), you must implement the IStdMarshalInfo interface in order to return the CLSID of the handler to be used for the object.

Note that your handler must aggregate the default handler.

When to Use

You typically don't call this interface yourself. COM queries for this interface when performing standard marshaling.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IStdMarshalInfo Method | Description
GetClassForHandler | Obtains the class identifier of the object handler in the destination process.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMarshal


IStdMarshalInfo::GetClassForHandler

Retrieves the CLSID of the object handler to be used in the destination process during standard marshaling.

HRESULT GetClassForHandler(
  DWORD dwDestContext,  //Destination process
  void * pvDestContext, //Reserved
  CLSID * pClsid        //Pointer to the CLSID
);

Parameters

dwDestContext
[in] Destination context, that is, the process in which the unmarshaling will be done. The legal values for dwDestContext are taken from the enumeration MSHCTX. For information on the MSHCTX enumeration, see the "Data Structures" section.
pvDestContext
[in] Reserved for future use; must be NULL.
pClsid
[out] Pointer to the handler's CLSID.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The CLSID was retrieved successfully.

Remarks

Notes to Implementers

Your implementation of IStdMarshalInfo::GetClassForHandler must return your own CLSID. This allows an object created by a different server.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


ISurrogate

ISurrogate defines two methods: one for dynamically loading new DLL servers into an existing surrogate, and one to free the surrogate when it is no longer needed.

A surrogate is an EXE process into which a DLL server can be loaded to give the DLL server the advantages of an EXE server without the coding overhead. It can also allow independent DLL servers to be located together within a single process, reducing the total number of processes needed. DLL servers are easy to write using standard development tools like Microsoft® Visual Basic®, Microsoft® Visual J++™, and Microsoft Visual Studio®, and running them in a surrogate process provides the benefits of an executable implementation, including fault isolation, the ability to serve multiple clients simultaneously, and allowing the server to provide services to remote clients in a distributed environment.

When to Implement

If you are using the system-supplied surrogate, which implements this interface, you do not need to implement it specifically. All surrogate processes must implement this interface, so if you are writing a custom surrogate, you must implement it along with IUnknown, IClassFactory, and IMarshal.

When to Use

In general, you would not call the methods of ISurrogate directly. COM calls the LoadDllServer method when there is an activation request for a class registered with the DllSurrogate named-value. COM also calls FreeSurrogate when all DLL servers running in the surrogate have terminated.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISurrogate Methods | Description
LoadDllServer | Creates an uninitialized object.
FreeSurrogate | Locks object application open in memory.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

Writing a Custom Surrogate, DLL Surrogates


ISurrogate::LoadDllServer

Loads a DLL server into the implementing surrogate. COM calls this method when there is an activation request for the DLL server's class, if the class is registered as DllSurrogate.

HRESULT LoadDllServer(
  REFCLSID  Clsid,  //CLSID of the DLL server to be loaded
);

Parameters

Clsid
[in] CLSID of the DLL server to be loaded.

Return Values

This method supports the standard return values E_UNEXPECTED, E_OUTOFMEMORY, and E_INVALIDARG, as well as the following:

S_OK
The DLL was loaded successfully.

Remarks

Upon receiving a load request through ISurrogate::LoadDllServer, the surrogate must perform the following steps:

  1. Create a class factory object that supports IUnknown, IClassFactory, and IMarshal.
  2. Call CoRegisterClassObject to register the new class factory object as the class factory for the requested CLSID.

This class factory's implementation of IClassFactory::CreateInstance will create an instance of the requested CLSID method by calling CoGetClassObject to get the class factory which creates an actual object for the given CLSID.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISurrogate, ISurrogate::FreeSurrogate, DllSurrogate, CoRegisterSurrogate, Writing a Custom Surrogate


ISurrogate::FreeSurrogate

Terminates the surrogate process. COM calls this method when all of the DLL servers running in a surrogate process have terminated.

HRESULT FreeSurrogate();

Return Values

This method supports the standard return values including E_FAIL, and E_UNEXPECTED, and S_OK.

Remarks

COM calls ISurrogate::FreeSurrogate when there are no more DLL servers running in the surrogate process. When ISurrogate::FreeSurrogate is called, the method must properly revoke all of the class factories registered in the surrogate, and then cause the surrogate process to exit.

Surrogate processes must call the CoFreeUnusedLibraries function periodically to unload DLL servers that are no longer in use. The surrogate process assumes this responsibility, which would normally be the client's responsibility. CoFreeUnusedLibraries calls the DllCanUnloadNow function on any loaded DLL servers. Because CoFreeUnusedLibraries depends on the existence and proper implementation of DllCanUnloadNow in DLL servers, it is not guaranteed to unload all DLL servers that should be unloaded --not every server implements DllCanUnloadNow, and this function is unreliable for free-threaded DLLs. Additionally, the surrogate has no way of being informed when all DLL servers are gone. COM, however, can determine when all DLL servers have been unloaded, and will then call the FreeSurrogate method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISurrogate, DllSurrogate, ISurrogate::LoadDllServer, Writing a Custom Surrogate


ISurrogateService

The ISurrogateService interface is used to initialize, launch, and release a COM+ application. You can also refresh the catalog and shut down the process.

When to Implement

To implement the methods of this interface, you must include unisrgt.idl in your project.

When to Use

In general, you would not call the methods of ISurrogateService directly.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISurrogateService Methods | Description
Init | Initializes the process server.
ApplicationLaunch | Launches the application.
ApplicationFree | Releases the application.
CatalogRefresh_ | Refreshes the catalog.
ProcessShutdown | Shuts down the process.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISurrogateService::ApplicationFree

Platform SDK: COM

ISurrogateService::ApplicationFree[C/C++]

SurrogateService.ApplicationFree Method [Visual Basic]

Releases the application.

[C/C++]
HRESULT ApplicationFree(
REFGUID rguidApplID
) = 0;

[Visual Basic]
object.ApplicationFree(rguidAppID, appType)

Parameters [C/C++]

rguidAppID
[in] The application ID.

Parameters [Visual Basic]

object
Required object that references a SurrogateService object.
rguidAppID
The application ID.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISurrogateService::ApplicationLaunch

Platform SDK: COM

ISurrogateService::ApplicationLaunch[C/C++]

SurrogateService.ApplicationLaunch Method [Visual Basic]

Launches the application.

[C/C++]
HRESULT ApplicationLaunch(
REFGUID rguidApplID,
ApplicationType appType
) = 0;

[Visual Basic]
object.ApplicationLaunch(rguidAppID, appType)

Parameters [C/C++]

rguidAppID
[in] The application ID.
appType
[in] The application type.

Parameters [Visual Basic]

object
Required object that references a SurrogateService object.
rguidAppID
The application ID.
appType
The application type.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISurrogateService::CatalogRefresh

Platform SDK: COM

ISurrogateService::CatalogRefresh[C/C++]

SurrogateService.CatalogRefresh Method [Visual Basic]

Refreshes the catalog.

[C/C++]
HRESULT CatalogRefresh(
ULONG ulReserved
) = 0;

[Visual Basic]
object.CatalogRefresh(ulReserved)

Parameters [C/C++]

ulReserved
[in] Reserved.

Parameters [Visual Basic]

object
Required object that references a SurrogateService object.
ulReserved
Reserved.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISurrogateService::Init

Platform SDK: COM

ISurrogateService::Init [C/C++]

SurrogateService.Init Method [Visual Basic]

Initializes the process server.

[C/C++]
HRESULT Init(
REFGUID rguidProcessID,
IProcessLock * pProcessLock,
BOOL * pfApplicationAware
) = 0;

[Visual Basic]
object.Init(rguidProcessID, pProcessLock, pfApplicationAware)

Parameters [C/C++]

rguidProcessID
[in] The process ID of the server application.
pProcessLock
[in] Points to an instance of IProcessLock.
pfApplicationAware
[out] True if the application is aware of the initialization.

Parameters [Visual Basic]

object
Required object that references a SurrogateService object.
rguidProcessID
The process ID.
pProcessLock
Points to an instance of IProcessLock.
pfApplicationAware
True if the application is aware of the initialization.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISurrogateService::ProcessShutdown

Platform SDK: COM

ISurrogateService::ProcessShutdown[C/C++]

SurrogateService.ProcessShutdown Method [Visual Basic]

Shuts down the process.

[C/C++]
HRESULT ProcessShutdown(
ShutdownType shutdownType
) = 0;

[Visual Basic]
object.ProcessShutdown(shutdownType)

Parameters [C/C++]

shutdownType
[in] The shutdown type.

Parameters [Visual Basic]

object
Required object that references a SurrogateService object.
shutdownType
The shutdown type.

Return Values [C/C++]

S_OK
Method completed successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objidl.h.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, CoGetInterceptor


ISynchronize

The ISynchronize interface provides asynchronous communication between objects about the occurrence of an event. Objects that implement ISynchronize can receive indications that an event has occurred, and they can respond to queries about the event. In this way, clients can make sure that one request has been processed before they submit a subsequent request that depends on completion of the first one.

When to Implement

You do not normally implement ISynchronize. This interface is implemented on two system-supplied synchronization objects, the standard event object (CLSID_StdEvent) and the manual reset event object (CLSID_ManualResetEvent). Objects can aggregate one of these objects to obtain an implementation of ISynchronize.

When to Use

Server objects use ISynchronize to indicate that an event has occurred. Clients can then use ISynchronize to make sure that an event has occurred before they process additional requests. For example, a client making calls on the asynchronous interface of a call object would call a Begin_ method on the asynchronous interface and then call ISynchronize::Wait to determine whether the Begin_ method was finished processing. Only after determining that the Begin_ method was complete would the client call the corresponding Finish_ method.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISynchronize Methods | Description
Wait | Waits for the synchronization object to be signaled or for a specified timeout period to elapse, whichever comes first.
Signal | Sets the synchronization object’s state to signaled.
Reset | Resets the synchronization object to the not signaled state.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


ISynchronize::Reset

Resets the synchronization object to the not signaled state.

HRESULT Reset(void);

Return Values

S_OK
The synchronization object was reset.

Remarks

The ISynchronize::Wait method implemented on a standard event object (CLSID_StdEvent) automatically calls Reset when the synchronization object has been signaled.

The implementation of ISynchronize::Wait on the manual reset event object (CLSID_ManualResetEvent) does not automatically call Reset. A server object normally calls Reset from a method that clients call after they detect that the synchronization object was signaled.

In general, it is the server's responsibility to call Reset. If, however, the client needs to begin with the synchronization object in an unsignaled state, it is good practice for the client to call Reset to be safe.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronize::Signal, ISynchronize::Wait


ISynchronize::Signal

Sets the synchronization object to the signaled state and causes pending calls to ISynchronize::Wait to return S_OK.

HRESULT Signal(void);

Return Values

S_OK
The synchronization object was signaled.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronize::Reset, ISynchronize::Wait


ISynchronize::Wait

Waits for the synchronization object to be signaled or for a specified timeout period to elapse, whichever comes first.

HRESULT Wait(
  DWORD dwFlags,         //Wait options
  DWORD dwMilliseconds   //Timeout period in ms
);

Parameters

dwFlags
[in] Wait options. Values are taken from the COWAIT_FLAGS enumeration.
dwMilliseconds
[in] Time, in milliseconds, this call will wait before returning. If INFINITE, the caller will wait until the synchronization object is signaled, no matter how long it takes. If 0, Wait returns immediately.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The synchronization object was signaled.
RPC_S_CALLPENDING
The timeout period elapsed before the synchronization object was signaled.

Remarks

If the caller is waiting in a single-thread apartment, Wait enters the COM modal loop. If the caller is waiting in a multithread apartment, the caller is blocked until Wait returns.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoWaitForMultipleHandles


ISynchronizeContainer

The ISynchronizeContainer interface manages a group of unsignaled synchronization objects.

When to Implement

You do not normally implement ISynchronizeContainer. This interface is implemented on a system-supplied synchronization object container (CLSID_SynchronizeContainer).

When to Use

Clients call the methods of ISynchronizeContainer when they have several outstanding asynchronous calls at one time. A client can add the synchronization object associated with each call to a synchronization object container and then wait for any one of them to be signaled.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISynchronizeContainer Methods | Description
AddSynchronize | Adds a synchronization object to the container.
WaitMultiple | Waits for any synchronization object in the container to be signaled or for a specified timeout period to elapse, whichever comes first.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronize


ISynchronizeContainer::AddSynchronize

Adds a synchronization object to the container.

HRESULT AddSynchronize(
  ISynchronize *pSync         //Pointer to synchronization object
);

Parameters

pSync
[in] Pointer to the synchronization object to be added to the container.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The synchronization object was added.
RPC_E_OUT_OF_RESOURCES
The synchronization object container is full.

Remarks

A synchronization container can hold pointers to as many as 63 synchronization objects.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


ISynchronizeContainer::WaitMultiple

Waits for any synchronization object in the container to be signaled or for a specified timeout period to elapse, whichever comes first.

HRESULT WaitMultiple(
  DWORD dwFlags,         //Wait options
  DWORD dwTimeout,       //Timeout period in ms
  ISynchronize **ppSync  //Synchronization object that was signaled
);

Parameters

dwFlags
[in] Wait options. Values are taken from the COWAIT_FLAGS enumeration. COWAIT_WAITALL is not a valid setting for this method.
dwTimeout
[in] Time, in milliseconds, this call will wait before returning. If INFINITE, the caller will wait until a synchronization object is signaled, no matter how long it takes. If 0, Wait returns immediately.
ppSync
[out] Address of a pointer to the ISynchronize interface on the synchronization object that was signaled. Cannot be NULL.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The synchronization object was signaled.
RPC_E_TIMEOUT
The timeout period elapsed before the synchronization object was signaled.
RPC_E_NO_SYNC
There are no synchronization objects in the container.

Remarks

If the caller is waiting in a single-thread apartment, WaitMultiple enters the COM modal loop. If the caller is waiting in a multithread apartment, the caller is blocked until WaitMultiple returns.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

CoWaitForMultipleHandles


ISynchronizeEvent

The ISynchronizeEvent interface assigns a Win32 event handle to a synchronization object.

The synchronization object can use a Win32 handle to manage its activities. For example, the Win32 functions that control waiting for an event and signaling and resetting event states use handles to identify the event they control. Thus, the logic of the ISynchronize::Signal method on an event synchronization object can pass its handle to the Win32 SetEvent function.

When to Implement

In most cases, you do not implement ISynchronizeEvent. This interface is implemented on two system-supplied synchronization objects, the standard event object (CLSID_StdEvent) and the manual reset event object (CLSID_ManualResetEvent).

When to Use

Use ISynchronizeEvent to assign a Win32 event handle to a synchronization object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISynchronizeEvent Method | Description
SetEventHandle | Assigns a handle to a synchronization object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronizeHandle


ISynchronizeEvent::SetEventHandle

Assigns a handle to a synchronization object.

HRESULT SetEventHandle(
  HANDLE h         //Handle associated with object
);

Parameters

h
[in] Handle associated with the synchronization object.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The handle was set.

Remarks

Call SetEventHandle to add a Win32 handle to a synchronization object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronizeHandle


ISynchronizeHandle

The ISynchronizeHandle interface obtains a Win32 handle associated with a synchronization object.

The synchronization object can use a Win32 handle to manage its activities. For example, the Win32 functions that control waiting for an event and signaling and resetting event states use handles to identify the event they control. Thus, the logic of the ISynchronize::Signal method on an event synchronization object can pass its handle to the Win32 SetEvent function. With the ISynchronizeHandle interface, you can obtain the handle that has been assigned to a synchronization object.

When to Implement

In most cases, you do not implement ISynchronizeHandle. This interface is implemented on two system-supplied synchronization objects, the standard event object (CLSID_StdEvent) and the manual reset event object (CLSID_ManualResetEvent).

When to Use

Use ISynchronizeHandle to obtain the Win32 handle assigned to a synchronization object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISynchronizeHandle Method | Description
GetHandle | Obtains the handle associated with the synchronization object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronizeEvent


ISynchronizeHandle::GetHandle

Obtains the handle associated with the synchronization object.

HRESULT GetHandle(
  HANDLE *ph         //Handle associated with object
);

Parameters

ph
[out] Pointer to the handle associated with the synchronization object.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The handle was obtained.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ISynchronizeEvent::SetEventHandle


IUnknown

The IUnknown interface lets clients get pointers to other interfaces on a given object through the QueryInterface method, and manage the existence of the object through the IUnknown::AddRef and IUnknown::Release methods. All other COM interfaces are inherited, directly or indirectly, from IUnknown. Therefore, the three methods in IUnknown are the first entries in the VTable for every interface.

When to Implement

You must implement IUnknown as part of every interface. If you are using C++ multiple inheritance to implement multiple interfaces, the various interfaces can share one implementation of IUnknown. If you are using nested classes to implement multiple interfaces, you must implement IUnknown once for each interface you implement.

When to Use

Use IUnknown methods to switch between interfaces on an object, add references, and release objects.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.


IUnknown::AddRef

The IUnknown::AddRef method increments the reference count for an interface on an object. It should be called for every new copy of a pointer to an interface on a given object.

ULONG AddRef(void);

Return Value

Returns an integer from 1 to n, the value of the new reference count. This information is meant to be used for diagnostic/testing purposes only, because, in certain situations, the value may be unstable.

Remarks

Objects use a reference counting mechanism to ensure that the lifetime of the object includes the lifetime of references to it. You use IUnknown::AddRef to stabilize a copy of an interface pointer. It can also be called when the life of a cloned pointer must extend beyond the lifetime of the original pointer. The cloned pointer must be released by calling IUnknown::Release.

Objects must be able to maintain (231)-1 outstanding pointer references. Therefore, the internal reference counter that IUnknown::AddRef maintains must be a 32-bit unsigned integer.

Notes to Callers

Call this function for every new copy of an interface pointer that you make. For example, if you are passing a copy of a pointer back from a function, you must call IUnknown::AddRef on that pointer. You must also call IUnknown::AddRef on a pointer before passing it as an in-out parameter to a function; the function will call IUnknown::Release before copying the out-value on top of it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.

See Also

IUnknown::Release


IUnknown::QueryInterface

Returns a pointer to a specified interface on an object to which a client currently holds an interface pointer. This function must call IUnknown::AddRef on the pointer it returns.

HRESULT QueryInterface(
  REFIID iid,        //Identifier of the requested interface
  void ** ppvObject  //Address of output variable that receives the
                     //interface pointer requested in iid
);

Parameters

iid
[in] Identifier of the interface being requested.
ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer to the object. If the object does not support the interface specified in iid, *ppvObject is set to NULL.

Return Value

S_OK if the interface is supported, E_NOINTERFACE if not.

Remarks

The QueryInterface method gives a client access to other interfaces on an object.

For any one object, a specific query for the IUnknown interface on any of the object's interfaces must always return the same pointer value. This allows a client to determine whether two pointers point to the same component by calling QueryInterface on both and comparing the results. It is specifically not the case that queries for interfaces (even the same interface through the same pointer) must return the same pointer value.

There are four requirements for implementations of QueryInterface (In these cases, "must succeed" means "must succeed barring catastrophic failure."):

  • The set of interfaces accessible on an object through IUnknown::QueryInterface must be static, not dynamic. This means that if a call to QueryInterface for a pointer to a specified interface succeeds the first time, it must succeed again, and if it fails the first time, it must fail on all subsequent queries.
  • It must be reflexive — if a client holds a pointer to an interface on an object, and queries for that interface, the call must succeed.
  • It must be symmetric — if a client holding a pointer to one interface queries successfully for another, a query through the obtained pointer for the first interface must succeed.
  • It must be transitive — if a client holding a pointer to one interface queries successfully for a second, and through that pointer queries successfully for a third interface, a query for the first interface through the pointer for the third interface must succeed.

Notes to Implementers

Implementations of QueryInterface must never check ACLs. The main reason for this rule is because COM requires that an object supporting a particular interface always return success when queried for that interface. Another reason is that checking ACLs on QueryInterface does not provide any real security because any client who has access to a particular interface can hand it directly to another client without any calls back to the server. Also, because COM caches interface pointers, it does not call QueryInterface on the server every time a client does a query.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.


IUnknown::Release

Decrements the reference count for the calling interface on a object. If the reference count on the object falls to 0, the object is freed from memory.

ULONG Release(void);

Return Value

Returns the resulting value of the reference count, which is used for diagnostic/testing purposes only.

Remarks

If IUnknown::AddRef has been called on this object's interface n times and this is the n+1th call to IUnknown::Release, the implementation of IUnknown::AddRef must cause the interface pointer to free itself. When the released pointer is the only existing reference to an object (whether the object supports single or multiple interfaces), the implementation must free the object.

Note Aggregation of objects restricts the ability to recover interface pointers.

Notes to Callers

Call this function when you no longer need to use an interface pointer. If you are writing a function that takes an in-out parameter, call IUnknown::Release on the pointer you are passing in before copying the out-value on top of it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in unknwn.h.

See Also

IUnknown::AddRef


BindMoniker

Locates an object by means of its moniker, activates the object if it is inactive, and retrieves a pointer to the specified interface on that object.

HRESULT BindMoniker(
  LPMONIKER pmk,     //Pointer to the object's moniker
  DWORD grfOpt,      //Reserved
  REFIID iidResult,  //Interface identifier
  LPVOID FAR *ppvResult
                     //Address of output variable that receives the
                     // interface pointer requested in iidResult
);

Parameters

pmk
[in] Pointer to the object's moniker.
grfOpt
[in] Reserved for future use; must be zero.
iidResult
[in] Interface identifier to be used to communicate with the object.
ppvResult
[out] Address of pointer variable that receives the interface pointer requested in iidResult. Upon successful return, *ppvResult contains the requested interface pointer. If an error occurs, *ppvResult is NULL. If the call is successful, the caller is responsible for releasing the pointer with a call to the object's IUnknown::Release method.

Return Values

S_OK
The object was located and activated, if necessary, and a pointer to the requested interface was returned.
MK_E_NOOBJECT
The object that the moniker object identified could not be found.

This function can also return any of the error values returned by the IMoniker::BindToObject method.

Remarks

BindMoniker is a helper function supplied as a convenient way for a client that has the moniker of an object to obtain a pointer to one of that object's interfaces. The BindMoniker function packages the following calls:

CreateBindCtx(0, &pbc);
pmk->BindToObject(pbc, NULL, riid, ppvObj);

CreateBindCtx creates a bind context object that supports the system implementation of IBindContext. The pmk parameter is actually a pointer to the IMoniker implementation on a moniker object. This implementation's BindToObject method supplies the pointer to the requested interface pointer.

If you have several monikers to bind in quick succession and if you know that those monikers will activate the same object, it may be more efficient to call the IMoniker::BindToObject method directly, which allows you to use the same bind context object for all the monikers. See the IBindCtx interface for more information.

Container applications that allow their documents to contain linked objects are a special client that generally does not make direct calls to IMoniker methods. Instead, the client manipulates the linked objects through the IOleLink interface. The default handler implements this interface and calls the appropriate IMoniker methods as needed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CreateBindCtx, IMoniker::BindToObject


CLSIDFromProgID

Looks up a CLSID in the registry, given a ProgID.

HRESULT CLSIDFromProgID(
  LPCOLESTR lpszProgID,  //Pointer to the ProgID
  LPCLSID pclsid         //Pointer to the CLSID
);

Parameters

lpszProgID
[in] Pointer to the ProgID whose CLSID is requested.
pclsid
[out] Pointer to the retrieved CLSID on return.

Return Values

S_OK
The CLSID was retrieved successfully.
CO_E_CLASSSTRING
The registered CLSID for the ProgID is invalid.
REGDB_E_WRITEREGDB
An error occurred writing the CLSID to the registry. See "Remarks" below.

Remarks

Given a ProgID, CLSIDFromProgID looks up its associated CLSID in the registry. If the ProgID cannot be found in the registry, CLSIDFromProgID creates an OLE 1 CLSID for the ProgID and a CLSID entry in the registry. Because of the restrictions placed on OLE 1 CLSID values, CLSIDFromProgID and CLSIDFromString are the only two functions that can be used to generate a CLSID for an OLE 1 object.

CLSIDFromProgID never triggers an automatic installation of missing classes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

ProgIDFromCLSID


CLSIDFromProgIDEx

CLSIDFromProgIDEx triggers automatic installation if the COMClassStore Policy is enabled (analogous to the behavior of CoCreateInstance(Ex) when neither CLSCTX_ENABLE_CODE_DOWNLOAD nor CLSCTX_NO_CODE_DOWNLOAD are specified).

HRESULT CLSIDFromProgIDEx(
  LPCOLESTR lpszProgID,  //Pointer to the ProgID
  LPCLSID pclsid         //Pointer to the CLSID
);

Parameters

lpszProgID
[in] Pointer to the ProgID whose CLSID is requested.
pclsid
[out] Pointer to the retrieved CLSID on return.

Return Values

S_OK
The CLSID was retrieved successfully.
CO_E_CLASSSTRING
The registered CLSID for the ProgID is invalid.
REGDB_E_WRITEREGDB
An error occurred writing the CLSID to the registry. See "Remarks" below.

Remarks

CLSCTX_ENABLE_CODE_DOWNLOAD enables automatic installation of missing classes through IntelliMirror/Application Management from the Active Directory. If this flag is not specified, the COMClassStore Policy ("Download missing COM components") determines the behavior (default: no download).

If the COMClassStore Policy enables automatic installation, CLSCTX_NO_CODE_DOWNLOAD can be used to explicitly disallow download for an activation.

If either of the following registry keys is enabled, automatic download of missing classes is enabled.

  • HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\App Management
    Value Name: COMClassStore, ValueType: DWORD. 1 = enabled, 0 = disabled (default = 0)
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\App Management
    Value Name: COMClassStore, ValueType: DWORD. 1 = enabled, 0 = disabled (default = 0)

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

ProgIDFromCLSID, CLSCTX, CoCreateInstance


CLSIDFromString

Converts a string generated by the StringFromCLSID function back into the original CLSID.

HRESULT CLSIDFromString(
  LPOLESTR lpsz,  //Pointer to the string representation of the CLSID
  LPCLSID pclsid  //Pointer to the CLSID
);

Parameters

lpsz
[in] Pointer to the string representation of the CLSID.
pclsid
[out] Pointer to the CLSID on return.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

NOERROR
The CLSID was obtained successfully.
CO_E_CLASSTRING
The class string was improperly formatted.
REGDB_E_WRITEREGDB
The CLSID corresponding to the class string was not found in the registry.

Remarks

Because of the restrictions placed on OLE 1 CLSID values, CLSIDFromProgID and CLSIDFromString are the only two functions that can be used to generate a CLSID for an OLE 1 object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CLSIDFromProgID, StringFromCLSID


CoAddRefServerProcess

Increments a global per-process reference count.

ULONG CoAddRefServerProcess(void);

Return Values

S_OK
The CLSID was retrieved successfully.

Remarks

Servers can call CoAddRefServerProcess to increment a global per-process reference count. This function is particularly helpful to servers that are implemented with multiple threads, either multi-apartmented or free-threaded. Servers of these types must coordinate the decision to shut down with activation requests across multiple threads. Calling CoAddRefServerProcess increments a global per-process reference count, and calling CoReleaseServerProcess decrements that count.

When that count reaches zero, OLE automatically calls CoSuspendClassObjects, which prevents new activation requests from coming in. This permits the server to deregister its class objects from its various threads without worry that another activation request may come in. New activation requests result in launching a new instance of the local server process.

The simplest way for a local server application to make use of these API functions is to call CoAddRefServerProcess in the constructor for each of it's instance objects, and in each of its IClassFactory::LockServer methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess in the destruction of each of its instance objects, and in each of its IClassFactory::LockServer methods when the fLock parameter is FALSE. Finally, the server application should pay attention to the return code from CoReleaseServerProcess and if it returns 0, the server application should initiate its cleanup, which, for a server with multiple threads, typically means that it should signal its various threads to exit their message loops and call CoRevokeClassObject and CoUninitialize.

If these APIs are used at all, they must be called in both the object instances and the LockServer method, otherwise the server application may be shut down prematurely. In-process servers typically should not call CoAddRefServerProcess or CoReleaseServerProcess.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoReleaseServerProcess, IClassFactory::LockServer, Out-of-process Server Implementation Helpers


CoAllowSetForegroundWindow

The system restricts which processes can call the SetForegroundWindow function and AllowSetForegroundWindow to set the foreground window. As a result, an application is blocked from stealing the focus from another application even when the user is interacting with it. The CoAllowSetForegroundWindow function enables the COM server process called to take focus away from the client application by using the IForegroundTransfer interface. The COM client then yields the foreground window to the specified COM server process whose proxy is pUnk. Yielding the foreground window is useful when the client wants a window owned by the server process to take focus.

HRESULT CoAllowSetForegroundWindow(
   IUnknown *pUnk,
   LPVOID lpvReserved
);

Parameters

pUnk
[in] Points to the proxy of the COM server process. Therefore, when a method call is done, the foreground window can be replaced with one that the server owns instead of one owned by the client.
lpvReserved
[in] Reserved for future use; must be NULL.

Return Values

This method returns the following values:

S_OK
The method was successful.
E_INVALIDARG
The lpvReserved parameter is not NULL.
E_NOINTERFACE
The punk parameter does not support foreground window control.

Remarks

CoAllowSetForegroundWindow assists a user that has a custom interface to get the same behavior that happens for OLE interfaces where a change of window is expected (primarily associated with linking and embedding). The following example demonstrates how CoAllowSetForegroundWindow uses the IForegroundTransfer interface and its method AllowForegroundTransfer:

pUnk->QueryInterface(IID_IForegroundTransfer, (void**)&pft);
pft->AllowForegroundTransfer(NULL);
pft->Release();

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

QueryInterface


CoBuildVersion

This function is obsolete.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoCancelCall

Requests cancellation of an outbound DCOM method call pending on a specified thread.

HRESULT CoCancelCall(
  DWORD dwThreadID,  //Thread issuing call
  ULONG ulTimeout    //Timeout period
);

Parameter

dwThreadID
Identifier of the thread on which the pending DCOM call is to be canceled. If 0, the call is on the current thread.
ulTimeout
Number of seconds CoCancelCall waits for the server to complete the outbound call after the client requests cancellation.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The cancellation request was made.
E_NOINTERFACE
There is no cancel object corresponding to the specified thread.
CO_E_CANCEL_DISABLED
Call cancellation is not enabled on the specified thread.
RPC_E_CALL_COMPLETE
The call was completed during the timeout interval.
RPC_E_CALL_CANCELLED
The call was already cancelled.

Remarks

CoCancelCall calls CoGetCancelObject and then calls ICancelMethodCalls::Cancel on the cancel object for the call being executed.

The object server can determine if the call has been canceled by periodically calling CoTestCancel. If the call has been canceled, the object server should clean up and return control to the client.

This function does not locate cancel objects for asynchronous calls.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoCopyProxy

Makes a private copy of the specified proxy.

HRESULT CoCopyProxy(
  IUnknown * pProxy,  //IUnknown pointer to the proxy to copy
  IUnknown ** ppCopy  //Address of output variable that receives
                      // the IUnknown interface pointer to the proxy
                      // copy
);

Parameter

pProxy
[in] Points to the IUnknown interface on the proxy to be copied. May not be NULL.
ppCopy
[out] Address of IUnknown* pointer variable that receives the interface pointer to the copy of the proxy. It may not be NULL.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.

Remarks

CoCopyProxy makes a private copy of the specified proxy. Typically, this function is called when a client needs to change the authentication information of its proxy through a call to either CoSetProxyBlanket or IClientSecurity::SetBlanket without changing this information for other clients. CoSetProxyBlanket affects all the users of an instance of a proxy, so creating a private copy of the proxy through a call to CoCopyProxy and then calling CoSetProxyBlanket (or IClientSecurity::SetBlanket) using the copy eliminates the problem.

This helper function encapsulates the following sequence of common calls (error handling excluded):

    pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
    pcs->CopyProxy(punkProxy, ppunkCopy);
    pcs->Release();

Local interfaces may not be copied. IUnknown and IClientSecurity are examples of existing local interfaces.

Copies of the same proxy have a special relationship with respect to QueryInterface. Given a proxy, a, of the IA interface of a remote object, suppose a copy of a is created, called b. In this case, calling QueryInterface from the b proxy for IID_IA will not retrieve the IA interface on b, but the one on a, the original proxy with the "default" security settings for the IA interface.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IClientSecurity::CopyProxy, IClientSecurity::SetBlanket, CoSetProxyBlanket, Security in COM


CoCreateFreeThreadedMarshaler

Creates an aggregatable object capable of context-dependent marshaling.

HRESULT CoCreateFreeThreadedMarshaler(
  LPUNKNOWN punkOuter,        //Pointer to object aggregating the
                              // marshaler object
  LPUNKNOWN * ppunkMarshaler  //Address of output variable that
                              // receives the IUnknown interface
                              // pointer to the marshaler object
);

Parameters

punkOuter
[in] Pointer to the aggregating object's controlling IUnknown.
ppunkMarshaler
[out] Address of IUnknown* pointer variable that receives the interface pointer to the aggregatable marshaler.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The marshaler was created.

Remarks

The CoCreateFreeThreadedMarshaler function enables an object to efficiently marshal interface pointers between threads in the same process. If your objects do not support interthread marshaling, you have no need to call this function. It is intended for use by free-threaded DLL servers that must be accessed directly by all threads in a process, even those threads associated with single-threaded apartments. It custom-marshals the real memory pointer over into other apartments as a bogus "proxy" and thereby gives direct access to all callers, even if they are not free-threaded.

The CoCreateFreeThreadedMarshaler function performs the following tasks:

  1. Creates a free-threaded marshaler object.
  2. Aggregates this marshaler to the object specified by the punkOuter parameter. This object is normally the one whose interface pointers are to be marshaled.

The aggregating object's implementation of IMarshal should delegate QueryInterface calls for IID_IMarshal to the IUnknown of the free-threaded marshaler. Upon receiving a call, the free-threaded marshaler performs the following tasks:

  1. Checks the destination context specified by the CoMarshalInterface function's dwDestContext parameter.
  2. If the destination context is MSHCTX_INPROC, copies the interface pointer into the marshaling stream.
  3. If the destination context is any other value, finds or creates an instance of COM's default (standard) marshaler and delegates marshaling to it.

Values for dwDestContext come from the MSHCTX enumeration. MSHCTX_INPROC indicates that the interface pointer is to be marshaled between different threads in the same process. Because both threads have access to the same address space, the client thread can dereference the pointer directly rather than having to direct calls to a proxy. In all other cases, a proxy is required, so CoCreateFreeThreadedMarshaler delegates the marshaling job to COM's default implementation.

Great care should be exercised in using the CoCreateFreeThreadedMarshaler function. This is because the performance of objects which aggregate the free-threaded marshaler is obtained through a calculated violation of the rules of COM, with the ever-present risk of undefined behavior unless the object operates within certain restrictions. The most important restrictions are:

  1. A free-threaded marshaler object cannot hold direct pointers to interfaces on an object that does not aggregate the free-threaded marshaler as part of its state. If the object were to use direct references to ordinary single-threaded aggregate objects, it may break their single threaded property. If the object were to use direct references to ordinary multi-threaded aggregate objects, these objects can behave in ways that show no sensitivity to the needs of direct single-threaded aggregate clients. For example, these objects can spin new threads and pass parameters to the threads that are references to ordinary single-threaded aggregate objects.
  2. A free-threaded marshaler object cannot hold references to proxies to objects in other apartments. Proxies are sensitive to the threading model and can return RPC_E_WRONG_THREAD if called by the wrong client.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoMarshalInterThreadInterfaceInStream, CoGetInterfaceAndReleaseStream


CoCreateGuid

Creates a GUID, a unique 128-bit integer used for CLSIDs and interface identifiers.

HRESULT CoCreateGuid(
  GUID  *pguid  //Pointer to the GUID on return
);

Parameter

pguid
[out] Pointer to the requested GUID on return.

Return Value

S_OK
The GUID was successfully created.

Win32 errors are returned by UuidCreate but wrapped as an HRESULT.

Remarks

The CoCreateGuid function calls the RPC function UuidCreate, which creates a GUID, a globally unique 128-bit integer. Use the CoCreateGuid function when you need an absolutely unique number that you will use as a persistent identifier in a distributed environment.To a very high degree of certainty, this function returns a unique value – no other invocation, on the same or any other system (networked or not), should return the same value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

UuidCreate


CoCreateInstance

Creates a single uninitialized object of the class associated with a specified CLSID. Call CoCreateInstance when you want to create only one object on the local system. To create a single object on a remote system, call CoCreateInstanceEx. To create multiple objects based on a single CLSID, refer to the CoGetClassObject function.

STDAPI CoCreateInstance(
  REFCLSID rclsid,     //Class identifier (CLSID) of the object
  LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
  DWORD dwClsContext,  //Context for running executable code
  REFIID riid,         //Reference to the identifier of the interface
  LPVOID * ppv         //Address of output variable that receives
                       // the interface pointer requested in riid
);

Parameters

rclsid
[in] CLSID associated with the data and code that will be used to create the object.
pUnkOuter
[in] If NULL, indicates that the object is not being created as part of an aggregate. If non-NULL, pointer to the aggregate object's IUnknown interface (the controlling IUnknown).
dwClsContext
[in] Context in which the code that manages the newly created object will run. The values are taken from the enumeration CLSCTX.
riid
[in] Reference to the identifier of the interface to be used to communicate with the object.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.

Return Values

S_OK
An instance of the specified object class was successfully created.
REGDB_E_CLASSNOTREG
A specified class is not registered in the registration database. Also can indicate that the type of server you requested in the CLSCTX enumeration is not registered or the values for the server types in the registry are corrupt.
CLASS_E_NOAGGREGATION
This class cannot be created as part of an aggregate.
E_NOINTERFACE
The specified class does not implement the requested interface, or the controlling IUnknown does not expose the requested interface.

Remarks

The CoCreateInstance helper function provides a convenient shortcut by connecting to the class object associated with the specified CLSID, creating an uninitialized instance, and releasing the class object. As such, it encapsulates the following functionality:

CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, &pCF);
hresult = pCF->CreateInstance(pUnkOuter, riid, ppvObj)
pCF->Release();

It is convenient to use CoCreateInstance when you need to create only a single instance of an object on the local machine. If you are creating an instance on remote machine, call CoCreateInstanceEx. When you are creating multiple instances, it is more efficient to obtain a pointer to the class object's IClassFactory interface and use its methods as needed. In the latter case, you should use the CoGetClassObject function.

In the CLSCTX enumeration, you can specify the type of server used to manage the object. The constants can be CLSCTX_INPROC_SERVER, CLSCTX_INPROC_HANDLER, CLSCTX_LOCAL_SERVER, or any combination of these values. The constant CLSCTX_ALL is defined as the combination of all three. For more information about the use of one or a combination of these constants, refer to CLSCTX.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetClassObject, IClassFactory::CreateInstance, CoCreateInstanceEx, CLSCTX, Instance Creation Helper Functions


CoCreateInstanceEx

Creates an instance of a specific class on a specific machine.

HRESULT CoCreateInstanceEx(
  REFCLSID rclsid,             //CLSID of the object to be created
  IUnknown *punkOuter,         //If part of an aggregate, the
                               // controlling IUnknown
  DWORD dwClsCtx,              //CLSCTX values
  COSERVERINFO *pServerInfo,   //Machine on which the object is to
                               // be instantiated
  ULONG cmq,                   //Number of MULTI_QI structures in
                               // pResults
  MULTI_QI *pResults           //Array of MULTI_QI structures
);

Parameters

rclsid
[in] CLSID of the object to be created.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggregate, and punkOuter is to be used as the new instance's controlling IUnknown. Aggregation is currently not supported cross-process or cross-machine. When instantiating an object out of process, CLASS_E_NOAGGREGATION will be returned if punkOuter is non-NULL.
dwClsCtx
[in] Values taken from the CLSCTX enumeration.
pServerInfo
[in] Information about the computer on which to instantiate the object. May be NULL, in which case the object is instantiated on the local computer or at the computer specified in the registry under the class's RemoteServerName named value, according to the interpretation of the dwClsCtx parameter. See the CLSCTX documentation for details).
cmq
[in] Number of MULTI_QI structures in pResults. Must be greater than zero.
pResults
[in] Array of MULTI_QI structures. Each structure has three members: the identifier for a requested interface (pIID), the location to return the interface pointer (pItf) and the return value of the call to QueryInterface (hr).

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Indicates success.
REGDB_E_CLASSNOTREG
A specified class is not registered in the registration database. Also can indicate that the type of server you requested in the CLSCTX enumeration is not registered or the values for the server types in the registry are corrupt.
CLASS_E_NOAGGREGATION
This class cannot be created as part of an aggregate.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the pResults array were successfully retrieved. The hr field of each of the MULTI_QI structures in pResults indicates with S_OK or E_NOINTERFACE whether the specific interface was returned.
E_NOINTERFACE
None of the interfaces requested in the pResults array were successfully retrieved.

Remarks

CoCreateInstanceEx creates a single uninitialized object associated with the given CLSID on a specified remote machine. This is an extension of the function CoCreateInstance, which creates an object on the local machine only. In addition, rather than requesting a single interface and obtaining a single pointer to that interface, CoCreateInstanceEx makes it possible to specify an array of structures, each pointing to an interface identifier (IID) on input, and, on return, containing (if available) a pointer to the requested interface and the return value of the QueryInterface call for that interface. This permits fewer round trips between machines.

The CoCreateInstanceEx helper function encapsulates three calls: first, to CoGetClassObject to connect to the class object associated with the specified CLSID, specifying the machine location of the class; second, to IClassFactory::CreateInstance to create an uninitialized instance, and finally, to IClassFactory::Release, to release the class object.

The object so created must still be initialized through a call to one of the initialization interfaces (such as IPersistStorage:::Load). The two helper functions, CoGetInstanceFromFile and CoGetInstanceFromIStorage encapsulate both the instance creation and initialization from the obvious sources.

The COSERVERINFO structure passed as the pServerInfo parameter contains the security settings that COM will use when creating a new instance of the specified object. Note that this parameter does not influence the security settings used when making method calls on the instantiated object. Those security settings are configurable, on a per-interface basis, with the CoSetProxyBlanket function.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetInstanceFromFile, CoGetInstanceFromIStorage, CLSCTX, COSERVERINFO, Instance Creation Helper Functions


CoCreateStandardMalloc

This function is obsolete. Refer to CoGetMalloc.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoDisableCallCancellation

Undoes the action of a call to CoEnableCallCancellation. Disables cancellation of synchronous calls on the calling thread when all calls to CoEnableCallCancellation are balanced by calls to CoDisableCallCancellation.

HRESULT CoDisableCallCancellation(
  LPVOID pvReserved
);

Parameters

pvReserved
Reserved for future use; must be NULL.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
Call cancellation was successfully disabled on the thread.
CO_E_CANCEL_DISABLED
There have been more successful calls to CoEnableCallCancellation on the thread than there have been calls to CoDisableCallCancellation. Cancellation is still enabled on the thread.

Remarks

When call cancellation is enabled on a thread, marshaled synchronous calls from that thread to objects on the same computer can suffer serious performance degradation. By default, then, synchronous calls cannot be cancelled, even if a cancel object is available. To enable call cancellation, you must call CoEnableCallCancellation first.

When call cancellation is disabled, attempts to gain a pointer to a call object will fail. If the calling thread already has a pointer to a call object, calls on that object will fail.

Unless you want to enable call cancellation on a thread at all times, you should pair calls to CoEnableCallCancellation with calls to CoDisableCallCancellation. Call cancellation is disabled only if each successful call to CoEnableCallCancellation is balanced by a successful call to CoDisableCallCancellation.

A call will be cancelable or not depending on the state of the thread at the time the call was made. Subsequently enabling or disabling call cancellation has no effect on any calls that are pending on the thread.

If a thread is uninitialized and then reinitialized by calls to CoUninitialize and CoInitialize, call cancellation is disabled on the thread, even if it was enabled when the thread was uninitialized.

See Also

CoEnableCallCancellation, ICancelMethodCalls

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoDisconnectObject

Disconnects all remote process connections being maintained on behalf of all the interface pointers that point to a specified object. Only the process that actually manages the object should call CoDisconnectObject.

STDAPI CoDisconnectObject(
  IUnknown * pUnk,  //Pointer to the interface on the object
  DWORD dwReserved  //Reserved for future use
);

Parameters

pUnk
[in] Pointer to any IUnknown-derived interface on the object to be disconnected.
dwReserved
[in] Reserved for future use; must be zero.

Return Values

S_OK
All connections to remote processes were successfully deleted.

Remarks

The CoDisconnectObject function enables a server to correctly disconnect all external clients to the object specified by pUnk.

The CoDisconnectObject function performs the following tasks:

  1. Checks to see if the object to be disconnected implements the IMarshal interface. If so, it gets the pointer to that interface; if not, it gets a pointer to the standard marshaler's (i.e., COM's) IMarshal implementation.
  2. Using whichever IMarshal interface pointer it has acquired, the function then calls IMarshal::DisconnectObject to disconnect all out-of-process clients.

An object's client does not call CoDisconnectObject to disconnect itself from the server (clients should use IUnknown::Release for this purpose). Rather, an OLE server calls CoDisconnectObject to forcibly disconnect an object's clients, usually in response to a user closing the server application.

Similarly, an OLE container that supports external links to its embedded objects can call CoDisconnectObject to destroy those links. Again, this call is normally made in response to a user closing the application. The container should first call IOleObject::Close for all its OLE objects, each of which should send IAdviseSink::OnClose notifications to their various clients. Then the container can safely call CoDisconnectObject to close any existing connections.

CoDisconnectObject does not necessarily disconnect out-of-process clients immediately. If any marshaled calls are pending on the server object, CoDisconnectObject disconnects the object only when those calls have returned. In the meantime, CoDisconnectObject sets a flag that causes any new marshaled calls to return CO_E_OBJECTNOTCONNECTED.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IOleObject::Close, IMarshal::DisconnectObject


CoDosDateTimeToFileTime

Converts the MS-DOS representation of the time and date to a FILETIME structure, which Win32 uses to determine the date and time.

BOOL CoDosDateTimeToFileTime(
  WORD nDosDate,         //16-bit MS-DOS date
  WORD nDosTime,         //16-bit MS-DOS time
  FILETIME * lpFileTime  //Pointer to the structure
);

Parameters

nDosDate
[in] 16-bit MS-DOS date.
nDosTime
[in] 16-bit MS-DOS time.
lpFileTime
[out] Pointer to the FILETIME structure.

Return Values

TRUE
The FILETIME structure was created successfully.
FALSE
The FILETIME structure was not created successfully, probably because of invalid arguments.

Remarks

The FILETIME structure and the CoDosDateTimeToFileTime and CoFileTimeToDosDateTime functions are part of the Win32 API definition. They are provided for compatibility in all OLE implementations, but are redundant on Win32 platforms.

MS-DOS records file dates and times as packed 16-bit values. An MS-DOS date has the following format:

Bits | Contents
0-4 | Days of the month (1-31).
5-8 | Months (1 = January, 2 = February, and so forth).
9-15 | Year offset from 1980 (add 1980 to get actual year).

An MS-DOS time has the following format:

Bits | Contents
0-4 | Seconds divided by 2.
5-10 | Minutes (0-59).
11-15 | Hours (0-23 on a 24-hour clock).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoFileTimeToDosDateTime, CoFileTimeNow


CoEnableCallCancellation

Enables cancellation of synchronous calls on the calling thread.

HRESULT CoEnableCallCancellation(
  LPVOID pvReserved
);

Parameters

pvReserved
Reserved for future use; must be NULL.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
Call cancellation was successfully enabled on the thread.

Remarks

When call cancellation is enabled on a thread, marshaled synchronous calls from that thread to objects on the same computer can suffer serious performance degradation. By default, synchronous calls cannot be cancelled, even if a cancel object is available. To enable call cancellation, you must call CoEnableCallCancellation first.

Unless you want to enable call cancellation on a thread at all times, you should pair calls to CoEnableCallCancellation with calls to CoDisableCallCancellation. Call cancellation is disabled only if CoDisableCallCancellation has been called once for each time CoEnableCallCancellation was called successfully.

A call will be cancelable or not depending on the state of the thread at the time the call was made. Subsequently enabling or disabling call cancellation has no effect on any calls that are pending on the thread.

See Also

CoDisableCallCancellation, ICancelMethodCalls

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoFileTimeNow

Returns the current time as a FILETIME structure.

HRESULT CoFileTimeNow(
  FILETIME * lpFileTime  //Pointer to return the structure
);

Parameter

lpFileTime
[out] Pointer to return the FILETIME structure.

Return Values

S_OK
The current time was converted to a FILETIME structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoDosDateTimeToFileTime, CoFileTimeToDosDateTime


CoFileTimeToDosDateTime

Converts a FILETIME into MS-DOS date and time values.

BOOL CoFileTimeToDosDateTime(
  FILETIME * lpFileTime,
                    //Pointer to the structure to be converted
  LPWORD lpDosDate, //Pointer to the 16-bit MS-DOS date
  LPWORD lpDosTime  //Pointer to the 16-bit MS-DOS time
);

Parameters

lpFileTime
[in] Pointer to the FILETIME structure to be converted.
lpDosDate
[out] Pointer to the 16-bit MS-DOS date.
lpDosTime
[out] Pointer to the 16-bit MS-DOS time.

Return Values

TRUE
The FILETIME structure was converted successfully.
FALSE
The FILETIME structure was not converted successfully.

Remarks

This is the inverse of the operation provided by the CoDosDateTimeToFileTime function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoDosDateTimeToFileTime, CoFileTimeNow


CoFreeAllLibraries

Frees all the DLLs that have been loaded with the CoLoadLibrary function (called internally by CoGetClassObject), regardless of whether they are currently in use. This function is usually not called directly, because CoUninitialize and OleUninitialize call it internally.

void CoFreeAllLibraries( );

Remarks

To unload libraries, CoFreeAllLibraries uses a list of loaded DLLs for each process that the COM library maintains. The CoUninitialize function calls CoFreeAllLibraries internally, so OLE applications usually have no need to call this function directly.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries, CoGetClassObject, CoUninitialize, OleUninitialize


CoFreeLibrary

Frees a library that, when loaded, was specified to be freed explicitly.

void CoFreeLibrary(
  HINSTANCE hInst  //Handle of the library module to be freed
);

Parameter

hInst
[in] Handle to the library module to be freed, as returned by CoLoadLibrary.

Remarks

The CoFreeLibrary function should be called to free a library that is to be freed explicitly. This is established when the library is loaded with the bAutoFree parameter of CoLoadLibrary set to FALSE. It is illegal to free a library explicitly when the corresponding CoLoadLibrary call specifies that it be freed automatically (the bAutoFree parameter is set to TRUE).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoFreeAllLibraries, CoFreeUnusedLibraries, CoLoadLibrary


CoFreeUnusedLibraries

Unloads any DLLs that are no longer in use and that, when loaded, were specified to be freed automatically.

void CoFreeUnusedLibraries( );

Remarks

Applications can call CoFreeUnusedLibraries periodically to free resources. It is most efficient to call it either at the top of a message loop or in some idle-time task. DLLs that are to be freed automatically have been loaded with the bAutoFree parameter of the CoLoadLibrary function set to TRUE. CoFreeUnusedLibraries internally calls DllCanUnloadNow for DLLs that implement and export that function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoFreeLibrary, CoFreeUnusedLibrariesEx, CoLoadLibrary, DLLCanUnloadNow


CoFreeUnusedLibrariesEx

Unloads any DLLs that are no longer in use and whose unload delay has expired.

void CoFreeUnusedLibrariesEx(
  DWORD dwUnloadDelay, //The unload delay for DLLs no longer in use
  DWORD dwReserved     //Reserved for future use
);

Parameters

dwUnloadDelay
[in] The delay in milliseconds between the time that the DLL has stated it can be unloaded until it becomes a candidate to unload. Setting dwUnloadDelay=INFINITE uses the system default delay (10 minutes). Setting dwUnloadDelay=0 forces the unloading of any DLLs without any delay.
dwReserved
[in] Reserved for future use; must be zero.

Remarks

COM supplies functions to reclaim memory held by DLLs containing components. The most commonly used function is CoFreeUnusedLibraries. CoFreeUnusedLibraries does not immediately release DLLs that have no active object. There is a 10-minute delay for multithreaded apartments (MTAs) and neutral apartments (NAs). For single-threaded apartments (STAs), there is no delay.

The 10-minute delay for CoFreeUnusedLibraries is to avoid multithread race conditions caused by unloading a component DLL. This default delay may be too long for many applications.

COM maintains a list of active DLLs that have had components loaded for the apartments that can be hosted on the thread where this function is called. When CoFreeUnusedLibrariesEx is called, each DLL on that list has its DllCanUnloadNow function called. If the DllCanUnloadNow function returns S_FALSE (or is not exported), this DLL is not ready to be unloaded. If DllCanUnloadNow returns S_OK, this DLL is moved off the active list to a "candidate-for-unloading" list.

Adding the DLL to the candidate-for-unloading list time-stamps the DLL dwUnloadDelay milliseconds from when this move occurs. When CoFreeUnusedLibrariesEx (or CoFreeUnusedLibraries) is called again, at least dwUnloadDelay milliseconds from the call that moved the DLL to the candidate-for-unloading list, the DLL is actually freed from memory. If COM uses the component DLL while the DLL is on the candidate-for-unloading list, it is moved back to the active list.

Using a dwUnloadDelay of 0 may lead to unexpected consequences. The component DLL may need some time for cleanup after it returns from the DllCanUnloadNow function. For example, if the DLL had its own worker threads, using a value of 0 would most likely lead to a problem because the code executing on these threads would be unmapped, caused by the unloading of the DLL before the worker threads have a chance to exit. Also, using too brief of a dwUnloadDelay time can lead to performance issues because there is a lot more overhead in reloading a DLL than letting it page out.

The behavior above is triggered by the DLL supplying components with threading models set to Free, Neutral, or Both. For a threading model set to Apartment (or if no threading model is specified), dwUnloadDelay is treated as 0 because these components are tied to the single thread hosting the apartment.

Requirements

Windows NT/2000: Requires Whistler.
Windows 95/98: Not supported.
Header: Declared in objbase.h.
Library: Use ole32.dll.

See Also

CoFreeLibrary, CoFreeUnusedLibraries, CoLoadLibrary, DLLCanUnloadNow


CoGetCallContext

Retrieves the context of the current call on the current thread.

HRESULT CoGetCallContext(
  REFIID riid,         //Interface identifier
  void ** ppInterface  //Address of output variable that receives the
                       // interface pointer requested in riid
);

Parameters

riid
[in] Interface identifier (IID) of the call context that is being requested. If you are using the default call context supported by standard marshaling, IID_IServerSecurity is available. For COM+ applications using role-based security, IID_ISecurityCallContext is available.
ppInterface
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppInterface contains the requested interface pointer.

Return Values

S_OK
Success.
E_NOINTERFACE
The call context does not support the interface identified by riid.

Remarks

CoGetCallContext retrieves the context of the current call on the current thread. The riid parameter specifies the interface on the context to be retrieved. This is one of the functions provided to give the server access to any contextual information of the caller.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IServerSecurity, Security in COM


CoGetCancelObject

Obtains a pointer to a call control interface, normally ICancelMethodCalls, on the cancel object corresponding to an outbound COM method call pending on the same or another client thread.

HRESULT CoGetCancelObject(
  DWORD dwThreadID,  //Thread with pending call
  REFIID riid,       //ID of requested interface
  void **ppUnk       //Pointer to requested interface
);

Parameters

dwThreadID
Identifier of the thread on which the pending COM call is to be canceled. If 0, the call is on the current thread.
riid
Globally unique identifier of an interface on the cancel object for the call to be canceled. This argument is normally IID_ICancelMethodCalls.
ppUnk
Address of a pointer to the interface specified by riid.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The call control object was obtained.
E_NOINTERFACE
The object on which the call is executing does not implement the requested interface.

Remarks

If two or more calls are pending on the same thread through nested calls, the thread ID may not be sufficient to identify the call to be canceled. In this case, CoGetCancelObject returns a cancel interface corresponding to the innermost call that is pending on the thread and has registered a cancel object.

This function does not locate cancel objects for asynchronous calls.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoGetClassObject

Provides a pointer to an interface on a class object associated with a specified CLSID. CoGetClassObject locates, and if necessary, dynamically loads the executable code required to do this.

Call CoGetClassObject directly when you want to create multiple objects through a class object for which there is a CLSID in the system registry. You can also retrieve a class object from a specific remote machine. Most class objects implement the IClassFactory interface. You would then call IClassFactory::CreateInstance to create an uninitialized object. It is not always necessary to go through this process. To create a single object, call instead the either the CoCreateInstanceEx function, which allows you to create an instance on a remote machine. This replaces the CoCreateInstance function, which can still be used to create an instance on a local machine. Both functions encapsulate connecting to the class object, creating the instance, and releasing the class object. Two other functions, CoGetInstanceFromFile and CoGetInstanceFromIStorage, provide both instance creation on a remote system, and object activation. OLE also provides many other ways to create an object in the form of numerous helper functions and interface methods whose function is to create objects of a single type and provide a pointer to an interface on that object.

Note

The CoGetClassObject function does not call CoLoadLibrary.

STDAPI CoGetClassObject(
  REFCLSID rclsid,  //CLSID associated with the class object
  DWORD dwClsContext,
                    //Context for running executable code
  COSERVERINFO * pServerInfo,
                    //Pointer to machine on which the object is to
                    // be instantiated
  REFIID riid,      //Reference to the identifier of the interface
  LPVOID * ppv      //Address of output variable that receives the
                    // interface pointer requested in riid
);

Parameters

rclsid
[in] CLSID associated with the data and code that you will use to create the objects.
dwClsContext
[in] Context in which the executable code is to be run. To enable a remote activation, CLSCTX_REMOTE_SERVER must be included. For more information on the context values and their use, see the CLSCTX enumeration.
pServerInfo
[in] Pointer to machine on which to instantiate the class object. May be NULL, in which case the class object is instantiated on the current machine or at the machine specified under the class's RemoteServerName key in the registry, according to the interpretation of the dwClsCtx parameter (see the CLSCTX documentation for details).
riid
[in] Reference to the identifier of the interface, which will be supplied in ppv on successful return. This interface will be used to communicate with the class object. Typically this value is IID_IClassFactory, although other values – such as IID_IClassFactory2 which supports a form of licensing – are allowed. All OLE-defined interface IIDs are defined in the OLE header files as IID_interfacename, where interfacename is the name of the interface.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer.

Return Values

S_OK
Location and connection to the specified class object was successful.
REGDB_E_CLASSNOTREG
CLSID is not properly registered. Can also indicate that the value you specified in dwClsContext is not in the registry.
E_NOINTERFACE
Either the object pointed to by ppv does not support the interface identified by riid, or the QueryInterface operation on the class object returned E_NOINTERFACE.
REGDB_E_READREGDB
Error reading the registration database.
CO_E_DLLNOTFOUND
In-process DLL or handler DLL not found (depends on context).
CO_E_APPNOTFOUND
EXE not found (CLSCTX_LOCAL_SERVER only).
E_ACCESSDENIED
General access failure (returned from LoadLib/CreateProcess).
CO_E_ERRORINDLL
EXE has error in image.
CO_E_APPDIDNTREG
EXE was launched, but it didn't register class object (may or may not have shut down).

Remarks

A class object in OLE is an intermediate object that supports an interface that permits operations common to a group of objects. The objects in this group are instances derived from the same object definition represented by a single CLSID. Usually, the interface implemented on a class object is IClassFactory, through which you can create object instances of a given definition (class).

A call to CoGetClassObject creates, initializes, and gives the caller access (through a pointer to an interface specified with the riid parameter) to the class object. The class object is the one associated with the CLSID that you specify in the rclsid parameter. The details of how the system locates the associated code and data within a given machine are transparent to the caller, as is the dynamic loading of any code that is not already loaded.

If the class context is CLSCTX_REMOTE_SERVER, indicating remote activation is required, the COSERVERINFO structure provided in the pServerInfo parameter allows you to specify the machine on which the server is located. For information on the algorithm used to locate a remote server when pServerInfo is NULL, refer to the CLSCTX enumeration.

There are two places to find a CLSID for a given class:

  • The registry holds an association between CLSIDs and file suffixes, and between CLSIDs and file signatures for determining the class of an object.
  • When an object is saved to persistent storage, its CLSID is stored with its data.

To create and initialize embedded or linked OLE document objects, it is not necessary to call CoGetClassObject directly. Instead, call one of the OleCreate or OleCreate Xxx helper functions. These functions encapsulate the entire object instantiation and initialization process, and call, among other functions, CoGetClassObject.

The riid parameter specifies the interface the client will use to communicate with the class object. In most cases, this interface is IClassFactory. This provides access to the IClassFactory::CreateInstance method, through which the caller can then create an uninitialized object of the kind specified in its implementation. All classes registered in the system with a CLSID must implement IClassFactory.

In rare cases, however, you may want to specify some other interface that defines operations common to a set of objects. For example, in the way OLE implements monikers, the interface on the class object is IParseDisplayName, used to transform the display name of an object into a moniker.

The dwClsContext parameter specifies the execution context, allowing one CLSID to be associated with different pieces of code in different execution contexts. The CLSCTX enumeration, defined in Compobj.H, specifies the available context flags. CoGetClassObject consults (as appropriate for the context indicated) both the registry and the class objects that are currently registered by calling the CoRegisterClassObject function.

To release a class object, use the class object's Release method. The function CoRevokeClassObject is to be used only to remove a class object's CLSID from the system registry.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoCreateInstanceEx, CoRegisterClassObject, CoRevokeClassObject, OleLoad, CLSCTX, Creating an Object through a Class Object


CoGetCurrentProcess

Returns a value that is unique to the current thread. CoGetCurrentProcess can be used to avoid thread ID reuse problems.

DWORD CoGetCurrentProcess(void);

Return Value

Unique DWORD value identifying the current thread.

Remarks

Using the value returned from a call to CoGetCurrentProcess can help you in maintaining tables that are keyed by threads or in uniquely identifying a thread to other threads or processes.

The CoGetCurrentProcess function returns a value that is effectively unique, because it is not used again until 232 more threads have been created on the current workstation or until the workstation is restarted.

The value returned by CoGetCurrentProcess will uniquely identify the same thread for the life of the caller. Because thread IDs can be reused without notice as threads are created and destroyed, this value is more reliable than the HTASK task handle value returned by the Win16 function GetCurrentTask or the DWORD returned by the Win32 function GetCurrentThreadId.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoGetInstanceFromFile

Creates a new object and initializes it from a file using IPersistFile::Load.

HRESULT CoGetInstanceFromFile(
  COSERVERINFO * pServerInfo,
                    //Pointer to COSERVERINFO struct indicating
                    // remote system
  CLSID* pclsid,    //Pointer to the class of the object to create
  IUnknown * punkOuter,
                    //If part of an aggregate, pointer to the
                    // controlling IUnknown
  DWORD dwClsCtx,   //CLSCTX values
  DWORD grfMode,    //Options for opening file
  OLECHAR* szName,  //File to initialize the object with
  ULONG cmq,        //Number of MULTI_QI structures in rgmqResults
  MULTI_QI * rgmqResults
                    //Array of MULTI_QI structures
);

Parameters

pServerInfo
[in] Pointer to a COSERVERINFO structure that specifies the machine on which to instantiate the object and the authentication setting to be used. May be NULL, in which case the object is instantiated (1) on the current machine, (2) at the machine specified under the RemoteServerName named-value for the class in the registry, or (3) at the machine where the szName file resides if the ActivateAtStorage named-value is specified for the class in the registry or there is no local registry information.
pclsid
[in] Pointer to the class of the object to create. May be NULL, in which case there is a call to GetClassFile, using szName as its parameter to get the class of the object to be instantiated.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggregate, and punkOuter is to be used as the pointer to the new instance's controlling IUnknown. Aggregation is currently not supported cross-process or cross-machine. When instantiating an object out of process, CLASS_E_NOAGGREGATION will be returned if punkOuter is non-NULL.
dwClsCtx
[in] Values taken from the CLSCTX enumeration.
grfMode
[in] Flags specifying how the file is to be opened. Values are taken from the STGM enumeration.
szName
[in] File to initialize the object with using IPersistFile::Load. May not be NULL.
cmq
[in] Number of MULTI_QI structures in rgmqResults. Must be greater than zero.
rgmqResults
[in] Array of MULTI_QI structures. Each structure has three members: the identifier for a requested interface (pIID), the location to return the interface pointer (pItf) and the return value of the call to QueryInterface (hr).

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the rgmqResults array were successfully retrieved. The hr field of each of the MULTI_QI structures in rgmqResults indicates with S_OK or E_NOINTERFACE whether or not the specific interface was returned.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retrieved.

Remarks

CoGetInstanceFromFile creates a new object and initializes it from a file using IPersistFile::Load. The result of this function is similar to creating an instance with a call to CoCreateInstanceEx, followed by an initializing call to IPersistFile::Load, with the following important distinctions:

  • Fewer network round trips are required by this function when instantiating an object on a remote machine.
  • In the case where dwClsCtx is set to CLSCTX_REMOTE_SERVER and pServerInfo is NULL, if the class is registered with the ActivateAtStorage sub-key or has no associated registry information, this function will instantiate an object on the machine where szName resides, providing the least possible network traffic. For example, if szName specified "\\myserver\users\johndo\file", the object would be instantiated on the "myserver" machine, and the object would access the file directly.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoCreateInstanceEx, CoGetInstanceFromIStorage, CLSCTX, Instance Creation Helper Functions


CoGetInstanceFromIStorage

Creates a new object and initializes it from a storage object through an internal call to IPersistStorage::Load.

HRESULT CoGetInstanceFromIStorage(
  COSERVERINFO * pServerInfo,
                         //Pointer to COSERVERINFO struct indicating
                         // remote system
  CLSID * pclsid,        //Pointer to the CLSID of the object to be
                         // created
  IUnknown * punkOuter,  //If part of an aggregate, pointer to the
                         // controlling IUnknown
  DWORD dwClsCtx,        //Values taken from the CLSCTX enumeration
  IStorage * pstg,       //Pointer to storage from which object is
                         // to be initialized
  ULONG cmq,             //Number of MULTI_QI structures in
                         // rgmqResults
  MULTI_QI * rgmqResults //Array of MULTI_QI structures
);

Parameters

pServerInfo
[in] Pointer to a COSERVERINFO structure that specifies the machine on which to instantiate the object and the authentication setting to be used. May be NULL, in which case the object is either instantiated (1) on the current machine, (2) at the machine specified under the RemoteServerName named-value for the class in the registry, or (3) at the machine where the storage object pointed to by pstg is located if the class is registered with ActivateAtStorage specified or has no local registry information.
pclsid
[in] Pointer to the class identifier (CLSID) of the object to be created. May be NULL, in which case there is a call to IStorage:Stat to find the class of the object.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggregate, and punkOuter is to be used as the pointer to the new instance's controlling IUnknown. Aggregation is currently not supported cross-process or cross-machine. When instantiating an object out of process, CLASS_E_NOAGGREGATION will be returned if punkOuter is non-NULL.
dwClsCtx
Values taken from the CLSCTX enumeration.
pstg
Pointer to storage to initialize the object with using IPersistStorage::Load. May not be NULL.
cmq
Number of MULTI_QI structures in rgmqResults. Must be greater than zero.
rgmqResults
Array of MULTI_QI structures. Each structure has three members: the identifier for a requested interface (pIID), the location to return the interface pointer (pItf) and the return value of the call to QueryInterface (hr).

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the rgmqResults array were successfully retrieved. The hr field of each of the MULTI_QI structures in rgmqResults indicates with S_OK or E_NOINTERFACE whether the specific interface pointer was retrieved.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retrieved.

Remarks

CoGetInstanceFromIStorage creates a new object and initializes it from a storage object through a call to IPersistStorage::Load. This function is similar to creating an instance using CoCreateInstanceEx followed by a call to IPersistStorage::Load, with the following important distinctions:

  • Fewer network round trips are required by thisfunction when instantiating remotely.
  • In the case where dwClsCtx is set to CLSCTX_REMOTE_SERVER and pServerInfo is NULL, if the class is registered with the ActivateAtStorage named value or has no associated registry information, this function will instantiate an object on the same machine where the storage object pointed to by pstg resides, providing the least possible network traffic. For example, if pstg were obtained through a call to StgCreateDocfile, specifying "\\myserver\users\johndo\file", the object would be instantiated on the "myserver" machine, and the object would access the storage object directly.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoCreateInstanceEx, CoGetInstanceFromFile, CLSCTX, Instance Creation Helper Functions


CoGetInterceptor

Instantiates the appropriate interceptor for the indicated interface to be intercepted and returns the newly created interceptor.

HRESULT CoGetInterceptor{
   REFIID  iidIntercepted,
                        //Reference to the identifier of the interface
                        // for which an interceptor is sought
   IUnknown *punkOuter, //Pointer to controlling IUnknown
   REFIID  iid,         //Reference to the interface identifier
   void **ppv           //Address of output variable that receives
                        // the interface pointer requested in iid
);

Parameters

iidIntercepted
[in] Reference to the identifier of the interface for which an interceptor is sought.
punkOuter
[in] If NULL, indicates that the object is not being created as part of an aggregate. If non-NULL, pointer to the aggregate object's IUnknown interface (the controlling IUnknown).
iid
[in] Reference to the identifier of the interface desired on the interceptor.
ppv
[out] Address of pointer variable that receives the interface pointer requested in iid. Upon successful return, **ppv contains the requested interceptor pointer.

Return Value

S_OK
The method returned successfully.
E_UNEXPECTED
An unexpected error occurred.

Requirements

Windows NT/2000: Requires Windows 2000 or later.
Windows 95/98: Not supported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

ICallFrame, ICallFrameEvents, ICallInterceptor, ICallUnmarshal, ISurrogateService


CoGetInterfaceAndReleaseStream

Unmarshals a buffer containing an interface pointer and releases the stream when an interface pointer has been marshaled from another thread to the calling thread.

HRESULT CoGetInterfaceAndReleaseStream(
  LPSTREAM pStm,  //Pointer to the stream from which the object is
                  // to be marshaled
  REFIID riid,    //Reference to the identifier of the interface
  LPVOID * ppv    //Address of output variable that receives the
                  // interface pointer requested in riid
);

Parameters

pStm
[in] Pointer to the IStream interface on the stream to be unmarshaled.
riid
[in] Reference to the identifier of the interface requested from the unmarshaled object.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer to the unmarshaled interface.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Indicates the output interface was unmarshaled and the stream was released.

This function can also return any of the values returned by CoUnmarshalInterface.

Remarks

The CoGetInterfaceAndReleaseStream function performs the following tasks:

  1. Calls CoUnmarshalInterface to unmarshal an interface pointer previously passed in a call to CoMarshalInterThreadInterfaceInStream.
  2. Releases the stream pointer. Even if the unmarshaling fails, the stream is still released because there is no effective way to recover from a failure of this kind.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoMarshalInterThreadInterfaceInStream, CoUnmarshalInterface


CoGetMalloc

Retrieves a pointer to the default OLE task memory allocator (which supports the system implementation of the IMalloc interface) so applications can call its methods to manage memory.

HRESULT CoGetMalloc(
  DWORD dwMemContext,  //Indicates if memory is private or shared
  LPMALLOC * ppMalloc  //Address of output variable that receives a
                       // pointer to the memory allocator
);

Parameters

dwMemContext
[in] Reserved; value must be 1.
ppMalloc
[out] Address of IMalloc* pointer variable that receives the interface pointer to the memory allocator.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
Indicates the allocator was retrieved successfully.

Remarks

The pointer to the IMalloc interface pointer received through the ppMalloc parameter cannot be used from a remote process—each process must have its own allocator.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMalloc, CoTaskMemAlloc


CoGetMarshalSizeMax

Returns an upper bound on the number of bytes needed to marshal the specified interface pointer to the specified object.

STDAPI CoGetMarshalSizeMax(
  ULONG *pulSize,       //Pointer to the upper-bound value
  REFIID riid,          //Reference to the identifier of the
                        // interface
  IUnknown * pUnk,      //Pointer to the interface to be marshaled
  DWORD dwDestContext,  //Destination process
  LPVOID pvDestContext, //Reserved for future use
  DWORD mshlflags       //Reason for marshaling
);

Parameters

pulSize
[out] Pointer to the upper-bound value on the size, in bytes, of the data packet to be written to the marshaling stream; a value of zero means that the size of the packet is unknown.
riid
[in] Reference to the identifier of the interface whose pointer is to be marshaled. This interface must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled. This interface must be derived from the IUnknown interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX.
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Values come from the enumeration MSHLFLAGS.

Return Values

This function supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The upper bound was returned successfully.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread before this function was called.

Remarks

This function performs the following tasks:

  1. Queries the object for an IMarshal pointer or, if the object does not implement IMarshal, gets a pointer to COM's standard marshaler.
  2. Using whichever pointer is obtained in the preceding step, calls IMarshal::GetMarshalSizeMax.
  3. Adds to the value returned by the call to GetMarshalSizeMax the size of the marshaling data header and, possibly, that of the proxy CLSID to obtain the maximum size in bytes of the amount of data to be written to the marshaling stream.

You do not explicitly call this function unless you are implementing IMarshal, in which case your marshaling stub should call this function to get the correct size of the data packet to be marshaled.

The value returned by this method is guaranteed to be valid only as long as the internal state of the object being marshaled does not change. Therefore, the actual marshaling should be done immediately after this function returns, or the stub runs the risk that the object, because of some change in state, might require more memory to marshal than it originally indicated.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoMarshalInterface, IMarshal::GetMarshalSizeMax


CoGetObject

Converts a display name into a moniker that identifies the object named, and then binds to the object identified by the moniker.

WINOLEAPI CoGetObject(
  LPCWSTR pszName,
  BIND_OPTS *pBindOptions,
  REFIID riid,
  void **ppv
);

Parameters

pszName
[in] Display name, in string format, of the object to be created.
pBindOptions
[in] Binding options used to create a moniker that creates the actual object. For details, see BIND_OPTS.
riid
[in] Reference to the ID of an interface that is implemented on the object to be created.
ppv
[out] Address of a pointer to the interface specified by riid on the object that is created.

Return Values

This function supports the standard return values E_FAIL, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was created successfully.
MK_E_SYNTAX
The pszName parameter is not a properly formed display name.
MK_E_NOOBJECT
The object identified by this moniker, or some object identified by the composite moniker of which this moniker is a part, could not be found.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by the BIND_OPTS structure passed in pBindOptions.
MK_E_CONNECTMANUALLY
The binding operation requires assistance from the end user. The most common reasons for returning this value are that a password is needed or that a floppy needs to be mounted.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
An intermediate object was found but it did not support an interface required to complete the binding operation. For example, an item moniker returns this value if its container does not support the IOleItemContainer interface.

Remarks

CoGetObject encapsulates calls to the COM library functions CreateBindCtx, MkParseDisplayName, and IMoniker::BindToObject.

See Also

CreateBindCtx, IMoniker, IMoniker::BindToObject, MkParseDisplayName

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoGetObjectContext

Gets the context for the current object.

WINOLEAPI CoGetObjectContext(
  REFIID riid,
  LPVOID **ppv
);

Parameters

riid
[in] Reference to the ID of an interface that is implemented on the context object. For objects running within COM+ applications, IID_IObjectContext, IID_IObjectContextInfo, IID_IObjectContextActivity, and IID_IContextState are available.
ppv
[out] Address of a pointer to the interface specified by riid on the context object.

Return Values

This function supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following (this is a subset of the possible return values for this function– other values may be returned in exceptional circumstances):

S_OK
The object context was successfully retrieved.
E_NOINTERFACE
The requested interface was not available.
CO_E_NOTINITIALIZED
The CoInitializeEx function was not called on the current thread before this function was called.

Remarks

CoGetObjectContext retrieves the context for the object from which it is called, and returns a pointer to an interface that can be used to manipulate context properties. Context properties are used to provide services to configured components running within COM+ applications.

For components running within COM+ applications, the following interfaces are supported for accessing context properties: IObjectContext, IObjectContextInfo, IObjectContextActivity, and IContextState.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

Contexts.


CoGetPSClsid

This function returns the CLSID of the DLL that implements the proxy and stub for the specified interface.

WINOLEAPI CoGetPSClsid(
  REFIID riid,   //Interface whose proxy/stub CLSID is to be
                 // returned
  CLSID *pclsid  //Where to store returned proxy/stub CLSID
);

Parameters

riid
[in] The interface whose proxy/stub CLSID is to be returned.
pclsid
[out] Where to store the proxy/stub CLSID for the interface specified by riid.

Return Values

S_OK
The proxy/stub CLSID was successfully returned.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.

Remarks

The CoGetPSClsid function looks at the HKEY_CLASSES_ROOT\Interfaces\{ string form of riid }\ProxyStubClsid32 key in the registry to determine the CLSID of the DLL to load in order to create the proxy and stub for the interface specified by riid. This function also returns the CLSID for any interface IID registered by CoRegisterPSClsid within the current process.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoRegisterPSClsid


CoGetStandardMarshal

Creates a default, or standard, marshaling object in either the client process or the server process, depending on the caller, and returns a pointer to that object's IMarshal implementation.

STDAPI CoGetStandardMarshal(
  REFIID riid,          //Reference to the identifier of the
                        // interface
  IUnknown * pUnk,      //Pointer to the interface to be marshaled
  DWORD dwDestContext,  //Destination process
  LPVOID pvDestContext, //Reserved for future use
  DWORD mshlflags,      //Reason for marshaling
  LPMARSHAL * ppMarshal //Address of output variable that receives
                        // the IMarshal interface pointer for the
                        // standard marshaler
);

Parameters

riid
[in] Reference to the identifier of the interface whose pointer is to be marshaled. This interface must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling can occur either in another apartment of the current process (MSHCTX_INPROC) or in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
ppMarshal
[out] Address of IMarshal* pointer variable that receives the interface pointer to the standard marshaler.

Return Values

This function supports the standard return values E_FAIL, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The IMarshal instance was returned successfully.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread before this function was called.

Remarks

The CoGetStandardMarshal function creates a default, or standard, marshaling object in either the client process or the server process, as may be necessary, and returns that object's IMarshal pointer to the caller. If you implement IMarshal, you may want your implementation to call CoGetStandardMarshal as a way of delegating to COM's default implementation any destination contexts that you don't fully understand or want to handle. Otherwise, you can ignore this function, which COM calls as part of its internal marshaling procedures.

When the COM library in the client process receives a marshaled interface pointer, it looks for a CLSID to be used in creating a proxy for the purposes of unmarshaling the packet. If the packet does not contain a CLSID for the proxy, COM calls CoGetStandardMarshal, passing a NULL pUnk value. This function creates a standard proxy in the client process and returns a pointer to that proxy's implementation of IMarshal. COM uses this pointer to call CoUnmarshalInterface to retrieve the pointer to the requested interface.

If your OLE server application's implementation of IMarshal calls CoGetStandardMarshal, you should pass both the IID of (riid), and a pointer to (pUnk), the interface being requested.

This function performs the following tasks:

  1. Determines whether pUnk is NULL.
  2. If pUnk is NULL, creates a standard interface proxy in the client process for the specified riid and returns the proxy's IMarshal pointer.
  3. If pUnk is not NULL, checks to see if a marshaler for the object already exists, creates a new one if necessary, and returns the marshaler's IMarshal pointer.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMarshal


CoGetStdMarshalEx

Creates an aggregated standard marshaler for use with lightweight client-side handlers.

HRESULT CoGetStdMarshalEx(
  IUnknown * pUnkOuter,     //Pointer to the controlling IUnknown
  DWORD dwSMEXFlags,      //Indicates whether marshaler is client- or server-side
  IUnknown ** ppUnkInner,    //Address of IUnknown pointer on aggregated marshaler
);

Parameters

pUnkOuter
[in] Pointer to the controlling IUnknown.
dwSMEXFlags
[in] One of two values indicating whether the aggregated standard marshaler is on the client side or the server side. These flags are defined in the STDMSHFLAGS enumeration as follows:
typedef enum tagSTDMSHLFLAGS
{
    SMEXF_SERVER   = 0x01,  //server side aggregated std marshaler
    SMEXF_HANDLER  = 0x02  //client side (handler) agg std marshaler
} STDMSHLFLAGS;
ppUnkInner
[out] On successful return, address of pointer to the IUnknown interface on the newly-created aggregated standard marshaler. If an error occurs, this value is NULL.

Return Values

S_OK
The standard marshaler was successfully created and aggregated with the proxy, and its IUnknown pointer was returned.

Remarks

The server calls CoGetStdMarshalEx passing in the flag SMEXF_SERVER. This creates a server side standard marshaler (a.k.a. a stub manager). The handler calls CoGetStdMarshalEx passing in the flag SMEXF_HANDLER. This creates a client side standard marshaler (a.k.a. a proxy manager). Note that when calling this API, the handler must pass the original controlling unknown that was passed to the handler when the handler was created. This will be the system implemented controlling unknown. Failure to pass the correct IUnknown results in an error returned. On success, the ppUnkInner returned is the controlling unknown of the inner object. The server and handler must keep this pointer, and may use it to call IUnknown::QueryInterface for the IMarshal interface.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IStdMarshalInfo, IMarshal, Lightweight Client-side Handlers


CoGetTreatAsClass

Returns the CLSID of an object that can emulate the specified object.

HRESULT CoGetTreatAsClass(
  REFCLSID clsidOld,   //CLSID of object that is being emulated
  LPCLSID pclsidNew    //Pointer to CLSID for object that can
                       // emulate clsidOld
);

Parameters

clsidOld
[in] CLSID of the object that can be emulated (treated as) an object with a different CLSID.
pclsidNew
[out] Pointer to where the CLSID that can emulate clsidOld objects is retrieved. This parameter cannot be NULL. If there is no emulation information for clsidOld objects, the clsidOld parameter is supplied.

Return Values

S_OK
A new CLSID was successfully returned.
S_FALSE
No emulation information for the clsidOld parameter and that the pclsidNew parameter is set to clsidOld.
REGDB_E_READREGDB
An error reading the registry.

This function can also return any of the error values returned by the CLSIDFromString function.

Remarks

CoGetTreatAsClass returns the TreatAs entry in the registry for the specified object. The TreatAs entry, if set, is the CLSID of a registered object (an application) that can emulate the object in question. The TreatAs entry is set through a call to the CoTreatAsClass function. Emulation allows an application to open and edit an object of a different format, while retaining the original format of the object. Objects of the original CLSID are activated and treated as objects of the second CLSID. When the object is saved, this may result in loss of edits not supported by the original format. If there is no TreatAs entry for the specfied object, this function returns the CLSID of the original object (clsidOld).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoTreatAsClass


CoImpersonateClient

Allows the server to impersonate the client of the current call for the duration of the call.

HRESULT CoImpersonateClient( )

Return Values

This function supports the standard return values, including the following value:

S_OK
Indicates success.

Remarks

This method allows the server to impersonate the client of the current call for the duration of the call. If you do not call CoRevertToSelf, COM reverts automatically for you. This function will fail unless the object is being called with RPC_C_AUTHN_LEVEL_CONNECT or higher authentication in effect (which is any authentication level except RPC_C_AUTHN_LEVEL_NONE). This function encapsulates the following sequence of common calls (error handling excluded):

    CoGetCallContext(IID_IServerSecurity, (void**)&pss);
    pss->ImpersonateClient();
    pss->Release();

CoImpersonateClient encapsulates the process of getting a pointer to an instance of IServerSecurity that contains data about the current call, calling its ImpersonateClient method, and then releasing the pointer. In Windows 2000, one call to CoRevertToSelf (or IServerSecurity::RevertToSelf) will undo any number of ImpersonateClient calls.

For more information, see Impersonation and Cloaking. For information about using impersonation with asynchronous calls, see Impersonation and Asynchronous Calls.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IServerSecurity::ImpersonateClient, Security in COM, Impersonation and Asynchronous Calls, Cloaking.


CoInitialize

Initializes the COM library on the current thread and identifies the concurrency model as single-thread apartment (STA). Applications must initialize the COM library before they can call COM library functions other than CoGetMalloc and memory allocation functions.

New applications should call CoInitializeEx instead of CoInitialize.

HRESULT CoInitialize(
  LPVOID pvReserved  //Reserved; must be NULL
);

Parameter

pvReserved
[in] Reserved; must be NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The COM library was initialized successfully on this thread.
S_FALSE
The COM library is already initialized on this thread.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified the concurrency model for this thread as multithread apartment (MTA). If running Windows 2000, this could also mean that a change from neutral threaded apartment to single threaded apartment occurred.

Remarks

CoInitializeEx provides the same functionality as CoInitialize and also provides a parameter to explicitly specify the thread's concurrency model. CoInitialize calls CoInitializeEx and specifies the concurrency model as single-thread apartment. Applications developed today should call CoInitializeEx rather than CoInitialize.

You need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions.

Once the concurrency model for a thread is set, it cannot be changed. A call to CoInitialize on an apartment that was previously initialized as multithreaded will fail and return RPC_E_CHANGED_MODE.

Typically, the COM library is initialized on a thread only once. Subsequent calls to CoInitialize or CoInitializeEx on the same thread will succeed, as long as they do not attempt to change the concurrency model, but will return S_FALSE. To close the COM library gracefully, each successful call to CoInitialize or CoInitializeEx, including those that return S_FALSE, must be balanced by a corresponding call to CoUninitialize. However, the first thread in the application that calls CoInitialize(0) or CoInitializeEx(COINIT_APARTMENTTHREADED) must be the last thread to call CoUninitialize(). If the call sequence is not in this order, then subsequent calls to CoInitialize on the STA will fail and the application will not work.

Because there is no way to control the order in which in-process servers are loaded or unloaded, it is not safe to call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoInitializeEx, CoUninitialize, OleInitialize, Processes, Apartments, and Threads


CoInitializeEx

Initializes the COM library for use by the calling thread, sets the thread's concurrency model, and creates a new apartment for the thread if one is required.

HRESULT CoInitializeEx(
  void * pvReserved,  //Reserved
  DWORD dwCoInit      //COINIT value
);

Parameters

pvReserved
[in] Reserved; must be NULL.
dwCoInit
[in] Flags specifying the concurrency model and initialization options for the thread. Values for this parameter are taken from the COINIT enumeration. Any combination of values from the COINIT enumeration can be used, except that the COINIT_APARTMENTTHREADED and COINIT_MULTITHREADED flags cannot both be set.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The COM library was initialized successfully on the calling thread.
S_FALSE
The COM library is already initialized on the calling thread.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified a different concurrency model for the calling thread. If running Windows 2000, this could also mean that a change from neutral threaded apartment to single threaded apartment occurred.

Remarks

A thread must call CoInitializeEx or CoInitialize before calling any other COM library function except CoGetMalloc function and other memory allocation calls (CoTaskMemAlloc, CoTaskMemFree, CoTaskMemReAlloc, and the IMalloc methods on the task allocation supplied by CoGetMalloc).

Once the concurrency model for a thread is set, it cannot be changed. A call to CoInitializeEx on a thread that was previously initialized with a different concurrency model will fail and return RPC_E_CHANGED_MODE.

CoInitializeEx must be called at least once, and is usually called only once, for each thread that uses the COM library. Multiple calls to CoInitializeEx by the same thread are allowed as long as they pass the same concurrency flag, but subsequent valid calls return S_FALSE. To close the COM library gracefully on a thread, each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize.

If neither concurrency model is specified by the dwCoInit parameter, the default is COINIT_MULTITHREADED.

Objects created in a single-threaded apartment (STA) receive method calls only from their apartment's thread, so calls are serialized and arrive only at message-queue boundaries (when the Win32 function PeekMessage or SendMessage is called).

Objects created on a COM thread in a multithread apartment (MTA) must be able to receive method calls from other threads at any time. You would typically implement some form of concurrency control in a multithreaded object's code using Win32 synchronization primitives such as critical sections, semaphores, or mutexes to protect the object's data.

CoInitializeEx provides the same functionality as CoInitialize and also provides a parameter to explicitly specify the thread's concurrency model. The current implementation of CoInitialize calls CoInitializeEx and specifies the concurrency model as single-thread apartment. Applications developed today should call CoInitializeEx rather than CoInitialize.

Because OLE technologies are not thread-safe, the OleInitialize function calls CoInitializeEx with the COINIT_APARTMENTTHREADED flag. As a result, an apartment that is initialized for multithreaded object concurrency cannot use the features enabled by OleInitialize.

Because there is no way to control the order in which in-process servers are loaded or unloaded, it is not safe to call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

COINIT, CoInitialize, Processes, Apartments, and Threads


CoInitializeSecurity

Registers security and sets the default security values for the process. This function is called exactly once per process, either explicitly or implicitly. It can be called by the client, server, or both. For legacy applications and other applications that do not explicitly call CoInitializeSecurity, COM calls this function implicitly with values from the registry. If you set processwide security using the registry and then call CoInitializeSecurity, the AppID registry values will be ignored and the CoInitializeSecurity values will be used.

HRESULT CoInitializeSecurity(
  PSECURITY_DESCRIPTOR pVoid,   //Points to security descriptor
  LONG cAuthSvc,                //Count of entries in asAuthSvc
  SOLE_AUTHENTICATION_SERVICE * asAuthSvc,
                                //Array of names to register
  void * pReserved1,            //Reserved for future use
  DWORD dwAuthnLevel,           //The default authentication level
                                // for proxies
  DWORD dwImpLevel,             //The default impersonation level
                                // for proxies
  SOLE_AUTHENTICATION_LIST * pAuthList,
                                //Authentication information for
                                // each authentication service
  DWORD dwCapabilities,         //Additional client and/or
                                // server-side capabilities
  void * pReserved3             //Reserved for future use
);

Parameters

pVoid
[in] Defines the access permissions that a server will use to receive calls. This parameter is used by COM only when a server calls CoInitializeSecurity. Its value can be NULL or a pointer to one of three types: an AppID, an IAccessControl object, or a Win32 SECURITY_DESCRIPTOR. See the Remarks section for more information.
cAuthSvc
[in] Count of entries in asAuthSvc. This parameter is used by COM only when a server calls CoInitializeSecurity. If this parameter is 0, no authentication services will be registered and the server cannot receive secure calls. A value of -1 tells COM to choose which authentication services to register, and if this is the case, the asAuthSvc parameter must be NULL. However, SChannel will never be chosen as an authentication service if this parameter is -1.
asAuthSvc
[in] Array of authentication services that a server is willing to use to receive a call. This parameter is used by COM only when a server calls CoInitializeSecurity. For more information, see SOLE_AUTHENTICATION_SERVICE.
pReserved1
[in] Reserved for future use; this value must be NULL.
dwAuthnLevel
[in] The default authentication level for the process. Both servers and clients use this parameter when they call CoInitializeSecurity. COM will fail calls that arrive with a lower authentication level. By default, all proxies will use at least this authentication level. This value should contain one of the flags from the RPC_C_AUTHN_LEVEL_xxx enumeration. By default, all calls to IUnknown are made at this level.
dwImpLevel
[in] The default impersonation level for proxies. The value of this parameter is used only when the process is a client. It should be a value from the RPC_C_IMP_LEVEL_xxx enumeration. Outgoing calls from the client always use the impersonation level as specified. (It is not negotiated.) Incoming calls to the client can be at any impersonation level. By default, all IUnknown calls are made with this impersonation level, so even security-aware applications should set this level carefully. To determine which impersonation levels each authentication service supports, see the description of the authentication services in COM and Security Packages. For more information about impersonation levels, see Impersonation.
pAuthList
[in] This value must be NULL on Windows NT 4. On Windows 2000, this parameter is a pointer to a SOLE_AUTHENTICATION_LIST, which is an array of SOLE_AUTHENTICATION_INFO structures. This list indicates the information for each authentication service that a client can use to call a server. This parameter is used by COM only when a client calls CoInitializeSecurity. For more information, see SOLE_AUTHENTICATION_INFO.
dwCapabilities
[in] Additional capabilities of the client or server, specified by setting one or more EOLE_AUTHENTICATION_CAPABILITIES flags. Some of these flags cannot be set simultaneously, and some cannot be set when particular authentication services are being used. For more information about these flags, see the EOLE_AUTHENTICATION_CAPABILITIES enumeration and the Remarks section.
pReserved3
[in] Reserved for future use; it must be set to NULL.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Indicates success.
RPC_E_TOO_LATE
CoInitializeSecurity has already been called.
RPC_E_NO_GOOD_SECURITY_PACKAGES
asAuthSvc was not NULL, and none of the authentication services in the list could be registered. Check the results saved in asAuthSvc for authentication service–specific error codes.
E_OUT_OF_MEMORY

Remarks

The CoInitializeSecurity function initializes the security layer and sets the specified values as the security default. If a process does not call CoInitializeSecurity, COM calls it automatically the first time an interface is marshaled or unmarshaled, registering the system default security. No default security packages are registered until then.

If pVoid points to an AppID, the EOAC_APPID flag must be set in dwCapabilities and all other parameters to CoInitializeSecurity must be zero or NULL. CoInitializeSecurity looks for the authentication level under the AppID key in the registry and uses it to determine the default security. Before Windows NT 4 SP 4, CoInitializeSecurity returned an error if it did not find the specified AppID in the registry. For more information about how the AppID key is used to set security, see Setting Processwide Security Through the Registry.

If pVoid is a pointer to an IAccessControl object, the EOAC_ACCESS_CONTROL flag must be set and dwAuthnLevel cannot be none. The IAccessControl object is used to determine who can call the process. DCOM will AddRef the IAccessControl and will Release it when CoUninitialize is called. The state of the IAccessControl object should not be changed.

If pVoid is a pointer to a Win32 SECURITY_DESCRIPTOR, neither the EOAC_APPID nor the EOAC_ACCESS_CONTROL flag can be set in dwCapabilities. The owner and group of the SECURITY_DESCRIPTOR must be set, and until DCOM supports auditing, the system ACL must be NULL. The access-control entries (ACEs) in the discretionary ACL (DACL) of the SECURITY_DESCRIPTOR are used to find out which callers are permitted to connect to the process's objects. A DACL with no ACEs allows no access, while a NULL DACL will allow calls from anyone. For more information on ACLs and ACEs, see Access Control Model. Applications should call AccessCheck (not IsValidSecurityDescriptor) to ensure that their SECURITY_DESCRIPTOR is correctly formed prior to calling CoInitializeSecurity.

If pVoid is NULL, the flags in dwCapabilities determine how CoInitializeSecurity defines the access permissions that a server will use, as follows:

  • If the EOAC_APPID flag is set, CoInitializeSecurity will look up the application's .exe name in the registry and use the AppID stored there.
  • If the EOAC_ACCESS_CONTROL flag is set, CoInitializeSecurity will return an error.
  • If neither the EOAC_APPID flag nor the EOAC_ACCESS_CONTROL flag is set, CoInitializeSecurity will construct a SECURITY_DESCRIPTOR that allows calls from anyone.

The CoInitializeSecurity function returns an error if both the EOAC_APPID and EOAC_ACCESS_CONTROL flags are set in dwCapabilities.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

Security in COM, Setting Processwide Security with CoInitializeSecurity CoSetProxyBlanket, LaunchPermission, AccessPermission


CoInvalidateRemoteMachineBindings

Tells the OLE Service Control Manager to flush any cached RPC binding handles for the specified machine.

HRESULT CoInvalidateRemoteMachineBindings(
  LPOLESTR pszMachineName      //Machine for which binding handles
                               // should be flushed
);

Parameters

pszMachineName
[in] The machine name for which binding handles should be flushed, or an empty string to signify that all handles in the cache should be flushed.

Return Values

This function supports the following return values:

S_OK
Indicates success.
CO_S_MACHINENAMENOTFOUND
Indicates that the specified machine name was not found or that the binding handle cache was empty, indicating that an empty string was passed instead of a specific machine name.
E_ACCESSDENIED
Indicates the caller was not an administrator for this machine.
E_INVALIDARG
Indicates that a NULL value was passed for pszMachineName.

Remarks

The OLE Service Control Manager is used by COM to send component activation requests to other machines. To do this, the OLE Service Control Manager maintains a cache of RPC binding handles to send activation requests to machines, keyed by machine name. Under normal circumstances, this works well, but in some scenarios, such as Web farms and load-balancing situations, the ability to purge this cache of specific handles might be needed in order to facilitate rebinding to a different physical server by the same name. CoInvalidateRemoteMachineBindings is used for this purpose.

Only administrators may call this API.

Note The OLE Service Control Manager will flush unused binding handles over time. It is not necessary to call CoInvalidateRemoteMachineBindings to do this.

Requirements

Requires Windows 2000, 64-bit Edition, or later.
Windows NT/2000: Unsupported
Windows 95/98: Unsupported
Windows CE: Unsupported.
Header: Declared in objbase.h.
Library: Included as a resource in ole32.lib.


CoIsHandlerConnected

Determines whether a remote object is connected to the corresponding in-process object.

BOOL CoIsHandlerConnected(
  LPUNKNOWN pUnk  //Pointer to the remote object
);

Parameter

pUnk
[in] Pointer to the controlling IUnknown interface on the remote object.

Return Values

TRUE
The object is not remote or that it is remote and is still connected to its remote handler.
FALSE
The object is remote and is invalid (no longer connected to its remote handler).

Remarks

The CoIsHandlerConnected function determines the status of a remote object. You can use it to determine when to release a remote object. You specify the remote object by giving the function a pointer to its controlling IUnknown interface (the pUnk parameter). A TRUE returned from the function indicates either that the specified object is not remote, or that it is remote and is still connected to its remote handler. A FALSE returned from the function indicates that the object is remote but is no longer connected to its remote handler; in this case, the caller should respond by releasing the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoIsOle1Class

Determines if a given CLSID represents an OLE 1 object.

BOOL CoIsOle1Class(
  REFCLSID rclsid   //CLSID to check
);

Parameter

rclsid
[in] CLSID to check.

Return Values

TRUE
CLSID refers to an OLE 1 object.
FALSE
CLSID does not refer to an OLE 1 object.

Remarks

CoIsOle1Class determines whether an object class is from OLE 1. You can use it to prevent linking to embedded OLE 1 objects within a container, which OLE 1 objects do not support. Once a container has determined that copied data represents an embedded object, the container code can call CoIsOle1Class to determine whether the embedded object is an OLE 1 object. If CoIsOle1Class returns TRUE, the container does not offer CF_LINKSOURCE as one of its clipboard formats. This is one of several OLE compatibility functions. Other compatibility functions, listed below, can be used to convert the storage formats of objects between OLE 1 and OLE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

OleConvertIStorageToOLESTREAM, OleConvertIStorageToOLESTREAMEx, OleConvertOLESTREAMToIStorage, OleConvertOLESTREAMToIStorageEx


CoLoadLibrary

Loads a specific DLL into the caller's process. CoLoadLibrary is equivalent to LoadLibraryEx. CoLoadLibrary does not do anything about the lifetime of the library.

HINSTANCE CoLoadLibrary(
  LPOLESTR lpszLibName,  //Pointer to the name of the library to be loaded
  BOOL bAutoFree        //Whether library is automatically freed
);

Parameters

lpszLibName
[in] Points to the name of the library to be loaded. The use of this name is the same as in the Win32 function LoadLibrary.
bAutoFree
[in] If TRUE, indicates that this library is freed when it is no longer needed, through a call to either the CoFreeUnusedLibraries or CoUninitialize functions. If FALSE, the library should be explicitly freed with the CoFreeLibrary function.

Return Values

Module Handle
Handle of the loaded library.
NULL
Library could not be loaded.

Remarks

The CoGetClassObject function does not call CoLoadLibrary. CoLoadLibrary loads a DLL specified by the lpszLibName parameter into the process that called CoGetClassObject. Containers should not call CoLoadLibrary directly.

Internally, a reference count is kept on the loaded DLL, by using CoLoadLibrary to increment the count and the CoFreeLibrary function to decrement it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoFreeAllLibraries, CoFreeLibrary, CoFreeUnusedLibraries, CoGetClassObject


CoLockObjectExternal

Called either to lock an object to ensure that it stays in memory, or to release such a lock. Call CoLockObjectExternal to place a strong lock on an object to ensure that it stays in memory.

STDAPI CoLockObjectExternal(
  IUnknown * pUnk,  //Pointer to object to be locked or unlocked
  BOOL fLock,       //TRUE = lock, FALSE = unlock
  BOOL fLastUnlockReleases
                    //TRUE = release all pointers to object
);

Parameters

pUnk
[in] Pointer to the IUnknown interface on the object to be locked or unlocked.
fLock
[in] Whether the object is to be locked or released. Specifying TRUE holds a reference to the object (keeping it in memory), locking it independently of external or internal AddRef/Release operations, registrations, or revocations. If fLock is TRUE, fLastLockReleases is ignored. FALSE releases a lock previously set with a call to this function.
fLastUnlockReleases
[in] Whether a given lock is the last reference that is supposed to keep an object alive. If it is, TRUE releases all pointers to the object (there may be other references that are not supposed to keep it alive).

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The object was locked successfully.

Remarks

The CoLockObjectExternal function prevents the reference count of an object from going to zero, thereby "locking" it into existence until the lock is released. The same function (with different parameters) releases the lock. The lock is implemented by having the system call IUnknown::AddRef on the object. The system then waits to call IUnknown::Release on the object until a later call to CoLockObjectExternal with fLock set to FALSE. This function can be used to maintain a reference count on the object on behalf of the end user, because it acts outside of the object, as does the user.

The CoLockObjectExternal function must be called in the process in which the object actually resides (the EXE process, not the process in which handlers may be loaded).

Calling CoLockObjectExternal sets a strong lock on an object. A strong lock keeps an object in memory, while a weak lock does not. Strong locks are required, for example, during a silent update to an OLE embedding. The embedded object's container must remain in memory until the update process is complete. There must also be a strong lock on an application object to ensure that the application stays alive until it has finished providing services to its clients. All external references place a strong reference lock on an object.

The CoLockObjectExternal function is typically called in the following situations:

  • Object servers should call CoLockObjectExternal with both fLock and fLastLockReleases set to TRUE when they become visible. This call creates a strong lock on behalf of the user. When the application is closing, free the lock with a call to CoLockObjectExternal, setting fLock to FALSE and fLastLockReleases to TRUE.
  • A call to CoLockObjectExternal on the server can also be used in the implementation of IOleContainer::LockContainer.

There are several things to be aware of when you use CoLockObjectExternal in the implementation of IOleContainer::LockContainer. An embedded object would call IOleContainer::LockContainer on its container to keep it running (to lock it) in the absence of other reasons to keep it running. When the embedded object becomes visible, the container must weaken its connection to the embedded object with a call to the OleSetContainedObject function, so other connections can affect the object.

Unless an application manages all aspects of its application and document shutdown completely with calls to CoLockObjectExternal, the container must keep a private lock count in IOleContainer::LockContainer so that it exits when the lock count reaches zero and the container is invisible. Maintaining all aspects of shutdown, and thereby avoiding keeping a private lock count, means that CoLockObjectExternal should be called whenever one of the following conditions occur:

  • A document is created and destroyed or made visible or invisible.
  • An application is started and shut down by the user.
  • A pseudo-object is created and destroyed.

For debugging purposes, it may be useful to keep a count of the number of external locks (and unlocks) set on the application.

Note The end user has explicit control over the lifetime of an application, even if there are external locks on it. That is, if a user decides to close the application (File, Exit), it must shut down. In the presence of external locks (such as the lock set by CoLockObjectExternal), the application can call the CoDisconnectObject function to force these connections to close prior to shutdown.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IOleContainer::LockContainer, OleSetContainedObject


CoMarshalHresult

Marshals an HRESULT to the specified stream, from which it can be unmarshaled using the CoUnmarshalHresult function.

STDAPI CoMarshalHresult(
  IStream * pStm,  //Pointer to the marshaling stream
  HRESULT hresult  //HRESULT to be marshaled
);

Parameters

pStm
[in] Pointer to the marshaling stream.
hresult
[in] HRESULT in the originating process.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The HRESULT was marshaled successfully.
STG_E_INVALIDPOINTER
Bad pointer passed in for pStm.
STG_E_MEDIUMFULL
The medium is full.

Remarks

An HRESULT is process-specific, so an HRESULT that is valid in one process might not be valid in another. If you are writing your own implementation of IMarshal and need to marshal an HRESULT from one process to another, either as a parameter or a return code, you must call this function. In other circumstances, you will have no need to call this function.

This function perfoms the following tasks:

  1. Writes an HRESULT to a stream.
  2. Returns an IStream pointer to that stream.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoUnmarshalHresult, IStream


CoMarshalInterface

Writes into a stream the data required to initialize a proxy object in some client process. The COM library in the client process calls the CoUnmarshalInterface function to extract the data and initialize the proxy. Before calling CoUnmarshalInterface, seek back to the original position in the stream. CoMarshalInterface can marshal only interfaces derived from IUnknown.

STDAPI CoMarshalInterface(
  IStream *pStm,        //Pointer to the stream used for marshaling
  REFIID riid,          //Reference to the identifier of the
                        // interface
  IUnknown *pUnk,       //Pointer to the interface to be marshaled
  DWORD dwDestContext,  //Destination context
  void *pvDestContext,  //Reserved for future use
  DWORD mshlflags       //Reason for marshaling
);

Parameters

pStm
[in] Pointer to the stream to be used during marshaling.
riid
[in] Reference to the identifier of the interface to be marshaled. This interface must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled. This interface must be derived from the IUnknown interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Values for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling can occur in another apartment of the current process (MSHCTX_INPROC), in another process on the same computer as the current process (MSHCTX_LOCAL), or in a process on a different machine (MSHCTX_DIFFERENTMACHINE).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag specifying whether the data to be marshaled is to be transmitted back to the client process—the normal case—or written to a global table, where it can be retrieved by multiple clients. Values come from the MSHLFLAGS enumeration.

Return Values

This function supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The interface pointer was marshaled successfully.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread before this function was called.
IStream errors
This function can also return any of the stream-access error values returned by the IStream interface.

Remarks

The CoMarshalInterface function marshals the interface referred to by riid on the object whose IUnknown implementation is pointed to by pUnk. To do so, the CoMarshalInterface function performs the following tasks:

  1. Queries the object for a pointer to the IMarshal interface. If the object does not implement IMarshal, meaning that it relies on COM to provide marshaling support, CoMarshalInterface gets a pointer to COM's default implementation of IMarshal.
  2. Gets the CLSID of the object's proxy by calling IMarshal::GetUnmarshalClass, using whichever IMarshal interface pointer has been returned.
  3. Writes the CLSID of the proxy to the stream to be used for marshaling.
  4. Marshals the interface pointer by calling IMarshal::MarshalInterface.

If you are implementing existing COM interfaces or defining your own interfaces using the Microsoft Interface Definition Language (MIDL), the MIDL-generated proxies and stubs call CoMarshalInterface for you. If you are writing your own proxies and stubs, your proxy code and stub code should each call CoMarshalInterface to correctly marshal interface pointers. Calling IMarshal directly from your proxy and stub code is not recommended.

If you are writing your own implementation of IMarshal, and your proxy needs access to a private object, you can include an interface pointer to that object as part of the data you write to the stream. In such situations, if you want to use COM's default marshaling implementation when passing the interface pointer, you can call CoMarshalInterface on the object to do so.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoUnmarshalInterface, IMarshal::MarshalInterface


CoMarshalInterThreadInterfaceInStream

Marshals an interface pointer from one thread to another thread in the same process.

HRESULT CoMarshalInterThreadInterfaceInStream(
  REFIID riid,     //Reference to the identifier of the interface
  LPUNKNOWN pUnk,  //Pointer to the interface to be marshaled
  LPSTREAM * ppStm //Address of output variable that receives the
                   // IStream interface pointer for the marshaled
                   // interface
);

Parameters

riid
[in] Reference to the identifier of the interface to be marshaled.
pUnk
[in] Pointer to the interface to be marshaled, which must be derived from IUnknown; can be NULL.
ppStm
[out] Address of IStream* pointer variable that receives the interface pointer to the stream that contains the marshaled interface.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The interface was marshaled successfully.

Remarks

The CoMarshalInterThreadInterfaceInStream function enables an object to easily and reliably marshal an interface pointer to another thread in the same process. The stream returned in ppStm is guaranteed to behave correctly when a client running in the receiving thread attempts to unmarshal the pointer. The client can then call the CoGetInterfaceAndReleaseStream to unmarshal the interface pointer and release the stream object.

The CoMarshalInterThreadInterfaceInStream function performs the following tasks:

  1. Creates a stream object.
  2. Passes the stream object's IStream pointer to CoMarshalInterface.
  3. Returns the IStream pointer to the caller.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetInterfaceAndReleaseStream


CoQueryAuthenticationServices

Retrieves a list of the authentication services registered when the process called CoInitializeSecurity.

HRESULT CoQueryAuthenticationServices(
  DWORD * pcAuthSvc,  //Pointer to the number of entries returned in
                      // the array
  SOLE_AUTHENTICATION_SERVICE** asAuthSvc
                      //Pointer to an array of structures
);

Parameters

pcAuthSvc
[out] Pointer to return the number of entries returned in the rgAuthSvc array. May not be NULL.
asAuthSvc
[out] Pointer to an array of SOLE_AUTHENTICATION_SERVICE structures. The list is allocated through a call to CoTaskMemAlloc. The caller must free the list when finished with it by calling CoTaskMemFree.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
Indicates success.

Remarks

CoQueryAuthenticationServices retrieves a list of the authentication services currently registered. If the process calls CoInitializeSecurity, these are the services registered through that call. If the application does not call it, CoInitializeSecurity is called automatically by COM, registering the default security package, the first time an interface is marshaled or unmarshaled.

This function returns only the authentication services registered with CoInitializeSecurity. It does not return all of the authentication services installed on the machine, but EnumerateSecurityPackages does. CoQueryAuthenticationServices is primarily useful for custom marshalers, to determine which principal names an application can use.

Different authentication services support different levels of security. For example, NTLMSSP does not support delegation or mutual authentication while Kerberos does. The application is responsible only for registering authentication services that provide the features the application needs. This function provides a way to find out which services have been registered with CoInitializeSecurity.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoInitializeSecurity, SOLE_AUTHENTICATION_SERVICE structure, Security in COM


CoQueryClientBlanket

Called by the server to find out about the client that invoked the method executing on the current thread. This is a helper function for IServerSecurity::QueryBlanket.

HRESULT CoQueryClientBlanket(
  DWORD* pAuthnSvc, //Pointer to the current authentication service
  DWORD* pAuthzSvc, //Pointer to the current authorization service
  OLECHAR ** pServerPrincName,
                    //Pointer to the current principal name
  DWORD * pAuthnLevel,
                    //Pointer to the current authentication level
  DWORD * pImpLevel,
                    //Reserved for future use; must be NULL
  RPC_AUTHZ_HANDLE * pPrivs,
                    //Pointer to handle to privilege information
  DWORD * pCapabilities
                    //Pointer to flags indicating further
                    // capabilities of the proxy
);

Parameters

pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This will be a single value taken from the list of RPC_C_AUTHN_xxx constants. If the caller specifies NULL, the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. If the caller specifies NULL, the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the callee using CoTaskMemAlloc, and must be freed by the caller using CoTaskMemFree when they are done with it. By default, SChannel principal names will be in the msstd form. The fullsic form will be returned if EOAC_MAKE_FULLSIC is specified in the pCapabilities parameter. For more information about the msstd and fullsic forms, see Principal Names. If the caller specifies NULL, the current principal name is not retrieved.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This will be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. If the caller specifies NULL, the current authentication level is not retrieved.
pImpLevel
[out] Must be NULL. This parameter is reserved for future use.
pPrivs
[out] Pointer to a handle that indicates the privilege information for the client application. The format of the structure that the handle refers to depends on the authentication service. The application should not write or free the memory. The information is valid only for the duration of the current call. For NTLMSSP, and Kerberos, this is a SEC_WINNT_AUTH_IDENTITY or SEC_WINNT_AUTH_IDENTITY_EX structure. For SChannel, this is a CERT_CONTEXT that represents the client's certificate. If the client has no certificate, NULL is returned. If the caller specifies NULL, the current privilege information is not retrieved.
pCapabilities
[in, out] Pointer to return flags indicating capabilities of the call. To request that the principal name be returned in fullsic form if SChannel is the authentication service, the caller can set the EOAC_MAKE_FULLSIC flag in this parameter. If the caller specifies NULL, the current capabilities are not retrieved.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pServerPrincName out parameter.

Remarks

CoQueryClientBlanket is called by the server to get security information about the client that invoked the method executing on the current thread. This function encapsulates the following sequence of common calls (error handling excluded):

    CoGetCallContext(IID_IServerSecurity, (void**)&pss);
    pss->QueryBlanket(pAuthnSvc, pAuthzSvc, pServerPrincName,
                pAuthnLevel, pImpLevel, pPrivs, pCapabilities);
    pss->Release();

This sequence calls CoGetCallContext to get a pointer to IServerSecurity and, with the resulting pointer, calls IServerSecurity::QueryBlanket and then releases the pointer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IServerSecurity::QueryBlanket, CoGetCallContext, Security in COM


CoQueryProxyBlanket

Retrieves the authentication information the client uses to make calls on the specified proxy. This is a helper function for IClientSecurity::QueryBlanket.

HRESULT CoQueryProxyBlanket(
  IUnknown * pProxy,      //Location for the proxy to query
  DWORD * pAuthnSvc,      //Location for the current
                          // authentication service
  DWORD * pAuthzSvc,      //Location for the current
                          // authorization service
  OLECHAR ** pServerPrincName,
                          //Location for the current principal name
  DWORD * pAuthnLevel,    //Location for the current authentication
                          // level
  DWORD * pImpLevel,      //Location for the current impersonation
                          // level
  RPC_AUTH_IDENTITY_HANDLE * ppAuthInfo,
                          //Location for the value passed to
                          // IClientSecurity::SetBlanket
  DWORD * pCapabilities   //Location for flags indicating further
                          // capabilities of the proxy
);

Parameters

pProxy
[in] Pointer indicating the proxy to query. For more information, see the Remarks section.
pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This will be a single value taken from the list of RPC_C_AUTHN_xxx constants. If the caller specifies NULL, the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. If the caller specifies NULL, the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the callee using CoTaskMemAlloc and must be freed by the caller using CoTaskMemFree when they are done with it. Notice that the actual principal name is returned. The EOAC_MAKE_FULLSIC flag is not accepted to convert the principal name. If the caller specifies NULL, the current principal name is not retrieved.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This will be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. If the caller specifies NULL, the current authentication level is not retrieved.
pImpLevel
[out] Pointer to a DWORD value defining the current impersonation level. This will be a single value taken from the list of RPC_C_IMP_LEVEL_xxx constants. If the caller specifies NULL, the current impersonation level is not retrieved.
ppAuthInfo
[out] Pointer to the pointer value indicating the identity of the client that was passed to the last IClientSecurity::SetBlanket call (or the default value). Default values are valid only until the proxy is released. If the caller specifies NULL, the client identity is not retrieved. The format of the structure that the returned handle refers to depends on the authentication service. For NTLMSSP and Kerberos, if the client specified a structure in the pAuthInfo parameter to CoInitializeSecurity, that value is returned. For SChannel, if a certificate for the client could be retrieved from the certificate manager, that value is returned here. Otherwise, NULL is returned. Because this points to the value itself and is not a copy, it should not be manipulated or freed.
pCapabilities
[out] Pointer to a DWORD of flags indicating the capabilities of the proxy. If the caller specifies NULL, the current capability flags are not retrieved.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pServerPrincName out parameter.

Remarks

CoQueryProxyBlanket is called by the client to retrieve the authentication information COM will use on calls made from the specified proxy. This function encapsulates the following sequence of common calls (error handling excluded):

pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
pcs->QueryBlanket(pProxy, pAuthnSvc, pAuthzSvc, pServerPrincName,
                 pAuthnLevel, pImpLevel, ppAuthInfo, pCapabilities);
pcs->Release();

This sequence calls QueryInterface on the proxy to get a pointer to IClientSecurity, and with the resulting pointer, calls IClientSecurity::QueryBlanket and then releases the pointer.

In pProxy, you can pass any proxy, such as a proxy you get through a call to CoCreateInstance or CoUnmarshalInterface, or you can pass an interface pointer. It can be any interface. You cannot pass a pointer to something that is not a proxy. Therefore, you can't pass a pointer to an interface that has the local keyword in its interface definition because no proxy is created for such an interface. IUnknown is the exception to this rule.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IClientSecurity::QueryBlanket, CoSetProxyBlanket, CoQueryClientBlanket, Security in COM


CoRegisterClassObject

Registers an EXE class object with OLE so other applications can connect to it. EXE object applications should call CoRegisterClassObject on startup. It can also be used to register internal objects for use by the same EXE or other code (such as DLLs) that the EXE uses.

STDAPI CoRegisterClassObject(
  REFCLSID rclsid,     //Class identifier (CLSID) to be registered
  IUnknown * pUnk,     //Pointer to the class object
  DWORD dwClsContext,  //Context for running executable code
  DWORD flags,         //How to connect to the class object
  LPDWORD  lpdwRegister
                       //Pointer to the value returned
);

Parameters

rclsid
[in] CLSID to be registered.
pUnk
[in] Pointer to the IUnknown interface on the class object whose availability is being published.
dwClsContext
[in] Context in which the executable code is to be run. For information on these context values, see the CLSCTX enumeration.
flags
[in] How connections are made to the class object. For information on these flags, see the REGCLS enumeration.
lpdwRegister
[out] Pointer to a value that identifies the class object registered; later used by the CoRevokeClassObject function to revoke the registration.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The class object was registered successfully.
CO_E_OBJISREG
Already registered in the class object table.

Remarks

Only EXE object applications call CoRegisterClassObject. Object handlers or DLL object applications do not call this function — instead, they must implement and export the DllGetClassObject function.

At startup, a multiple-use EXE object application must create a class object (with the IClassFactory interface on it), and call CoRegisterClassObject to register the class object. Object applications that support several different classes (such as multiple types of embeddable objects) must allocate and register a different class object for each.

Multiple registrations of the same class object are independent and do not produce an error. Each subsequent registration yields a unique key in lpdwRegister.

Multiple document interface (MDI) applications must register their class objects. Single document interface (SDI) applications must register their class objects only if they can be started by means of the /Embedding switch.

The server for a class object should call CoRevokeClassObject to revoke the class object (remove its registration) when all of the following are true:

  • There are no existing instances of the object definition.
  • There are no locks on the class object.
  • The application providing services to the class object is not under user control (not visible to the user on the display).

After the class object is revoked, when its reference count reaches zero, the class object can be released, allowing the application to exit.

For information on the flags parameter, refer to the REGCLS enumeration.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetClassObject, CoRevokeClassObject, DllGetClassObject, REGCLS, CLSCTX


CoRegisterMallocSpy

Registers an implementation of the IMallocSpy interface in OLE, thereafter requiring OLE to call its wrapper methods around every call to the corresponding IMalloc method. IMallocSpy is defined in OLE to allow developers to debug memory allocations.

HRESULT CoRegisterMallocSpy(
  LPMALLOCSPY pMallocSpy  //Pointer to the interface
);

Parameter

pMallocSpy
[in] Pointer to an instance of the IMallocSpy implementation.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The IMallocSpy object is successfully registered.
CO_E_OBJISREG
There is already a registered spy.

Remarks

The CoRegisterMallocSpy function registers the IMallocSpy object, which is used to debug calls to IMalloc methods. The function calls QueryInterface on the pointer pMallocSpy for the interface IID_IMallocSpy. This is to ensure that pMallocSpy really points to an implementation of IMallocSpy. By the rules of OLE, it is expected that a successful call to QueryInterface has added a reference (through the AddRef method) to the IMallocSpy object. That is, CoRegisterMallocSpy does not directly call AddRef on pMallocSpy, but fully expects that the QueryInterface call will.

When the IMallocSpy object is registered, whenever there is a call to one of the IMalloc methods, OLE first calls the corresponding IMallocSpy pre-method. Then, after executing the IMalloc method, OLE calls the corresponding IMallocSpy post-method. For example, whenever there is a call to IMalloc::Alloc, from whatever source, OLE calls IMallocSpy::PreAlloc, calls IMalloc::Alloc, and after that allocation is completed, calls IMallocSpy::PostAlloc.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMallocSpy, CoRevokeMallocSpy, CoGetMalloc


CoRegisterMessageFilter

Registers with OLE the instance of an IMessageFilter interface, which is to be used for handling concurrency issues on the current thread. Only one message filter can be registered for each thread. Threads in multi-threaded apartments cannot have message filters.

HRESULT CoRegisterMessageFilter(
  LPMESSAGEFILTER lpMessageFilter,  //Pointer to interface
  LPMESSAGEFILTER * lplpMessageFilter  //Address of output variable
                                       // that receives the
                                       // IMessageFilter interface
                                       // pointer of a previously
                                       // registered message filter
);

Parameters

lpMessageFilter
[in] Pointer to the IMessageFilter interface on the message filter. This message filter should be registered on the current thread, replacing the previous message filter (if any). Can be NULL, indicating that no message filter should be registered on the current thread.
lplpMessageFilter
[out] Address of the IMessageFilter* pointer variable that receives the interface pointer to the previously registered message filter. If there was no previously registered message filter for the current thread, the value of *lplpMessageFilter is NULL.

Return Values

S_OK
The IMessageFilter instance registered or revoked successfully.
S_FALSE
Error registering or revoking IMessageFilter instance.

Remarks

To revoke the registered message filter, pass the previous message filter (possibly NULL) as the lpMessageFilter parameter to CoRegisterMessageFilter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoRegisterPSClsid

Enables a downloaded DLL to register its custom interfaces within its running process so that the marshaling code will be able to marshal those interfaces.

WINOLEAPI CoRegisterPSClsid(
  REFIID riid,     //Custom interface to be registered
  REFCLSID rclsid  //DLL containing the proxy/stub code for riid
);

Parameters

riid
[in] Points to the IID of the interface to be registered.
rclsid
[in] Points to the CLSID of the DLL that contains the proxy/stub code for the custom interface specified by riid.

Return Values

S_OK
The custom interface was successfully registered.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.

Remarks

Normally the code responsible for marshaling an interface pointer into the current running process reads the HKEY_CLASSES_ROOT\Interfaces section of the registry to obtain the CLSID of the DLL containing the ProxyStub code to be loaded. To obtain the ProxyStub CLSIDs for an existing interface, the code calls the CoGetPSClsid function.

In some cases, however, it may be desirable or necessary for an in-process handler or in-process server to make its custom interfaces available without writing to the registry. A DLL downloaded across a network may not even have permission to access the local registry, and because the code originated on another machine, the user, for security purposes, may want to run it in a restricted environment. Or a DLL may have custom interfaces that it uses to talk to a remote server and may also include the ProxyStub code for those interfaces. In such cases, a DLL needs an alternative way to register its interfaces. CoRegisterPSClsid, used in conjunction with CoRegisterClassObject, provides that alternative.

A DLL would normally call CoRegisterPSClsid as shown in the following code fragment:

HRESULT RegisterMyCustomInterface(DWORD *pdwRegistrationKey)
{
    HRESULT hr = CoRegisterClassObject(CLSID_MyProxyStubClsid,
        pIPSFactoryBuffer,
        CLSCTX_INPROC_SERVER,
        REGCLS_MULTIPLEUSE
        pdwRegistrationKey);
    if(SUCCEEDED)(hr))
    {
        hr = CoRegisterPSClsid(IID_MyCustomInterface, CLSID_MyProxyStubClsid);
    }

    return hr;
}

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetPSClsid, CoRegisterClassObject


CoRegisterSurrogate

Registers the surrogate process through its ISurrogate interface pointer.

HRESULT CoRegisterSurrogate(
  ISurrogate  * pSurrogate  //ISurrogate pointer on the process to
                            // be registered
);

Parameter

pSurrogate
[in] Pointer to the ISurrogate interface on the surrogate process to be registered.

Return Value

S_OK
The surrogate process was registered.

Remarks

The CoRegisterSurrogate function sets a global interface pointer to the ISurrogate interface implemented on the surrogate process. This pointer is set in the ole32 DLL loaded in the surrogate process. COM uses this global pointer in ole32 to call the methods of ISurrogate. This function is usually called by the surrogate implementation when it is launched.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

ISurrogate, Writing a Custom Surrogate


CoReleaseMarshalData

Destroys a previously marshaled data packet.

STDAPI CoReleaseMarshalData(
  IStream * pStm  //Pointer to stream containing data packet
);

Parameter

pStm
[in] Pointer to the stream that contains the data packet to be destroyed.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The data packet was successfully destroyed.
STG_E_INVALIDPOINTER
An IStream error dealing with the pStm parameter.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread before this function was called.

Remarks

The CoReleaseMarshalData function performs the following tasks:

  1. The function reads a CLSID from the stream.
  2. If COM's default marshaling implementation is being used, the function gets an IMarshal pointer to an instance of the standard unmarshaler. If custom marshaling is being used, the function creates a proxy by calling the CoCreateInstance function, passing the CLSID it read from the stream, and requesings an IMarshal interface pointer to the newly created proxy.
  3. Using whichever IMarshal interface pointer it has acquired, the function calls IMarshal::ReleaseMarshalData.

You typically do not call this function. The only situation in which you might need to call this function is if you use custom marshaling (write and use your own implementation of IMarshal). Examples of when CoReleaseMarshalData should be called include the following situations:

  • An attempt was made to unmarshal the data packet, but it failed.
  • A marshaled data packet was removed from a global table.

As an analogy, the data packet can be thought of as a reference to the original object, just as if it were another interface pointer being held on the object. Like a real interface pointer, that data packet must be released at some point. The use of IMarshal::ReleaseMarshalData to release data packets is analogous to the use of IUnknown::Release to release interface pointers.

Note that you do not need to call CoReleaseMarshalData after a successful call of the CoUnmarshalInterface function; that function releases the marshal data as part of the processing that it does.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMarshal::ReleaseMarshalData


CoReleaseServerProcess

Decrements the global per-process reference count.

ULONG CoReleaseServerProcess(void);

Return Values

0
The server application should initiate its cleanup.
Other values
Server application should not yet initiate its cleanup.

Remarks

Servers can call CoReleaseServerProcess to decrement a global per-process reference count incremented through a call to CoAddRefServerProcess.

When that count reaches zero, OLE automatically calls CoSuspendClassObjects, which prevents new activation requests from coming in. This permits the server to deregister its class objects from its various threads without worry that another activation request may come in. New activation requests result in launching a new instance of the local server process.

The simplest way for a local server application to make use of these API functions is to call CoAddRefServerProcess in the constructor for each of its instance objects, and in each of its IClassFactory::LockServer methods when the fLock parameter is TRUE. The server application should also call CoReleaseServerProcess in the destruction of each of its instance objects, and in each of its IClassFactory::LockServer methods when the fLock parameter is FALSE. Finally, the server application must check the return code from CoReleaseServerProcess; if it returns 0, the server application should initiate its cleanup. This typically means that a server with multiple threads should signal its various threads to exit their message loops and call CoRevokeClassObject and CoUninitialize.

If these APIs are used at all, they must be called in both the object instances and the LockServer method, otherwise the server application may be shutdown prematurely. In-process Servers typically should not call CoAddRefServerProcess or CoReleaseServerProcess.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoSuspendClassObjects, CoReleaseServerProcess, IClassFactory::LockServer, Out-of-process Server Implementation Helpers


CoResumeClassObjects

Called by a server that can register multiple class objects to inform the OLE SCM about all registered classes, and permits activation requests for those class objects.

WINOLEAPI CoResumeClassObjects(void);

Return Values

S_OK
The CLSID was retrieved successfully.

Remarks

Servers that can register multiple class objects call CoResumeClassObjects once, after having first called CoRegisterClassObject, specifying REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED for each CLSID the server supports. This function causes OLE to inform the SCM about all the registered classes, and begins letting activation requests into the server process.

This reduces the overall registration time, and thus the server application startup time, by making a single call to the SCM, no matter how many CLSIDs are registered for the server. Another advantage is that if the server has multiple apartments with different CLSIDs registered in different apartments, or is a free-threaded server, no activation requests will come in until the server calls CoResumeClassObjects. This gives the server a chance to register all of its CLSIDs and get properly set up before having to deal with activation requests, and possibly shutdown requests.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoRegisterClassObject, CoSuspendClassObjects, Out-of-process Server Implementation Helpers


CoRevertToSelf

Restores the authentication information on a thread of execution.

HRESULT CoRevertToSelf( )

Return Values

This function supports the standard return values, including the following values:

S_OK
Indicates success.

Remarks

CoRevertToSelf, which is a helper function that calls IServerSecurity::RevertToSelf, restores the authentication information on a thread to the authentication information on the thread before impersonation began. For more details, see IServerSecurity::RevertToSelf.

CoRevertToSelf encapsulates the following common sequence of calls (error handling excluded):

    CoGetCallContext(IID_IServerSecurity, (void**)&pss);
    pss->RevertToSelf();
    pss->Release();

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IServerSecurity::RevertToSelf, CoGetCallContext, Security in COM


CoRevokeClassObject

Informs OLE that a class object, previously registered with the CoRegisterClassObject function, is no longer available for use.

HRESULT CoRevokeClassObject(
  DWORD dwRegister  //Token on class object
);

Parameter

dwRegister
[in] Token previously returned from the CoRegisterClassObject function.

Return Values

This function supports the standard return values E_INVALIDARG,

E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The class object was successfully revoked.

Remarks

A successful call to CoRevokeClassObject means that the class object has been removed from the global class object table (although it does not release the class object). If other clients still have pointers to the class object and have caused the reference count to be incremented by calls to IUnknown::AddRef, the reference count will not be zero. When this occurs, applications may benefit if subsequent calls (with the obvious exceptions of IUnknown::AddRef and IUnknown::Release) to the class object fail.

An object application must call CoRevokeClassObject to revoke registered class objects before exiting the program. Class object implementers should call CoRevokeClassObject as part of the release sequence. You must specifically revoke the class object even when you have specified the flags value REGCLS_SINGLEUSE in a call to CoRegisterClassObject, indicating that only one application can connect to the class object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetClassObject, CoRegisterClassObject


CoRevokeMallocSpy

Revokes a registered IMallocSpy object.

HRESULT CoRevokeMallocSpy( );

Return Values

S_OK
The IMallocSpy object is successfully revoked.
CO_E_OBJNOTREG
No spy is currently registered.
E_ACCESSDENIED
Spy is registered but there are outstanding allocations (not yet freed) made while this spy was active.

Remarks

The IMallocSpy object is released when it is revoked. This release corresponds to the call to IUnknown::AddRef in the implementation of the QueryInterface function by the CoRegisterMallocSpy function. The implementation of the IMallocSpy interface should then do any appropriate cleanup.

If the return code is E_ACCESSDENIED, there are still outstanding allocations that were made while the spy was active. In this case, the registered spy cannot be revoked at this time because it may have attached arbitrary headers and/or trailers to these allocations that only the spy knows about. Only the spy's PreFree (or PreRealloc) method knows how to account for these headers and trailers. Before returning E_ACCESSDENIED, CoRevokeMallocSpy notes internally that a revoke is pending. When the outstanding allocations have been freed, the revoke proceeds automatically, releasing the IMallocSpy object. Thus, it is necessary to call CoRevokeMallocSpy only once for each call to CoRegisterMallocSpy, even if E_ACCESSDENIED is returned.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMallocSpy, CoRegisterMallocSpy, CoGetMalloc


CoSetCancelObject

Sets (registers) or resets (unregisters) a cancel object for use during subsequent cancel operations on the current thread.

HRESULT CoSetCancelObject(
  IUnknown *pUnk  //Cancel object to be set or reset
);

Parameters

pUnk
Pointer to the IUnknown interface on the cancel object to be set or reset on the current thread. If NULL, the topmost cancel object is reset.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The cancel object was successfully set or reset.
E_ACCESSDENIED
The cancel object cannot be set or reset at this time because of a block on cancel operations.

Remarks

For objects that support standard marshaling, the proxy object begins marshaling a method call by calling CoSetCancelObject to register a cancel object for the current thread.

CoSetCancelObject calls QueryInterface for ICancelMethodCalls on the cancel object. If the cancel object does not implement ICancelMethodCalls, CoSetCancelObject fails with E_NOINTERFACE. To disable cancel operations on a custom-marshaled interface, the implementation of ICancelMethodCalls::Cancel should do nothing but return E_NOTIMPL, E_FAIL, or some other appropriate value.

Before returning control to the client, the proxy unregisters the cancel object by calling CoSetCancelObject again, passing NULL as the parameter. In this case, CoSetCancelObject unregisters the cancel object and sets the next object in the cancel stack as the topmost cancel object. It is important that CoSetCancelObject be called when a method is called and again when it returns so the stack of cancel objects corresponds to the calling stack on the thread.

CoSetCancelObject calls IUnknown::AddRef on objects that it registers and Release on objects that it unregisters.

CoSetCancelObject does not set or reset cancel objects for asynchronous methods.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoSetProxyBlanket

Sets the authentication information that will be used to make calls on the specified proxy. This is a helper function for IClientSecurity::SetBlanket.

HRESULT CoSetProxyBlanket(
  IUnknown * pProxy,         //Indicates the proxy to set
  DWORD dwAuthnSvc,          //Authentication service to use
  DWORD dwAuthzSvc,          //Authorization service to use
  WCHAR * pServerPrincName,  //Server principal name to use with
                             // the authentication service
  DWORD dwAuthnLevel,        //Authentication level to use
  DWORD dwImpLevel,          //Impersonation level to use
  RPC_AUTH_IDENTITY_HANDLE   pAuthInfo,
                             //Identity of the client
  DWORD dwCapabilities       //Capability flags
);

Parameter

pProxy
[in] Indicates the proxy to set.
dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants, indicating the authentication service to use. It may be RPC_C_AUTHN_NONE if no authentication is required. If RPC_C_AUTHN_DEFAULT is specified, DCOM will pick an authentication service following its normal security blanket negotiation algorithm.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants, indicating the authorization service to use. If RPC_C_AUTHZ_DEFAULT is specified, DCOM will pick an authorization service following its normal security blanket negotiation algorithm. RPC_C_AUTHZ_NONE should be used as the authorization service if NTLMSSP, Kerberos, or SChannel is used as the authentication service.
pServerPrincName
[in] Pointer to a WCHAR string that indicates the server principal name to use with the authentication service. If COLE_DEFAULT_PRINCIPAL is specified, DCOM will pick a principal name using its security blanket negotiation algorithm. If Kerberos is used as the authentication service, this value must not be NULL. It must be the correct principal name of the server or the call will fail.

If SChannel is used as the authentication service, this value must be one of the msstd or fullsic forms described in Principal Names, or NULL if you do not want mutual authentication.

Generally, specifying NULL will not reset the server principal name on the proxy; rather, the previous setting will be retained. You must be careful when using NULL as pServerPrincName when selecting a different authentication service for the proxy, because there is no guarantee that the previously set principal name would be valid for the newly selected authentication service.

dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants, indicating the authentication level to use. If RPC_C_AUTHN_LEVEL_DEFAULT is specified, DCOM will pick an authentication level following its normal security blanket negotiation algorithm. If this value is none, the authentication service must also be none.
dwImpLevel
[in] A single DWORD value from the list of RPC_C_IMP_LEVEL_xxx constants, indicating the impersonation level to use. If RPC_C_IMP_LEVEL_DEFAULT is specified, DCOM will pick an impersonation level following its normal security blanket negotiation algorithm. If NTLMSSP is the authentication service, this value must be RPC_C_IMP_LEVEL_IMPERSONATE or RPC_C_IMP_LEVEL_IDENTIFY. In Windows 2000 and later versions, NTLMSSP also supports delegate-level impersonation (RPC_C_IMP_LEVEL_DELEGATE) on the same machine. If SChannel is the authentication service, this parameter must be RPC_C_IMP_LEVEL_IMPERSONATE.
pAuthInfo
[in] Pointer to an RPC_AUTH_IDENTITY_HANDLE value that establishes the identity of the client. This parameter is not used for calls on the same machine. The format of the structure referred to by the handle depends on the provider of the authentication service.

For NTLMSSP or Kerberos, the structure is a SEC_WINNT_AUTH_IDENTITY or SEC_WINNT_AUTH_IDENTITY_EX structure. The client must ensure that the memory remains valid and unchanged until a different identity is set on the proxy or until all proxies on the object are released. If NULL is specified, DCOM uses the current proxy identity (which is either the process token or the impersonation token). If the handle refers to a structure, that identity is used.

For SChannel, this parameter must be either a pointer to a CERT_CONTEXT that contains the client's X.509 certificate or NULL if the client wishes to make an anonymous connection to the server. If a certificate is specified, the caller must not free it as long as any proxy to the object exists in the current apartment.

For Snego, this parameter either is NULL or points to a SEC_WINNT_AUTH_IDENTITY_EX structure. If it is NULL, Snego will pick a list of authentication services based on those available on the client machine. If non-NULL, the structure's PackageList member must point to a string containing a comma-separated list of authentication service names and the PackageListLength member must give the number of bytes in the PackageList string. If PackageList is NULL, all calls using Snego will fail.

If COLE_DEFAULT_AUTHINFO is specified for this parameter, DCOM will pick the authentication information following its normal security blanket negotiation algorithm.

CoSetProxyBlanket will return an error if pAuthInfo is set and one of the cloaking flags is set in dwCapabilities.

dwCapabilities
[in] A DWORD defining flags that specify the capabilities of this proxy, which are defined in the EOLE_AUTHENTICATION_CAPABILITIES enumeration. The only flags that can be set through this function are EOAC_MUTUAL_AUTH, EOAC_STATIC_CLOAKING, EOAC_DYNAMIC_CLOAKING, EOAC_ANY_AUTHORITY, EOAC_MAKE_FULLSIC, and EOAC_DEFAULT. Either EOAC_STATIC_CLOAKING or EOAC_DYNAMIC_CLOAKING can be set if pAuthInfo is not set and SChannel is not the authentication service. (See Cloaking for more information.) If any capability flags other than those mentioned here are set, CoSetProxyBlanket will return an error.

Return Values

S_OK
Success.
E_INVALIDARG
One or more arguments is invalid.

Remarks

CoSetProxyBlanket sets the authentication information that will be used to make calls on the specified proxy. This function encapsulates the following sequence of common calls (error handling excluded):

    pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
    pcs->SetBlanket(pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName,
        dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities);
    pcs->Release();

This sequence calls QueryInterface on the proxy to get a pointer to IClientSecurity, and with the resulting pointer, calls IClientSecurity::SetBlanket and then releases the pointer.

For more information about how to use this function, see the Remarks section of the IClientSecurity::SetBlanket method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IClientSecurity::SetBlanket, CoQueryProxyBlanket, Security in COM, Setting Security at the Interface Proxy Level


CoSuspendClassObjects

Prevents any new activation requests from the SCM on all class objects registered within the process.

WINOLEAPI CoSuspendClassObjects(void);

Return Values

S_OK
Activation of class objects was successfully suspended.

Remarks

CoSuspendClassObjects prevents any new activation requests from the SCM on all class objects registered within the process. Even though a process may call this API, the process still must call CoRevokeClassObject for each CLSID it has registered, in the apartment it registered in. Applications typically do not need to call this API, which is generally only called internally by OLE when used in conjunction with CoReleaseServerProcess.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoRevokeClassObject, CoReleaseServerProcess, Out-of-process Server Implementation Helpers


CoSwitchCallContext

Switches the call context object used by CoGetCallContext.

HRESULT CoSwitchCallContext(
  IUnknown *pNewObject,    //Pointer to an interface on the new call context
  IUnknown **ppOldObject   //Address of output variable that receives a
                           // pointer to the old call context object
);

Parameters

pNewObject
[in] A pointer to an interface on the new call context object. COM will keep a reference to the pointer until CoSwitchCallContext is called with another object. It may be NULL if you are calling CoSwitchCallContext to switch back to the original call context and there was no original call context.
ppOldObject
[out] Address of pointer variable that receives a pointer to the call context object of the call currently in progress. This value is returned so that the original call context can be restored by the custom marshaller. The returned pointer will be NULL if there was no call in progress.

Return Values

S_OK
Success.
E_OUT_OF_MEMORY

Remarks

Custom marshallers call CoSwitchCallContext to change the call context object used by CoGetCallContext. Before dispatching an arriving call, custom marshallers call CoSwitchCallContext, specifying the new context object. After sending a reply, they must restore the original call context by calling CoSwitchCallContext again, this time passing a pointer to the original context object.

Call context objects provided by custom marshallers should support IServerSecurity.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IServerSecurity, Security in COM, CoGetCallContext


CoTaskMemAlloc

Allocates a block of task memory in the same way that IMalloc::Alloc does.

LPVOID CoTaskMemAlloc(
  ULONG cb  //Size in bytes of memory block to be allocated
);

Parameter

cb
[in] Size, in bytes, of the memory block to be allocated.

Return Values

Allocated memory block
Memory block allocated successfully.
NULL
Insufficient memory available.

Remarks

The CoTaskMemAlloc function uses the default allocator to allocate a memory block in the same way that IMalloc::Alloc does. It is not necessary to call the CoGetMalloc function before calling CoTaskMemAlloc.

The initial contents of the returned memory block are undefined – there is no guarantee that the block has been initialized. The allocated block may be larger than cb bytes because of the space required for alignment and for maintenance information.

If cb is zero, CoTaskMemAlloc allocates a zero-length item and returns a valid pointer to that item. If there is insufficient memory available, CoTaskMemAlloc returns NULL.

Note Applications should always check the return value from this method, even when requesting small amounts of memory, because there is no guarantee the memory will be allocated.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMalloc::Alloc, CoGetMalloc, CoTaskMemFree, CoTaskMemRealloc


CoTaskMemFree

Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function.

void CoTaskMemFree(
  void *pv  //Pointer to memory block to be freed
);

Parameter

pv
[in] Pointer to the memory block to be freed.

Remarks

The CoTaskMemFree function, using the default OLE allocator, frees a block of memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function.

The number of bytes freed equals the number of bytes that were originally allocated or reallocated. After the call, the memory block pointed to by pv is invalid and can no longer be used.

Note The pv parameter can be NULL, in which case this method has no effect.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoTaskMemAlloc, CoTaskMemRealloc, CoGetMalloc, IMalloc::Free


CoTaskMemRealloc

Changes the size of a previously allocated block of task memory.

LPVOID CoTaskMemRealloc(
  LPVOID pv,  //Pointer to memory block to be reallocated
  ULONG cb    //Size of block to be reallocated
);

Parameters

pv
[in] Pointer to the memory block to be reallocated. It can be a NULL pointer, as discussed in the Remarks.
cb
[in] Size, in bytes, of the memory block to be reallocated. It can be zero, as discussed in the following remarks.

Return Values

Reallocated memory block
Memory block successfully reallocated.
NULL
Insufficient memory or cb is zero and pv is not NULL.

Remarks

The CoTaskMemRealloc function changes the size of a previously allocated memory block in the same way that IMalloc::Realloc does. It is not necessary to call the CoGetMalloc function to get a pointer to the OLE allocator before calling CoTaskMemRealloc.

The pv argument points to the beginning of the memory block. If pv is NULL, CoTaskMemRealloc allocates a new memory block in the same way as the CoTaskMemAlloc function. If pv is not NULL, it should be a pointer returned by a prior call to CoTaskMemAlloc.

The cb argument specifies the size (in bytes) of the new block. The contents of the block are unchanged up to the shorter of the new and old sizes, although the new block can be in a different location. Because the new block can be in a different memory location, the pointer returned by CoTaskMemRealloc is not guaranteed to be the pointer passed through the pv argument. If pv is not NULL and cb is zero, then the memory pointed to by pv is freed.

CoTaskMemRealloc returns a void pointer to the reallocated (and possibly moved) memory block. The return value is NULL if the size is zero and the buffer argument is not NULL, or if there is not enough memory available to expand the block to the given size. In the first case, the original block is freed; in the second, the original block is unchanged.

The storage space pointed to by the return value is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoTaskMemAlloc, CoTaskMemFree, CoGetMalloc, IMalloc::Realloc


CoTestCancel

Determines if the call being executed on the server has been canceled by the client.

HRESULT CoTestCancel(void);

Return values

This function supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

RPC_S_CALLPENDING
The call is still pending and has not yet been canceled by the client.
RPC_E_CALL_CANCELED
The call has been canceled by the client.

Remarks

Server objects should call CoTestCancel at least once before returning to detect client cancellation requests. Doing so can save the server unnecessary work if the client has issued a cancellation request, and it can reduce the client's wait time if it has set the cancel timeout as RPC_C_CANCEL_INFINITE_TIMEOUT. Furthermore, if the server object detects a cancellation request before returning from a pending call, it can clean up any memory, marshaled interfaces, or handles it has created or obtained.

CoTestCancel calls CoGetCallContext to obtain the ICancelMethodCalls interface on the current cancel object and then calls ICancelMethodCalls::TestCancel. Objects that implement custom marshaling should first call CoSwitchCallContext to install the appropriate call context object.

This function does not test cancel for asynchronous calls.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CoTreatAsClass

Establishes or removes an emulation, in which objects of one class are treated as objects of a different class.

STDAPI CoTreatAsClass(
  REFCLSID clsidOld,
                     //CLSID for the original object to be emulated
  REFCLSID clsidNew  //CLSID for the new object that emulates the
                     // original
);

Parameters

clsidOld
[in] CLSID of the object to be emulated.
clsidNew
[in] CLSID of the object that should emulate the original object. This replaces any existing emulation for clsidOld. Can be CLSID_NULL, in which case any existing emulation for clsidOld is removed.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The emulation was successfully established or removed.
REGDB_E_CLASSNOTREG
The clsidOld parameter is not properly registered in the registration database.
REGDB_E_READREGDB
Error reading from registration database.
REGDB_E_WRITEREGDB
Error writing to registration database.

Remarks

This function sets the TreatAs entry in the registry for the specified object, allowing the object to be emulated by another application. Emulation allows an application to open and edit an object of a different format, while retaining the original format of the object. After this entry is set, whenever any function like CoGetClassObject specifies the object's original CLSID (clsidOld), it is transparently forwarded to the new CLSID (clsidNew), thus launching the application associated with the TreatAs CLSID. When the object is saved, it can be saved in its native format, which may result in loss of edits not supported by the original format.

You would call CoTreatAsClass in two situations if your application supports emulation:

  • In response to an end-user request (through a conversion dialog box) that a specified object be treated as an object of a different class (an object created under one application be run under another application, while retaining the original format information).
  • In a setup program, to register that one class of objects be treated as objects of a different class.

An example of the first case is that an end user might wish to edit a spreadsheet created by one application using a different application that can read and write the spreadsheet format of the original application. For an application that supports emulation, CoTreatAsClass can be called to implement a Treat As option in a conversion dialog box.

An example of the use of CoTreatAsClass in a setup program would be in an updated version of an application. When the application is updated, the objects created with the earlier version can be activated and treated as objects of the new version, while retaining the previous format information. This would allow you to give the user the option to convert when they save, or to save it in the previous format, possibly losing format information not available in the older version.

One result of setting an emulation is that when you enumerate verbs, as in the IOleObject::EnumVerbs method implementation in the default handler, this would enumerate the verbs from clsidNew instead of clsidOld.

To ensure that existing emulation information is removed when you install an application, your setup programs should call CoTreatAsClass, setting the clsidNew parameter to CLSID_NULL to remove any existing emulation for the classes they install.

If there is no CLSID assigned to the AutoTreatAs key in the registry, setting clsidNew and clsidOld to the same value removes the TreatAs entry, so there is no emulation. If there is a CLSID assigned to the AutoTreatAs key, that CLSID is assigned to the TreatAs key.

The CoTreatAsClass function does not validate whether an appropriate registry entry for clsidNew currently exists.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetTreatAsClass


CoUninitialize

Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other resources that the thread maintains, and forces all RPC connections on the thread to close.

void CoUninitialize( );

Remarks

A thread must call CoUninitialize once for each successful call it has made to CoInitialize or CoInitializeEx. Only the CoUninitialize call corresponding to the CoInitialize or CoInitializeEx call that initialized the library can close it.

Calls to OleInitialize must be balanced by calls to OleUninitialize. The OleUninitialize function calls CoUninitialize internally, so applications that call OleUninitialize do not also need to call CoUninitialize.

CoUninitialize should be called on application shutdown, as the last call made to the COM library after the application hides its main windows and falls through its main message loop. If there are open conversations remaining, CoUninitialize starts a modal message loop and dispatches any pending messages from the containers or server for this COM application. By dispatching the messages, CoUninitialize ensures that the application does not quit before receiving all of its pending messages. Non-COM messages are discarded.

Because there is no way to control the order in which in-process servers are loaded or unloaded, it is not safe to call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoInitialize, CoInitializeEx, OleUninitialize


CoUnmarshalHresult

Unmarshals an HRESULT type from the specified stream.

STDAPI CoUnmarshalHresult(
  LPSTREAM pStm,      //Pointer to stream used for unmarshaling
  HRESULT * phresult  //Pointer to the HRESULT
);

Parameters

pStm
[in] Pointer to the stream from which the HRESULT is to be unmarshaled.
phresult
[out] Pointer to the unmarshaled HRESULT.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The HRESULT was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.

Remarks

You do not explicitly call this function unless you are performing custom marshaling (that is, writing your own implementation of IMarshal), and your implementation needs to unmarshal an HRESULT.

You must use CoUnmarshalHresult to unmarshal HRESULTs previously marshaled by a call to the CoMarshalHresult function.

This function performs the following tasks:

  1. Reads an HRESULT from a stream.
  2. Returns the HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoMarshalHresult, IStream


CoUnmarshalInterface

Initializes a newly created proxy using data written into the stream by a previous call to the CoMarshalInterface function, and returns an interface pointer to that proxy.

STDAPI CoUnmarshalInterface(
  IStream * pStm,  //Pointer to the stream
  REFIID riid,     //Reference to the identifier of the interface
  void ** ppv      //Address of output variable that receives the
                   // interface pointer requested in riid
);

Parameters

pStm
[in] Pointer to the stream from which the interface is to be unmarshaled.
riid
[in] Reference to the identifier of the interface to be unmarshaled.
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer for the unmarshaled interface.

Return Values

This function supports the standard return value E_FAIL, as well as the following:

S_OK
The interface pointer was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread before this function was called.
CO_E_OBJNOTCONNECTED
The object application has been disconnected from the remoting system (for example, as a result of a call to the CoDisconnectObject function).
REGDB_E_CLASSNOTREG
An error occurred reading the registration database.
E_NOINTERFACE
The final QueryInterface of this function for the requested interface returned E_NOINTERFACE.
CoCreateInstance errors
An error occurred when creating the handler.

Remarks

The CoUnmarshalInterface function performs the following tasks:

  1. Reads from the stream the CLSID to be used to create an instance of the proxy.
  2. Gets an IMarshal pointer to the proxy that is to do the unmarshaling. If the object uses COM's default marshaling implementation, the pointer thus obtained is to an instance of the generic proxy object. If the marshaling is occurring between two threads in the same process, the pointer is to an instance of the in-process free threaded marshaler. If the object provides its own marshaling code, CoUnmarshalInterface calls the CoCreateInstance function, passing the CLSID it read from the marshaling stream. CoCreateInstance creates an instance of the object's proxy and returns an IMarshal interface pointer to the proxy.
  3. Using whichever IMarshal interface pointer it has acquired, the function then calls IMarshal::UnmarshalInterface and, if appropriate, IMarshal::ReleaseMarshalData.

The primary caller of this function is COM itself, from within interface proxies or stubs that unmarshal an interface pointer. There are, however, some situations in which you might call CoUnmarshalInterface. For example, if you are implementing a stub, your implementation would call CoUnmarshalInterface when the stub receives an interface pointer as a parameter in a method call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoMarshalInterface, IMarshal::UnmarshalInterface


CoWaitForMultipleHandles

Waits for specified handles to be signaled or for a specified timeout period to elapse.

HRESULT CoWaitForMultipleHandles(
  DWORD dwFlags,
  DWORD dwTimeout,
  ULONG cHandles,
  LPHANDLE pHandles,
  LPDWORD  lpdwindex
);

Parameters

dwFlags
Wait options. Values are taken from the COWAIT_FLAGS enumeration.
dwTimeout
Timeout period, in milliseconds.
cHandles
Number of elements in the pHandles array.
pHandles
Array of Win32 handles.
lpdwindex
Returns the index within the pHandles array of the handle that signaled. *lpdwindex is valid when the returned status is S_OK.

Return Values

S_OK
The required handle or handles were signaled.
RPC_S_CALLPENDING
The timeout period elapsed before the required handle or handles were signaled.

Remarks

Depending on which flags are set in the dwFlags parameter, CoWaitForMultipleHandles blocks the calling thread until one of the of the following events occurs:

  • One or all of the handles is signaled.
  • An asynchronous procedure call (APC) has been queued to the calling thread with a call to the Win32 function QueueUserAPC.
  • The timeout period expires.

If the caller resides in a single-thread apartment, CoWaitForMultipleHandles enters the COM modal loop, and the thread’s message loop will continue to dispatch messages using the thread’s message filter. If no message filter is registered for the thread, the default COM message processing is used.

If the calling thread resides in a multithread apartment (MTA), CoWaitForMultipleHandles calls the Win32 function WaitForMultipleObjects.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Library: Use ole32.lib.


CreateAntiMoniker

Creates and supplies a new anti-moniker.

WINOLEAPI CreateAntiMoniker(
  LPMONIKER FAR *ppmk  //Address of output variable that receives
                       // the IMoniker interface pointer
);

Parameter

ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the new anti-moniker. When successful, the function has called IUnknown::AddRef on the anti-moniker and the caller is responsible for calling IUnknown::Release. When an error occurs, the anti-moniker pointer is NULL.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The anti-moniker has been created successfully.

Remarks

You would call this function only if you are writing your own moniker class (implementing the IMoniker interface). If you are writing a new moniker class that has no internal structure, you can use CreateAntiMoniker in your implementation of the IMoniker::Inverse method, and then check for an anti-moniker in your implementation of IMoniker::ComposeWith.

Like the ".." directory in MS-DOS file systems, which acts as the inverse to any directory name just preceding it in a path, an anti-moniker acts as the inverse of a simple moniker that precedes it in a composite moniker. An anti-moniker is used as the inverse of simple monikers with no internal structure. For example, the system-provided implementations of file monikers, item monikers, and pointer monikers all use anti-monikers as their inverse; consequently, an anti-moniker composed to the right of one of these monikers composes to nothing.

A moniker client (an object that is using a moniker to bind to another object) typically does not know the class of a given moniker, so the client cannot be sure that an anti-moniker is the inverse. Therefore, to get the inverse of a moniker, you would call IMoniker::Inverse rather than CreateAntiMoniker.

To remove the last piece of a composite moniker, you would do the following:

  1. Call IMoniker::Enum on the composite, specifying FALSE as the first parameter. This creates an enumerator that returns the component monikers in reverse order.
  2. Use the enumerator to retrieve the last piece of the composite.
  3. Call IMoniker::Inverse on that moniker. The moniker returned by IMoniker::Inverse will remove the last piece of the composite.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::Inverse, IMoniker::ComposeWith, IMoniker - Anti-Moniker Implementation


CreateAsyncBindCtx

Creates an asynchronous bind context for use with asynchronous monikers.

HRESULT CreateAsyncBindCtx(
  DWORD dwReserved,  //Reserved for future use; must be zero
  IBindStatusCallback *pbsc,
                     //Pointer to callback interface
  IEnumFORMATETC *penumfmtetc,
                     //Pointer to enumerator object for formats
  IBindCtx **ppbc    //Address of output variable that receives the
                     // IBindCtx interface pointer
);

Parameters

dwReserved
[in] Reserved for future use; must be zero.
pbsc
[in] Pointer to the IBindStatusCallback interface used for receiving data availability and progress notification.
penumfmtetc
[in] Pointer to the IEnumFORMATETC interface that can be used to enumerate formats for format negotiation during binding. This parameter can be NULL, in which case the caller is not interested in format negotiation during binding, and the default format of the object will be bound to.
ppbc
[out] Address of IBindCtx* pointer variable that receives the interface pointer to the new bind context.

Return Values

S_OK
The operation completed successfully.
E_OUTOFMEMORY
The method ran out of memory and did not complete.
E_INVALIDARG
One or more parameters are invalid.

Remarks

This function automatically registers the IBindStatusCallback Interface and the IEnumFORMATETC interfaces with the bind context. The grfBSCOption parameter allows the client to specify flags indicating which callback notifications the client is capable of receiving. If the client does not wish to receive certain notification, it can choose to implement those callback methods as empty function stubs (returning E_NOTIMPL), and they should not be called.

The RegisterBindStatusCallback function can also be used to register callback interfaces in the bind context.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in urlmon.h.
Library: Use urlmon.lib.

See Also

IBindStatusCallback Interface, BSCO_OPTION, RegisterBindStatusCallback Function


CreateBindCtx

Supplies a pointer to an implementation of IBindCtx (a bind context object). This object stores information about a particular moniker-binding operation. The pointer this function supplies is required as a parameter in many methods of the IMoniker interface and in certain functions related to monikers.

WINOLEAPI CreateBindCtx(
  DWORD reserved, //Reserved for future use
  LPBC FAR* ppbc  //Address of output variable that receives the
                  // IBindCtx interface pointer
);

Parameters

reserved
[in] Reserved for future use; must be zero.
ppbc
[out] Address of IBindCtx* pointer variable that receives the interface pointer to the new bind context object. When the function is successful, the caller is responsible for calling IUnknown::Release on the bind context. A NULL value for the bind context indicates that an error occurred.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The bind context was allocated and initialized successfully.

Remarks

CreateBindCtx is most commonly used in the process of binding a moniker (locating and getting a pointer to an interface by identifying it through a moniker), as in the following steps:

  1. Get a pointer to a bind context by calling the CreateBindCtx function.
  2. Call the IMoniker::BindToObject method on the moniker, retrieving an interface pointer to the object to which the moniker refers.
  3. Release the bind context.
  4. Use the interface pointer.
  5. Release the interface pointer.

The following code fragment illustrates these steps:

// pMnk is an IMoniker * that points to a previously acquired moniker
IFoo *pFoo;
IBindCtx *pbc;

CreateBindCtx( 0, &pbc );
pMnk->BindToObject( pbc, NULL, IID_IFoo, &pFoo );
pbc->Release();
// pFoo now points to the object; safe to use pFoo
pFoo->Release();

Bind contexts are also used in other methods of the IMoniker interface besides IMoniker::BindToObject and in the MkParseDisplayName function.

A bind context retains references to the objects that are bound during the binding operation, causing the bound objects to remain active (keeping the object's server running) until the bind context is released. Reusing a bind context when subsequent operations bind to the same object can improve performance. You should, however, release the bind context as soon as possible, because you could be keeping the objects activated unnecessarily.

A bind context contains a BIND_OPTS structure, which contains parameters that apply to all steps in a binding operation. When you create a bind context using CreateBindCtx, the fields of the BIND_OPTS structure are initialized to the following values:

cbStruct = sizeof(BIND_OPTS)
grfFlags = 0
grfMode = STGM_READWRITE
dwTickCountDeadline = 0.

You can call the IBindCtx::SetBindOptions method to modify these default values.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

BIND_OPTS, IBindCtx, IMoniker, MkParseDisplayName


CreateClassMoniker

Creates a class moniker that refers to the given class.

WINOLEAPI CreateClassMoniker(
  REFCLSID rclsid, //Class this moniker binds to
  IMoniker **ppmk  //Address of output variable that receives the
                   // IMoniker interface pointer
);

Parameters

rclsid
[in] Reference to the CLSID of the object type to which this moniker binds.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the new class moniker. On successful return, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. When an error occurs, the value of the moniker pointer is NULL.

Return Values

S_OK
The moniker has been created successfully.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The class moniker will supports binding to a fresh instance of the class identified by the CLSID in rclsid.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker - Class Moniker Implementation


CreateFileMoniker

Creates a file moniker based on the specified path.

WINOLEAPI CreateFileMoniker(
  LPCOLESTR lpszPathName,  //Pointer to path to be used
  LPMONIKER FAR *ppmk  //Address of output variable that receives
                       // the IMoniker interface pointer
);

Parameters

lpszPathName
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the path on which this moniker is based.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the new file moniker. When successful, the function has called IUnknown::AddRef on the file moniker and the caller is responsible for calling IUnknown::Release. When an error occurs, the value of the interface pointer is NULL.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The moniker has been created successfully.
MK_E_SYNTAX
Error in the syntax of a path was encountered while creating a moniker.

Remarks

CreateFileMoniker creates a moniker for an object that is stored in a file. A moniker provider (an object that provides monikers to other objects) can call this function to create a moniker to identify a file-based object that it controls, and can then make the pointer to this moniker available to other objects. An object identified by a file moniker must also implement the IPersistFile interface so it can be loaded when a file moniker is bound.

When each object resides in its own file, as in an OLE server application that supports linking only to file-based documents in their entirety, file monikers are the only type of moniker necessary. To identify objects smaller than a file, the moniker provider must use another type of moniker (such as an item moniker) in addition to file monikers, creating a composite moniker. Composite monikers would be needed in an OLE server application that supports linking to objects smaller than a document (such as sections of a document or embedded objects).

The lpszPathName can be a relative path, a UNC path (e.g., \\server\share\path), or a drive-letter-based path (e.g., c:\). If based on a relative path, the resulting moniker must be composed onto another file moniker before it can be bound.

A file moniker can be composed to the right only of another file moniker when the first moniker is based on an absolute path and the other is a relative path, resulting in a single file moniker based on the combination of the two paths. A moniker composed to the right of another moniker must be a refinement of that moniker, and the file moniker represents the largest unit of storage. To identify objects stored within a file, you would compose other types of monikers (usually item monikers) to the right of a file moniker.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker - File Moniker Implementation


CreateGenericComposite

Performs a generic composition of two monikers and supplies a pointer to the resulting composite moniker.

WINOLEAPI CreateGenericComposite(
  LPMONIKER pmkFirst, //Pointer to the first moniker
  LPMONIKER pmkRest,  //Pointer to the second moniker
  LPMONIKER FAR *ppmkComposite
                      //Address of output variable that receives the
                      // IMoniker interface pointer
);

Parameters

pmkFirst
[in] Pointer to the moniker to be composed to the left of the moniker that pmkRest points to. Can point to any kind of moniker, including a generic composite.
pmkRest
[in] Pointer to the moniker to be composed to the right of the moniker that pmkFirst points to. Can point to any kind of moniker compatible with the type of the pmkRest moniker, including a generic composite.
ppmkComposite
[out] Address of IMoniker* pointer variable that receives the interface pointer to the composite moniker object that is the result of composing pmkFirst and pmkRest. This object supports the OLE composite moniker implementation of IMoniker. When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. If either pmkFirst or pmkRest are NULL, the supplied pointer is the one that is non-NULL. If both pmkFirst and pmkRest are NULL, or if an error occurs, the returned pointer is NULL.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The two input monikers were successfully composed.
MK_E_SYNTAX
The two monikers could not be composed due to an error in the syntax of a path (for example, if both pmkFirst and pmkRest are file monikers based on absolute paths).

Remarks

CreateGenericComposite joins two monikers into one. The moniker classes being joined can be different, subject only to the rules of composition. Call this function only if you are writing a new moniker class by implementing the IMoniker interface, within an implementation of IMoniker::ComposeWith that includes generic composition capability.

Moniker providers should call IMoniker::ComposeWith to compose two monikers together. Implementations of ComposeWith should (as do OLE implementations) attempt, when reasonable for the class, to perform non-generic compositions first, in which two monikers of the same class are combined. If this is not possible, the implementation can call CreateGenericComposite to do a generic composition, which combines two monikers of different classes, within the rules of composition. You can define new types of non-generic compositions if you write a new moniker class.

During the process of composing the two monikers, CreateGenericComposite makes all possible simplifications. Consider the example where pmkFirst is the generic composite moniker, A + B + C, and pmkRest is the generic composite moniker, C -1 + B -1 + Z (where C -1 is the inverse of C). The function first composes C to C -1, which composes to nothing. Then it composes B and B -1 to nothing. Finally, it composes A to Z, and supplies a pointer to the generic composite moniker, A + Z.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::ComposeWith, IMoniker - Generic Composite Moniker Implementation


CreateItemMoniker

Creates an item moniker that identifies an object within a containing object (typically a compound document).

WINOLEAPI CreateItemMoniker(
  LPCOLESTR lpszDelim, //Pointer to delimiter string
  LPCOLESTR lpszItem,  //Pointer to item name
  LPMONIKER FAR *ppmk  //Address of output variable that receives
                       // the IMoniker interface pointer
);

Parameters

lpszDelim
[in] Pointer to a wide character string (two bytes per character) zero-terminated string containing the delimiter (typically "!") used to separate this item's display name from the display name of its containing object.
lpszItem
[in] Pointer to a zero-terminated string indicating the containing object's name for the object being identified. This name can later be used to retrieve a pointer to the object in a call to IOleItemContainer::GetObject.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the item moniker. When successful, the function has called IUnknown::AddRef on the item moniker and the caller is responsible for calling IUnknown::Release. If an error occurs, the supplied interface pointer has a NULL value.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The moniker was created successfully.

Remarks

A moniker provider, which hands out monikers to identify its objects so they are accessible to other parties, would call CreateItemMoniker to identify its objects with item monikers. Item monikers are based on a string, and identify objects that are contained within another object and can be individually identified using a string. The containing object must also implement the IOleContainer interface.

Most moniker providers are OLE applications that support linking. Applications that support linking to objects smaller than file-based documents, such as a server application that allows linking to a selection within a document, should use item monikers to identify the objects. Container applications that allow linking to embedded objects use item monikers to identify the embedded objects.

The lpszItem parameter is the name used by the document to uniquely identify the object. For example, if the object being identified is a cell range in a spreadsheet, an appropriate name might be something like "A1:E7." An appropriate name when the object being identified is an embedded object might be something like "embedobj1." The containing object must provide an implementation of the IOleItemContainer interface that can interpret this name and locate the corresponding object. This allows the item moniker to be bound to the object it identifies.

Item monikers are not used in isolation. They must be composed with a moniker that identifies the containing object as well. For example, if the object being identified is a cell range contained in a file-based document, the item moniker identifying that object must be composed with the file moniker identifying that document, resulting in a composite moniker that is the equivalent of "C:\work\sales.xls!A1:E7."

Nested containers are allowed also, as in the case where an object is contained within an embedded object inside another document. The complete moniker of such an object would be the equivalent of "C:\work\report.doc!embedobj1!A1:E7." In this case, each containing object must call CreateItemMoniker and provide its own implementation of the IOleItemContainer interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::ComposeWith, IOleItemContainer, IMoniker - Item Moniker Implementation


CreateObjrefMoniker

Creates an OBJREF moniker based on a pointer to an object.

WINOLEAPI CreateObjrefMoniker(
    LPUNKNOWN pUnk,    //Pointer to the object
    LPMONIKER *ppMk    //Address of pointer to OBJREF moniker
);

Parameters

pUnk
Pointer to the IUnknown interface on the object that the moniker is to represent.
ppMk
Address of a pointer to the IMoniker interface on the OBJREF moniker created.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The OBJREF moniker was successfully created.

Remarks

Clients use OBJREF monikers to obtain a marshaled pointer to a running object in the server’s address space.

The server typically calls CreateObjrefMoniker to create an OBJREF moniker and then calls IMoniker::GetDisplayName, and finally releases the moniker. The display name for an OBJREF moniker is of the form:

OBJREF:nnnnnnnn

Where nnnnnnnn is an arbitrarily long base-64 encoding that encapsulates the machine location, process endpoint, and interface pointer ID (IPID) of the running object.

The display name can then be transferred to the client as text. For example, the display name can reside on an HTML page that the client downloads.

The client can pass the display name to MkParseDisplayName, which creates an OBJREF moniker based on the display name. A call to the moniker’s IMoniker::BindToObject method then obtains a marshaled pointer to the running instance on the server.

For example, a server-side COM component contained in an Active Server Page can create an OBJREF moniker, obtain its display name, and write the display name to the HTML output that is sent to the client browser. A script that runs on the client side can use the display name to get access to the running object itself. A client-side Visual Basic script, for instance, could store the display name in a variable called strMyName and include this line:

objMyInstance = GetObject(strMyName)

The script engine internally makes the calls to MkParseDisplayName and IMoniker::BindToObject, and the script can then use objMyInstance to refer directly to the running object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP4 or later.
Windows 95/98: Requires Windows 98 (or Windows 95 with DCOM).
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker - OBJREF Moniker Implementation


CreatePointerMoniker

Creates a pointer moniker based on a pointer to an object.

WINOLEAPI CreatePointerMoniker(
  LPUNKNOWN punk,      //Pointer to the interface to be used
  LPMONIKER FAR *ppmk  //Address of output variable that receives
                       // the IMoniker interface pointer
);

Parameters

punk
[in] Pointer to an IUnknown interface on the object to be identified by the resulting moniker.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the new pointer moniker. When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. When an error occurs, the returned interface pointer has a NULL value.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The pointer moniker was created successfully.

Remarks

A pointer moniker wraps an existing interface pointer in a moniker that can be passed to those interfaces that require monikers. Pointer monikers allow an object that has no persistent representation to participate in a moniker-binding operation.

Pointer monikers are not commonly used, so this function is not often called.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker - Pointer Moniker Implementation


DllCanUnloadNow

Determines whether the DLL that implements this function is in use. If not, the caller can safely unload the DLL from memory.

Note OLE does not provide this function. DLLs that support the OLE Component Object Model (COM) should implement and export DllCanUnloadNow.

STDAPI DllCanUnloadNow();

Return Values

S_OK
The DLL can be unloaded.
S_FALSE
The DLL cannot be unloaded now.

Remarks

A call to DllCanUnloadNow determines whether the DLL from which it is exported is still in use. A DLL is no longer in use when it is not managing any existing objects (the reference count on all of its objects is 0).

Notes to Callers

You should not have to call DllCanUnloadNow directly. OLE calls it only through a call to the CoFreeUnusedLibraries function. When it returns S_OK, CoFreeUnusedLibraries safely frees the DLL.

Notes to Implementers

You need to implement DllCanUnloadNow in, and export it from, DLLs that are to be dynamically loaded through a call to the CoGetClassObject function. (You also need to implement and export the DllGetClassObject function in the same DLL).

If a DLL loaded through a call to CoGetClassObject fails to export DllCanUnloadNow, the DLL will not be unloaded until the application calls the CoUninitialize function to release the OLE libraries.

If the DLL links to another DLL, returning S_OK from DllCanUnloadNow will also cause the second, dependent DLL to be unloaded. To eliminate the possibility of a crash, the primary DLL should call the CoLoadLibrary function, specifying the path to the second DLL as the first parameter, and setting the auto free parameter to TRUE. This forces the COM library to reload the second DLL and set it up for a call to CoFreeUnusedLibraries to free it separately when appropriate.

DllCanUnloadNow should return S_FALSE if there are any existing references to objects that the DLL manages.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Included as a resource in oleaut32.dll.

See Also

DllGetClassObject


DllGetClassObject

Retrieves the class object from a DLL object handler or object application. DllGetClassObject is called from within the CoGetClassObject function when the class context is a DLL.

Note OLE does not provide this function. DLLs that support the OLE Component Object Model (COM) must implement DllGetClassObject in OLE object handlers or DLL applications.

STDAPI DllGetClassObject(
  REFCLSID rclsid,  //CLSID for the class object
  REFIID riid,      //Reference to the identifier of the interface
                    // that communicates with the class object
  LPVOID * ppv      //Address of output variable that receives the
                    // interface pointer requested in riid
);

Parameters

rclsid
[in] CLSID that will associate the correct data and code.
riid
[in] Reference to the identifier of the interface that the caller is to use to communicate with the class object. Usually, this is IID_IClassFactory (defined in the OLE headers as the interface identifier for IClassFactory).
ppv
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppv contains the requested interface pointer. If an error occurs, the interface pointer is NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was retrieved successfully.
CLASS_E_CLASSNOTAVAILABLE
The DLL does not support the class (object definition).

Remarks

If a call to the CoGetClassObject function finds the class object that is to be loaded in a DLL, CoGetClassObject uses the DLL's exported DllGetClassObject function.

Notes to Callers

You should not call DllGetClassObject directly. When an object is defined in a DLL, CoGetClassObject calls the CoLoadLibrary function to load the DLL, which, in turn, calls DllGetClassObject.

Notes to Implementers

You need to implement DllGetClassObject in (and export it from) DLLs that support the OLE Component Object Model.

Example

Following is an example (in C++) of an implementation of DllGetClassObject. In this example, DllGetClassObject creates a class object and calls its QueryInterface method to retrieve a pointer to the interface requested in riid. The implementation safely releases the reference it holds to the IClassFactory interface because it returns a reference-counted pointer to IClassFactory to the caller.

HRESULT_export  PASCAL DllGetClassObject
        (REFCLSID rclsid, REFIID riid, LPVOID * ppvObj)
{
    HRESULT hres = E_OUTOFMEMORY;
    *ppvObj = NULL;

    CClassFactory *pClassFactory = new CClassFactory(rclsid);
    if (pClassFactory != NULL)   {
        hRes = pClassFactory->QueryInterface(riid, ppvObj);
        pClassFactory->Release();
    }
    return hRes;

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CoGetClassObject, DllCanUnloadNow


DllRegisterServer

Instructs an in-process server to create its registry entries for all classes supported in this server module. If this function fails, the state of the registry for all its classes is indeterminate.

STDAPI DllRegisterServer(void);

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The registry entries were created successfully.
SELFREG_E_TYPELIB
The server was unable to complete the registration of all the type libraries used by its classes.
SELFREG_E_CLASS
The server was unable to complete the registration of all the object classes.

Remarks

E_NOTIMPL is not a valid return code.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: User-defined.

See Also

DllUnregisterServer


DllUnregisterServer

Instructs an in-process server to remove only those entries created through DllRegisterServer.

STDAPI DllUnregisterServer(void);

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The registry entries were created successfully.
S_FALSE
Unregistration of this server's known entries was successful, but other entries still exist for this server's classes.
SELFREG_E_TYPELIB
The server was unable to remove the entries of all the type libraries used by its classes.
SELFREG_E_CLASS
The server was unable to remove the entries of all the object classes.

Remarks

The server must not disturb any entries that it did not create which currently exist for its object classes. For example, between registration and unregistration, the user may have specified a TreatAs relationship between this class and another. In that case, unregistration can remove all entries except the TreatAs key and any others that were not explicitly created in DllRegisterServer. The Win32 registry functions specifically disallow the deletion of an entire populated tree in the registry. The server can attempt, as the last step, to remove the CLSID key, but if other entries still exist, the key will remain.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: User-defined.

See Also

DllRegisterServer


FACILITY_NT_BIT

#define FACILITY_NT_BIT 0x10000000

Identifies an HRESULT as a Win32 status value. FACILITY_NT_BIT is used in conjuction with the HRESULT_FROM_NT macro.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


FAILED

#define FAILED(Status) ((HRESULT)(Status)<0)

Provides a generic test for failure on any status value. Negative numbers indicate failure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


GetClassFile

Supplies the CLSID associated with the given filename.

WINOLEAPI GetClassFile(
  LPCWSTR szFileName,
                  //Pointer to filename for which you are requesting
                  // a CLSID
  CLSID * pclsid  //Pointer to location for returning the CLSID
);

Parameters

szFileName
[in] Points to the filename for which you are requesting the associated CLSID.
pclsid
[out] Points to the location where the associated CLSID is written on return.

Return Values

S_OK
Indicates the CLSID was successfully supplied.
MK_E_CANTOPENFILE
Indicates unable to open the specified filename.
MK_E_INVALIDEXTENSION
Indicates the specified extension in the registry is invalid.

Note This function can also return any file system errors.

Remarks

When given a filename, the GetClassFile function finds the CLSID associated with that file. Examples of its use are in OleCreateFromFile, which is passed a file name and requires an associated CLSID, and in the OLE implementation of IMoniker::BindToObject, which, when a link to a file-based document is activated, calls GetClassFile to locate the object application that can open the file.

GetClassFile uses the following strategies to determine an appropriate CLSID:

  1. If the file contains a storage object, as determined by a call to the StgIsStorageFile function, GetClassFile returns the CLSID that was written with the IStorage::SetClass method.
  2. If the file is not a storage object, the GetClassFile function attempts to match various bits in the file against a pattern in the registry. A pattern in the registry can contain a series of entries of the form:
    regdb key = offset, cb, mask, value

    The value of the offset item is an offset from the beginning or end of the file and the cb item is a length in bytes. These two values represent a particular byte range in the file. (A negative value for the offset item is interpreted from the end of the file). The mask value is a bit mask that is used to perform a logical AND operation with the byte range specified by offset and cb. The result of the logical AND operation is compared with the value item. If the mask is omitted, it is assumed to be all ones.

    Each pattern in the registry is compared to the file in the order of the patterns in the database. The first pattern where each of the value items matches the result of the AND operation determines the CLSID of the file. For example, the pattern contained in the following entries of the registry requires that the first four bytes be AB CD 12 34 and that the last four bytes be FE FE FE FE:

    HKEY_CLASSES_ROOT
    FileType
        {12345678-0000-0001-C000-000000000095}
            0 = 0, 4, FFFFFFFF, ABCD1234
            1 = -4, 4, , FEFEFEFE

    If a file contains such a pattern, the CLSID {12345678-0000-0001-C000-000000000095} will be associated with this file.

  3. If the above strategies fail, the GetClassFile function searches for the File Extension key in the registry that corresponds to the .ext portion of the filename. If the database entry contains a valid CLSID, this function returns that CLSID.
  4. If all strategies fail, the function returns MK_E_INVALIDEXTENSION.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

WriteClassStg


GetRunningObjectTable

Supplies a pointer to the IRunningObjectTable interface on the local Running Object Table (ROT).

WINOLEAPI GetRunningObjectTable(
  DWORD reserved,  //Reserved
  LPRUNNINGOBJECTTABLE *pprot
                   //Address of output variable that receives the
                   // IRunningObjectTable interface pointer
);

Parameters

reserved
[in] Reserved for future use; must be zero.
pprot
[out] Address of IRunningObjectTable* pointer variable that receives the interface pointer to the local ROT. When the function is successful, the caller is responsible for calling IUnknown::Release on the interface pointer. If an error occurs, *pprot is undefined.

Return Values

This function supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
An IRunningObjectTable pointer was successfully returned.

Remarks

Each workstation has a local ROT that maintains a table of the objects that have been registered as running on that machine. This function returns an IRunningObjectTable interface pointer, which provides access to that table.

Moniker providers, which hand out monikers that identify objects so they are accessible to others, should call GetRunningObjectTable. Use the interface pointer returned by this function to register your objects when they begin running, to record the times that those objects are modified, and to revoke their registrations when they stop running. See the IRunningObjectTable interface for more information.

Compound-document link sources are the most common example of moniker providers. These include server applications that support linking to their documents (or portions of a document) and container applications that support linking to embeddings within their documents. Server applications that do not support linking can also use the ROT to cooperate with container applications that support linking to embeddings.

If you are implementing the IMoniker interface to write a new moniker class, and you need an interface pointer to the ROT, call IBindCtx::GetRunningObjectTable rather than the GetRunningObjectTable function. This allows future implementations of the IBindCtx interface to modify binding behavior.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IBindCtx::GetRunningObjectTable, IMoniker, IRunningObjectTable


GetScode

#define GetScode(hr) ((SCODE) (hr))

Extracts the SCODE from an HRESULT.

This macro is obsolete and should not be used.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


HRESULT_CODE

#define HRESULT_CODE(hr) ((hr) & 0xFFFF)

Returns the error code part of the HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


HRESULT_FACILITY

#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff)

Returns the facility from the HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


HRESULT_FROM_NT

#define HRESULT_FROM_NT(x) ((HRESULT) ((x) | FACILITY_NT_BIT))

Maps an NT status value into an HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


HRESULT_FROM_WIN32

#define HRESULT_FROM_WIN32(x) (x ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)) : 0 )

Maps a WIN32 error value into an HRESULT. Note that this assumes WIN32 errors fall in the range of -32k to 32k.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


HRESULT_SEVERITY

#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1)

Returns the severity bit from the HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


IIDFromString

Converts a string generated by the StringFromIID function back into the original interface identifier (IID).

WINOLEAPI IIDFromString(
  LPOLESTR lpsz,  //Pointer to the string representation of the IID
  LPIID lpiid     //Pointer to the requested IID on return
);

Parameters

lpsz
[in] Pointer to the string representation of the IID.
lpiid
[out] Pointer to the requested IID on return.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The string was successfully converted.

Remarks

The function converts the interface identifier in a way that guarantees different interface identifiers will always be converted to different strings.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

StringFromIID


IsAccelerator

Determines whether the keystroke maps to an accelerator in the given accelerator table.

BOOL IsAccelerator(
  HACCEL hAccel,      //Handle to accelerator table
  INT cAccelEntries,  //Number of entries in the accelerator table
  LPMSG lpMsg,        //Pointer to the keystroke message to be
                      // translated
  WORD * lpwCmd       //Pointer to return the corresponding command
                      // identifier
);

Parameters

hAccel
[in] Handle to the accelerator table.
cAccelEntries
[in] Number of entries in the accelerator table.
lpMsg
[in] Pointer to the keystroke message to be translated.
lpwCmd
[out] Pointer to where to return the corresponding command identifier if there is an accelerator for the keystroke. It may be NULL.

Return Values

TRUE
The message is for the object application.
FALSE
The message is not for the object and should be forwarded to the container.

Remarks

While an object is active in-place, the object always has first chance to translate the keystrokes into accelerators. If the keystroke corresponds to one of its accelerators, the object must not call the OleTranslateAccelerator function — even if its call to the Windows TranslateAccelerator function fails. Failure to process keystrokes in this manner can lead to inconsistent behavior.

If the keystroke is not one of the object's accelerators, then the object must call OleTranslateAccelerator to let the container try its accelerator translation.

The object's server can call IsAccelerator to determine if the accelerator message belongs to it. Some servers do accelerator translation on their own and do not call TranslateAccelerator. Those applications will not call IsAccelerator, because they already have the information.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleTranslateAccelerator, TranslateAccelerator in Win32


IS_ERROR

#define IS_ERROR(Status) ((unsigned long)(Status) >> 31 == SEVERITY_ERROR)

Provides a generic test for errors on any status value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


IsEqualGUID

Determines whether two GUIDs are equal.

BOOL IsEqualGUID(
  REFGUID rguid1, //GUID to compare to rguid2
  REFGUID rguid2  //GUID to compare to rguid1
);

Parameters

rguid1
[in] GUID to compare to rguid2.
rguid2
[in] GUID to compare to rguid1.

Return Values

TRUE
The GUIDs are equal.
FALSE
The GUIDs are not equal.

Remarks

IsEqualGUID is used by the IsEqualCLSID and IsEqualIID functions.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IsEqualCLSID, IsEqualIID


IsEqualCLSID

Determines whether two CLSIDs are equal.

BOOL IsEqualCLSID(
  REFCLSID rclsid1, //CLSID to compare to rclsid2
  REFCLSID rclsid2  //CLSID to compare to rclsid1
);

Parameters

rclsid1
[in] CLSID to compare to rclsid2.
rclsid2
[in] CLSID to compare to rclsid1.

Return Values

TRUE
The CLSIDs are equal.
FALSE
The CLSIDs are not equal.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.

See Also

IsEqualGUID, IsEqualIID


IsEqualIID

Determines whether two interface identifiers are equal.

BOOL IsEqualIID(
  REFGUID riid1, //Interface identifier to compare to riid2
  REFGUID riid2  //Interface identifier to compare to riid1
);

Parameters

riid1
[in] Interface identifier to compare with riid2.
riid2
[in] Interface identifier to compare with riid1.

Return Values

TRUE
The interface identifiers are equal.
FALSE
The interface identifiers are not equal.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.

See Also

IsEqualGUID, IsEqualCLSID


IsValidIid

This function is obsolete.


IsValidInterface

This function is obsolete.


IsValidPtrIn

This function is obsolete.


IsValidPtrOut

This function is obsolete.


MAKE_HRESULT

#define MAKE_HRESULT(sev,fac,code)\
((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) \
| ((unsigned long)(code))) )

Creates an HRESULT value from component pieces of the 32-bit value.

Parameters

Sev
Severity code.
Value | Definition
00 | Success
01 | Informational
10 | Warning
11 | Error
Fac
The facility code.
#define FACILITY_WINDOWS                 8
#define FACILITY_URT                     19
#define FACILITY_STORAGE                 3
#define FACILITY_SSPI                    9
#define FACILITY_SCARD                   16
#define FACILITY_SETUPAPI                15
#define FACILITY_SECURITY                9
#define FACILITY_RPC                     1
#define FACILITY_WIN32                   7
#define FACILITY_CONTROL                 10
#define FACILITY_NULL                    0
#define FACILITY_MSMQ                    14
#define FACILITY_MEDIASERVER             13
#define FACILITY_INTERNET                12
#define FACILITY_ITF                     4
#define FACILITY_DISPATCH                2
#define FACILITY_COMPLUS                 17
#define FACILITY_CERT                    11
#define FACILITY_ACS                     20
#define FACILITY_AAF                     18
Code
The facility's status code.

See Also

Structure of COM Error Codes

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


MAKE_SCODE

#define MAKE_SCODE(sev,fac,code)\
((SCODE) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) \
| ((unsigned long)(code))) )

Returns an SCODE given an HRESULT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


MkParseDisplayName

Converts a string into a moniker that identifies the object named by the string. This is the inverse of the IMoniker::GetDisplayName operation, which retrieves the display name associated with a moniker.

WINOLEAPI MkParseDisplayName(
  LPBC pbc,             //Pointer to the bind context object
  LPCOLESTR szUserName, //Pointer to display name
  ULONG FAR *pchEaten,  //Pointer to the number of characters
                        // consumed
  LPMONIKER FAR *ppmk   //Address of output variable that receives
                        // the IMoniker interface pointer
);

Parameters

pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used in this binding operation.
szUserName
[in] Pointer to a zero-terminated wide character string (two bytes per character) containing the display name to be parsed.
pchEaten
[out] Pointer to the number of characters of szUserName that were consumed. If the function is successful, *pchEaten is the length of szUserName; otherwise, it is the number of characters successfully parsed.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the moniker that was built from szUserName. When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. If an error occurs, the supplied interface pointer value is NULL.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The parse operation was successful and the moniker was created.
MK_E_SYNTAX
Error in the syntax of a file name or an error in the syntax of the resulting composite moniker.

This function can also return any of the error values returned by IMoniker::BindToObject, IOleItemContainer::GetObject, or IParseDisplayName::ParseDisplayName.

Remarks

The MkParseDisplayName function parses a human-readable name into a moniker that can be used to identify a link source. The resulting moniker can be a simple moniker (such as a file moniker), or it can be a generic composite made up of the component moniker pieces. For example, the following display name:

"c:\mydir\somefile!item 1"

could be parsed into the following generic composite moniker:

(FileMoniker based on "c:\mydir\somefile") + (ItemMoniker based on "item 1")

The most common use of MkParseDisplayName is in the implementation of the standard Links dialog box, which allows an end user to specify the source of a linked object by typing in a string. You may also need to call MkParseDisplayName if your application supports a macro language that permits remote references (reference to elements outside of the document).

Parsing a display name often requires activating the same objects that would be activated during a binding operation, so it can be just as expensive (in terms of performance) as binding. Objects that are bound during the parsing operation are cached in the bind context passed to the function. If you plan to bind the moniker returned by MkParseDisplayName, it is best to do so immediately after the function returns, using the same bind context, which removes the need to activate objects a second time.

MkParseDisplayName parses as much of the display name as it understands into a moniker. The function then calls IMoniker::ParseDisplayName on the newly created moniker, passing the remainder of the display name. The moniker returned by IMoniker::ParseDisplayName is composed onto the end of the existing moniker and, if any of the display name remains unparsed, IMoniker::ParseDisplayName is called on the result of the composition. This process is repeated until the entire display name has been parsed.

The MkParseDisplayName function attempts the following strategies to parse the beginning of the display name, using the first one that succeeds:

  1. The function looks in the Running Object Table for file monikers corresponding to all prefixes of szDisplayName that consist solely of valid file name characters. This strategy can identify documents that are as yet unsaved.
  2. The function checks the maximal prefix of szDisplayName, which consists solely of valid file name characters, to see if an OLE 1 document is registered by that name (this may require some DDE broadcasts). In this case, the returned moniker is an internal moniker provided by the OLE 1 compatibility layer of OLE 2.
  3. The function consults the file system to check whether a prefix of szDisplayName matches an existing file. The file name can be drive-absolute, drive-relative, working-directory relative, or begin with an explicit network share name. This is the common case.
  4. If the initial character of szDisplayName is an '@', the function finds the longest string immediately following it that conforms to the legal ProgID syntax. The function converts this string to a CLSID using the CLSIDFromProgID function. If the CLSID represents an OLE 2 class, the function loads the corresponding class object and asks for an IParseDisplayName interface pointer. The resulting IParseDisplayName interface is then given the whole string to parse, starting with the '@'. If the CLSID represents an OLE 1 class, then the function treats the string following the ProgID as an OLE1/DDE link designator having <filename>!<item> syntax.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::ParseDisplayName, IMoniker::GetDisplayName, IParseDisplayName


MonikerCommonPrefixWith

Creates a new moniker based on the common prefix that this moniker (the one comprising the data of this moniker object) shares with another moniker. This function is intended to be called only in implementations of IMoniker::CommonPrefixWith.

WINOLEAPI MonikerCommonPrefixWith(
  LPMONIKER pmkThis,  //Pointer to the first moniker being compared
  LPMONIKER pmkOther, //Pointer to the second moniker being compared
  LPMONIKER FAR *ppmkCommon
                      //Address of output variable that receives the
                      // IMoniker interface pointer
);

Parameters

pmkThis
[in] Pointer to the IMoniker interface on one of the monikers for which a common prefix is sought; usually the moniker in which this call is used to implement IMoniker::CommonPrefixWith.
pmkOther
[in] Pointer to the IMoniker interface on the other moniker to compare with the first moniker.
ppmkCommon
[out] Address of IMoniker* pointer variable that receives the interface pointer to the moniker based on the common prefix of pmkThis and pmkOther. When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. If an error occurs, the supplied interface pointer value is NULL.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
A common prefix exists that is neither pmkThis nor pmkOther.
MK_S_HIM
The entire pmkOther moniker is a prefix of the pmkThis moniker.
MK_S_ME
The entire pmkThis moniker is a prefix of the pmkOther moniker.
MK_S_US
The pmkThis and pmkOther monikers are equal.
MK_E_NOPREFIX
The monikers have no common prefix.
MK_E_NOTBINDABLE
This function was called on a relative moniker. It is not meaningful to take the common prefix of relative monikers.

Remarks

Call MonikerCommonPrefixWith only in the implementation of IMoniker::CommonPrefixWith for a new moniker class.

Your implementation of IMoniker::CommonPrefixWith should first check whether the other moniker is of a type that you recognize and handle in a special way. If not, you should call MonikerCommonPrefixWith, passing itself as pmkThis and the other moniker as pmkOther. MonikerCommonPrefixWith correctly handles the cases where either moniker is a generic composite.

You should call this function only if pmkThis and pmkOther are both absolute monikers (where an absolute moniker is either a file moniker or a generic composite whose leftmost component is a file moniker, and where the file moniker represents an absolute path). Do not call this function on relative monikers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::CommonPrefixWith


MonikerRelativePathTo

Provides a moniker that, when composed onto the end of the first specified moniker (or one with a similar structure), yields the second specified moniker. This function is intended for use only by IMoniker::RelativePathTo implementations.

WINOLEAPI MonikerRelativePathTo(
  LPMONIKER pmkSrc,  //Pointer to the source identified by the
                     // moniker
  LPMONIKER pmkDest, //Pointer to the destination identified by the
                     // moniker
  LPMONIKER FAR * ppmkRelPath,
                     //Address of output variable that receives the
                     // IMoniker interface pointer
  BOOL dwReserved    //Reserved; must be non-zero
);

Parameters

pmkSrc
[in] Pointer to the IMoniker interface on the moniker that, when composed with the relative moniker to be created, produces pmkDest. This moniker identifies the "source" of the relative moniker to be created.
pmkDest
[in] Pointer to the IMoniker interface on the moniker to be expressed relative to pmkSrc. This moniker identifies the destination of the relative moniker to be created.
ppmkRelPath
[out] Address of IMoniker* pointer variable that receives the interface pointer to the new relative moniker. When successful, the function has called IUnknown::AddRef on the moniker and the caller is responsible for calling IUnknown::Release. If an error occurs, the interface pointer value is NULL.
dwReserved
[in] Reserved; must be non-zero.

Return Values

This function supports the standard return value E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
A meaningful relative path has been returned.
MK_S_HIM
The only form of the relative path is the other moniker.
MK_E_NOTBINDABLE
Indicates that pmkSrc is a relative moniker, such as an item moniker, and must be composed with the moniker of its container before a relative path can be determined.

Remarks

Call MonikerRelativePathTo only in the implementation of IMoniker::RelativePathTo if you are implementing a new moniker class.

Your implementation of IMoniker::RelativePathTo should first check whether the other moniker is of a type you recognize and handle in a special way. If not, you should call MonikerRelativePathTo, passing itself as pmkThis and the other moniker as pmkOther. MonikerRelativePathTo correctly handles the cases where either moniker is a generic composite.

You should call this function only if pmkSrc and pmkDest are both absolute monikers, where an absolute moniker is either a file moniker or a generic composite whose leftmost component is a file moniker, and where the file moniker represents an absolute path. Do not call this function on relative monikers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IMoniker::RelativePathTo


OleDoAutoConvert

Automatically converts an object to a new class if automatic conversion for that object class is set in the registry.

WINOLEAPI OleDoAutoConvert(
  IStorage * pStg,   //Pointer to storage object to be converted
  LPCLSID pClsidNew  //Pointer to new CLSID of converted object
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object to be converted.
pClsidNew
[out] Pointserto the new CLSID for the object being converted. If there was no automatic conversion, this may be the same as the original class.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
No conversion is needed or a conversion was successfully completed.
REGDB_E_KEYMISSING
The function cannot read a key from the registry.

This function can also return any of the error values returned by the OleGetAutoConvert function. When accessing storage and stream objects, see the IStorage::OpenStorage and IStorage::OpenStream methods for possible errors. When it is not possible to determine the existing CLSID or when it is not possible to update the storage object with new information, see the IStream interface for other error return values.

Remarks

The OleDoAutoConvert function automatically converts an object if automatic conversion has previously been specified in the registry by the OleSetAutoConvert function. Object conversion means that the object is permanently associated with a new CLSID. Automatic conversion is typically specified by the setup program for a new version of an object application, so that objects created by its older versions can be automatically updated.

A container application that supports object conversion should call OleDoAutoConvert each time it loads an object. If the container uses the OleLoad helper function, it need not call OleDoAutoConvert explicitly because OleLoad calls it internally.

OleDoAutoConvert first determines whether any conversion is required by calling the OleGetAutoConvert function, which, if no conversion is required, returns S_OK. If the object requires conversion, OleDoAutoConvert modifies and converts the storage object by activating the new object application. The new object application reads the existing data format, but saves the object in the new native format for the object application.

If the object to be automatically converted is an OLE 1 object, the ItemName string is stored in a stream called "\1Ole10ItemName." If this stream does not exist, the object's item name is NULL.

The storage object must be in the unloaded state when OleDoAutoConvert is called.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleSetAutoConvert


OleGetAutoConvert

Determines whether the registry is set for objects of a specified CLSID to be automatically converted to another CLSID, and if so, retrieves the new CLSID.

WINOLEAPI OleGetAutoConvert(
  REFCLSID clsidOld,   //CLSID of the object to be converted
  LPCLSID pClsidNew    //Pointer to new CLSID for object being
                       // converted
);

Parameters

clsidOld
[in] CLSID for an object to determine whether that CLSID is set for automatic conversion.
pClsidNew
[out] Pointer to where the new CLSID, if any, is written. If auto-conversion for clsidOld is not set in the registry, clsidOld is written to that location. The pClsidNew parameter is never NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
IA value was successfully returned through the pclsidNew parameter.
REGDB_E_CLASSNOTREG
The clsidOld CLSID is not properly registered in the registry.
REGDB_E_READREGDB
Error reading the registry.
REGDB_E_KEYMISSING
Auto-convert is not active or there was no registry entry for the clsidOld parameter.

Remarks

The OleGetAutoConvert function returns the AutoConvertTo entry in the registry for the specified object. The AutoConvertTo subkey specifies whether objects of a given CLSID are to be automatically converted to a new CLSID. This is usually used to convert files created by older versions of an application to the current version. If there is no AutoConvertTo entry, this function returns the value of clsidOld.

The OleDoAutoConvert function calls OleGetAutoConvert to determine if the object specified is to be converted. A container application that supports object conversion should call OleDoAutoConvert each time it loads an object. If the container uses the OleLoad helper function, it need not call OleDoAutoConvert explicitly because OleLoad calls it internally.

To set up automatic conversion of a given class, you can call the OleSetAutoConvert function (typically in the setup program of an application installation). This function uses the AutoConvertTo subkey to tag a class of objects for automatic conversion to a different class of objects. This is a subkey of the CLSID key, and contains the following information:

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleSetAutoConvert, OleDoAutoConvert


OleGetIconOfClass

Returns a handle to a metafile containing an icon and a string label for the specified CLSID.

HGLOBAL OleGetIconOfClass(
  REFCLSID rclsid,     //CLSID for which information is requested
  LPOLESTR lpszLabel,  //Pointer to string to use as label for icon
  BOOL fUseTypeAsLabel //Whether to use CLSID's user type name as
                       // icon label
);

Parameters

rclsid
[in] CLSID for which the icon and string are requested.
lpszLabel
[in] Pointer to a string to use as a label for the icon.
fUseTypeAsLabel
[in] Whether or not to use the user type string in the CLSID as the icon label.

Return Value

HGLOBAL
The hGlobal value returned when the function succeeds is a handle to a metafile that contains an icon and label for the specified CLSID. If the CLSID cannot be found in the registration database, NULL is returned.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleGetIconOfFile, OleMetafilePictFromIconAndLabel


OleGetIconOfFile

Returns a handle to a metafile containing an icon and string label for the specified file name.

HGLOBAL OleGetIconOfFile(
  LPOLESTR lpszPath,    //Pointer to string that specifies the file
                        // for which info is requested
  BOOL fUseFileAsLabel  //Whether to use the file name as the icon
                        // label
);

Parameters

lpszPath
[in] Pointer to a file for which the icon and string are requested.
fUseFileAsLabel
[in] Whether or not to use the file name as the icon label.

Return Value

HGLOBAL
The hGlobal returned is a handle to a metafile that contains an icon and label for the specified file. If there is no CLSID in the registration database for the file, then the string "Document" is used. If the value of lpszPath is NULL, then NULL is returned.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleGetIconOfClass, OleMetafilePictFromIconAndLabel


OleIconToCursor

Converts an icon to a cursor. For Win32 applications, this function calls the Win32 function CopyCursor(hIcon).

STDAPI OleIconToCursor(
  HINSTANCE hinstExe,  //Ignored in Win32
  HICON hIcon          //Handle to the icon
);

Parameters

hinstExe
[in] Ignored in Win32.
hIcon
[in] Handle to the icon to be converted to a cursor.

Remarks

The return value is an HCURSOR for the new cursor object. The caller is responsible for deleting this cursor with the Win32 function DestroyCursor. If the conversion could not be completed, the return value is NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: Included as a resource in olepro32.dll.


OleRegGetMiscStatus

Gets miscellaneous information about the presentation and behaviors supported by the specified CLSID from the registry. Used by developers of custom DLL object applications to emulate the behavior of the OLE default handler.

WINOLEAPI OleRegGetMiscStatus(
  REFCLSID clsid,    //Class identifier
  DWORD dwAspect,    //Value specifying aspect of requested class
  DWORD * pdwStatus  //Pointer to returned status information
);

Parameters

clsid
[in] CLSID of the class for which status information is requested.
dwAspect
[in] DWORD specifying the presentation aspect of the class for which information is requested. Values are taken from the DVASPECT enumeration.
pdwStatus
[out] Pointer to the location of the status information on the function's return.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The status information was returned successfully.
REGDB_E_CLASSNOTREG
No CLSID is registered for the class object.
REGDB_E_READREGDB
An error occurred reading the registry.
OLE_E_REGDB_KEY
The GetMiscStatus key is missing from the registry.

Remarks

Object applications can ask OLE to get miscellaneous status information in one of two ways. One way is to call OleRegGetMiscStatus. The other is to return OLE_S_USEREG in response to calls by the default object handler to IOleObject::GetMiscStatus. OLE_S_USEREG instructs the default handler to call OleRegGetMiscStatus. Because DLL object applications cannot return OLE_S_USEREG, they must call OleRegGetMiscStatus rather than delegating the job to the object handler.

OleRegGetMiscStatus and its sibling functions, OleRegGetUserType, OleRegEnumFormatEtc, and OleRegEnumVerbs, provide a way for developers of custom DLL object applications to emulate the behavior of OLE's default object handler in getting information about objects from the registry. By using these functions, you avoid the considerable work of writing your own, and the pitfalls inherent in working directly in the registry. In addition, you get future enhancements and optimizations of these functions without having to code them yourself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

DVASPECT, FORMATETC, OLEMISC, IOleObject::GetMiscStatus, OleRegEnumFormatEtc, OleRegEnumVerbs, OleRegGetUserType


OleRegGetUserType

Gets the user type of the specified class from the registry. Developers of custom DLL object applications use this function to emulate the behavior of the OLE default handler.

WINOLEAPI OleRegGetUserType(
  REFCLSID clsid,         //Class identifier
  DWORD dwFormOfType,     //Specifies form of type name
  LPOLESTR * pszUserType  //Pointer to storage of string pointer
);

Parameters

clsid
[in] Class identifier for which user type is requested.
dwFormOfType
[in] Value that describes the form of the user-presentable string from the enumeration USERCLASSTYPE.
pszUserType
[out] Pointer to a string that stores the user type on return.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The user type was returned successfully.
REGDB_E_CLASSNOTREG
There is no CLSID registered for the class object.
REGDB_E_READREGDB
There was an error reading the registry.
OLE_E_REGDB_KEY
The ProgID = MainUserTypeName and CLSID = MainUserTypeName keys are missing from the registry.

Remarks

Object applications can ask OLE to get the user type name of a specified class in one of two ways. One way is to call OleRegGetUserType. The other is to return OLE_S_USEREG in response to calls by the default object handler to IOleObject::GetUserType. OLE_S_USEREG instructs the default handler to call OleRegGetUserType. Because DLL object applications cannot return OLE_S_USEREG, they must call OleRegGetUserType, rather than delegating the job to the object handler.

The OleRegGetUserType function and its sibling functions, OleRegGetMiscStatus, OleRegEnumFormatEtc, and OleRegEnumVerbs, provide a way for developers of custom DLL object applications to emulate the behavior of OLE's default object handler in getting information about objects from the registry. By using these functions, you avoid the considerable work of writing your own, and the pitfalls inherent in working directly in the registry. In addition, you get future enhancements and optimizations of these functions without having to code them yourself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

IOleObject::GetUserType, OleRegGetMiscStatus, OleRegEnumFormatEtc, OleRegEnumVerbs, USERCLASSTYPE


OleSetAutoConvert

Specifies a CLSID for automatic conversion to a different class when an object of that class is loaded.

WINOLEAPI OleSetAutoConvert(
  REFCLSID clsidOld, //CLSID to be converted
  REFCLSID clsidNew  //New CLSID after conversion
);

Parameters

clsidOld
[in] CLSID of the object class to be converted.
clsidNew
[in] CLSID of the object class that should replace clsidOld. This new CLSID replaces any existing auto-conversion information in the registry for clsidOld. If this value is CLSID_NULL, any existing auto-conversion information for clsidOld is removed from the registry.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was tagged successfully.
REGDB_E_CLASSNOTREG
The CLSID is not properly registered in the registry.
REGDB_E_READREGDB
Error reading from the registry.
REGDB_E_WRITEREGDB
Error writing to the registry.
REGDB_E_KEYMISSING
Cannot read a key from the registry.

Remarks

The OleSetAutoConvert function goes to the system registry, finds the AutoConvertTo subkey under the CLSID specified by clsidOld, and sets it to clsidNew. This function does not validate whether an appropriate registry entry for clsidNew currently exists. These entries appear in the registry as subkeys of the CLSID key:

CLSID\{clsid}=MainUserTypeName\AutoConvertTo = clsid

Object conversion means that the object's data is permanently associated with a new CLSID. Automatic conversion is typically specified in the setup program of a new version of an object application, so objects created by its older versions can be automatically updated to the new version.

For example, it may be necessary to convert spreadsheets that were created with earlier versions of a spreadsheet application to the new version. The spreadsheet objects from earlier versions have different CLSIDs than the new version. For each earlier version that you want automatically updated, you would call OleSetAutoConvert in the setup program, specifying the CLSID of the old version, and that of the new one. Then, whenever a user loads an object from a previous version, it would be automatically updated. To support automatic conversion of obects, a server that supports conversion must be prepared to manually convert objects that have the format of an earlier version of the server. Automatic conversion relies internally on this manual-conversion support.

Before setting the desired AutoConvertTo value, setup programs should also call OleSetAutoConvert to remove any existing conversion for the new class, by specifying the new class as the clsidOld parameter, and setting the clsidNew parameter to CLSID_NULL.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Library: Use ole32.lib.

See Also

OleDoAutoConvert


ProgIDFromCLSID

Retrieves the ProgID for a given CLSID.

WINOLEAPI ProgIDFromCLSID(
  REFCLSID clsid,  //CLSID for which the ProgID is requested
  LPOLESTR * lplpszProgID
                   //Address of output variable that receives a
                   // pointer to the requested ProgID string
);

Parameters

clsid
[in] Specifies the CLSID for which the ProgID is requested.
lplpszProgID
[out] Address of LPOLESTR pointer variable that receives a pointer to the ProgID string.

Return Values

S_OK
The ProgID was returned successfully.
REGDB_E_CLASSNOTREG
Class not registered in the registry.
REGDB_E_READREGDB
Error reading registry.

Remarks

Every OLE object class listed in the Insert Object dialog box must have a programmatic identifier (ProgID), a string that uniquely identifies a given class, stored in the registry. In addition to determining the eligibility for the Insert Object dialog box, the ProgID can be used as an identifier in a macro programming language to identify a class. Finally, the ProgID is also the class name used for an object of an OLE class that is placed in an OLE 1 container.

The ProgIDFromCLSID function uses entries in the registry to do the conversion. OLE application authors are responsible for ensuring that the registry is configured correctly in the application's setup program.

The ProgID string must be different than the class name of any OLE 1 application, including the OLE 1 version of the same application, if there is one. In addition, a ProgID string must not contain more than 39 characters, start with a digit, or, except for a single period, contain any punctuation (including underscores).

The ProgID must never be shown to the user in the user interface. If you need a short displayable string for an object, call IOleObject::GetUserType.

Call the CLSIDFromProgID function to find the CLSID associated with a given ProgID. CLSIDs can be freed with the task allocator (refer to the CoGetMalloc function).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CLSIDFromProgID


PropagateResult

#define PropagateResult(hrPrevious, scBase) ((HRESULT) scBase)

PropagateResult is a NO-OP.

This macro is obsolete and should not be used.


ResultFromScode

#define ResultFromScode(sc) ((HRESULT) (sc))

Converts an SCODE into an HRESULT.

This macro is obsolete and should not be used.


SCODE_CODE

#define SCODE_CODE(sc) ((sc) & 0xFFFF)

Extracts the code part of the SCODE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


SCODE_FACILITY

#define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff)

Extracts the facility from the SCODE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


SCODE_SEVERITY

#define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1)

Extracts the severity field from the SCODE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


StringFromCLSID

Converts a CLSID into a string of printable characters. Different CLSIDs always convert to different strings.

WINOLEAPI StringFromCLSID(
  REFCLSID rclsid, //CLSID to be converted
  LPOLESTR * ppsz  //Address of output variable that receives a
                   // pointer to the resulting string
);

Parameters

rclsid
[in] CLSID to be converted.
ppsz
[out] Address of LPOLESTR pointer variable that receives a pointer to the resulting string.

Return Values

This function supports the standard return value E_OUTOFMEMORY; as well as the following:

S_OK
Indicates the CLSID was successfully converted and returned.

Remarks

The StringFromCLSID function calls the StringFromGuid2 function to convert a globally unique identifier (GUID) into a string of printable characters.

The caller is responsible for freeing the memory allocated for the string by calling CoTaskMemFree.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

CLSIDFromString, StringFromGuid2


StringFromGUID2

Converts a globally unique identifier (GUID) into a string of printable characters.

int StringFromGUID2(
  REFGUID rguid,  //GUID to be converted
  LPOLESTR lpsz,  //Pointer to resulting string
  int cchMax      //Size of array at lpsz
);

Parameters

rguid
[in] GUID to be converted.
lpsz
[out] Pointer to a caller-allocated string variable to contain the resulting string on return.
cchMax
[in] Number of characters available in the buffer indicated by lpsz.

Return Values

0 (zero)
Array at lpsz is too small to contain a string representation of a GUID.
Non-zero value
The number of characters in the returned string, including the null terminator.

Remarks

The string that the lpsz parameter receives has a format like that of the following sample:

{c200e360-38c5-11ce-ae62-08002b2b79ef}

where the successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

StringFromCLSID


StringFromIID

Converts an interface identifier into a string of printable characters.

WINOLEAPI StringFromIID(
  REFIID rclsid,     //Interface identifier to be converted
  LPOLESTR * lplpsz  //Address of output variable that receives a
                     // pointer to the resulting string
);

Parameters

rclsid
[in] Interface identifier to be converted.
lplpsz
[out] Address of LPOLESTR pointer variable that receives a pointer to the resulting string.

Return Values

This function supports the standard return value E_OUTOFMEMORY; as well as the following:

S_OK
The character string was successfully returned.

Remarks

The caller is responsible for freeing the memory allocated for the string by calling CoTaskMemFree.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Library: Use ole32.lib.

See Also

IIDFromString, CoGetMalloc


SUCCEEDED

#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)

Provides a generic test for success on any status value. Non-negative numbers indicate success.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winerror.h.


ACTRL_ACCESS

The ACTRL_ACCESS structure contains an array of access-control lists for an object and its properties.

typedef struct _ACTRL_ALIST {
    ULONG                   cEntries;
    PACTRL_PROPERTY_ENTRY   pPropertyAccessList;
} ACTRL_ACCESS , *PACTRL_ACCESS;

Members

cEntries
Specifies the number of entries in the pPropertyAccessList array.
pPropertyAccessList
Pointer to an array of ACTRL_PROPERTY_ENTRY structures. Each structure contains a list of access-control entries for an object or a specified property on the object.

Remarks

IAccessControl::GrantAccessRights uses an ACTRL_ACCESSW structure (the Unicode-compatible version of this structure) to represent the access rights to be added to the object's existing access rights.

See Also

IAccessControl::GrantAccessRights


ACTRL_ACCESS_ENTRY

The ACTRL_ACCESS_ENTRY structure contains access-control information for a specified trustee. This structure stores information equivalent to the access-control information stored in an ACE.

typedef struct _ACTRL_ACCESS_ENTRY {
    TRUSTEE         Trustee;
    ULONG           fAccessFlags;
    ACCESS_RIGHTS   Access;
    ACCESS_RIGHTS   ProvSpecificAccess;
    INHERIT_FLAGS   Inheritance;
    LPCTSTR         lpInheritProperty;
} ACTRL_ACCESS_ENTRY;

Members

Trustee
A TRUSTEE structure that identifies the user, group, or program (such as a Win32 service) to which the access-control entry applies.
fAccessFlags
Indicates how the access rights specified by the Access and ProvSpecificAccess members apply to the trustee. This member can be one of the following values. If you are using this structure with the COM implementation of IAccessControl, this member must be ACTRL_ACCESS_ALLOWED or ACTRL_ACCESS_DENIED.
Value | Meaning
ACTRL_ACCESS_ALLOWED | The rights are allowed.
ACTRL_ACCESS_DENIED | The rights are denied.
ACTRL_AUDIT_FAILURE | The system generates audit messages for failed attempts to use the rights.
ACTRL_AUDIT_SUCCESS | The system generates audit messages for successful attempts to use the rights.
Access
A bit mask that specifies the access rights that the entry allows, denies, or audits for the trustee.

This member must use the provider-independent access flags, such as ACTRL_READ_CONTROL, rather than the Windows NT access flags, such as READ_CONTROL. The provider for the object type converts these provider-independent flags to the corresponding provider-specific flags.

If you are using this structure with the COM implementation of IAccessControl, this member must be COM_RIGHTS_EXECUTE.

ProvSpecificAccess
A bit mask that specifies access rights specific to the provider type. The functions that use the ACTRL_ACCESS_ENTRY structure pass these bits on to the provider without interpreting them. In most cases, this value should be zero.

If you are using this structure with the COM implementation of IAccessControl, this value must be zero.

Inheritance
A set of bit flags that determines whether other containers or objects can inherit the access-control entry from the primary object to which the access list is attached.

If you are using this structure with the COM implementation of IAccessControl, this value must be NO_INHERITANCE, which indicates that the access-control entry is not inheritable. Otherwise, this value can be NO_INHERITANCE or it can be a combination of the following values:
Value | Meaning
CONTAINER_
INHERIT_ACE 		Other containers that are contained by the primary object inherit the entry.
INHERIT_ONLY_ACE | The ACE does not apply to the primary object to which the ACL is attached, but objects contained by the primary object inherit the entry.
NO_PROPAGATE_
INHERIT_ACE 		The OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags are not propagated to an inherited entry.
OBJECT_INHERIT_ACE | Noncontainer objects contained by the primary object inherit the entry.
SUB_CONTAINERS_
ONLY_INHERIT 		Other containers that are contained by the primary object inherit the entry. This flag corresponds to the CONTAINER_INHERIT_ACE flag.
SUB_OBJECTS_
ONLY_INHERIT 		Noncontainer objects contained by the primary object inherit the entry. This flag corresponds to the OBJECT_INHERIT_ACE flag.
SUB_CONTAINERS_AND_
OBJECTS_INHERIT 		Both containers and noncontainer objects that are contained by the primary object inherit the entry. This flag corresponds to the combination of the CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE flags.

lpInheritProperty
Pointer to a null-terminated string that identifies the object types that can inherit the entry. If you are using this structure with the COM implementation of IAccessControl, this must be NULL.

Remarks

The pAccessList member of the ACTRL_ACCESS_ENTRY_LIST structure points to an array of ACTRL_ACCESS_ENTRY structures.

See Also

TRUSTEE, ACTRL_ACCESS_ENTRY_LIST


ACTRL_ACCESS_ENTRY_LIST

The ACTRL_ACCESS_ENTRY_LIST structure contains a list of access entries.

typedef struct _ACTRL_ACCESS_ENTRY_LIST {
    ULONG                   cEntries;
    PACTRL_ACCESS_ENTRY     pAccessList;
} ACTRL_ACCESS_ENTRY_LIST, *PACTRL_ACCESS_ENTRY_LIST;

Members

cEntries
Specifies the number of entries in the pAccessList array.
pAccessList
Pointer to an array of ACTRL_ACCESS_ENTRY structures. Each structure specifies access-control information for a specified trustee.

Remarks

To create an empty access list, set cEntries to zero and pAccessList to NULL. An empty list does not grant access to any trustee, and thus, denies all access to an object.

To create a null access list, set the pAccessEntryList member of an ACTRL_PROPERTY_ENTRY structure to NULL. A null access list grants everyone full access to the object.

ACTRL_ACCESS_ENTRY_LISTW is the Unicode-compatible version of this structure; its pAccessList member is of type PACTRL_ACCESS_ENTRYW.

See Also

IAccessControl, ACTRL_ACCESS_ENTRY


ACTRL_PROPERTY_ENTRY

The ACTRL_PROPERTY_ENTRY structure stores a list of access-control entries for an object or a specified property on an object.

typedef struct _ACTRL_PROPERTY_ENTRY {
    LPCTSTR                    lpProperty;
    PACTRL_ACCESS_ENTRY_LIST   pAccessEntryList;
    ULONG                      fListFlags;
} ACTRL_PROPERTY_ENTRY, *PACTRL_PROPERTY_ENTRY;

Members

lpProperty
Pointer to a null-terminated string that specifies the GUID of a property on an object. You can use the UuidToString function to generate a string representation of a property GUID.

If this member is NULL, the access entry list applies to the object; otherwise, it applies to the specified property.

pAccessEntryList
Pointer to an ACTRL_ACCESS_ENTRY_LIST structure that contains a list of access-control entries.
fListFlags
Flags that specify information about the lpProperty property. This member can be the following value.
Value | Meaning
ACTRL_ACCESS_PROTECTED | Protects the object or property from inheriting access-control entries.

Remarks

To create an ACTRL_PROPERTY_ENTRY structure that grants everyone full access to an object, set the pAccessEntryList member to NULL.

To create an ACTRL_PROPERTY_ENTRY structure that denies all access to an object, set the pAccessEntryList member to point to an ACTRL_ACCESS_ENTRY_LIST structure whose cEntries member is zero and pAccessList member is NULL.

ACTRL_PROPERTY_ENTRYW is the Unicode-compatible version of this structure; its lpProperty member is of type LPWSTR, and its pAccessEntryList member is of type PACTRL_ACCESS_ENTRY_LISTW.

See Also

ACTRL_ACCESS, ACTRL_ACCESS_ENTRY_LIST, UuidToString


BIND_OPTS

Contains parameters used during a moniker-binding operation. The BIND_OPTS2 structure may be used in its place. A BIND_OPTS structure is stored in a bind context; the same bind context is used by each component of a composite moniker during binding, allowing the same parameters to be passed to all components of a composite moniker. See IBindCtx for more information about bind contexts.

If you're a moniker client (that is, you use a moniker to acquire an interface pointer to an object), you typically do not need to specify values for the fields of this structure. The CreateBindCtx function creates a bind context with the bind options set to default values that are suitable for most situations; the BindMoniker function does the same thing when creating a bind context for use in binding a moniker. If you want to modify the values of these bind options, you can do so by passing a BIND_OPTS structure to the IBindCtx::SetBindOptions method. Moniker implementers can pass a BIND_OPTS structure to the IBindCtx::GetBindOptions method to retrieve the values of these bind options.

The BIND_OPTS structure is defined in OBJIDL.IDL.

typedef struct tagBIND_OPTS
{
    DWORD cbStruct;
    DWORD grfFlags;
    DWORD grfMode;
    DWORD dwTickCountDeadline;
} BIND_OPTS, *LPBIND_OPTS;

Members

cbStruct
Size of this structure in bytes (that is, the size of the BIND_OPTS structure).
grfFlags
Flags that control aspects of moniker binding operations. This value is any combination of the bit flags in the BIND_FLAGS enumeration. New values may be defined in the future, so moniker implementations should ignore any bits in this field that they do not understand. The CreateBindCtx function initializes this field to zero.
grfMode
Flags that should be used when opening the file that contains the object identified by the moniker. The values are taken from the STGM enumeration. The binding operation uses these flags in the call to IPersistFile::Load when loading the file. If the object is already running, these flags are ignored by the binding operation. The CreateBindCtx function initializes this field to STGM_READWRITE.
dwTickCountDeadline
Clock time (in milliseconds, as returned by the GetTickCount function) by which the caller would like the binding operation to be completed. This member lets the caller limit the execution time of an operation when speed is of primary importance. A value of zero indicates that there is no deadline. Callers most often use this capability when calling the IMoniker::GetTimeOfLastChange method, though it can be usefully applied to other operations as well. The CreateBindCtx function initializes this field to zero.

Typical deadlines allow for a few hundred milliseconds of execution. This deadline is a recommendation, not a requirement; however, operations that exceed their deadline by a large amount may cause delays for the end user. Each moniker implementation should try to complete its operation by the deadline, or fail with the error MK_E_EXCEEDEDDEADLINE.

If a binding operation exceeds its deadline because one or more objects that it needs are not running, the moniker implementation should register the objects responsible in the bind context using the IBindCtx::RegisterObjectParam. The objects should be registered under the parameter names "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and so on. If the caller later finds the object in the Running Object Table, the caller can retry the binding operation.

The GetTickCount function indicates the number of milliseconds since system startup, and wraps back to zero after 2^31 milliseconds. Consequently, callers should be careful not to inadvertently pass a zero value (which indicates no deadline), and moniker implementations should be aware of clock wrapping problems (see the GetTickCount function for more information).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

BIND_OPTS2, BIND_FLAGS, CreateBindCtx, IBindCtx::SetBindOptions


BIND_OPTS2

Contains parameters used during a moniker-binding operation. A BIND_OPTS2 structure is stored in a bind context; the same bind context is used by each component of a composite moniker during binding, allowing the same parameters to be passed to all components of a composite moniker. See IBindCtx for more information about bind contexts. BIND_OPTS2 replaces the previously defined BIND_OPTS structure, including the previously defined members, and adding four new members.

Moniker clients (those using a moniker to acquire an interface pointer to an object) typically do not need to specify values for the fields of this structure. The CreateBindCtx function creates a bind context with the bind options set to default values that are suitable for most situations. The BindMoniker function does the same thing when creating a bind context for use in binding a moniker. If you want to modify the values of these bind options, you can do so by passing a BIND_OPTS2 structure to the IBindCtx::SetBindOptions method. Moniker implementers can pass a BIND_OPTS2 structure to the IBindCtx::GetBindOptions method to retrieve the values of these bind options.

The BIND_OPTS2 structure is defined in OBJIDL.IDL

typedef struct tagBIND_OPTS2 {
    DWORD           cbStruct;       //  sizeof(BIND_OPTS2)
    DWORD           grfFlags;
    DWORD           grfMode;
    DWORD           dwTickCountDeadline;
    DWORD           dwTrackFlags;
    DWORD           dwClassContext;
    LCID            locale;
    COSERVERINFO *  pServerInfo;
} BIND_OPTS2, * LPBIND_OPTS2;

Members

cbStruct
Size of this structure in bytes (that is, the size of the BIND_OPTS2 structure).
grfFlags
Flags that control aspects of moniker binding operations. This value is any combination of the bit flags in the BINDFLAGS enumeration. New values may be defined in the future, so moniker implementations should ignore any bits in this field that they do not understand. The CreateBindCtx function initializes this field to zero.
grfMode
Flags that should be used when opening the file that contains the object identified by the moniker. The values are taken from the STGM enumeration. The binding operation uses these flags in the call to IPersistFile::Load when loading the file. If the object is already running, these flags are ignored by the binding operation. The CreateBindCtx function initializes this field to STGM_READWRITE.
dwTickCountDeadline
Clock time (in milliseconds, as returned by the GetTickCount function) by which the caller would like the binding operation to be completed. This member lets the caller limit the execution time of an operation when speed is of primary importance. A value of zero indicates that there is no deadline. Callers most often use this capability when calling the IMoniker::GetTimeOfLastChange method, though it can be usefully applied to other operations as well. The CreateBindCtx function initializes this field to zero.

Typical deadlines allow for a few hundred milliseconds of execution. This deadline is a recommendation, not a requirement; however, operations that exceed their deadline by a large amount may cause delays for the end user. Each moniker implementation should try to complete its operation by the deadline, or fail with the error MK_E_EXCEEDEDDEADLINE.

If a binding operation exceeds its deadline because one or more objects that it needs are not running, the moniker implementation should register the objects responsible in the bind context using the IBindCtx::RegisterObjectParam. The objects should be registered under the parameter names "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and so on. If the caller later finds the object in the Running Object Table, the caller can retry the binding operation.

The GetTickCount function indicates the number of milliseconds since system startup, and wraps back to zero after 2^31 milliseconds. Consequently, callers should be careful not to inadvertently pass a zero value (which indicates no deadline), and moniker implementations should be aware of clock wrapping problems (see the GetTickCount function for more information).

dwTrackFlags
A moniker can use this value during link tracking. If the original persisted data that the moniker is referencing has been moved, the moniker can attempt to reestablish the link by searching for the original data though some adequate mechanism. dwTrackFlags provides additional information on how the link should be resolved. See the documentation of the fFlags parameter in IShellLink::Resolve.

COM's file moniker implementation uses the shell link mechanism to reestablish links and passes these flags to IShellLink::Resolve.

dwClassContext
The class context, taken from the CLSCTX enumeration, that is to be used for instantiating the object. Monikers typically pass this value to the dwClsContext parameter of CoCreateInstance.
locale
The LCID value indicating the client's preference for the locale to be used by the object to which they are binding. A moniker passes this value to IClassActivator::GetClassObject.
pServerInfo
Points to a COSERVERINFO structure. This member allows clients calling IMoniker::BindToObject to specify server information. Clients may pass a BIND_OPTS2 structure to the IBindCtx::SetBindOptions method. If a server name is specified in the COSERVERINFO struct, the moniker bind will be forwarded to the specified machine. SetBindOptions only copies the struct members of BIND_OPTS2, not the COSERVERINFO structure and the pointers it contains. Callers may not free any of these pointers until the bind context is released. COM's new class moniker does not currently honor the pServerInfo flag.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

BIND_OPTS, BIND_FLAGS, CreateBindCtx, IBindCtx::SetBindOptions


CATEGORYINFO Structure

Platform SDK: COM

CATEGORYINFO

The CATEGORYINFO structure describes a component category.

typedef struct tagCATEGORYINFO{
  CATID catid;
  LCID lcid;
  OLECHAR szDescription [128];
} CATEGORYINFO;

Parameters

catid
The category identifier for the component.
lcid
The locale identifier.
szDescription
The description of the category (cannot exceed 128 characters).


COAUTHIDENTITY

The COAUTHIDENTITY structure represents a user name and password. A pointer to a COAUTHIDENTITY structure is a member of the COAUTHINFO structure, which specifies authentication settings for remote activation requests.

typedef struct _COAUTHIDENTITY
{
  USHORT *User;
  ULONG UserLength;
  USHORT *Domain;
  ULONG DomainLength;
  USHORT *Password;
  ULONG PasswordLength;
  ULONG Flags;
} COAUTHIDENTITY;

Members

User
String containing the user's name.
UserLength
Length of the User string, without the terminating NULL.
Domain
String containing the domain or workgroup name.
DomainLength
Length of the Domain string, without the terminating NULL.
Password
String containing the user's password in the domain or workgroup.
PasswordLength
Length of the Password string, without the terminating NULL.
Flags
Value indicating that the strings are ANSI (0x1) or Unicode(0x2). This value can be SEC_WINNT_AUTH_IDENTITY_ANSI or SEC_WINNT_AUTH_IDENTITY_UNICODE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in wtypes.h.

See Also

COAUTHINFO, COSERVERINFO


COAUTHINFO

The COAUTHINFO structure specifies the authentication settings used while making a remote activation request from the client machine to the server machine.

typedef struct _COAUTHINFO
{
    DWORD               dwAuthnSvc;
    DWORD               dwAuthzSvc;
    LPWSTR              pwszServerPrincName;
    DWORD               dwAuthnLevel;
    DWORD               dwImpersonationLevel;
    COAUTHIDENTITY *    pAuthIdentityData;
    DWORD               dwCapabilities;
} COAUTHINFO;

Members

dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants indicating the authentication service to use. It may be RPC_C_AUTHN_NONE if no authentication is required. RPC_C_AUTHN_WINNT is the default and RPC_C_AUTHN_GSS_KERBEROS is also supported.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants indicating the authorization service to use. If you are using the NT authentication service, use RPC_C_AUTHZ_NONE.
pwszServerPrincName
Pointer to a WCHAR string that indicates the server principal name to use with the authentication service. If you are using RPC_C_AUTHN_WINNT, the principal name must be NULL.
dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants indicating the authentication level to use.
dwImpersonationLevel
[in] Specifies the impersonation level to be used. This value must be RPC_C_IMP_LEVEL_IMPERSONATE.
pAuthIdentityData
Pointer to a COAUTHIDENTITY structure that establishes a nondefault client identity. If NULL, the actual identity of the client is used. Values of structure members are authentication-service specific. This value must be NULL if dwAuthnSvc is not NTLMSSP or Kerberos. The form of the COAUTHIDENTITY structure is identical to that of the Windows NT and Windows 2000 SEC_WINNT_AUTH_IDENTITY structure.
dwCapabilities
[in] A DWORD defining flags that indicate the further capabilities of this proxy. Currently, this value must be EOAC_NONE.

Remarks

A pointer to a COAUTHINFO structure is a member of the COSERVERINFO structure. The COAUTHINFO structure provides security information to object creation functions across the network. The values of the COAUTHINFO structure determine the authentication settings used while making a remote activation request from the client machine to the server machine.

If pAuthInfo in COSERVERINFO is set to NULL, NTLMSSP with the identity of the actual client will be used. However, a non-NULL COAUTHINFO structure can be specified for pAuthInfo to meet any one of the following special needs:

  • To specify a different client identity for machine remote activations. The specified identity will be used for the launch permission check on the server rather than the real client identity.
  • To specify that Kerberos, rather than NTLMSSP, is used for machine remote activation. A nondefault client identity may or may not be specified.
  • To request unsecure activation.
  • To specify a proprietary authentication service.

Specifying a COAUTHINFO structure allows DCOM activations to work correctly with security providers other than NTLMSSP. You can also specify additional security information used during remote activations for interoperability with alternate implementations of DCOM.

For more information on the uses of COAUTHINFO, see COSERVERINFO.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in wtypes.h.

See Also

COSERVERINFO


CONNECTDATA

The CONNECTDATA structure is the type enumerated through the IEnumConnections::Next method. Each structure describes a connection that exists to a given connection point.

typedef struct tagCONNECTDATA
{
    IUnknown*  pUnk;
    DWORD      dwCookie;
} CONNECTDATA;

Members

pUnk
Pointer to the IUnknown interface on a connected advisory sink. The caller must call IUnknown::Release using this pointer when the CONNECTDATA structure is no longer needed. The caller is responsible for calling Release for each CONNECTDATA structure enumerated through IEnumConnections::Next.
dwCookie
Connection where this value is the same token that is returned originally from calls to IConnectionPoint::Advise. This token can be used to disconnect the sink pointed to by a pUnk by passing dwCookie to IConnectionPoint::Unadvise.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IEnumConnections


COSERVERINFO

Identifies a remote machine resource to the new or enhanced activation functions. The structure is defined as follows in the Wtypes.h header file:

typedef struct  _COSERVERINFO
    {
    DWORD dwReserved1;
    LPWSTR pwszName;
    COAUTHINFO  *pAuthInfo;
    DWORD dwReserved2;
    }   COSERVERINFO;

Members

dwReserved1
Reserved for future use. Must be 0.
pwszName
Pointer to the name of the machine to be used.
pAuthInfo
Set to a pointer to a COAUTHINFO structure to override the default activation security for machine remote activations. Otherwise, set to NULL to indicate that default values should be used. For more information, see the Remarks section.
dwReserved2
Reserved for future use. Must be 0.

Remarks

The COSERVERINFO structure is used primarily to identify a remote system in object creation functions. Machine resources are named using the naming scheme of the network transport. By default, all UNC ("\\server" or "server") and DNS names ("domain.com", "example.microsoft.com", or "135.5.33.19") names are allowed.

If pAuthInfo is set to NULL, NTLMSSP with the identity of the actual client will be used. To meet any one of the needs in the following list, pAuthInfo can be set to point to a non-NULL COAUTHINFO structure:

  • To specify a different client identity for machine remote activations. The specified identity will be used for the launch permission check on the server rather than the real client identity.
  • To specify that Kerberos, rather than NTLMSSP, is used for machine remote activation. A nondefault client identity may or may not be specified.
  • To request unsecure activation.
  • To specify a proprietary authentication service.

If pAuthInfo is not NULL, those values will be used to specify the authentication settings for the remote call. These settings will be passed to RpcBindingSetAuthInfoEx.

If the pAuthInfo parameter is not specified, any values in the AppID section of the registry will be used to override the following default authentication settings:

dwAuthnSvc | RPC_C_AUTHN_WINNT
dwAuthzSvc | RPC_C_AUTHZ_NONE
pszServerPrincName | NULL
dwAuthnLevel | RPC_C_AUTHN_LEVEL_CONNECT
dwImpersonationLevel | RPC_C_IMP_LEVEL_IMPERSONATE
pvAuthIdentityData | NULL
dwCapabilities | RPC_C_QOS_CAPABILITIES_DEFAULT

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CLSCTX, CoGetClassObject, CoGetInstanceFromFile, CoGetInstanceFromIStorage, CoCreateInstanceEx, Locating a Remote Object


FILETIME

The FILETIME data structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. It is the means by which Win32 determines the date and time. FILETIME is used by the CoDosDateTimeToFileTime, CoFileTimeToDosDateTime, and CoFileTimeNow functions. It is defined as follows:

typedef struct STRUCT tagFILETIME
{
    DWORD  dwLowDateTime;
    DWORD  dwHighDateTime;
} FILETIME;

Members

dwLowDateTime
The low 32 bits of the Win32 date/time value.
dwHighDateTime
The upper 32 bits of the Win32 date/time value.

Remarks

The FILETIME data structure is used in the time conversion functions between DOS and Win32.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.

See Also

CoDosDateTimeToFileTime, CoFileTimeNow, CoFileTimeToDosDateTime


INTERFACEINFO

The INTERFACEINFO structure contains information about incoming calls. The structure is defined as follows:

typedef struct tagINTERFACEINFO
{
    LPUNKNOWN  pUnk;
    IID        iid;
    WORD       wMethod;
} INTERFACEINFO,  * LPINTERFACEINFO;

Members

pUnk
Pointer to the IUnknown interface on the object.
iid
Identifier of the requested interface.
wMethod
Interface method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMessageFilter::HandleIncomingCall


MULTI_QI

To optimize network performance, most remote activation functions take an array of MULTI_QI structures rather than just a single IID as input and a single pointer to the requested interface on the object as output, as do local machine activation functions. This allows a set of pointers to interfaces to be returned from the same object in a single round-trip to the server. In network scenarios, requesting multiple interfaces at the time of object construction can save considerable time over using a number of calls to the QueryInterface method for unique interfaces, each of which would require a round-trip to the server.

typedef struct _MULTI_QI {
        const IID*    pIID;
        IUnknown *    pItf;
        HRESULT       hr;
    } MULTI_QI;

Members

pIID
[in] Pointer to an interface identifier.
pItf
[out] Pointer to the interface requested in pIID. Must be set to NULL on entry.
hr
[out] Return value of the QueryInterface call made to satisfy the request for the interface requested in pIID. Common return values are S_OK and E_NOINTERFACE. Must be set to zero on entry.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoGetInstanceFromFile, CoGetInstanceFromIStorage, CoCreateInstanceEx


SOLE_AUTHENTICATION_INFO

Identifies an authentication service, authorization service, and the authentication information for the specified authentication service. The SOLE_AUTHENTICATION_LIST structure, which is pointed to by the pAuthList parameter of CoInitializeSecurity, contains an array of SOLE_AUTHENTICATION_INFO structures.

    typedef struct  tagSOLE_AUTHENTICATION_INFO {
        DWORD dwAuthnSvc;
        DWORD dwAuthzSvc;
        void *pAuthInfo;
    } SOLE_AUTHENTICATION_INFO;

Members

dwAuthnSvc
The authentication service. It may contain a single value taken from the list of RPC_C_AUTHN_xxx constants.
dwAuthzSvc
The authorization service. It may contain a single value taken from the list of RPC_C_AUTHZ_xxx constants.
pAuthInfo
A pointer to the authentication information, whose type is specific to the authentication service identified by dwAuthnSvc. For SChannel (RPC_C_AUTHN_GSS_SCHANNEL), this member either points to a CERT_CONTEXT that contains the client's certificate or is NULL if the client has no certificate or wishes to remain anonymous to the server. For NTLMSSP (RPC_C_AUTHN_WINNT) and Kerberos (RPC_C_AUTHN_GSS_KERBEROS), this member points to a SEC_WINNT_AUTH_IDENTITY (or SEC_WINNT_AUTH_IDENTITY_EX ) structure containing the user name and password.

For Snego (RPC_C_AUTHN_GSS_NEGOTIATE), this member is either NULL or points to a SEC_WINNT_AUTH_IDENTITY_EX structure. If it is NULL, Snego will pick a list of authentication services based on those available on the client machine. If non-NULL, the structure's PackageList member must point to a string containing a comma-separated list of authentication service names and the PackageListLength member must give the number of bytes in the PackageList string.

For authentication services that DCOM doesn't know about, pAuthInfo must be set to NULL and DCOM will use the process identity to represent the client. For more information, see COM and Security Packages.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

RPC_C_AUTHN_xxx, RPC_C_AUTHZ_xxx, CoInitializeSecurity


SOLE_AUTHENTICATION_LIST

Indicates the default authentication information to use with each authentication service. This structure is passed by clients to CoInitializeSecurity by using the pAuthList parameter. When DCOM negotiates the default authentication service for a proxy, it picks the default authentication information from this list.

typedef struct  tagSOLE_AUTHENTICATION_LIST {
    DWORD cAuthInfo;
    SOLE_AUTHENTICATION_INFO *aAuthInfo;
} SOLE_AUTHENTICATION_LIST;

Members

cAuthInfo
Count of pointers in the array pointed to by aAuthInfo.
aAuthInfo
An array of SOLE_AUTHENTICATION_INFO structures. Each of these structures contains an identifier for an authentication service, an identifier for the authorization service, and a pointer to authentication information to use with the specified authentication service.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objidl.h.

See Also

SOLE_AUTHENTICATION_INFO, CoInitializeSecurity


SOLE_AUTHENTICATION_SERVICE

Identifies an authentication service that a server is willing to use to communicate to a client. This structure is passed by servers to CoInitializeSecurity as a member of the asAuthSvc parameter and can be retrieved through a call to CoQueryAuthenticationServices.

    typedef struct tagSOLE_AUTHENTICATION_SERVICE {
        DWORD       dwAuthnSvc;
        DWORD       dwAuthzSvc;
        OLECHAR*    pPrincipalName;
        HRESULT     hr;
    } SOLE_AUTHENTICATION_SERVICE;

Members

dwAuthnSvc
The authentication service. It may contain a single value taken from the list of RPC_C_AUTHN_xxx constants. RPC_C_AUTHN_NONE turns off authentication.
dwAuthzSvc
The authorization service. It may contain a single value taken from the list of RPC_C_AUTHZ_xxx constants. This parameter is ignored when using the NTLMSSP (RPC_C_AUTHN_WINNT) authentication service and should be none when using SChannel (RPC_C_AUTHN_GSS_SCHANNEL), Kerberos (RPC_C_AUTHN_GSS_KERBEROS), or Snego (RPC_C_AUTHN_GSS_NEGOTIATE).
pPrincipalName
Principal name to be used with the authentication service. If the principal name is NULL, the current user identifier is assumed. A NULL principal name is allowed for NTLMSSP, Kerberos, and Snego authentication services but may not work for other authentication services. For SChannel, this member must point to a CERT_CONTEXT that contains the server's certificate; it cannot be NULL.
hr
When used in CoInitializeSecurity, set on return to indicate the status of the call to register the authentication services.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

RPC_C_AUTHN_xxx, RPC_C_AUTHZ_xxx, CoInitializeSecurity


ACTRL_ACCESS_xxx

These values indicate whether an entry in the access list describes rights that are allowed or denied.

#define ACTRL_ACCESS_ALLOWED   0x00000000
#define ACTRL_ACCESS_DENIED    0x10000000

Values

ACTRL_ACCESS_ALLOWED
Denotes that this is an access-allowed entry.
ACTRL_ACCESS_DENIED
Denotes that this is an access-denied entry.

Remarks

These values can be assigned to the fAccessFlags member of the ACTRL_ACCESS_ENTRY structure.

See Also

ACTRL_ACCESS_ENTRY


BIND_FLAGS

The BIND_FLAGS enumeration values are used to control aspects of moniker binding operations. The values are used in the BIND_OPTS structure. Callers of IMoniker methods can specify values from this enumeration, and implementers of IMoniker methods can use these values in determining what they should do.

typedef enum tagBIND_FLAGS
{
    BIND_MAYBOTHERUSER     = 1,
    BIND_JUSTTESTEXISTENCE = 2,
} BIND_FLAGS;

Elements

BIND_MAYBOTHERUSER
If this flag is specified, the moniker implementation can interact with the end user. If not present, the moniker implementation should not interact with the user in any way, such as by asking for a password for a network volume that needs mounting. If prohibited from interacting with the user when it otherwise would, a moniker implementation can use a different algorithm that does not require user interaction, or it can fail with the error MK_MUSTBOTHERUSER.
BIND_JUSTTESTEXISTENCE
If this flag is specified, the caller is not interested in having the operation carried out, but only in learning whether the operation could have been carried out had this flag not been specified. For example, this flag lets the caller indicate only an interest in finding out whether an object actually exists by using this flag in a IMoniker::BindToObject call. Moniker implementations can, however, ignore this possible optimization and carry out the operation in full. Callers must be able to deal with both cases.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

BIND_OPTS, IBindCtx


BINDSPEED

The BINDSPEED enumeration values indicate approximately how long the caller will wait to bind to an object. Callers of the IOleItemContainer::GetObject method specify values from this enumeration, and implementers of that method use these values as a guideline for how quickly they must complete their operation.

typedef enum tagBINDSPEED
{
    BINDSPEED_INDEFINITE   = 1,
    BINDSPEED_MODERATE     = 2,
    BINDSPEED_IMMEDIATE    = 3
} BINDSPEED;

Elements

BINDSPEED_INDEFINITE
There is no time limit on the binding operation.
BINDSPEED_MODERATE
The IOleItemContainer::GetObject operation must be completed in a moderate amount of time. If this flag is specified, the implementation of IOleItemContainer::GetObject should return MK_E_EXCEEEDEDDEADLINE unless the object is one of the following:
  • Already in the running state.
  • A pseudo-object (i.e., an object internal to the item container, such as a cell-range in a spreadsheet or a character-range in a word processor).
  • An object supported by an in-process server (so it is always in the running state when it is loaded). In this case, IOleItemContainer::GetObject should load the designated object, and, if the OleIsRunning function indicates that the object is running, return successfully.
BINDSPEED_IMMEDIATE
The caller will wait only a short time. In this case, IOleItemContainer::GetObject should return MK_E_EXCEEEDEDDEADLINE unless the object is already in the running state or is a pseudo-object.

Remarks

The system-supplied item moniker implementation is the primary caller of IOleItemContainer::GetObject. The BINDSPEED value that it specifies depends on the deadline specified by the caller of the moniker operation.

The deadline is stored in the dwTickCountDeadline field of the BIND_OPTS structure in the bind context passed to the moniker operation. This value is based on the return value of the GetTickCount function. If dwTickCountDeadline is zero, indicating no deadline, the item moniker implementation specifies BINDSPEED_INDEFINITE. (This is the default dwTickCountDeadline value for a bind context returned by the CreateBindCtx function.) If the difference between dwTickCountDeadline and the value returned by the GetTickCount function is greater than 2500, the item moniker implementation specifies BINDSPEED_MODERATE. If the difference is less than 2500, the item moniker implementation specifies BINDSPEED_IMMEDIATE.

Implementations of IOleItemContainer::GetObject can use the BINDSPEED value as a shortcut approximation of the binding deadline, or they can use the IBindCtx instance parameter to determine the exact deadline.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

BIND_OPTS, IBindCtx::GetBindOptions, IOleItemContainer::GetObject


BSCO_OPTION

A client of a binding operation provides values from the BSCO_OPTION enumeration when calling the RegisterBindStatusCallback function to register the client's IBindStatusCallback Interface. The BSCO_OPTION values specify the callback notifications that the client wants from the moniker. The values correspond to methods in the IBindStatusCallback interface. Simple clients of IMoniker::BindToStorage that want nothing but the data bits need specify only BSCO_ONDATAAVAILABLE.

typedef enum tagBSCO_OPTION {
    BSCO_ONSTARTBINDING,
    BSCO_GETPRIORITY,
    BSCO_ONLOWRESOURCE,
    BSCO_ONPROGRESS,
    BSCO_ONSTOPBINDING,
    BSCO_GETBINDINFO,
    BSCO_ONDATAAVAILABLE,
    BSCO_ONOBJECTAVAILABLE,
    BSCO_ALLONIBSC
} BSCO_OPTION;

Elements

BSCO_ONSTARTBINDING
The client would like to receive the OnStartBinding callback.
BSCO_GETPRIORITY
The client would like to receive the GetPriority callback.
BSCO_ONLOWRESOURCE
The client would like to receive the OnLowResource callback.
BSCO_ONPROGRESS
The client would like to receive the OnProgress callback.
BSCO_ONSTOPBINDING
The client would like to receive the OnStopBinding callback.
BSCO_GETBINDINFO
The client would like to receive the GetBindInfo callback.
BSCO_ONDATAAVAILABLE
The client would like to receive the OnDataAvailable callback.
BSCO_ONOBJECTAVAILABLE
The client would like to receive the OnObjectAvailable callback.
BSCO_ALLONIBSC
The client would like to receive all callbacks.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.

See Also

IBindStatusCallback Interface, RegisterBindStatusCallback Function


CALLTYPE

The CALLTYPE enumeration constant specifies the call types used by IMessageFilter::HandleInComingCall.

typedef enum tagCALLTYPE
{
    CALLTYPE_TOPLEVEL             = 1,
    CALLTYPE_NESTED               = 2,
    CALLTYPE_ASYNC                = 3,
    CALLTYPE_TOPLEVEL_CALLPENDING = 4,
    CALLTYPE_ASYNC_CALLPENDING    = 5
} CALLTYPE;

Elements

CALLTYPE_TOPLEVEL
A top-level call has arrived and that the object is not currently waiting for a reply from a previous outgoing call. Calls of this type should always be handled.
CALLTYPE_NESTED
A call has arrived bearing the same logical thread identifier as that of a previous outgoing call for which the object is still awaiting a reply. Calls of this type should always handled.
CALLTYPE_ASYNC
An aysnchronous call has arrived. Calls of this type cannot be rejected. OLE always delivers calls of this type.
CALLTYPE_TOPLEVEL_CALLPENDING
A new top-level call has arrived with a new logical thread identifier and that the object is currently waiting for a reply from a previous outgoing call. Calls of this type may be handled or rejected.
CALLTYPE_ASYNC_CALLPENDING
An asynchronous call has arrived with a new logical thread identifier and that the object is currently waiting for a reply from a previous outgoing call. Calls of this type cannot be rejected.

async call – can NOT be rejected

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMessageFilter::HandleInComingCall, IMessageFilter


CLSCTX

Values from the CLSCTX enumeration are used in activation calls to indicate the execution contexts in which an object is to be run. These values are also used in calls to CoRegisterClassObject to indicate the set of execution contexts in which a class object is to be made available for requests to construct instances.

typedef enum tagCLSCTX
{
    CLSCTX_INPROC_SERVER    = 1,
    CLSCTX_INPROC_HANDLER   = 2,
    CLSCTX_LOCAL_SERVER     = 4
    CLSCTX_REMOTE_SERVER    = 16
    CLSCTX_NO_CODE_DOWNLOAD = 400
    CLSCTX_NO_FAILURE_LOG = 4000
} CLSCTX;
#define CLSCTX_SERVER    (CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER)
#define CLSCTX_ALL       (CLSCTX_INPROC_HANDLER | CLSCTX_SERVER)

Elements

CLSCTX_INPROC_SERVER
The code that creates and manages objects of this class runs in the same process as the caller of the function specifying the class context.
CLSCTX_INPROC_HANDLER
The code that manages objects of this class is an in-process handler. This is a DLL that runs in the client process and implements client-side structures of this class when instances of the class are accessed remotely.
CLSCTX_LOCAL_SERVER
The EXE code that creates and manages objects of this class is loaded in a separate process space (runs on same machine but in a different process).
CLSCTX_REMOTE_SERVER
A remote machine context. The LocalServer32 or LocalService code that creates and manages objects of this class is run on a different machine.
CLSCTX_NO_CODE_DOWNLOAD
If the COMClassStore Policy enables automatic installation, CLSCTX_NO_CODE_DOWNLOAD can be used to explicitly disallow download for an activation.
If either of the following registry keys is enabled, automatic download of missing classes is enabled.
  • HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\App Management
    Value Name: COMClassStore, ValueType: DWORD. 1 = enabled, 0 = disabled (default = 0)
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\App Management
    Value Name: COMClassStore, ValueType: DWORD. 1 = enabled, 0 = disabled (default = 0)
CLSCTX_NO_FAILURE_LOG
If the ActivationFailureLoggingLevel is created the following values can determine the status of event logging. The CLSCTX_NO_FAILURE_LOG can be used to override the logging of failures in CoCreateInstanceEx.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole

Value Name: ActivationFailureLoggingLevel, ValueType: DWORD. 0 = Discretionary logging. Log by default, but clients can override by specifying CLSCTX_NO_FAILURE_LOG in CoCreateInstanceEx. 1 = Always log all failures no matter what the client specified. 2 = Never log any failures no matter what client specified. If the registry entry is missing the default will be 0. If you need to control customer applications, it is recommended that you set this value to 0 and write the client code to override failures. It is strongly recommended that you do not set the value to 2. If event logging is disabled this will make it more difficult to diagnose problems.

Defined Terms

CLSCTX_SERVER
Indicates server code, whether in-process, local, or remote. This definition ORs CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER, and CLSCTX_REMOTE_SERVER.
CLSCTX_ALL
Indicates all class contexts. This definition ORs CLSCTX_INPROC_HANDLER and CLSCTX_SERVER.

Remarks

Values from the CLSCTX enumeration are used in activation calls (CoCreateInstance, CoCreateInstanceEx, CoGetClassObject, etc.) to indicate the preferred execution contexts – in-process, local, or remote – in which an object is to be run. They are also used in calls to CoRegisterClassObject to indicate the set of execution contexts in which a class object is to be made available for requests to construct instances (IClassFactory::CreateInstance).

To indicate that more than one context is acceptable, you can string multiple values together with Boolean ORs. The contexts are tried in the order in which they are listed.

The following table shows how other OLE functions and methods that call CoGetClassObject use the CLSCTX values:

Function Called | Context Flag Used
OleLoad | CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER Putting an OLE object into the loaded state requires in-process access; but, it doesn't matter if all of the object's function is presently available.
IRunnableObject::Run | CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER Running an OLE object requires connecting to the full code of the object wherever it is located.
CoUnMarshalInterface | CLSCTX_INPROC_HANDLER Unmarshaling needs the form of the class designed for remote access.
IMoniker::BindToObject, for a file moniker created through a call to CreateFileMoniker | In this case, uses CLSCTX_SERVER interally to create the instance after calling GetClassFile to determine the class to be instantiated.

The CLSCTX_REMOTE_SERVER value is added to the CLSCTX enumeration for distributed COM. The CLSCTX_SERVER and CLSCTX_ALL constants are further updated to include the CLSCTX_REMOTE_SERVER value.

Given a set of CLSCTX flags, dwClsCtx, the execution context to be used depends on the availability of registered class codes and other parameters according to the following algorithm:

The first part of the processing determines whether CLSCTX_REMOTE SERVER should be specified as follows:

  1. If the call specifies either
    1. an explicit COSERVERINFO structure indicating a machine different from the current machine, or
    2. there is no explicit COSERVERINFO structure specified, but the specified class is registered with either the RemoteServerName or ActivateAtStorage named-value.

    then CLSCTX_REMOTE_SERVER is implied and is added to dwClsCtx. The second case allows applications written prior to the release of distributed COM to be the configuration of classes for remote activation to be used by client applications available prior to DCOM and the CLSCTX_REMOTE_SERVER flag. The cases in which there would be no explicit COSERVERINFO structure are 1) The value is specified as NULL, or 2) It is not one of the function parameters, as would be the case in calls to CoCreateInstance or CoGetClassObject in existing applications.

  2. If the explicit COSERVERINFO parameter indicates the current machine, CLSCTX_REMOTE_SERVER is removed (if present) from dwClsCtx.

The rest of the processing proceeds by looking at the value(s) of dwClsCtx in the following sequence.

  1. If dwClsCtx includes CLSCTX_REMOTE_SERVER and no COSERVERINFO parameter is specified, if the activation request indicates a persistent state from which to initialize the object (with CoGetInstanceFromFile, CoGetInstanceFromIStorage, or, for a file moniker, in a call to IMoniker::BindToObject) and the class has an ActivateAtStorage sub-key or no class registry information whatsoever, the request to activate and initialize is forwarded to the machine where the persistent state resides. (Refer to the remote activation functions listed in the See Also section for details.)
  2. If dwClsCtx includes CLSCTX_INPROC_SERVER, the class code in the DLL found under the class's InprocServer32 key is used if this key exists. The class code will run within the same process as the caller.
  3. If dwClsCtx includes CLSCTX_INPROC_HANDLER, the class code in the DLL found under the class's InprocHandler32 key is used if this key exists. The class code will run within the same process as the caller.
  4. If dwClsCtx includes CLSCTX_LOCAL_SERVER, the class code in the Win32 service found under the class's LocalService key is used if this key exists. If no Win32 service is specified, but an EXE is specified under that same key, the class code associated with that EXE is used. The class code (in either case) will be run in a separate service process on the same machine as the caller.
  5. If dwClsCtx is set to CLSCTX_REMOTE_SERVER and an additional COSERVERINFO parameter to the function specifies a particular remote machine, a request to activate is forwarded to this remote machine with dwClsCtx modified to be CLSCTX_LOCAL_SERVER. The class code will run in its own process on this specific machine, which must be different from that of the caller.
  6. Finally, if dwClsCtx includes CLSCTX_REMOTE_SERVER and no COSERVERINFO parameter is specified, if a machine name is given under the class's RemoteServerName named-value, the request to activate is forwarded to this remote machine with dwClsCtx modified to be CLSCTX_LOCAL_SERVER. The class code will run in its own process on this specific machine, which must be different from that of the caller.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in wtypes.h.

See Also

CoCreateInstance, CoGetClassObject, CoRegisterClassObject, CoGetInstanceFromFile, CoGetInstanceFromIStorage, CoCreateInstanceEx, COSERVERINFO structure, Creating an Object through a Class Object, Registering a Running EXE Server


COINIT

A set of values from the COINIT enumeration is passed as the dwCoInit parameter to CoInitializeEx. This value determines the concurrency model used for incoming calls to objects created by this thread. This concurrency model can be either apartment-threaded or multi-threaded.

The COINIT enumeration is defined as follows:

typedef enum tagCOINIT{
  COINIT_APARTMENTTHREADED = 0x2,  // Apartment model
  COINIT_MULTITHREADED     = 0x0,  // OLE calls objects on any thread.
  COINIT_DISABLE_OLE1DDE   = 0x4,  // Don't use DDE for Ole1 support.
  COINIT_SPEED_OVER_MEMORY = 0x8,  // Trade memory for speed.
} COINIT;

Elements

COINIT_MULTITHREADED
Initializes the thread for multi-threaded object concurrency (see Remarks).
COINIT_APARTMENTTHREADED
Initializes the thread for apartment-threaded object concurrency (see Remarks).
COINIT_DISABLE_OLE1DDE
Disables DDE for Ole1 support.
COINIT_SPEED_OVER_MEMORY
Trades memory for speed.

Remarks

When a thread is initialized through a call to CoInitializeEx, you choose whether to initialize it as apartment-threaded or multi-threaded by designating one of the members of COINIT as its second parameter. This designates how incoming calls to any object created by that thread are handled, that is, the object's concurrency.

Apartment-threading, while allowing for multiple threads of execution, serializes all incoming calls by requiring that calls to methods of objects created by this thread always run on the same thread – the apartment/thread that created them. In addition, calls can arrive only at message-queue boundaries (i.e., only during a PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serialization, it is not typically necessary to write concurrency control into the code for the object, other than to avoid calls to PeekMessage and SendMessage during processing that must not be interrupted by other method invocations or calls to other objects in the same apartment/thread.

Multi-threading (also called free-threading) allows calls to methods of objects created by this thread to be run on any thread. There is no serialization of calls – many calls may occur to the same method or to the same object or simultaneously. Multi-threaded object concurrency offers the highest performance and takes the best advantage of multi-processor hardware for cross-thread, cross-process, and cross-machine calling, since calls to objects are not serialized in any way. This means, however, that the code for objects must enforce its own concurrency model, typically through the use of Win32 synchronization primitives, such as critical sections, semaphores, or mutexes. In addition, because the object doesn't control the lifetime of the threads that are accessing it, no thread-specific state may be stored in the object (in Thread-Local-Storage).

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objbase.h.

See Also

CoInitializeEx, Processes, Apartments, and Threads


COWAIT_FLAGS

The values of the COWAIT_FLAGS enumeration specify the behavior of the CoWaitForMultipleHandles function. This enumeration is also used by the ISynchronize::Wait and ISynchronizeContainer::WaitMultiple methods, which typically call CoWaitForMultipleHandles.

typedef enum tagCOWAIT_FLAGS
{
   COWAIT_WAITALL          = 0x00000001,
   COWAIT_ALERTABLE        = 0x00000002
}COWAIT_FLAGS;

Values

COWAIT_WAITALL
If set, the call to CoWaitForMultipleHandles will return S_OK only when all handles associated with the synchronization object have been signaled. If not set, the call will return S_OK as soon as any handle associated with the synchronization object has been signaled.
COWAIT_ALERTABLE
If set, the call to CoWaitForMultipleHandles will return S_OK if an asynchronous procedure call (APC) has been queued to the calling thread with a call to the Win32 function QueueUserAPC, even if no handle has been signaled.


EOC_ChangeType

The EOC_ChangeType enumeration constants indicate the nature of the change to an event object. Values from this enumeration are passed to methods of the IEventObjectChange interface.

typedef enum
{
    EOC_NewObject       //A new object was added
    EOC_ModifiedObject  //An object was modified
    EOC_DeletedObject   //An object was deleted
}EOC_ChangeType;

Elements

EOC_NewObject
A new object was added.
EOC_ModifiedObject
An existing object was modified.
EOC_DeletedObject
An existing object was deleted.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in eventsys.h.

See Also

IEventObjectChange


EOLE_AUTHENTICATION_CAPABILITIES

The EOLE_AUTHENTICATION_CAPABILITIES enumeration constants set various capabilities in CoInitializeSecurity and IClientSecurity::SetBlanket (or its helper function CoSetProxyBlanket).

It has the following definition:

typedef enum tagEOLE_AUTHENTICATION_CAPABILITIES
{
    EOAC_NONE                    = 0x0,
    EOAC_MUTUAL_AUTH             = 0x1,
    EOAC_SECURE_REFS             = 0x2,
    EOAC_ACCESS_CONTROL          = 0x4,
    EOAC_APPID                   = 0x8,
    EOAC_DYNAMIC                 = 0x10,
    EOAC_STATIC_CLOAKING         = 0x20,
    EOAC_DYNAMIC_CLOAKING        = 0x40,
    EOAC_ANY_AUTHORITY           = 0x80,
    EOAC_MAKE_FULLSIC            = 0x100,
    EOAC_REQUIRE_FULLSIC         = 0x200,
    EOAC_AUTO_IMPERSONATE        = 0x400,
    EOAC_DEFAULT                 = 0x800,
    EOAC_DISABLE_AAA             = 0x1000,
    EOAC_NO_CUSTOM_MARSHAL       = 0x2000
} EOLE_AUTHENTICATION_CAPABILITIES;

Elements

EOAC_NONE
Indicates that no capability flags are set.
EOAC_MUTUAL_AUTH
Unused. If this flag is specified, it will be ignored. Support for mutual authentication is automatically provided by some authentication services. See COM and Security Packages for more information.
EOAC_SECURE_REFS
Authenticates distributed reference count calls to prevent malicious users from releasing objects. If this flag is set, which can be done only in a call to CoInitializeSecurity by the client, the authentication level (in dwAuthnLevel) cannot be set to none.
EOAC_ACCESS_CONTROL
Indicates that the pVoid parameter to CoInitializeSecurity is a pointer to an IAccessControl interface on an access control object. When DCOM makes security checks, it calls IAccessControl::IsAccessAllowed. This flag is set only by the server.
EOAC_APPID
Indicates that the pVoid parameter to CoInitializeSecurity is a pointer to a GUID that is an AppID. The CoInitializeSecurity function looks up the AppID in the registry and reads the security settings from there. If this flag is set, all other parameters to CoInitializeSecurity are ignored and must be zero. Only the server can set this flag. For more information about this capability flag, see the Remarks section below.
EOAC_DYNAMIC
This flag is not currently supported.
EOAC_STATIC_CLOAKING
Sets static cloaking. When this flag is set, DCOM uses the thread token (if present) when determining the client's identity. However, the client's identity is determined on the first call on each proxy (if SetBlanket is not called) and each time CoSetProxyBlanket is called on the proxy. This flag can be set by clients only, for use with Windows 2000 and later versions. For more information about static cloaking, see Cloaking.
EOAC_DYNAMIC_CLOAKING
Sets dynamic cloaking. When this flag is set, DCOM uses the thread token (if present) when determining the client's identity. On each call to a proxy, the current thread token is examined to determine whether the client's identity has changed (incurring an additional performance cost) and the client is authenticated again only if necessary. Dynamic cloaking can be set by clients only, for use with Windows 2000 and later versions. For more information about dynamic cloaking, see Cloaking.
EOAC_ANY_AUTHORITY
Tells DCOM to trust an SChannel server certificate even if the top level certificate authority is not trusted. That is, when a call arrives using SChannel, DCOM will allow the call even if the top level certificate authority is not installed on the machine. Care must be taken when specifying this flag because having an untrusted root severely compromises session security. Applications should use this flag only if manual validation of the server certificate is performed. This flag can be set by clients only and can be used with Windows 2000 and later versions.
EOAC_MAKE_FULLSIC
Causes DCOM to extract SChannel principal names in fullsic format from certificates. This flag is for use with Windows 2000 and later versions. For more information about the fullsic form, see Principal Names.
EOAC_REQUIRE_FULLSIC
Causes DCOM to fail CoSetProxyBlanket calls where an SChannel principal name is specified in any format other than fullsic. This flag is currently for clients only. For more information about the fullsic form, see Principal Names.
EOAC_AUTO_IMPERSONATE
This flag is not currently supported.
EOAC_DEFAULT
Tells DCOM to use the valid capabilities from the call to CoInitializeSecurity. If CoInitializeSecurity was not called, EOAC_NONE will be used for the capabilities flag. This value can be used with Windows 2000 and later versions. This flag can be set only by clients in a call to IClientSecurity::SetBlanket or CoSetProxyBlanket.
EOAC_DISABLE_AAA
Causes any activation where a server process would be launched under the caller's identity (activate-as-activator) to fail with E_ACCESSDENIED. This value, which can be specified only in a call to CoInitializeSecurity by the client, allows an application that runs under a privileged account (such as LocalSystem) to prevent its identity from being used to launch untrusted components. It can be used with Windows 2000 and later versions.
EOAC_NO_CUSTOM_MARSHAL
As of Windows 2000, specifying this flag contributes to better server security when using DCOM because, by unmarshaling CLSIDs implemented only in ole32.dll and Component Services, it reduces the chances of executing arbitrary DLLs. DCOM marshals references to objects by constructing OBJREFs that contain CLSIDs. CLSIDs are vulnerable to security attacks during unmarshaling because arbitrary DLLs could be loaded. Therefore, EOAC_NO_CUSTOM_MARSHAL prevents the use of CLSIDs from OBJREFS that are not implemented in ole32.dll and COM+ Services. Any service that is critical to system operation is expected to set this flag. Processes that have declared EOAC_NO_CUSTOM_MARSHAL in their security capabilities by calling CoInitializeSecurity can also use CLSIDs that implement the CATID_Marshaler category ID.

Remarks

When the EOAC_APPID flag is set, CoInitializeSecurity looks for the authentication level under the AppID. If the authentication level is not found, it looks for the default authentication level. If the default authentication level is not found, it generates a default authentication level of connect. If the authentication level is not RPC_C_AUTHN_LEVEL_NONE, CoInitializeSecurity looks for the access permission value under the AppID. If not found, it looks for the default access permission value. If not found, it generates a default access permission. All the other security settings are determined the same way as for a legacy application.

The CoInitializeSecurity function returns an error if both the EOAC_APPID and EOAC_ACCESS_CONTROL flags are set.

The IClientSecurity::SetBlanket method and CoSetProxyBlanket function return an error if any of the following flags are set in the capabilities parameter: EOAC_SECURE_REFS, EOAC_ACCESS_CONTROL, EOAC_APPID, EOAC_DYNAMIC, EOAC_REQUIRE_FULLSIC, EOAC_DISABLE_AAA, or EOAC_NO_CUSTOM_MARSHAL.

CoInitializeSecurity and IClientSecurity::SetBlanket return errors if both cloaking flags are set or if either flag is set when SChannel is the authentication service.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in objidl.h.

See Also

CoInitializeSecurity, IClientSecurity::SetBlanket, CoSetProxyBlanket, IAccessControl


EXTCONN

The EXTCONN enumeration specifies the type of external connection existing on an embedded object. Currently, the only supported type is EXTCONN_STRONG, meaning that the external connection is a link. This EXTCONN constant is used in the IExternalConnection::AddConnection and IExternalConnection::ReleaseConnection methods.

typedef enum tagEXTCONN
{
    EXTCONN_STRONG      = 0X0001,
    EXTCONN_WEAK        = 0X0002,
    EXTCONN_CALLABLE    = 0X0004
} EXTCONN;

Elements

EXTCONN_STRONG
If this value is specified, the external connection must keep the object alive until all strong external connections are cleared through IExternalConnection::ReleaseConnection.
EXTCONN_WEAK
This value is currently not used.
EXTCONN_CALLABLE
This value is currently not used.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


MKRREDUCE

The MKRREDUCE enumeration constants are used to specify how far the moniker should be reduced. They are used in the IMoniker::Reduce method.

typedef enum tagMKRREDUCE
{
    MKRREDUCE_ONE            = 3<<16,
    MKRREDUCE_TOUSER         = 2<<16,
    MKRREDUCE_THROUGHUSER    = 1<<16,
    MKRREDUCE_ALL            = 0
} MKRREDUCE;

Elements

MKRREDUCE_ONE
Performs only one step of reducing the moniker. In general, the caller must have specific knowledge about the particular kind of moniker to take advantage of this option.
MKRREDUCE_TOUSER
Reduces the moniker to a form that the user identifies as a persistent object. If no such point exists, then this option should be treated as MKRREDUCE_ALL.
MKRREDUCE_THROUGHUSER
Reduces the moniker to where any further reduction would reduce it to a form that the user does not identify as a persistent object. Often, this is the same stage as MKRREDUCE_TOUSER.
MKRREDUCE_ALL
Reduces the moniker until it is in its simplest form, that is, reduce it to itself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::Reduce


MKSYS

The MKSYS enumeration constants indicate the moniker's class. They are returned from the IMoniker::IsSystemMoniker method. MKSYS is defined in Objidl.h.

typedef enum tagMKSYS
{
    MKSYS_NONE                = 0,
    MKSYS_GENERICCOMPOSITE    = 1,
    MKSYS_FILEMONIKER         = 2,
    MKSYS_ANTIMONIKER         = 3,
    MKSYS_ITEMMONIKER         = 4,
    MKSYS_POINTERMONIKER      = 5,
    MKSYS_CLASSMONIKER        = 7
} MKSYS;

Elements

MKSYS_NONE
Indicates a custom moniker implementation.
MKSYS_GENERICCOMPOSITE
Indicates the system's generic composite moniker class.
MKSYS_FILEMONIKER
Indicates the system's file moniker class.
MKSYS_ANTIMONIKER
Indicates the system's anti-moniker class.
MKSYS_ITEMMONIKER
Indicates the system's item moniker class.
MKSYS_POINTERMONIKER
Indicates the system's pointer moniker class.
MKSYS_CLASSMONIKER
Indicates the system's class moniker class.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMoniker::IsSystemMoniker


MSHCTX

The MSHCTX enumeration constants specify the destination context, which is the process in which the unmarshaling is to be done. These flags are used in the IMarshal and IStdMarshalInfo interfaces and in the CoMarshalInterface and CoGetStandardMarshal functions.

typedef enum tagMSHCTX
{
    MSHCTX_LOCAL               = 0,
    MSHCTX_NOSHAREDMEM         = 1,
    MSHCTX_DIFFERENTMACHINE    = 2,
    MSHCTX_INPROC              = 3
} MSHCTX;

Elements

MSHCTX_LOCAL
The unmarshaling process is local and has shared memory access with the marshaling process.
MSHCTX_NOSHAREDMEM
The unmarshaling process does not have shared memory access with the marshaling process.
MSHCTX_DIFFERENTMACHINE
The unmarshaling process is on a different machine. The marshaling code cannot assume that a particular piece of application code is installed on that machine.
MSHCTX_INPROC
The unmarshaling will be done in another apartment in the same process.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in wtypes.h.

See Also

CoGetStandardMarshal, CoMarshalInterface, IMarshal, IStdMarshalInfo


MSHLFLAGS

The MSHLFLAGS enumeration constants determine why the marshaling is to be done. These flags are used in the IMarshal interface and the CoMarshalInterface and CoGetStandardMarshal functions.

typedef enum tagMSHLFLAGS
{
    MSHLFLAGS_NORMAL         = 0,
    MSHLFLAGS_TABLESTRONG    = 1,
    MSHLFLAGS_TABLEWEAK      = 2
    MSHLFLAGS_NOPING         = 3
} MSHLFLAGS;

Elements

MSHLFLAGS_NORMAL
The marshaling is occurring because an interface pointer is being passed from one process to another. This is the normal case. The data packet produced by the marshaling process will be unmarshaled in the destination process. The marshaled data packet can be unmarshaled just once, or not at all. If the receiver unmarshals the data packet successfully, the CoReleaseMarshalData function is automatically called on the data packet as part of the unmarshaling process. If the receiver does not or cannot unmarshal the data packet, the sender must call the CoReleaseMarshalData function on the data packet.
MSHLFLAGS_TABLESTRONG
The marshaling is occurring because the data packet is to be stored in a globally accessible table from which it can be unmarshaled one or more times, or not at all. The presence of the data packet in the table counts as a strong reference to the interface being marshaled, meaning that it is sufficient to keep the object alive. When the data packet is removed from the table, the table implementer must call the CoReleaseMarshalData function on the data packet.

MSHLFLAGS_TABLESTRONG is used by the RegisterDragDrop function when registering a window as a drop target. This keeps the window registered as a drop target no matter how many times the end user drags across the window. The RevokeDragDrop function calls CoReleaseMarshalData.

MSHLFLAGS_TABLEWEAK
The marshaling is occurring because the data packet is to be stored in a globally accessible table from which it can be unmarshaled one or more times, or not at all. However, the presence of the data packet in the table acts as a weak reference to the interface being marshaled, meaning that it is not sufficient to keep the object alive. When the data packet is removed from the table, the table implementer must call the CoReleaseMarshalData function on the data packet.

MSHLFLAGS_TABLEWEAK is typically used when registering an object in the Running Object Table (ROT). This prevents the object's entry in the ROT from keeping the object alive in the absence of any other connections. See IRunningObjectTable::Register for more information.

MSHLFLAGS_NOPING
Adding this flag to an original object marshaling (as opposed to marshaling a proxy) will disable the ping protocol for that object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in wtypes.h.

See Also

CoGetStandardMarshal, CoMarshalInterface, CoReleaseMarshalData, IMarshal


PENDINGMSG

The PENDINGMSG enumeration constants are return values of IMessageFilter::MessagePending.

Defined in the IMessageFilter interface (msgflt.idl).

typedef enum tagPENDINGMSG
{
    PENDINGMSG_CANCELCALL        = 0,
    PENDINGMSG_WAITNOPROCESS     = 1,
    PENDINGMSG_WAITDEFPROCESS    = 2
} PENDINGMSG;

Elements

PENDINGMSG_CANCELCALL
Cancel the outgoing call.
PENDINGMSG_WAITNOPROCESS
Wait for the return and don't dispatch the message.
PENDINGMSG_WAITDEFPROCESS
Wait and dispatch the message.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMessageFilter::MessagePending


PENDINGTYPE

The PENDINGTYPE enumeration constants indicate the level of nesting in the IMessageFilter::MessagePending method.

typedef enum tagPENDINGTYPE
{
    PENDINGTYPE_TOPLEVEL    = 1,
    PENDINGTYPE_NESTED      = 2
} PENDINGTYPE;

Elements

PENDINGTYPE_TOPLEVEL
Top-level call.
PENDINGTYPE_NESTED
Nested call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMessageFilter::MessagePending


REGCLS

The REGCLS enumeration defines values used in CoRegisterClassObject to control the type of connections to a class object. It is defined as follows:

typedef enum tagREGCLS
{
    REGCLS_SINGLEUSE         = 0,
    REGCLS_MULTIPLEUSE       = 1,
    REGCLS_MULTI_SEPARATE    = 2,
    REGCLS_SUSPENDED         = 4,
    REGCLS_SURROGATE         = 8,
} REGCLS;

Elements

REGCLS_SINGLEUSE
Once an application is connected to a class object with CoGetClassObject, the class object is removed from public view so that no other applications can connect to it. This value is commonly used for single document interface (SDI) applications. Specifying this value does not affect the responsibility of the object application to call CoRevokeClassObject; it must always call CoRevokeClassObject when it is finished with an object class.
REGCLS_MULTIPLEUSE
Multiple applications can connect to the class object through calls to CoGetClassObject. If both the REGCLS_MULTIPLEUSE and CLSCTX_LOCAL_SERVER are set in a call to CoRegisterClassObject, the class object is also automatically registered as an in-process server, whether or not CLSCTX_INPROC_SERVER is explicitly set.
REGCLS_MULTI_SEPARATE
Useful for registering separate CLSCTX_LOCAL_SERVER and CLSCTX_INPROC_SERVER class factories through calls to CoGetClassObject. If REGCLS_MULTI_SEPARATE is set, each execution context must be set separately: CoRegisterClassObject does not automatically register an out-of-process server (for which CLSCTX_LOCAL_SERVER is set) as an in-process server. This allows the EXE to create multiple instances of the object for in-process needs, such as self embeddings, without disturbing its CLSCTX_LOCAL_SERVER registration. If an EXE registers a REGCLS_MULTI_SEPARATE class factory and a CLSCTX_INPROC_SERVER class factory, instance creation calls that specify CLSCTX_INPROC_SERVER in the CLSCTX parameter executed by the EXE would be satisfied locally without approaching SCM. This mechanism is useful when the EXE uses functions such as OleCreate and OleLoad to create embeddings, but at the same does not wish to launch a new instance of itself for the self-embedding case. The distinction is important for embeddings because the default handler aggregates the Proxy Manager by default and the application should override this default behavior by calling OleCreateEmbeddingHelper for the self-embedding case.

If your application need not distinguish between the local and inproc case, you need not register your class factory using REGCLS_MULTI_SEPARATE. In fact, the application incurs an extra network round trip to SCM when it registers its MULTIPLEUSE class factory as MULTI_SEPARATE and does not register another class factory as INPROC_SERVER.

REGCLS_SUSPENDED
Suspends registration and activation requests for the specified CLSID until there is a call to CoResumeClassObjects. This is used typically to register the CLSIDs for servers that can register multiple class objects to reduce the overall registration time, and thus the server application startup time, by making a single call to the SCM, no matter how many CLSIDs are registered for the server.
REGCLS_SURROGATE
The class object is a surrogate process used to run DLL servers. The class factory registered by the surrogate process is not the actual class factory implemented by the DLL server, but a generic class factory implemented by the surrogate. This generic class factory delegates instance creation and marshaling to the class factory of the DLL server running in the surrogate. For further information on DLL Surrogates, see the DllSurrogate registry named-value.

Remarks

In CoRegisterClassObject, members of both the REGCLS and the CLSCTX enumerations, taken together, determine how the class object is registered.

An EXE surrogate (in which DLL servers are run) calls CoRegisterClassObject to register a class factory using a new REGCLS value, REGCLS_SURROGATE.

The following table summarizes the allowable REGCLS value combinations and the object registrations affected by the combinations.

 | REGCLS_ SINGLEUSE | REGCLS_ MULTIPLEUSE | REGCLS_ MULTI_ SEPARATE | Other
CLSCTX_ INPROC_ SERVER | Error | In-process | In-process | Error
CLSCTX_ LOCAL_ SERVER | Local | In-process/ local | Local | Error
Both of the above | Error | In-process/ local | In-process/ local | Error
Other | Error | Error | Error | Error

All class factories for dll surrogates should be registered with REGCLS_SURROGATE set. Do not set REGCLS_SINGLUSE or REGCLS_MULTIPLEUSE when you register a surrogate for DLL servers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.

See Also

CoGetClassObject, CoRegisterClassObject, CoRevokeClassObject, DllGetClassObject, DllSurrogate


RPC_C_AUTHN_xxx

These values specify authentication services by identifying the security package that provides the service, such as NTLMSSP, Kerberos, or SChannel. They are used in the SOLE_AUTHENTICATION_SERVICE and the SOLE_AUTHENTICATION_INFO structures. The SOLE_AUTHENTICATION_SERVICE structure is passed by the server to the CoInitializeSecurity function and can be retrieved by the CoQueryAuthenticationServices function. A pointer to a SOLE_AUTHENTICATION_INFO structure is passed by the client to the CoInitializeSecurity function. For more information on the security packages identified by these values, such as NTLMSSP and Kerberos, see COM and Security Packages.

Note Windows NT 4.0 supports only RPC_C_AUTHN_WINNT.

#define RPC_C_AUTHN_NONE           0
#define RPC_C_AUTHN_DCE_PRIVATE    1
#define RPC_C_AUTHN_DCE_PUBLIC     2
#define RPC_C_AUTHN_DEC_PUBLIC     4
#define RPC_C_AUTHN_GSS_NEGOTIATE  9
#define RPC_C_AUTHN_WINNT         10
#define RPC_C_AUTHN_GSS_SCHANNEL  14
#define RPC_C_AUTHN_GSS_KERBEROS  16
#define RPC_C_AUTHN_MSN           17
#define RPC_C_AUTHN_DPA           18
#define RPC_C_AUTHN_MQ           100
#define RPC_C_AUTHN_DEFAULT      0xFFFFFFFFL

Values

RPC_C_AUTHN_NONE
No authentication.
RPC_C_AUTHN_DCE_PRIVATE
DCE private key authentication.
RPC_C_AUTHN_DCE_PUBLIC
DCE public key authentication.
RPC_C_AUTHN_DEC_PUBLIC
DEC public key authentication. Reserved for future use.
RPC_C_AUTHN_GSS_NEGOTIATE
Snego security support provider.
RPC_C_AUTHN_WINNT
NTLMSSP (Windows NT LAN Manager Security Support Provider).
RPC_C_AUTHN_GSS_SCHANNEL
SChannel security support provider. This authentication service supports SSL 2.0, SSL 3.0, TLS, and PCT. This value can be used with Windows 2000 and later versions.
RPC_C_AUTHN_GSS_KERBEROS
Kerberos security support provider. This value can be used with Windows 2000 and later versions.
RPC_C_AUTHN_MSN
Not currently supported.
RPC_C_AUTHN_DPA
Not currently supported.
RPC_C_AUTHN_MQ
Not currently supported.
RPC_C_AUTHN_DEFAULT
The system default authentication service. When this value is specified on Windows 2000 and later versions, COM uses its normal security blanket negotiation algorithm to pick an authentication service. On Win32-based applications, RPC_C_AUTHN_DEFAULT causes COM to use the NTLMSSP (RPC_C_AUTHN_WINNT) authentication service. For more information, see Security Blanket Negotiation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in rpcdce.h.

See Also

CoInitializeSecurity, CoQueryAuthenticationServices


RPC_C_AUTHN_LEVEL_xxx

Used in the security functions and interfaces to specify an authentication level, which indicates the amount of authentication provided to ensure the integrity of the data. Each level includes the protection provided by the previous levels.

#define RPC_C_AUTHN_LEVEL_DEFAULT       0
#define RPC_C_AUTHN_LEVEL_NONE          1
#define RPC_C_AUTHN_LEVEL_CONNECT       2
#define RPC_C_AUTHN_LEVEL_CALL          3
#define RPC_C_AUTHN_LEVEL_PKT           4
#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5
#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY   6

Values

RPC_C_AUTHN_LEVEL_DEFAULT
Tells DCOM to choose the authentication level using its normal security blanket negotiation algorithm. This behavior occurs in Windows 2000 and later versions. In Windows NT 4.0, this value defaults to RPC_C_AUTHN_LEVEL_CONNECT. For more information, see Security Blanket Negotiation.
RPC_C_AUTHN_LEVEL_NONE
Performs no authentication.
RPC_C_AUTHN_LEVEL_CONNECT
Authenticates the credentials of the client only when the client establishes a relationship with the server. Datagram transports always use RPC_AUTHN_LEVEL_PKT instead.
RPC_C_AUTHN_LEVEL_CALL
Authenticates only at the beginning of each remote procedure call when the server receives the request. Datagram transports use RPC_C_AUTHN_LEVEL_PKT instead.
RPC_C_AUTHN_LEVEL_PKT
Authenticates that all data received is from the expected client.
RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
Authenticates and verifies that none of the data transferred between client and server has been modified.
RPC_C_AUTHN_LEVEL_PKT_PRIVACY
Authenticates all previous levels and encrypts the argument value of each remote procedure call.

Windows 95: While Windows 95 can make calls at any level, it can receive calls only at NONE or CONNECT.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in rpcdce.h.

See Also

IClientSecurity, IServerSecurity


RPC_C_AUTHZ_xxx

These values define what the server authorizes, and they are used by methods of the IClientSecurity interface. They are used in the SOLE_AUTHENTICATION_SERVICE structure, which is retrieved by the CoQueryAuthenticationServices function. The RPC_C_AUTHZ_xxx values are also used in the SOLE_AUTHENTICATION_INFO structure, which in turn is a member of the SOLE_AUTHENTICATION_LIST structure. This structure, which is a list of authentication services, the authorization services they perform, and the authentication information for each service, is passed to the CoInitializeSecurity function and the IClientSecurity::SetBlanket method.

#define RPC_C_AUTHZ_NONE    0
#define RPC_C_AUTHZ_NAME    1
#define RPC_C_AUTHZ_DCE     2
#define RPC_C_AUTHZ_DEFAULT 0xffffffff

Values

RPC_C_AUTHZ_NONE
Server performs no authorization. Currently RPC_C_AUTHN_WINNT, RPC_C_AUTHN_GSS_SCHANNEL, and RPC_C_AUTHN_GSS_KERBEROS all use only RPC_C_AUTHZ_NONE.
RPC_C_AUTHZ_NAME
Server performs authorization based on the client's principal name.
RPC_C_AUTHZ_DCE
Server performs authorization checking using the client's DCE privilege attribute certificate (PAC) information, which is sent to the server with each remote procedure call made using the binding handle. Generally, access is checked against DCE access control lists (ACLs).
RPC_C_AUTHZ_DEFAULT
Tells DCOM to choose the authorization service using its normal security blanket negotiation algorithm. This value can be used with Windows 2000 and later versions. For more information, see Security Blanket Negotiation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in rpcdce.h.

See Also

SOLE_AUTHENTICATION_SERVICE


RPC_C_IMP_LEVEL_xxx

Used in the security functions and interfaces to specify an impersonation level, which indicates the amount of authority given to the server when it is impersonating the client.

#define RPC_C_IMP_LEVEL_DEFAULT      0
#define RPC_C_IMP_LEVEL_ANONYMOUS    1
#define RPC_C_IMP_LEVEL_IDENTIFY     2
#define RPC_C_IMP_LEVEL_IMPERSONATE  3
#define RPC_C_IMP_LEVEL_DELEGATE     4

Values

RPC_C_IMP_LEVEL_DEFAULT
Tells DCOM to choose the impersonation level by using its normal security blanket negotiation algorithm. This value can be used with Windows 2000 and later versions. For more information, see Security Blanket Negotiation.
RPC_C_IMP_LEVEL_ANONYMOUS
(Not supported in this release.) The client is anonymous to the server. The server process can impersonate the client, but the impersonation token will not contain any information and cannot be used for anything.
RPC_C_IMP_LEVEL_IDENTIFY
The server can obtain the client's identity. The server can impersonate the client for ACL checking, but it cannot access system objects as the client.

Note GetUserName will fail while impersonating at identify level. The workaround is to impersonate, OpenThreadToken, revert, call GetTokenInformation, and finally, call LookupAccountSid.

RPC_C_IMP_LEVEL_IMPERSONATE
The server process can impersonate the client's security context while acting on behalf of the client. This level of impersonation can be used to access local resources such as files. When impersonating at this level, the impersonation token can only be passed across one machine boundary. The SChannel authentication service only supports this level of impersonation.
RPC_C_IMP_LEVEL_DELEGATE
Supported for Windows 2000 and later versions. The server process can impersonate the client's security context while acting on behalf of the client. The server process can also make outgoing calls to other servers while acting on behalf of the client, using Cloaking. The server may use the client's security context on other machines to access local and remote resources as the client. When impersonating at this level, the impersonation token can be passed across any number of machine boundaries.

Comments

Only the RPC_C_IMP_LEVEL_IDENTIFY and RPC_C_IMP_LEVEL_IMPERSONATE levels are supported in Windows NT 4.0. In Windows 2000, RPC_C_IMP_LEVEL_DELEGATE is supported as well.

Using CoSetProxyBlanket, the client sets the impersonation level and proxy identity that will be available when a server calls CoImpersonateClient. The identity the server will see when impersonating takes place is described in How Cloaking Affects Client Identity. Note that when making a call while impersonating, the callee will normally receive the caller's process token, not the caller's impersonation token. To receive the caller's impersonation token, the caller must enable cloaking.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in rpcdce.h.

See Also

CoInitializeSecurity, Cloaking and Impersonation Levels


SERVERCALL

The SERVERCALL enumeration constants indicate the status of server call — returned by IMessageFilter::HandleInComingCall and passed to IMessageFilter::RetryRejectedCall.

Defined in the IMessageFilter interface (msgflt.idl).

typedef enum tagSERVERCALL
{
    SERVERCALL_ISHANDLED     = 0,
    SERVERCALL_REJECTED      = 1,
    SERVERCALL_RETRYLATER    = 2
} SERVERCALL;

Elements

SERVERCALL_ISHANDLED
The object may be able to process the call.
SERVERCALL_REJECTED
The object cannot handle the call due to an unforeseen problem, such as network unavailability.
SERVERCALL_RETRYLATER
The object cannot handle the call at this time. For example, an application might return this value when it is in a user-controlled modal state.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IMessageFilter::HandleInComingCall, IMessageFilter::RetryRejectedCall


COM Registry Entries

To become functional, COM servers and containers must add information to the system registry. Most of that information is stored in keys and named values under the HKEY_LOCAL_MACHINE\SOFTWARE\Classes and HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE keys. This section discusses the registry keys and information required to support COM functionality. For general information on the registry, the structure of registry entries, and registry functions, search for Registry topics in the Platform SDK.

Note that HKEY_CLASSES_ROOT is equivalent to HKEY_LOCAL MACHINE\SOFTWARE\Classes and that these keys can be used interchangeably. HKEY_CLASSES_ROOT also provides compatibility with Windows 3.1 and Windows 95.


HKEY_LOCAL_MACHINE\SOFTWARE\Classes

The subkeys and named values associated with key contain information about an application that is needed to support COM functionality. This information includes such topics as supported data formats, compatibility information, programmatic identifiers, distributed com, and controls.


AppID Key

Groups the configuration options, a set of named values, for one or more distributed COM objects into one centralized location in the registry. Distributed COM objects hosted by the same executable are grouped into one AppID to simplify the management of common security and configuration settings. The HKEY_LOCAL_MACHINE\SOFTWARE\Classes key corresponds to the HKEY_CLASSES_ROOT key, which was retained for compatibility with earlier versions of COM.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\
 {AppID_value}\named_value = value

Comments

AppIDs are located in a newly created registry key hierarchy. The AppID_value is a 128-bit Globally Unique Identifier (GUID) that identifies the AppID key.

AppIDs are mapped to executables and classes using two different mechanisms:

  • Classes indicate the corresponding AppID under the CLSID key in a named-value "AppID" of type REG_SZ. This contains the string value corresponding to the AppID_value under the AppID key. This mapping is used during activation.
  • Executables are registered under the AppID key in a named value indicating the module name (such as "MYOLDAPP.EXE"). This named value is of type REG_SZ and contains the string representation of the AppID associated with the executable. This mapping is used to obtain the default access permissions and authentication level.

For COM servers, the mapping is usually generated and written to the registry during the registration process or when running dcomcnfg.exe. However, COM clients that want to set security using the AppID key must create appropriate registry keys and specify the required mapping by calling Win32 registry functions or using regedit. Then values such as AccessPermissions or AuthenticationLevel can be set for the client. For example, suppose the name of your executable for your client process is "YourClient.exe" and you want to set the authentication level to None. You would use guidgen.exe or uuidgen.exe to create the GUID that is the AppID for your executable. Then, you would set values in the registry as shown in the following example, where 00000001 represents an authentication level of None:

[REGEDIT4]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\yourclient.exe]
@="YourClient"
"AppID"="{YourGuid}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{YourGuid}]
@="YourClient"
"AuthenticationLevel"=dword:00000001

Be aware that the AuthenticationLevel named value is supported only on Windows NT 4.0 SP4 and Windows 2000 as well as dcom951.1. For more information about authentication levels, see RPC_C_AUTHN_LEVEL_xxx.

Named Values:

RemoteServerName = value | Sets name of remote server
ActivateAtStorage = value | Configures client to activate on same system as persistent storage
LocalService = value | Sets the application as a Win32 service
ServiceParameters = value | Sets parameters to be passed to a LocalService on invocation
RunAs = value | Sets an application to run only as a given user.
LaunchPermission = value | Sets an ACL that determines who can launch the application
AccessPermission = value | Sets an ACL that determines access
DllSurrogate = path | Specifies that a DLL server is to use a surrogate EXE file. If path is not specified, system-provided surrogate is used
AuthenticationLevel = value | Sets the authentication level for the AppID, overriding LegacyAuthenticationLevel

See Also

CLSID key, COM Registry Entries, Registering COM Servers


RemoteServerName

A server may install the RemoteServerName named-value on client machines to configure the client to request the object be run at a particular machine whenever an activation function is called for which a COSERVERINFO structure is not specified.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\RemoteServerName = server_name

Remarks

As described in the documentation for the CLSCTX enumeration and the COSERVERINFO structure, one of the parameters of the distributed COM activation is a pointer to a COSERVERINFO structure. When this value is not NULL, the information in the COSERVERINFO structure overrides the setting of the RemoteServerName key for the function call.

RemoteServerName allows simple configuration management of client applications – they may be written without hard-coded server names, and can be configured by modifying the RemoteServerName registry values of the classes of objects they use.

See Also

CLSCTX enumeration, COSERVERINFO structure, CoCreateInstanceEx, CoGetInstanceFromFile, CoGetInstanceFromIStorage, Registering COM Servers


ActivateAtStorage

Servers can install this named-value on client machines. When this value is not overridden by certain parameters passed to distributed COM activation API functions, the ActivateAtStorage named-value configures the client to instantiate objects on the same machine as the persistent state they are using or from which they are initialized.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\ActivateAtStorage = value

Remarks

The ActivateAtStorage named-value can be set by the server to configure the client to instantiate its objects on the same machine as the persistent state used or from which the objects are initialized. The value is a REG_SZ; any value beginning with Y or y means that ActivageAtStorage should be used.

When a value is set for ActivateAtStorage, this becomes the default behavior in calls to the CoGetInstanceFromFile and CoGetInstanceFromIStorage functions, as well as to the file moniker implementation of IMoniker::BindToObject.

In all of these calls, specifying a COSERVERINFO structure overrides the setting of ActivateAtStorage for the duration of the function call. The caller can pass COSERVERINFO information to IMoniker::BindToObject through the BIND_OPTS2 structure.

The value set for ActivateAtStorage is also the default behavior when CLSCTX_REMOTE_SERVER is specified if no registry information for the class is installed on the client's machine. Client applications written to take advantage of ActivateAtStorage may therefore require less administration.

The ActivateAtStorage capability provides a transparent way to allow clients to locate running objects on the same machine as the data that they use. This reduces network traffic, because the object performs local file-system calls instead of calls across the network.

See Also

CoGetInstanceFromFile, CoGetInstanceFromIStorage, IMoniker::BindToObject, COSERVERINFO structure, CLSCTX, Registering COM Servers


LocalService

Allows an object to be installed as a Win32 service.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\LocalService = service_name

Remarks

In addition to running as a local server executable (EXE), a COM object may also choose to package itself to run as a Win32 service when activated by a local or remote client. Win32 services support numerous useful and UI-integrated administrative features, including local and remote starting, stopping, pausing, and restarting, as well as the ability to establish the server to run under a specific user account and Window Station, and optionally interactive with the desktop.

An object written as a Win32 service is installed for use by COM by establishing a LocalService named-value under its CLSID key and performing a standard service installation (refer to the Win32 documentation and the SECSVR distributed COM sample application for more information on writing, installing, and debugging Win32 services.). The LocalService named-value must be set to the service name – as configured in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services – as the default REG_SZ value.

Additionally, when LocalService is set, any string assigned to the named REG_SZ value, ServiceParameters, will be passed on the command line to the service as it is being launched.

For example, the following keys register the c:\mydb\mydb.exe executable to launch as the MyDatabase service when activated remotely. When launched as a service, the process receives "-Service" on its command-line.

HKEY_CLASSES_ROOT\APPID\{c5b7ac20-523f-11cf-8117-00aa00389b71}\LocalService = MyDatabase

HKEY_CLASSES_ROOT\APPID\{c5b7ac20-523f-11cf-8117-00aa00389b71}\ServiceParameters = -Service

The service configuration is preferred in many situations where the capabilities of the local and remote service management APIs and user interface might be useful for the services that the object provides. For example, if the object is long-lived, or readily supports concepts such as starting, stopping, resetting, or pausing, leveraging the existing administrative framework of the service architecture should be an obvious choice.

Services can be dynamically configured using the 'Services' application in the Control Panel, and can be configured to run automatically when the machine boots, or to be launched when requested by a client application.

There are several additional points you should be aware of if you are implementing classes as services:

  • The LocalService named-value is used in preference to the LocalServer32 key for local and remote activation requests – if LocalService exists and refers to a valid service, the LocalServer32 key is ignored.
  • Currently only a single instance of a Win32 service may be running at a given time on a machine. COM services must therefore register their class objects on launch using REGCLS_MULTIPLEUSE to support multiple clients.
  • COM services configured to run automatically when a machine boots must include "RPCSS" in their list of dependent services in order to launch and initialize properly.
  • Although services may be configured to interact with the interactive user, they are always run in a separate window-station and desktop from the interactive user. This prevents service processes from being automatically terminated when the interactive user logs off.

See Also

ServiceParameters, Registering COM Servers


ServiceParameters

When an object is written as a Win32 service and installed for use by COM through setting the LocalService named value under the AppID key, the ServiceParameters named-value can be set to pass the given parameter on the command line when the service is launched.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\ServiceParameters = parameter

Remarks

The parameter value for the ServiceParameters named-value is any REG_SZ string value. This string is passed on the command line to the service as it is being launched.

For example, the following keys register the c:\mydb\mydb.exe executable to launch as the MyDatabase service when activated remotely. When launched as a service, the process receives "-Service" on its command-line.

HKEY_CLASSES_ROOT\APPID\{c5b7ac20-523f-11cf-8117-00aa00389b71}\LocalService = MyDatabase

HKEY_CLASSES_ROOT\APPID\{c5b7ac20-523f-11cf-8117-00aa00389b71}\ServiceParameters = -Service

Refer to the Platform SDK documentation about services and the SECSVR sample Distributed COM application for more information about writing, installing, and debugging Win32 services.

See Also

LocalService, Registering COM Servers


RunAs

Configures a class to run under a specific user account when activated by a remote client without being written as a Win32 service. To do this, the RunAs named-value is set for the class to a user-name and optionally a password. These are then used when the Service Control Manager launches its local server process.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\RunAs = value

Remarks

The value specifies the user name, and must either of the form username, domain\username, or the string Interactive User.

Classes configured to RunAs a particular user may not be registered under any other identity, so calls to CoRegisterClassObject with this CLSID will fail unless the process was launched by COM on behalf of an actual activation request.

The user-name is taken from the RunAs named-value under the class's AppID key. If the user-name is "Interactive User", the server is run in the identity of the user currently logged on and is connected to the interactive desktop.

Otherwise, the password is retrieved from a secret and secure portion of the registry available only to administrators of the machine and to the system. The user-name and password are then used to create a logon-session in which the server is run. When launched in this way, the user runs with its own desktop and window-station, and does not share window-handles, the clipboard, or other UI elements with the interactive user or other user running in other user accounts.

To establish a password for a RunAs class, you must use the DCOMCNFG administrative tool installed in the system directory.

For RunAs identities used by DCOM servers, the user account specified in the value must have the rights to log on as a batch job. This right can be added using the NT User Manager, under Policies-User Rights. Click on the Show Advanced User Rights box, select log on as a batch job, and add the RunAs user account.

The RunAs value is not used for servers configured to be run as services. COM services that wish to run under an identity other than LocalSystem should set the appropriate user name and password using the services control panel applet or the service controller functions (For more information about these functions, see the Services section of the Windows Base Services documentation in the Platform SDK.)

See Also

Registering COM Servers


LaunchPermission

Contains data describing the Access Control List (ACL) of the principals that can start new servers for this class. The LaunchPermission named-value may be added under any AppID key to limit activation by remote clients of specific classes.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\LaunchPermission = ACL

Remarks

The LaunchPermission named-value is REG_BINARY. Upon receiving a local or remote request to launch the server of this class, the ACL described by this value is checked while impersonating the client, and its success either allows or disallows the launching of the server. If this value does not exist, the machine-wide DefaultLaunchPermission value is checked in the same way as a default to determine if the class code can be launched.

See Also

DefaultLaunchPermission, Security in COM, CoInitializeSecurity


AccessPermission

Contains data describing the Access Control List (ACL) of the principals that can access instances of this class. This ACL is only used by applications that do not call CoInitializeSecurity.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\AccessPermission = ACL

Remarks

This named-value is a REG_BINARY. It contains data describing the Access Control List (ACL) of the principals that can access instances of this class. Upon receiving a request to connect to an existing object of this class, the ACL is checked by the application being called while impersonating the caller. If the access-check fails the connection is disallowed. If this named-value does not exist, the machine-wide DefaultAccessPermission ACL is tested in an identical manner (see above) as a default to determine if the connection is to be allowed.

See Also

DefaultAccessPermission, Security in COM, CoInitializeSecurity


DllSurrogate

Enables DLL servers to run in a surrogate process. If an empty string is specified, the system-supplied surrogate is used, otherwise the value specifies the path of the surrogate to be used.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\
 {AppID_value}\DllSurrogate = surrogate_path

Note

HKEY_CLASSES_ROOT is equivalent to HKEY_LOCAL MACHINE\SOFTWARE\Classes. These keys can be used interchangeably. HKEY_CLASSES_ROOT also provides compatibility with Microsoft® Windows® 3.1 and Windows 95.

Remarks

The DllSurrogate named value specifies that the class is a DLL that is to be activated in a surrogate EXE process, and the surrogate process to be used. The surrogate_path value is of type REG_SZ. To use the system-supplied generic surrogate process, set surrogate_path to an empty string or NULL. To specify another surrogate process, set surrogate_path to the path of the surrogate. As in the specification of the path of an EXE server under the LocalServer32 key, a full path specification is not necessary. The surrogate EXE must be written to properly communicate with the DCOM service as described in Writing a Custom Surrogate.

The DllSurrogate named value must be present for a DLL server to be activated in a surrogate. Activation refers to a call to CoGetClassObject, CoCreateInstance[Ex], CoGetInstanceFromFile, CoGetInstanceFromIStorage, or IMoniker::BindToObject. DLL servers are easy to write using standard development tools like Microsoft® Visual Basic®, Microsoft® Visual J++™, and Microsoft Visual Studio®. Running DLLs in a surrogate process provides the benefits of an executable implementation, including fault isolation, the ability to serve multiple clients simultaneously, and allowing the server to provide services to remote clients in a distributed environment.

See Also

AppID key, ISurrogate, CoRegisterSurrogate, DLL Surrogates, Using the System-Supplied Surrogate, DLL Server Requirements, Registering the DLL Server for Surrogate Activation


AuthenticationLevel

Sets the authentication level for applications that do not call CoInitializeSecurity or for applications that call CoInitializeSecurity and specify an AppID. This named-value is only available on Windows NT 4.0 SP4 and Windows 2000.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Classes\AppID\{GUID}
 AuthenticationLevel= authentication_level

Remarks

The AuthenticationLevel named-value is a REG_DWORD that sets the level of authentication for all applications that do not call CoInitializeSecurity or for applications that call CoInitializeSecurity and specify an AppID.

The AuthenticationLevel value is similar to the LegacyAuthenticationLevel value. If the AuthenticationLevel value is present, it will be used instead of the LegacyAuthenticationLevel value for that AppID.

Values are from 1 through 6, and correspond to the RPC_C_AUTHN_LEVEL_xxx constants:

Value | Meaning
1 | RPC_C_AUTHN_LEVEL_NONE
2 | RPC_C_AUTHN_LEVEL_CONNECT
3 | RPC_C_AUTHN_LEVEL_CALL
4 | RPC_C_AUTHN_LEVEL_PKT
5 | RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
6 | RPC_C_AUTHN_LEVEL_PKT_PRIVACY

If the AuthenticationLevel value is of the wrong type or out of range, CoInitializeSecurity will fail, causing interface marshaling to fail. This will prevent the application from making any calls at all (cross-apartment, cross-thread, cross-process, or cross-machine).

The AuthenticationLevel and AccessPermission values under the AppID are independent. If one is not present, the default will be used. The following rules list the interaction between the AuthenticationLevel value and the AccessPermission value.

  • If the AuthenticationLevel is NONE, the AccessPermission and DefaultAccessPermission values are ignored (for that application).
  • If the AuthenticationLevel is not present and the LegacyAuthenticationLevel is NONE, the AccessPermission and DefaultAccessPermission values are ignored (for that application).

See Also

CoInitializeSecurity, LegacyAuthenticationLevel, RPC_C_AUTHN_LEVEL_xxx, Registering COM Servers, Security in COM


CLSID Key

A CLSID is a globally unique identifier that identifies a COM class object. If your server or container allows linking to its embedded objects, then you need to register a CLSID for each supported class of objects.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID = <CLSID>

Value Entries

<CLSID>
Specifies a name that can be displayed in the user interface.

Remarks

The CLSID key contains information used by the default COM handler to return information about a class when it is in the running state.

To obtain a CLSID for your application, you can use the UUIDGEN.EXE found in the \TOOLs directory of the COM Toolkit, or use CoCreateGuid.

The CLSID is a 128 bit number, spelled in hex, within a pair of brarces.

SubKeys and Named Values

\CLSID = <CLSID>

\<CLSID> | Human readable name.
\ AppID | Application identifiers.
\ AutoConvertTo | Automatic object class conversion.
\ AutoTreatAs | Assigns the TreatAs value.
\ AuxUserType | Identifies object as a control.
\ Control | Displayable application name.
\ Conversion | Conversion used by the Convert dialog.
\ DataFormats | Formats supported by applications.
\ DefaultIcon | Provides default icon information.
\ InprocHandler | Registers a 16-bit handler DLL.
\ InprocHandler32 | Registers a 32-bit handler DLL.
\ InprocServer | Registers a 16-bit in-process server DLL.
\ InprocServer32 | Registers a 32-bit in-process server DLL.
\ Insertable | Indicates object is insertable in COM applications.
\ Interface | Associates interface name with IID.
\ LocalServer | Full path to a 16- or 32-bit application.
\ LocalServer32 | Full path to a 32-bit application.
\ MiscStatus | Default status used for all aspects.
\ ProgID | Programmatic identifier for a class.
\ ToolBoxBitmap32 | Module name and resourdeID for a 16 x 16 bitmap.
\ TreatAs | Specifies the CLSID of a class that can emulate the current class.
\ Verb | Verbs associated with an application.
\ Version | Version number of the control.

See Also

CoCreateGuid


<CLSID>

A globally unique identifier (GUID) used to map information about a component class.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\<CLSID>= HumanReadableName

Value Entries

HumanReadableName
Specifies a name that can be displayed in the user interface.

Remarks

COM uses the information mapped by the CLSID to locate and create an instance the object associated with the CLISID.

See Also

CoCreateGuid


AppID

Associate an AppID with a CLSID.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\AppID = <CLSID>

Value Entries

<CLSID>
The AppID to associate with this CLSID.

See Also

AppID

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


AutoConvertTo

Specifies the automatic conversion of a given class of objects (ClsidOld) to a new class of objects (pClsidNew).

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\AutoConvertTo = <CLSID>

Value Entries

<CLSID>
The class identifier of the object to which a given object or class of objects should be converted.

Remarks

This key is typically used to automatically convert files created by an older version of an application to a newer version of the application.

See Also

OleGetAutoConvert, OleDoAutoConvert, OleSetAutoConvert


AutoTreatAs

Automatically sets the CLSID for the TreatAs key to the specified value.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\AutoTreatAs = <CSID>

Value Entries

<MCLSID>
The CLSID that will automatically be assigned to the TreatAs entry.

See Also

CoTreatAsClass


AuxUserType

Specifies an application's short display name and application names.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
 \AuxUserType
 \2 = <ShortDisplayName>
\3 = <ApplicationName>

Value Entries

<ShortDisplayName>
Specifies an application's short display name, used in menus, including pop-ups.
<ApplicationName>
Specifies an application name, used in the Results field of the Paste Special dialog box.

Remarks

The recommended maximum length for the short display name is 15 characters. For example:

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \AuxUserType\2 = In-Place Outline

The application name should contain the actual name of the application (such as "Acme Draw 2.0"). For example:

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \AuxUserType\3 = COM In-Place Server

See Also

IOleObject::GetUserType


Control

Identifies an object as an ActiveX Control.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\Control

Remarks

This optional entry is used by containers to fill in dialog boxes. The container uses this subkey to determine whether to include an object in a dialog box that displays ActiveX Controls.. A control can omit this entry if it is only designed to work with a specific container and thus does not wish to be inserted in other containers.


Conversion

Used by the Convert Dialog to determines the formats an application can read and write.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\Conversion
\Readable
\Main = <format>, ...
\ReadWritable
\Main = <format>, ...

Value Entries

<format>
The file format an application can read (convert from).
<format>
The file format an application can read and write (activate as).

Remarks

Conversion information is used by the Convert dialog box to determine what formats an application can read and write. A comma-delimited file format is indicated by a number if it is one of the Clipboard formats defined in WINDOWS.H. A string indicates the format is not one defined in WINDOWS.H (private). In this case, the readable and writable format is CF_OUTLINE (private).

The following is a \Readable entry:

KEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \Conversion\Readable\Main = Outline,1

The following is a \ReadWritable entry:

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \Conversion\Readwritable\Main = Outline,1


DataFormats

Specifies the default and main data formats supported by an application.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\DataFormats
\DefaultFile = <defaultfile/objectformat>
\GetSet
\ n = <default formats for EnumFormatEt>

Value Entries

<default file/object format>
Specifies the default, main file or object format for objects of this class.
<default formats for EnumFormatEtc>
Specifies a list of formats for default implementations of EnumFormatEtc, where <n> is a zero-based integer index. For example, <n> = <format ,aspect, medium, flag>, where format is a clipboard format, aspect is one or more members of DVASPECT, medium is one or more members of TYMED, and flag is one or more members of DATADIR.

Remarks

Information associated with this entry is used by: IDataObject::GetData, IDataObject::SetData and IDataObject::EnumFormatEtc methods.

The values defined in the following example entry are CF_TEXT, DVASPECT_CONTENT, TYMED_HGLOBAL, and DATADIR_GET | DATADIR_SET.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DataFormats\GetSet\0 = 1,1,1,3

The values defined in the following entry are: CF_METAFILEPICT DVASPECT_CONTENT, TYMED_MFPICT, DATADIR_GET.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DataFormats\GetSet\1 = 3,1,32,1

The values defined in the following entry are: 2 = cfEmbedSource, DVASPECT_CONTENT, TYMED_ISTORAGE, and DATADIR_GET.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DataFormats\GetSet\2 = Embed Source,1,8,1

The values defined in the following entry are: 3 = cfOutline, DVASPECT_CONTENT, TYMED_HGLOBAL, and DATADIR_GET | DATADIR_SET.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DataFormats\GetSet\3 = Outline,1,1,3

The following entry declares that the default File Format supported by this application is CF_OUTLINE.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DataFormats\DefaultFile = Outline

See Also

IDataObject::GetData, IDataObject::SetData, IDataObject::EnumFormatEtc


DefaultIcon

Provides default icon information for iconic presentations of objects.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\DefaultIcon = <full path to exe, resource id>

Value Entries

<full path to exe, resource index>
Specifies the full path to the executable name of the object application and the resource index of the icon within the executable.

Remarks

DefaultIcon identifies the icon to use when, for example, a control is minimized to an icon. This entry contains the full path to the executable name of the server application and the resource index of the icon within the executable. Applications can use the information provided by DefaultIcon to obtain an icon handle with ExtractIcon.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \DefaultIcon = c:\samp\isvrotl.exe,0

See Also

ExtractIcon


InprocHandler

Specifies whether or not an application uses a custom handler.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\InprocHandler = <handler.dll>

Value Entries

<handler.dll>
Specifies the custom handler used by the application.

Remarks

If A custom handler is not used, the entry should be set to COM2.DLL, as shown in the following example.

HKEY_-CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \InprocHandler = ole2.dll

If a container is searching the registry for an InprocHandler, the 16-bit version has priority with a 16-bit container, and the 32-bit version has priority with a 32-bit container.

See Also

InprocHandler32


InprocHandler32

Specifies whether or not an application uses a custom handler.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\InprocHandler32 = <handler.dll>

Value Entries

<handler.dll>
Specifies the custom handler used by the application.

Remarks

If a custom handler is not used, the entry should be set to OLE32.DLL, as shown in the following example.

HKEY_-CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \InprocHandler = ole32.dll

If a container is searching the registry for an InprocHandler, the 16-bit version has priority with a 16-bit container, and the 32-bit version has priority with a 32-bit container.

See Also

InprocHandler


InprocServer

Specifies the path to the in-process server DLL.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\InprocServer = <full path>

Value Entries

<full path>
Specifies the path to the in-process server DLL.

Remarks

The InprocServer entry is relatively rare for insertable classes.

In-process servers are currently registered using the InprocServer registry entry. The 32-bit and 64-bit in-process servers should use the InprocServer32 entry. If a container is searching the registry for an InprocServer, the 16-bit version has priority with a 16-bit container, and 32-bit version has priority with a 32-bit container.

See Also

InprocServer32


InprocServer32

Registers a 32-bit in-process server and specifies the threading model of the apartment the server can run in.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\InprocServer32 = <path to 32-bit inproc server>
\ThreadingModel = <threading model>

Value Entries

<path to 32-bit inproc server>
Specifies the path to the 32-bit in-process server.
<threading model>
Specifies the threading model of the apartment the server can run in. In-process servers are loaded into an existing apartment and so do not call CoInitialize or CoInitializeEx; they must use the registry to specify an application's threading model. Allowable values are:
  • ThreadingModel=Apartment. Single-threaded apartment.
  • ThreadingModel=Both. Single-threaded or multithreaded apartment.
  • ThreadingModel=Free. Multithreaded apartment.
  • ThreadingModel=Neutral. Neutral apartment (available in Windows 2000).

Remarks

If the ThreadingModel key is not in the registry or no value is entered for it, the server is loaded into the first apartment that was initialized in the process. This apartment is sometimes referred to as the main single-threaded apartment (STA). If the first STA in a process is initialized by COM, rather than by an explicit call to CoInitialize or CoInitializeEx, it is called the host STA. COM creates a host STA, for example, if an in-process server to be loaded requires an STA but there is currently no STA in the process.

The ThreadingModel value must be the same for all objects provided by an in-process server.

Whenever possible, the in-process server is loaded in the same apartment as the client that loads it. If the threading model of the client apartment is not compatible with the model specified by the ThreadingModel value of the InprocServer32 key, the server is loaded as follows:

Threading model of server | Apartment server runs in
Free | Multithreaded apartment
Neutral | Neutral apartment
Both | Same apartment as client
Not specified | Main STA

If the threading model of the server is Apartment, the apartment the server is loaded in depends on the apartment the client is running in:

Apartment client runs in | Apartment server runs in
Multithreaded | Host STA
Neutral (on STA thread) | Same apartment as client
Neutral (on MTA thread) | Host STA

COM can also create a host multithreaded apartment (MTA). If a client in a single-threaded apartment requests an in-process server whose threading model is Free when there is no MTA in the process, COM creates a host MTA and loads the server into it.

For a 32-bit in-process server, the InprocHandler32, InprocServer, InprocServer32, and Insertable keys must be registered. The InprocServer entry is needed only for backward compatibility. If it is missing, the class will still work, but it cannot be loaded in 16-bit applications.

If a container is searching the registry for an in-process server, the 16-bit version has priority with a 16-bit container, and the 32-bit version has priority with a 32-bit container.

See Also

InprocServer, CoInitialize, CoInitializeEx


Insertable

Indicates that objects of this class should appear in the Insert Object dialog box's list box when used by COM container applications.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\Insertable

Remarks

This key is a required entry for 32-bit COM applications whose objects can be inserted into existing 16-bit applications. Existing 16-bit applications look in the registry for this key, which informs the application that the server supports embeddings. If the Insertable key exists, 16-bit applications may also attempt to verify that the server exists on the machine. 16-bit applications typically will retrieve the value of the LocalServer key from the class and check to see if it is a valid file on the system. Therefore, for a 32-bit application to be insertable by a 16-bit application, the 32-bit application should register the LocalServer subkey in addition to registering LocalServer32.

Used with controls, this entry indicates that an object can act only as an in-place embedded object with no special control features. Objects that have this key appear in the Insert Object dialog box fo their container. When used with controls, this entry also indicates the control has been tested with non-control containers. This entry is also optional and can be omitted when a control is not designed to work with older containers that do not understand controls.

Note that this key is not present for internal classes like the moniker classes.

See Also

ProgID


Interface

An optional entry that specifies all interface IDs (IIDs) supported by the associated class.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\Interface
\<IID> = <name of interface1>
 \<IID> = <name of interface2>
 \...

Value Entries

<name of interface1>, <name of interface2>, ...
Interfaces supported by this class.

Remarks

If an interface name is not present in this list, then the interface can never be supported by an instance of this class.

See Also

Interface Key


LocalServer

Specifies the full path to a 16-bit local server application.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\LocalServer = <full path>

Value Entries

<full path>
Specifies the full path to the local server, and can include command-line arguments.

Remarks

COM appends the "-Embedding" flag to the string, so the application that uses flags must parse the whole string and check for the -Embedding flag.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \LocalServer = c:\samp\isvrotl.exe

To run a COM object server in a separate memory space (Windows NT 3.5 and above only), change the LocalServer key in the registry for the CLSID to the following:

cmd /c start /separate <path.exe

If a container is searching the registry for a local server, a 32-bit local server has priority over a 16-bit local server.

See Also

LocalServer32


LocalServer32

Specifies the full path to a 32-bit local server application.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\LocalServer32 = <full path>

Value Entries

<full path>
Specifies the full path to the 32-bit local server, and can include command-line arguments.

Remarks

COM appends the "-Embedding" flag to the string, so the application that uses flags will need to parse the whole string and check for the -Embedding flag.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \LocalServer32 = c:\samp\isvrotl.exe

When COM starts a 32-bit local server, the server must register a class object within an elapsed time set by the user. By default, the elapsed time value must be at least five minutes, in milliseconds, but cannot exceed the number of milliseconds in 30 days. Applications typically should not set this value which is in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\COM2\ServerStartElapsedTime entry.

The required entries for 32-bit local servers are InprocHandler32, LocalServer, LocalServer32, and Insertable.

If a container is searching the registry for a local server, a 32-bit local server has priority over a 16-bit local server.

See Also

LocalServer


MiscStatus

Specifies how to create and display an object.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\MiscStatus = <object description>
\n = value

Value Entries

object description
The value or combination of values from COMMISC that describe the object.
value
Specifies a value from the COMMISC enumeration.

Remarks

Information used to describe objects is found in the COMMISC enumeration.

The default value of MiscStatus is used if a subkey corresponding to the specified DVASPECT is not found. To set an ActiveX control, specify DVASPECT==1. This will cause the following to occur in the Registry:

HKEY_CLASSES_ROOT\CLSID\ . . .\MiscStatus = 1

The following is an example of a MiscStatus entry. Refer to the IOleObject::GetMiscStatus method description for information on the different settings.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \MiscStatus = 2

See Also

IOleObject::GetMiscStatus


ProgID

Associates a ProgID with a CLSID.

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\ProgID = <programatic identifier>

Value Entries

<programatic identifier>
Specifies the ProgID: <vendor>.<component>.<version>.

Remarks

Every insertable object class has a <ProgID>. For information on creating a <ProgID>, see the <ProgID> key.

See Also

VersionIndependentProgID Key, <ProgID Key>


ToolBoxBitmap32

Identifies the module name and resourdeID for a 16 x 16 bitmap to use for the face of a toolbar or toolbox button.

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\ToolBoxBitmap32 = <filename>.<ext>, resourceID

Value Entries

<filename>.<ext>, resourceID
Specifies the module name and the resorucID for the bitmap.

Remarks

The standard Windows icon size is too large to be used for this purpose. This specifically supports control containers that have a design mode in which one selects controls and places them on a form being designed. For example, in Visual Basic, the control's icon is displayed in the Visual Basic Toolbox during design mode.


TreatAs

Specifies the CLSID of a class that can emulate the current class.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\TreatAs = <CLSID>

Value Entries

<CLSID>
Class that is to perform the emulation.

Remarks

Emulation is the ability of one application to open and edit an object of a different class, while retaining the original format of the object.

See Also

CoTreatAsClass, CoGetTreatAsClass, AutoTreatAs, IOleObject::EnumVerbs


Verb

Specifies the verbs to be registered for an application.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\Verb
\1 = <verb1>
 \2 = <verb2>
\3 =

Value Entries

<verb1>, <verb2>, ...
Each value specifies a verb, and its associated menu and verb flags. Each verb is specified by its own NamedValue / Value pair, as in verb number = <name, menu flag, verb flag> For example:
\Verb
0 = &Edit, 0, 2 // primary verb; on menu, possibly dirties object,
1 = &Play, 0, 3 // other verb; on menu; leaves object clean
-3 = Hide, 0, 1 // pseudo verb, hides window; not on menu, opt.
-2 = Open, 0, 1 // pseudo verb, opens in sep. window; not on menu, opt.
-1 = Show, 0, 1 // pseudo verb, show in preferred state; not on menu, opt.

See IOleObject::DoVerb for general information about verbs, descriptions of COM predefined verbs, and positive and negative verbs, plus other material.

Remarks

Verbs must be numbered consecutively. The first value after the verb string describes how the verb is appended by an AppendMenu function call.

The second value indicates whether the verb will dirty the object. It also indicates whether the verb should appear in the menu, as defined by COMVERBATTRIB enumeration, used by the COMVERB structure.

For still more information, see IOleObject::EnumVerbs and OleRegEnumVerbs.

Following are two example entries:

Verb 0: "Edit", MF_UNCHECKED | MF_ENABLED, no COMVERATTRIB flags:

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \Verb\0 = &Edit,0,0

Verb 1: "Open", MF_UNCHECKED | MF_ENABLED, no COMVERATTRIB flags:

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \Verb\1 = &Open,0,0

See Also

IOleObject::EnumVerbs, OleRegEnumVerbs, AppendMenu, COMVERB, COMVERBATTRIB


Version

Specifies the version number of the control.

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
\Version = <VersionNumber>

Value Entries

<VersionNumber>
The version number of the control.

Remarks

The version number should match the version of the type library associated with the control.


ProgID Key

A ProgID, or programmatic identifier, is a registry entry that can be associated with a CLSID. The format of a ProgID is <Vendor>.<Component>.<Version>, separated by periods and with no spaces, as in Word.Document.6. Like the CLSID, the ProgID identifies a class, but with less precision.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\<ProgID> =

You can use a ProgID in programming situations where it is not possible to use a CLSID. ProgIDs should not appear in the user interface. ProgIDs are not guaranteed to be unique so they can be used only where name collisions are manageable.

The <ProgID> must:

  • Have no more than 39 characters.
  • Contain no punctuation (including underscores) except one or more periods.
  • Not start with a digit.
  • Be different from the class name of any OLE 1 application, including the OLE 1 version of the same application, if there is one.

Since the <ProgID> should not appear in the user interface, you can obtain a displayable name by calling IOleObject::GetUserType. Also, see OleRegGetUserType>

The value of the <ProgID> is a human readable name such as Microsoft Word Document, and is displayed in dialog boxes.

SubKeys and Named Values

\<ProgID> = <HumanReadableNamen>

\ CLSID | Object's CLSID
\ Insertable | Indicates that class is insertable in OLE 2 containers
\ Protocol | Indicates class is insertable in OLE 1 container
\ Shell | Windows 3.1 File Manager information

See Also

IOleObject::GetUserType, OleRegGetUserType


CLSID

Associates a ProgID with a CLSID.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ProgID
\CLSID = <CLSID>

Value Entries

<CLSID>
The object's CLSID.

See Also

GetClassFile


Insertable

Indicates that this class is insertable in OLE 2 containers.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ProgID
\Insertable

Remarks

This is a required entry for objects that are insertable in OLE 2 containers.


Protocol

Indicates that this OLE 2 class is insertable in OLE 1 containers.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ProgID
\Protocol
\StdFileEditing
\Server = <full path to OLE 2 server app>
\Verb =
\0 = <verb value0>
\1 = <verb value>
 \, ...

Value Entries

<full path to OLE 2 server app>
Specifies the full path to the OLE 2 server application.
<verb value>
The primary verb; must start with zero.
<verb value>, ...
Additional verb, numbered consecutively.

Remarks

The StdFileEditing entry is specifies OLE 1 compatibility information. It should be present only if, objects of this class are insertable in OLE 1 containers.


Shell

Provides Windows 3.1 shell printing and File Open information.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ProgID
\Shell
\Open
\command = <path to application exe %1>
\Print
\command = <path to application exe %1>

Value Entries

<path to application exe %1>
Specifies the path to the executable.

Remarks

These entries should provide the path and filename of the application. The examples provided here are simple entries. More complex entries could contain DDE entries.

HKEY_CLASSES_ROOT\OLE2ISvrOtl\Shell\Print\Command =
    c:\svr\isvrotl.exe %1
HKEY_CLASSES_ROOT\OLE2ISvrOtl\Shell\Open\Command =
    c:\svr\isvrotl.exe %1

Requirements

Windows NT/2000: Requires Windows 2000 (or Windows NT 4.0 with Internet Explorer 4.0 or later).
Windows 95/98: Requires Windows 98 (or Windows 95 with Internet Explorer 4.0 or later).
Header: Declared in shldisp.h.


VersionIndependentProgID Key

The values associated with this key associate a ProgID with a CLSID. Used to determine the latest version of an object application.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
 \<VersionIndependentProgID> = <Vendor>.<Component>
 \CLSID = <CLSID>
\CurVer = <ProgID>

Value Entries

<Vendor>.<Component>
The name of the latest version of the object application.
<CLSID>
The CLSID of the newest installed version of the class.
<ProgID>
The <ProgID> of the newest installed version of the class.

Remarks

The format or the <VersionIndependentProgID> is <Vendor>.<Component>, separated by periods, no spaces, and no version number. The version-independent ProgID, like the ProgID, can be registered with a human readable name.

Applications must register a version-independent programmatic identifier under the VersionIndependentProgID key. The <VersionIndependentProgID> refers to the application's class, and does not change from version to version, instead remaining constant across all versions, for example, Microsoft Word Document. It is used with macro languages and refers to the currently installed version of the application's class. The <VersionIndependentProgID> must correspond to the name of the latest version of the object application.

The <VersionIndependentProgID> is used when, for example, a container application creates a chart or table with a toolbar button. In this situation the application can use the <VersionIndependentProgID> to determine the latest version of the needed object application.

The <VersionIndependentProgID> is stored and maintained solely by application code. When given the VersionIndependentProgID, the CLSIDFromProgID function returns the CLSID of the current version.

You can use CLSIDFromProgID and ProgIDFromCLSID to convert between these two representations.

You can use IOleObject::GetUserType or OleRegGetUserType to change the identifier to a displayable string.

If A custom handler is not used, the entry should be set to OLE32.DLL, as shown in the following example.

HKEY_-CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}
    \InprocHandler = ole32.dll

In addition to the preceding registry entry, you should add the following corresponding entry under the CLSID key:

\CLSID
    \<CLSID>  = <human readable name>
        \<VersionIndependentProgID> = human readable name

See Also

ProgID, CLSIDFromProgID, ProgIDFromCLSID, IOleObject::GetUserType, OleRegGetUserType


File Extension Key

Associates a file extension with a ProgID, indicating that an OLE 2 application can handle requests from the Windows 3.1 File Manager.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
\<.ext> = <ProgID>

Value Entries

<ProgID>
The ProgID associated with

Remarks

The information contained in the file extension key is used by both File Manager and File Monikers. GetClassFile uses the file extension key to supply the associated CLSID.

See Also

GetClassFile


(Non-Compound) FileType Key

Used by GetClassFile to match patterns against various file bytes in a non-compound file.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\
\FileType
\<CLSID>
\<n> = <offset, cb, mask, value>

Value Entries

offset
This value determines how far from the beginning or end of the file to begin the comparison. If the offset is a negative value then the comparison will begin from the end of the file minus the offset value. The offset value is a decimal type unless preceded by “0x”.
cb
Represents the length in bytes from the beginning to the end of the file. Represents the byte range in the file. The cb value is a decimal unless preceded by “0x”.
mask
A binary value used for masking, which is performed by using a logical AND operation, and the byte range specified by offset and cb. If this value is omitted then the bytes will be set to all ones. This value is always hexadecimal.
value
Represents the pattern that must match for a file to be of this FileType. The pattern is used to properly identify a known file format from its contents not by its extension.

Remarks

Entries under the FileType key are used by the GetClassFile function to match patterns against various file bytes in a non-compound file. FileType has CLSID sub keys, each of which has a series of sub keys \0, \1, \2.… These values contain a pattern that, if matched, yield the indicated CLSID. Following are examples of FileType entries.
\0 = 0, 4, FFFFFFFF, ABCD1234
where the first 4 bytes must be ABCD1234, in that order, or
\1 = 0, 4, FFFFFFFF, 9876543
where they must match 9876543, or
\2 = -4, 4, FEFEFEFE
where the last four bytes in the file must be FEFEFEFE.

See Also

GetClassFile, File Extension


Interface Key

Registers new interfaces by associating an interface name with an interface ID (IID).

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface

If your application adds a new interface, the Interface key must be completed for OLE 2 to register the new interface. There must be one IID subkey for each new interface.

Note that you must use ProxyStubCLSID32 because the IID-to-CLSID mapping may be different for 16-and 32-bit interfaces. The IID-to-CLSID depends on the way the interface proxies are packaged into a set of proxy DLLs.

SubKeys and Named Values

\Interface

<IID> | Interface identifier
\BaseInterface | Interface derived from
\NumMethods | Number of methods
\ProsyStubCLSID | Maps IID to CLSiD (16-bit DLLs)
\ProsyStubCLSID32 | Maps IID to CLSiD (32-bit DLLs)

See Also

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\Interface


<IID>

Associates an interface ID (IID) with a textual name for the interface.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
\Interface
\<IID> = name of interface

Value Entries

<name of interface>
Textual name for a given interface. For example:
\{00000112-0000-0000-C000-0000000000-46} = IOleObject

Remarks

If your application adds a new interface, the interface key must be completed for OLE 2 to register the new interface. There must be one entry for each new interface.


BaseInterface

Identifies the interface from which the current interface is derived.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface
\BaseInterface = <name of interface>

Value Entries

<name of interface>
Identifies the name of the interface from which the current interface is derived.


NumMethods

Contains the number of interfaces in the associated interface.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
\Interface
\NumMethods = <number of methods>

Value Entries

<number of methods>
Specifies the number of methods in the interface.


ProxyStubClsid

Maps an IID to a CLSID in 16-bit proxy DLLs.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
\Interface
\ProxyStubClsid = <CLSID>

Value Entries

<CLSID>
Specifies the CLSID to map the IID to.

Remarks

If you add interfaces, you must use this entry to register them (16-bit systems) so that OLE can find the appropriate remoting code to establish interprocess communication.

See Also

ProxyStubClsid32


ProxyStubClsid32

Maps an IID to a CLSID in 32-bit proxy DLLs.

Registry Entry

HKEY_LOCAL_MACHINE\SOFTWARE\Classes
\Interface
\ProxyStubClsid32 = <CLSID>

Value Entries

<CLSID>
Specifies the CLSID to map the IID to.

Remarks

This is a required entry since the IID-to-CLSID mapping may be different for 16- and 32-bit interfaces. The IID-to-CLSID mapping depends on the way the interface proxies are packaged into a set of proxy DLLs.

If you add interfaces, you must use this entry to register them (32-bit systems) so that OLE can find the appropriate remoting code to establish interprocess communication.

See Also

ProxyStubClsid, IMarshal


HKEY_LOCAL_MACHINE\Software\Microsoft\OLE

The named-values under the HKEY_LOCAL_MACHINE\Software\Microsoft\OLE key control Distributed COM's default launch and access permission settings and call-level security capabilities for applications that do not call CoInitializeSecurity. Only machine administrators, the object's creator, and the system have full-access to this portion of the registry. All other users have only read-access.

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\named_value = value

Named Values:

\ EnableDCOM = value | Sets the global activation policy for the machine
\ DefaultLaunchPermission = value | Defines default launch ACL for the machine
\ DefaultAccessPermission = value | Defines default access permission list for the machine
\ LegacyAuthenticationLevel = value | Sets the default authentication level
\ LegacyImpersonationLevel = value | Sets the default impersonation level
\ LegacyMutualAuthentication = value | Determines if mutual authentication is enabled
\ LegacySecureReferences = value | Determines if AddRef/Release invocations are secure

See Also

Security in COM


EnableDCOM

The EnableDCOM controls the global activation and call policies of the machine. Only machine administrators and the system have full-access to this portion of the registry. All other users have only read-access.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\
 EnableDCOM = value

Remarks

This named-value is a REG_SZ, and has two possible values.

N (or n)
No remote clients may launch servers or connect to objects on this machine. Local launching of class code and connecting to objects is allowed on a per-class basis according to the value and access permissions of the class's AppID\{...}\LaunchPermission key and the global DefaultLaunchPermission key. See the description of these keys, below.
Y (or y)
Launching of servers and connecting to objects by remote clients is allowed on a per-class basis according to the value and access permissions of the class's LaunchPermission named-value and the global DefaultLaunchPermission named-value.

See Also

LaunchPermission, DefaultLaunchPermission, Security in COM


DefaultLaunchPermission

Defines the Access Control List (ACL) of the principals that can launch classes that do not specify their own ACL through the LaunchPermission named-value

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\named_value

Remarks

The value for the DefaultLaunchPermission named-value is a REG_BINARY that contains the Access Control List (ACL) of the principals who can launch classes on the current system. If the LaunchPermission named-value is set for a server, it takes precedence over the DefaultLaunchPermission named-value. Upon receiving a local or remote request to launch a server whose APPID key has no LaunchPermission value of its own, the ACL described by this value is checked while impersonating the client, and its success either allows or disallows the launching of the class code.

This entry supports a simple level of centralized administration of the default launching access to otherwise unadministered classes on a machine. For example, an administrator might use the DCOMCNFG tool to configure the system to allow read-access only for power-users of the machine. OLE would therefore restrict requests to launch class code to members of the power-users group. The administrator could subsequently configure launch permissions for individual classes to grant the ability to launch class code to other groups or individual users as needed.

The access-permissions in this named-value default to the following:

  • machine-administrators: allow-launch
  • SYSTEM: allow-launch
  • INTERACTIVE: allow-launch

See Also

LaunchPermission, Registering COM Servers


DefaultAccessPermission

Sets the Access Control List (ACL) of the principals that can access classes for which there is no AccessPermission setting. This ACL is only used by applications that don't call CoInitializeSecurity and do not have an AccessPermission value under their AppID.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\DefaultAccessPermission = ACL

Remarks

The DefaultAccessPermission is a named-value that is set to a REG_BINARY that contains data describing the Access Control List (ACL) of the principals who can access classes for which there is no AccessPermission named-value. In this case, the COM runtime in the server checks the ACL described by this value while impersonating the caller that is attempting to connect to the object, and its success determines if the access is allowed or disallowed. If the access-check fails, the connection to the object is disallowed. If this named value does not exist, only the server principal and local system are allowed to call the server.

This named-value supports a simple level of centralized administration of the default connection access to running objects on a machine.

By default, this named-value has no entries in it. Only the server principal and system are allowed to call the server.

See Also

AccessPermission, Registering COM Servers


LegacyAuthenticationLevel

Sets the default authentication level for applications that do not call CoInitializeSecurity.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\
 LegacyAuthenticationLevel= default_authentication_level

Remarks

The LegacyAuthenticationLevel named-value is a REG_WORD that sets the default level of authentication for all applications which do not call CoInitializeSecurity. Values are from 1 through 6, and correspond to the RPC_C_AUTHN_LEVEL_xxx constants:

Value | Meaning
1 | RPC_C_AUTHN_LEVEL_NONE
2 | RPC_C_AUTHN_LEVEL_CONNECT
3 | RPC_C_AUTHN_LEVEL_CALL
4 | RPC_C_AUTHN_LEVEL_PKT
5 | RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
6 | RPC_C_AUTHN_LEVEL_PKT_PRIVACY

When this named-value is not present, the default authentication level established by the system is 2 (RPC_C_AUTHN_CONNECT).

See Also

CoInitializeSecurity, RPC_C_AUTHN_LEVEL_xxx, Registering COM Servers


LegacyImpersonationLevel

Sets the default level of impersonation for applications that do not call CoInitializeSecurity.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\LegacyImpersonationLevel= default_impersonation_level

Remarks

The LegacyImpersonationLevel named-value is a REG_WORD that sets the default level of impersonation. The values, from 1 through 4, correspond to the values of the RPC_C_IMP_LEVEL_xxx constants.

Value | Meaning
1 | RPC_C_IMP_LEVEL_ANONYMOUS
2 | RPC_C_IMP_LEVEL_IDENTIFY
3 | RPC_C_IMP_LEVEL_IMPERSONATE
4 | RPC_C_IMP_LEVEL_DELEGATE
When this named-value is not present, the default impersonation level established by the system is 2 (RPC_C_IMP_LEVEL_IDENTIFY). NTLMSSP on Windows NT 4.0 supports only RPC_C_IMP_LEVEL_IDENTIFY and RPC_C_IMP_LEVEL_IMPERSONATE. In Windows 2000, the Kerberos v5 protocol supports RPC_C_IMP_LEVEL_DELEGATE. However, RPC_C_IMP_LEVEL_ANONYMOUS is not supported by either Windows NT 4.p or 5.0.

See Also

RPC_C_IMP_LEVEL_xxx, Registering COM Servers


LegacyMutualAuthentication

Determines whether mutual authentication is enabled.

Note Mutual authentication is not supported and is not available by default with Windows NT 4.0. This named-value is only useful if the network administrator installs a security provider that supports mutual authentication.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\LegacyMutualAuthentication= value

Remarks

The LegacyMutualAuthentication named-value is a REG_SZ that provides the default setting for use of mutual authentication for all applications that do not call CoInitializeSecurity. Values of "Y" or "y" indicate that mutual authentication is enabled. Any other value or the lack of this named-value implies that mutual authentication is disabled.

See Also

CoInitializeSecurity, Registering COM Servers


LegacySecureReferences

Determines whether AddRef/Release invocations are secure for applications that do not call CoInitializeSecurity.

Registry Entry

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\LegacySecureReferences= ACL

Remarks

HKEY_LOCAL_MACHINE\Software\Microsoft\OLE

The LegacySecureReferences named-value is a REG_SZ that provides the default setting for securing IUnknown::AddRef and IUnknown::Release method invokations for all applications that do not call CoInitializeSecurity. Values of "Y" or "y" indicate that AddRef/Release is secured. Any other value or the lack of this named-value implies that AddRef/Release is not secured. Enabling secure references slows remote calls.

See Also

CoInitializeSecurity, Registering COM Servers


Compound Documents

OLE compound documents enable users working within a single application to manipulate data written in various formats and derived from multiple sources. For example, a user might insert into a word processing document a graph created in a second application and a sound object created in a third application. Activating the graph causes the second application to load its user interface, or at least that part containing tools necessary to edit the object. Activating the sound object causes the third application to play it. In both cases, a user is able to manipulate data from external sources from within the context of a single document.

OLE compound document technology rests on a foundation consisting of COM, structured storage, and .uniform data transfer. As summarized below, each of these core technologies plays a critical role in OLE compound documents:

COM
A compound document object is essentially a COM object that can be embedded in, or linked to, an existing document. As a COM object, a compound document object exposes the IUnknown interface, through which clients can obtain pointers to its other interfaces, including several, such as IOleObject, IOleLink, and IViewObject2, that provide special features unique to compound document objects.
Structured Storage
A compound document object must implement the IPersistStorage or, optionally, IPersistStream interfaces to manage its own storage. A container used to create compound documents must supply the IStorage interface, through which objects store and retrieve data. Containers almost always provide instances of IStorage obtained from OLE’s Compound Files implementation. Containers must also use an object’s IPersistStorage and/or IPersistStream interfaces.
Uniform Data Transfer
Applications that support compound documents must implement IDataObject because embedded objects and linked objects begin as data that has been transferred using special OLE clipboard formats, rather than standard Microsoft® Windows® clipboard formats. In other words, formatting data as an embedded or linked object is simply one more option provided by OLE’s uniform data transfer model.

OLE’s compound document technology benefits both software developers and users alike. Instead of feeling obligated to cram every conceivable feature into a single application, software developers are now free, if they like, to develop smaller, more focused applications that rely on other applications to supply additional features. In cases where a software developer decides to provide an application with capabilities beyond its core features, the developer can implement these additional services as separate DLLs, which are loaded into memory only when their services are required. Users benefit from smaller, faster, more capable software that they can mix and match as needed, manipulating all required components from within a single master document.


Containers and Servers

Compound document applications are of two basic types: container applications and server applications. OLE container applications provide users with the ability to create, edit, save, and retrieve compound documents. OLE server applications provide users with the means to create documents and other data representations that can be contained as either links or embeddings in container applications. An OLE application can be a container application, a server application, or both.

OLE server applications also differ in whether they are implemented as in-process servers or local servers. An in-process server is a dynamic link library (DLL) that runs in the container application’s process space. You can run an in-process server only from within the container application.

Note Future releases of OLE will enable linking and embedding across machine boundaries, so that a container application on one computer will be able to use a compound document object provided by a remote server running on another computer. From a container application’s point of view, any OLE server application that runs in its own process space, whether on the same computer or a remote machine, is an out-of-process server.


Linking and Embedding

Users can create two types of compound-document objects: linked or embedded. The difference between the two types lies in how and where the object’s source data is stored. Where the object resides affects, in turn, the object’s portability and methods of activation, how data updates are performed, and the size and structure of its container.


Linked Objects

When a link to an object is inserted in a compound document, the source data, or link source, continues to reside wherever it was initially created, usually in another document. The compound document contains only a reference, or link, to the actual data stored at the link source, along with information about how to present that data to the user.

Windows 2000 incorporates a distributed link tracking service for tracking OLE links.

Activating a link runs the link source’s server application, which the user requires in order to edit or otherwise manipulate the link data. Linking keeps the size of a compound document small. It is also useful when the data source is maintained by someone else and must be shared among many users. If the person maintaining the link source changes the data, the change is automatically updated in all documents containing a link to that data. In addition to creating simple links, users can nest links and combine linked and embedded objects to create complex documents.


Embedded Objects

An embedded object is physically stored in the compound document, along with all the information needed to manage the object. In other words, the embedded object is actually a part of the compound document in which it resides. This arrangement has a couple of disadvantages. First, a compound document containing embedded objects will be larger than one containing the same objects as links. Second, changes made to the source of an embedded object will not be automatically replicated in the embedded copy, and changes in the source will not be reflected in the source, as they are with a link.

Still, for certain purposes, embedding offers several advantages over links. First, users can transfer compound documents with embedded objects to other computers, or other locations on the same computer, without breaking a link. Second, users can edit embedded objects without changing the content of the original. Sometimes, this separation is precisely what is required. Third, embedded objects can be activated in place, meaning that the user can edit or otherwise manipulate the object without having to work in a separate window from that of the object’s container. Instead, when the object is activated, the container application’s user interface changes to expose those tools that are necessary to manage or modify the object.


Object Handlers

If an OLE server application is a local server, meaning that it runs in its own process space, communication between container and server must occur across process boundaries. Since this process is expensive, OLE relies on a surrogate object loaded into the container’s process space to act on behalf of a local server application. This surrogate object, known as an object handler services container requests that do not require the attention of the server application, such as requests for drawing. When a container requests something that the object handler cannot provide, the handler communicates with the server application using COM’s out-of-process communication mechanism.

An object handler is unique to an object class. When you create an instance of a handler for one class, you cannot use it for another. When used for a compound document, the object handler implements the container-side data structures when objects of a particular class are accessed remotely.

OLE provides a default object handler that local server applications can use. For applications that require special behaviors, developers can implement a custom handler that either replaces the default handler or uses it to provide certain default behaviors.

An object handler is a DLL containing several interacting components. These components include remoting pieces to manage communication between the handler and its server application, a cache for storing an object’s data, along with information on how that data should be formatted and displayed, and a controlling object that coordinates the activities of the DLL’s other components. In addition, if an object is a link, the DLL also includes a linking component, or linked object, which keeps track of the name and location of the link source.

The cache contains data and presentation information sufficient for the handler to display a loaded, but not running, object in its container. OLE provides an implementation of the cache used by OLE’s default object handler and the link object. The cache stores data in formats needed by the object handler to satisfy container draw requests. When an object’s data changes, the object sends a notification to the cache so that an update can occur. For more information on the cache, see View Caching.


The Default Handler and Custom Handlers

The default handler, an implementation provided by OLE, is used by most applications as the handler. An application implements a custom handler when the default handler’s capabilities are insufficient. A custom handler can either completely replace the default handler or use parts of the functionality it provides where appropriate. In the latter case, the application handler is implemented as an aggregate object composed of a new control object and the default handler. Combination application/default handlers are also known as in-process handlers. The remoting handler is used for objects that are not assigned a CLSID in the system registry or that have no specified handler. All that is required from a handler for these types of objects is that they pass information across the process boundary.


In-Process Servers

If you implement an OLE server application as an in-process server — a DLL running in the process space of the container application — rather than as a local server — an EXE running in its own process space — communication between container and server is simplified because communication between the two can take the form of normal function calls. Remote procedure calls are not required because the two applications run in the same process space. As you would expect, the objects that manage the marshaling of parameters are also unnecessary, although they may be aggregated within the DLL without interfering with the communication between container and server.

When an OLE server application is implemented as an in-process server, a separate object handler is not required because the server itself lives in the client’s process space. The main difference between an in-process server and object handler is that the server is able to manage the object in a running state while the handler cannot. One consequence of this difference is that a server must provide a user interface for manipulating the running object, while a handler delegates this requirement to the object’s server. In creating an in-process server, you can aggregate on the OLE default handler, letting it handle basic chores, such as display, storage, and notifications while you implement only those services that the handler either does not provide or does not implement in the way you require.


Advantages

The advantages of implementing your application as an in-process server are speed and combining some the advantages of an object handler and a local server. In-process servers are faster than local servers for several reasons. First, because they are smaller and run in the process space of the container application, they load more quickly. Second, they are optimized to perform certain tasks. Third, communication between container and server does not rely on remote procedure calls.


Disadvantages

In-process servers provide the speed and size advantage of an object handler with the editing capability of a local server. So why would you ever choose to implement your OLE application as a local server rather than an in-process server? There are several reasons:

  • Security. Only a local server has its address space isolated from that of the client. An in-process server shares the address space and process context of the client and can therefore be less robust in the face of faults or malicious programming.
  • Granularity. A local server can host multiple instances of its object across many different clients, sharing server state between objects in multiple clients in ways that would be difficult or impossible if implemented as an in-process server, which is simply a DLL loaded into each client.
  • Compatibility. If you choose to implement an in-process server, you relinquish compatibility with OLE 1, which does not support such servers. This will not be a consideration for many developers, but if it is, then it is of critical concern.
  • Inability to support links. An in-process server cannot serve as a link source. Since a DLL cannot run by itself, it cannot create a file object to be linked to.

Despite these disadvantages, an in-process server can be an excellent choice for its speed and size — if it fits all your other requirements.


Linked Objects

Linked objects, like embedded objects, rely on an object handler to communicate with server applications.The linked object itself, however, manages the naming and tracking of link sources.The linked object acts like an in-process server. For example, when activated, a linked object locates and launches the OLE server application that is the link source.

A linked object’s handler is made up of two main components: the handler component and the linking component. The handler component contains the controlling and remoting pieces and functions much like a handler for an embedded object. The linking component has its own controller and cache and provides access to the object’s structured storage. The linking component’s controller supports source naming through the use of monikers, and binding, the process of locating and running the link source. (For more information on monikers and binding, see “The Component Object Model.”)

When a user initially creates a linked object or loads an existing one from storage, the container loads an instance of the linking component into memory, along with the object handler. The linking component supplies interfaces — most notably IOleLink — that identify the object as a link and enable it to manage the naming, tracking, and updating of its link source.

By implementing the IOleLink interface, a linked object provides its container with functions that support linking. Only linked objects implement IOleLink, and by querying for this interface a container can determine whether a given object is embedded or linked. The most important function provided by IOleLink enables a container to binding to the source of the linked object, that is, to activate the connection to the document that stores the linked object’s native data. IOleLink also defines functions for managing information about the linked object, such as cached presentation data and the location of the link source.

When a compound document containing a linked object is saved, the link’s data is saved with the link source, not with the container. Only information about its name and location is saved along with the compound document. This behavior is in contrast to that of an embedded object, whose data is stored along with that of its container.

Container applications can provide information about their embedded objects such that the latter, or portions thereof, can act as link sources. By implementing support for linking to your container’s embedded objects, you make nested embeddings possible, relieving the user of having to track down the originals of every embedding object to which a link is desired. For example, if a user wants to embed a Microsoft® Excel worksheet in Microsoft® Word, and the worksheet contains a bitmap created in Paintbrush™, the user can link to a copy of the bitmap contained in the worksheet rather than the original.


Notifications

Notifications are callbacks generated by an object when it detects a change in its name, state, data, or presentation. Containers and other clients require notifications to respond appropriately to these changes. A container registers to receive notifications by setting up an advisory connection to an object of interest. Other interested clients can do the same. The container also creates an advisory sink to receive the notifications. Using the connection established by the container, an object experiencing a change notifies the advisory sink. Upon receiving a notification, the container takes whatever action has been defined for the type of change that has occurred.


Types of Notifications

Notifications fall into three groups: compound document, data, and view. An object sends compound document notifications in response to being renamed, saved, closed or, in the case of a link, having its link source renamed. As you would expect, objects send data notifications in response to changes in their data and send view notifications in response to changes in their presentation. Container applications must register separately for each of these notification types, but all can be handled by a single advisory sink.

All containers, the object handler, and the linked object register for compound document notifications. The typical container also registers for view notifications. Data notifications are usually sent to both the linked object and object handler. A special purpose container, such as one that renders the data itself, might benefit from receiving data notifications instead of view notifications. For example, an embedded chart container with a link to a table can register for data notifications. Because a change to the table affects the chart, the receipt of a data notification would direct the container to get the new tabular data.


How Notifications Work

Notifications originate in the object application and flow to the container by way of the object handler. If the object is a linked object, the linked object intercepts the notifications from the object handler and notifies the container directly.

An object application must manage registration requests, keeping track of where to send which notifications and sending those notifications when appropriate. OLE provides two component objects to simplify this task: the OleAdviseHolder for compound document notifications and the DataAdviseHolder for data notifications.

Object applications determine the conditions that prompt the sending of each specific notification and the frequency with which each notification should be sent. When it is appropriate for multiple notifications to be sent, it does not matter which notification is sent first; they can be sent in any order.

The timing of notifications affects the performance and coordination between an object application and its containers. Whereas notifications sent too frequently slow processing, notifications sent too infrequently result in an out-of-sync container. Notification frequency can be compared with the rate at which an application repaints. Therefore, using similar logic for the timing of notifications (as is used for repainting) is wise.


Compound Document Interfaces

The following tables list the interfaces implemented by OLE containers, OLE servers, and compound document objects. The required interfaces must be implemented on the components for which they are listed. For example, containers must implement the IOleClientSite and IAdviseSink interfaces. All other features are optional. If you want to include a particular feature in your application, however, you must implement the interfaces shown for that feature in the table below. All other interfaces are required only if you are including a particular feature. For example, if you want your application to do message filtering (recommended), you must implement IMessageFilter.

The following table lists required and optional behaviors for OLE containers and which interfaces you must implement for each.

OLE Containers

Behavior | Interfaces
Required Behaviors | IOleClientSite
 | IAdviseSink
Message Filtering | IMessageFilter
Linking | none
Linking to Embedded Objects | IOleItemContainer
 | IPersistFile
 | IClassFactory
In-Place Activation | IOleInPlaceSite
 | IOleInPlaceActive-Frame
 | IOleInPlaceUIObject
Drag and Drop | IDropSource
 | IDropTarget
 | IDataObject

The following table lists required and optional behaviors for OLE servers and their compound document objects and which interfaces you must implement for each. The table distinguishes OLE servers and their objects in order to clarify which component implements which interfaces. The table also notes the different requirements of objects provided by out-of-process versus in-process servers.

Feature | OLE Server | Compound Document Object
 |  | Out-of-Process | In-Process
Required Behaviors | IClassFactory | IOleObject | IOleObject
 |  | IDataObject | IDataObject
 |  | IPersistStorage | IPersistStorage
 |  |  | IViewObject2
 |  |  | IOleCache2
Message Filtering | IMessageFilter |  |
Linking | IOleItemContainer |  | IOleLink
 | IPersistFile |  | IExternalConnection
In-Place- Activation |  | IOleInPlaceObject | IOleInPlaceObject
 |  | IOleInPlace- ActiveObject | IOleInPlaceActive- Object
Drag and Drop | IDropSource |  |
 | IDropTarget |  |
 | IDataObject |  |


Object States

A compound object exists in one of three states: passive, loaded, or running. A compound-document object’s state describes the relationship between the object in its container and the application responsible for its creation. The following table summarizes these states.

Object State | Description
Passive | The compound-document object exists only in storage, either on disk or in a database. In this state, the object is unavailable for viewing or editing.
Loaded | The object’s data structures created by the object handler are in the container’s memory. The container has established communication with the object handler and there is cached presentation data available for rendering the object. Calls are processed by the object handler. This state, because of its low overhead, is used when a user is simply viewing or printing an object.
Running | The objects that control remoting have been created and the OLE server application is running. The object’s interfaces are accessible, and the container can receive notification of changes. In this state, an end user can edit or otherwise manipulate the object.


Entering the Loaded State

When an object enters the loaded state, the in-memory structures representing the object are created so that operations can be invoked on it. The object’s handler or in-process server is loaded. This process, referred to as instantiation, occurs when an object is loaded from persistent storage (a transition from the passive to the loaded state) or when an object is being created for the first time.

Internally, instantiation is a two-phase process. An object of the appropriate class is created, after which a method on that object is called to perform initialization and give access to the object’s data. The initialization method is defined in one of the object’s supported interfaces. The particular initialization method called depends on the context in which the object is being instantiated and the location of the initialization data.


Entering the Running State

When an embedded object makes the transition to the running state, the object handler must locate and run the server application in order to utilize the services that only the server provides. Embedded objects are placed in the running state either explicitly through a request by the container, such as a need to draw a format not currently cached, or implicitly by OLE in response to invoking some operation, such as when a user of the container double-clicks the object.

When a linked object makes the transition into the running state, the process is known as binding. In the process of binding, the object handler asks its stored moniker to locate the link’s data, then runs the server application.

At first glance, binding a linked object appears to be no more complicated than running an embedded object. However, the following points complicate the process:

  • A link can refer to an object, or a portion thereof, that is embedded in another container. This capability implies a potential for nested embeddings. Resolving references to such a hierarchy requires recursively traversing a composite moniker, beginning with the rightmost member.
  • When the link source is running, OLE binds to the running instance of the object rather than running another instance. In the case of nested embedded objects, one of which is the link source, OLE must be able to bind to an already running object at any point.
  • Running an object requires accessing the storage area for the object. When an embedded object is run, OLE receives a pointer to the storage during the load process, which it passes on to the OLE server application. For linked objects, however, there is no standard interface for accessing storage. The OLE server application may use the file system interface or some other mechanism.


Entering the Passive State

Object closure forces an embedded or linked object into the passive state. It is typically initiated from the OLE server application’s user interface, such as when the user selects the File Close command. In this case, the OLE server application notifies the container, which releases its reference count on the object. When all references to the object have been released, the object can be freed. When all objects have been freed, the OLE server application can safely terminate.

A container application can also initiate object closure. To close an object, the container releases its reference count after completing an optional save operation. You can design containers to release objects when they are deactivating after an in-place activation session, allowing the user to click outside the object without losing the active editing session.


Implementing In-Place Activation

In-place activation enables a user to interact with an embedded object without leaving the container document. When a user activates the object, a composite menu bar comprising elements from both the container application’s and server application’s menu bars replaces the container’s main menu bar. Commands and features from both applications are thus available to the user, including context sensitive help for the active object. When a user begins working with some non-object portion of the document, the object is deactivated, causing the container document’s original menu to replace the composite menu.

This capability originally went by the name of in-place editing. The name was changed because editing is only one way for a user to interact with a running object. Sound clips, for example, can be listened to instead of editing. Video clips can be viewed instead of editing. In-place activation is particularly apt in the case of video clips because it allows them to run in place, without calling up a separate window. This could be critical if the video were to be viewed, say, in conjunction with adjacent text data in the container document.

Implementing in-place activation is strictly optional for both container and server applications. OLE still supports the model in which activating an object causes the server application to open a separate window. Linked objects always open in a separate window to emphasize that they reside in a separate document.

In-place activation begins with the object in response to an IOleObject::DoVerb call from its container. This call usually happens in response to a user double-clicking the object or selecting a command (verb) from the container application’s Edit menu.

The in-place window receives keyboard and mouse input while the embedded object is active. When a user selects commands from the composite menu bar, the command and associated menu messages are sent to the container or object application, depending on which owns the particular drop-down menu selected. Input by means of an object’s rulers, toolbars, or frame adornments go directly to the embedded object, which owns these windows.

An in-place-activated embedded object remains active until either the container deactivates it in response to user input or the object voluntarily gives up the active state, as a video clip might do, for example. A user can deactivate an object by clicking inside the container document but outside the object’s in-place-activation window, or simply by clicking another object. An in-place-activated object remains active, however, if the user clicks the container’s title bar, scroll bar or, in particular, menu bar.

You can implement an in-place-activation-object server either as an in-process server (DLL) or a local server (EXE). In both cases, the composite menu bar contains items (typically drop-down menus) from both the server and container processes. In the case of a in-process server, the in-place activation window is simply another child window in the container’s window hierarchy, receiving its input through the container application’s message pump.

In the case of a local server, the in-place activation window belongs to the embedded object’s server application process, but its parent window belongs to the container. Input for the in-place-activation window appears in the server’s message queue and is dispatched by the server’s message loop. The OLE libraries are responsible for seeing to it that menu commands and messages are dispatched correctly.


Creating Linked and Embedded Objects from Existing Data

A user typically assembles a compound document by using either the clipboard or drag and drop to copy a data object from its server application to the user’s container application. With applications that support OLE, the user can initiate the transfer from either the server or the container. For example, the server can copy data to the clipboard in the server application, then switch to the container application and choose Paste Special/Embedded Object or an equivalent menu command to create a new embedded object from the selected data. Or, the user can drag the data from one application to the other. The process is similar for creating a linked object.

Note An application that functions as both OLE server and container can use a selection of its own data to create an embedded or linked object at a new location within the same document.

Data transfer between OLE server and container applications is built on uniform data transfer, as described in Data Transfer. OLE servers and object handlers implement IDataObject in order to make their data available for transfers using either the clipboard or drag and drop. OLE objects support all the usual clipboard formats. In addition, they support six clipboard formats that support the creation of linked and embedded objects from a selected data object.

OLE clipboard formats describe data objects that, upon being dropped or pasted in OLE containers, are to become embedded or linked compound-document objects. The data object presents these formats to container applications in order of their fidelity as descriptions of the data. In other words, the object presents first the format that best represents it, followed by the next best format, and so on. This intentional ordering encourages a container application to use the best possible format.


View Caching

A container application must be able to obtain a presentation of an object for the purpose of displaying or printing it for users when the document is open but the object’s server application is not running or is not installed on the user’s machine. To assume, however, that the servers for all the objects that might conceivably be found in a document are installed on every user’s machine and can always run on demand is unrealistic. The default object handler, which is available at all times, solves this dilemma by caching object presentations in the document’s storage and manipulating these presentations on any platform regardless of the availablility of the object server on any particular installation of the container.

Containers can maintain drawing presentations for one or more specific target devices in addition to the one required to maintain the object on screen. Moreover, if you port the object from one platform to another, OLE automatically converts the object’s data formats to ones supported on the new platform. For example, if you move an object from Windows to the Macintosh, OLE will convert its metafile presentations to PICT formats.

In order to present an accurate representation of an embedded object to the user, the object’s container application initiates a dialog with the object handler, requesting data and drawing instructions. To be able to fulfill the container’s requests, the handler must implement the IDataObject, IViewObject2, and IOleCache interfaces.

IDataObject enables an OLE container application to get data from and send data to its embedded or linked objects. When data changes in an object, this interface provides a way for the object to make its new data available to its container and provides the container with a way to update the data in its copy of the object. (For a discussion of data transfer in general, see Chapter 4, “Data Transfer.”)

The IViewObject2 interface is very much like the IDataObject interface except that it asks an object to draw itself on a device context, such as a screen, printer, or metafile, rather than move or copy its data to memory or some other transfer medium. The purpose of the interface is to enable an OLE container to obtain alternative pictorial representations of its embedded objects, whose data it already has, thereby avoiding the overhead of having to transfer entirely new instances of the same data objects simply to obtain new drawing instructions. Instead, the IViewObject2 interface enables the container to ask an object to provide a pictorial representation of itself by drawing on a device context specified by the container.

When calling the IViewObject2 interface, a container application can also specify that the object draw itself on a target device different than the one on which it will actually be rendered. This enables the container, as needed, to generate different renderings from a single object. For example, the caller could ask the object to compose itself for a printer even though the resulting drawing will be rendered on screen. The result, of course, would be a print-preview of the object.

The IViewObject2 interface also provides methods that enable containers to register for view-change notifications. As with data and OLE advisories, a view advisory connection enables a container to update its renderings of an object at its own convenience rather than in response to a call from the object. For example, if a new version of an object’s server application were to offer additional views of the same data, the object’s default handler would call each container’s implementation of IAdviseSink::OnViewChange to let them know that the new presentations were available. The container would retrieve this information from the advise sink only when needed.

Because Windows device contexts have meaning only within a single process, you cannot pass IViewObject2 pointers across process boundaries. As a result, OLE local and remote servers have no need whatsoever to implement the interface, which wouldn’t work properly even if they did. Only object handlers and in-process servers implement the IViewObject2 interface. OLE provides a default implementation, which you can use in your own OLE in-process servers and object handlers simply by aggregating the OLE default handler. Or you can write your own implementation of IViewObject.

An object implements the IOleCache interface in order to let the handler know what capabilities it should cache. The object handler also owns the cache and ensures it is kept up to date. As the embedded object enters the running state, the handler sets up appropriate advisory connections on the server object, with itself acting as the sink. The IDataObject and IViewObject2 interface implementations operate out of data cached on the client side. The handler’s implementation of IViewObject2 is responsible for determining what data formats to cache in order to satisfy client draw requests. The handler’s implementation of IDataObject is responsible for getting data in various formats, etc., between memory and the underlying IStorage instance of the embedded object. Custom handlers can use these implementations by aggregating on the default handler.

Note The IViewObject2 interface is a simple functional extension of IViewObject and should be implemented instead of the latter interface, which is now obsolete. In addition to providing the IViewObject methods, the IViewObject2 interface provides a single additional member, GetExtent, which enables a container application to get the size of an object’s presentation from the cache without first having to move the object into the running state with a call to IOleObject::GetExtent.


Data Transfer

The Component Object Model (COM) provides a standard mechanism for transferring data between applications. This mechanism is the data object, which is simply any COM object that implements the IDataObject interface. Some data objects, such as a piece of text copied to the clipboard, have IDataObject as their sole interface. Others, such as compound document objects, expose several interfaces, of which IDataObject is simply one. Data objects are fundamental to the working of compound documents, although they also have widespread application outside that OLE technology.

By exchanging pointers to a data object, providers and consumers of data can manage data transfers in a uniform manner, regardless of the format of the data, the type of medium used to transfer the data, or the target device on which it is to be rendered. You can include support in your application for basic clipboard transfers, drag and drop transfers, and OLE compound document transfers with a single implementation of IDataObject. Having done that, the amount of code required to accommodate the special semantics of each protocol is minimal.


Data Transfer Interfaces

The IDataObject interface provides consumers of data with methods for getting and setting an object’s data, determining which formats the object supports, and registering for and receiving notifications when data in the object changes. When obtaining data, a caller can specify the format in which it wants to render the data. The source of the data, however, determines the storage medium, which it returns in an out parameter provided by the caller.

By itself, IDataObject supplies all the tools you need to implement Microsoft® Windows® clipboard transfers or compound document transfers in your applications. If you also want to support drag and drop transfers, you need to implement the IDropSource and IDropTarget interfaces along with IDataObject.

The IDataObject interface combined with OLE clipboard APIs provide all the capabilities of the Microsoft® Win32® clipboard APIs. Using both the platform’s clipboard APIs and OLE’s is usually redundant and unnecessary. Suppliers of data that support either drag and drop transfers or OLE compound documents must implement the IDataObject interface. If your application supports only clipboard transfers now, but you intend to add drag and drop or compound documents in later releases, you may want to implement IDataObject and the OLE clipboard APIs now in order to minimize the amount of time spent recoding and debugging later. You may also want to implement IDataObject in order to utilize transfer media other than global memory.

The following table summarizes which ones to use, depending on what types of data transfer you want to support:

If You Want to Support | You Must Use
Compound documents | IDataObject
Drag and drop transfers | IDataObject, IDropSource, IDropTarget, DoDragDrop (or the equivalent)
Windows clipboard transfers using global memory exclusively | Windows clipboard APIs
Windows clipboard transfers using exchange mediums other than global memory. | IDataObject
Clipboard transfers now but drag and drop or compound documents later | IDataObject and the interfaces and function listed above for “Drag and drop transfers”

When a user initiates a data transfer operation, the source application creates an instance of IDataObject and through it makes the data available in one or more formats. In a clipboard transfer, the application calls the OleSetClipboard function to pass a data-object pointer to OLE. (OleSetClipboard also offers standard clipboard data formats for both OLE version 1 and non-OLE applications.) In a drag and drop transfer, the application calls the DoDragDrop function instead.

On the receiving side of the transfer, the destination receives the IDataObject pointer either as an argument to an invocation of IDropTarget::Drop or by calling the OleGetClipboard function, depending on whether the transfer is by means of drag and drop or the clipboard. Having obtained this pointer, the destination calls IDataObject::EnumFormatEtc to learn what formats are available for retrieval and on what types of media they can be obtained. Armed with this information, the receiving application requests the data with a call to IDataObject::GetData.


Data Formats and Transfer Media

Most platforms, including Windows, define a standard protocol for transferring data between applications, based on a set of functions called the clipboard. Applications using these functions can share data even if their native data formats are wildly different. Generally, these clipboards have two significant shortcomings that COM has overcome.

First, data descriptions use only a format identifier, such as the single 16-bit clipboard format identifier on Windows, which means the clipboard can only describe the structure of its data, that is, the ordering of the bits. It can report, “I have a bitmap” or “I have some text,” but it cannot specify the target devices for which the data is composed, which views or aspects of itself the data can provide, or which storage media are best suited for its transfer. For example, it cannot report, “I have a string of text that is stored in global memory and formatted for presentation either on screen or on a printer” or “I have a thumbnail sketch bitmap rendered for a 100 dpi dot-matrix printer and stored as a disk file.”

Second, all data transfers using the clipboard generally occur through global memory. Using global memory is reasonably efficient for small amounts of data but horribly inefficient for large amounts, such as a 20 MB multimedia object. Global memory is slow for a large data object, whose size requires considerable swapping to virtual memory on disk. In cases where the data being exchanged is going to reside mostly on disk anyway, forcing it through this virtual-memory bottleneck is highly inefficient. A better way would skip global memory entirely and simply transfer the data directly to disk.

To alleviate these problems, COM provides two data structures: FORMATETC and STGMEDIUM.


The FORMATETC Structure

The FORMATETC structure is a generalized clipboard format, enhanced to encompass a target device, an aspect or view of the data, and a storage medium. A data consumer, such as an OLE container application, passes the FORMATETC structure as an argument in calls to IDataObject to indicate the type of data it wants from a data source, such as a compound document object. The source uses the FORMATETC structure to describe what formats it can provide. FORMATETC can describe virtually any data, including other objects such as monikers. A container can ask one of its embedded objects to list its data formats by calling IDataObject::EnumFormatetc, which returns an enumerator object that implements the IEnumFormatEtc interface. Instead of replying merely that it has “text and a bitmap,” the object can provide a detailed description of the data, including the device (normally screen or printer) for which it is rendered, the aspect to be presented to the user (full contents, thumbnail, icon, or formatted for printing), and the storage medium containing the data (global memory, disk file, storage object, or stream). This ability to tightly describe data will, in time, result in higher quality printer and screen output as well as more efficiency in data browsing, where a thumbnail sketch is much faster to retrieve and display than a fully detailed rendering.

The following table lists fields of the FORMATETC data structure and the information they specify:

Field | Specifies
cfFormat | The format in which the data is to be rendered, which can be a standard clipboard format, a proprietary format, or an OLE format. For more information on OLE formats, see Chapter 5, “Compound Documents.”
ptd | A DVTARGETDEVICE structure, which contains enough information about a Windows target device, such as a screen or printer, so that a handle to its device context (hDC) can be created using the Windows CreateDC function.
dwAspect | The aspect or view of the data to be rendered; can be the full contents, a thumbnail sketch, an icon, or formatted for printing.
lindex | The part of the aspect that is of interest; for the present, the value must be -1, indicating that the entire view is of interest.
tymed | The data’s storage medium, which can be global memory, disk file, or an instance of one of COM’s structured-storage interfaces.


The STGMEDIUM Structure

Just as the FORMATETC structure is an enhancement of the Windows clipboard format identifier, so the STGMEDIUM structure is an improvement of the global memory handle used to transfer the data. The STGMEDIUM structure includes a flag, tymed, which indicates the medium to be used, and a union comprising pointers and a handle for getting whichever medium is specified in tymed.

The STGMEDIUM structure enables both data sources and consumers to choose the most efficient exchange medium on a per-rendering basis. If the data is so big that it should be kept on disk, the data source can indicate a disk-based medium in its preferred format, only using global memory as a backup if that’s the only medium the consumer understands. Being able to use the best medium for exchanges as the default improves overall performance of data exchange between applications. For example, if some of the data to be transferred is already on disk, the source application can move or copy it to a new destination, either in the same application or in some other, without having first to load the data into global memory. At the receiving end, the consumer of the data does not have to write it back to disk.


Drag and Drop

“Drag and drop” refers to data transfers in which a mouse or other pointing device is used to specify both the data source and its destination. In a typical drag and drop operation, a user selects the object to be transferred by moving the mouse pointer to it and holding down either the left button or some other button designated for this purpose. While continuing to hold down the button, the user initiates the transfer by dragging the object to its destination, which can be any OLE container. Drag and drop provides exactly the same functionality as the OLE Clipboard copy and paste but adds visual feedback and eliminates the need for menus. In fact, if an application supports Clipboard copy and paste, little extra is needed to support drag and drop.

During an OLE drag and drop operation, the following three separate pieces of code are used:

Drag-and-drop code source | Implementation and use
IDropSource interface | Implemented by the object containing the dragged data, referred to as the drag source.
IDropTarget interface | Implemented by the object that is intended to accept the drop, referred to as the drop target.
DoDragDrop function | Implemented by OLE and used to initiate a drag and drop operation. Once the operation is in progress, it facilitates communication between the drag source and the drop target.

The IDropSource and IDropTarget interfaces can be implemented in either a container or in an object application. The role of drag source or drop target is not limited to any one type of OLE application.

The OLE function DoDragDrop implements a loop that tracks mouse and keyboard movement until such time as the drag is canceled or a drop occurs. DoDragDrop is the key function in the drag and drop process, facilitating communication between the drag source and drop target.

During a drag and drop operation, three types of feedback can be displayed to the user:

Type of feedback | Description
Source feedback | Provided by the drag source, the source feedback indicates the data is being dragged and does not change during the course of the drag. Typically, the data is highlighted to signal it has been selected.
Pointer feedback | Provided by the drag source, the pointer feedback indicates what happens if the mouse is released at any given moment. Pointer feedback changes continually as the user moves the mouse and/or presses a modifier key. For example, if the pointer is moved into a window that cannot accept a drop, the pointer changes to the “not allowed” symbol.
Target feedback | Provided by the drop target, target feedback indicates where the drop is to occur.

Although the Windows 3.1 File Manager does not use OLE to implement drag and drop transfers, it is nevertheless an example of an application that acts as both a drag source and drop target and that provides all three types of feedback. When a user selects a file to copy, File Manager supplies source feedback by indicating the selection. As the selection is dragged and modifier keys are pressed, the mouse pointer changes. For example, if the user presses the CTRL key, a plus sign is added, indicating that the drop would result in a copy rather than moving the original. When the file is dragged over an area that is not a drop target, the pointer changes appropriately. Target feedback, a line drawn around a file or directory, is provided when the pointer is over an area that is a drop target.


Drag Source Responsibilities

The drag source is responsible for the following tasks:

  • Providing a data-transfer object for the drop target that exposes the IDataObject and IDropSource interfaces.
  • Generating pointer and source feedback.
  • Determining when the drag operation has been canceled or a drop operation has occurred.
  • Performing any action on the original data caused by the drop operation, such as deleting the data or creating a link to it.

The main task is creating a data-transfer object that exposes the IDataObject and IDropSource interfaces. The drag source might or might not include a copy of the selected data. Including it is not mandatory, but doing so safeguards against inadvertent changes and allows the Clipboard operations code to be identical to the drag and drop code.

While a drag operation is in progress, the drag source is responsible for setting the mouse pointer and, if appropriate, for providing additional source feedback to the user. The drag source cannot provide any feedback that tracks the mouse position other than by actually setting the real pointer (see the Windows SetCursor function). This rule must be enforced to avoid conflicts with the feedback provided by the drop target. (A drag source can also be a drop target. When dropping on itself, the source/target can, of course, provide target feedback to track the mouse position. In this case, however, it is the drop target tracking the mouse, not the source.) Based on the feedback offered by the drop target, the source sets an appropriate pointer.


Data Notification

Objects that consume data from an external source sometimes need to be informed when data in that source changes. For example, a stock ticker tape viewer that relies on data in some spreadsheet needs to be notified when that data changes so it can update its display. Similarly, a compound document needs information about data changes in its embedded objects so that it can update its data caches. In cases such as this, where dynamic updating of data is desirable, sources of data require some mechanism of notifying data consumers of changes as they occur without obligating the consumers to drop everything in order to update their data. Ideally, having been notified that a change has occurred in the data source, a consuming object can ask for an updated copy at its leisure.

COM’s mechanism for handling asynchronous notifications of this type is an object called an advise sink, which is simply any COM object that implements an interface called IAdviseSink. Consumers of data implement the IAdviseSink. They register to receive notifications by handing a pointer to the data object of interest.

The IAdviseSink interfaces exposes the following methods for receiving asynchronous notifications:

Method | Notifies the Advise Sink that
OnDataChange | Calling object’s data has changed.
OnViewChange | Instructions for drawing the calling object have changed.
OnRename | Calling object’s moniker has changed.
OnSave | Calling object has been saved to persistent storage.
OnClose | Calling object has been closed.

As the table indicates, the IAdviseSink interface exposes methods for notifying the advise sink of events other than changes in the calling object’s data. The calling object can also notify the sink when the way in which it draws itself changes, or it is renamed, saved, or closed. These other notifications are used mainly or entirely in the context of compound documents, although the notification mechanism is identical. For more information on compound-document notifications, see “Compound Documents.”

In order to take advantage of the advise sink, a data source must implement IDataObject::DAdvise, IDataObject::DUnadvise, and IDataObject::EnumDAdvise. A data consumer calls the DAdvise mothod to notify a data object that it wishes to be notified when the object’s data changes. The consuming object calls the DUnadvise method to tear down this connection. Any interested party can call the EnumAdvise method to learn the number of objects having an advisory connection with a data object.

When data changes at the source, the data object calls IAdviseSink::OnDataChange on all data consumers that have registered to receive notifications. To keep track of advisory connections and manage the dispatch of notifications, data sources rely on an object called a data advise holder. You can create your own data advise holder by implementing the IDataAdviseHolder interface. Or, you can let COM do it for you by calling the helper function CreateDataAdviseHolder.


IAdviseSink

The IAdviseSink interface enables containers and other objects to receive notifications of data changes, view changes, and compound-document changes occurring in objects of interest. Container applications, for example, require such notifications to keep cached presentations of their linked and embedded objects up-to-date. Calls to IAdviseSink methods are asynchronous, so the call is sent and then the next instruction is executed without waiting for the call's return.

For an advisory connection to exist, the object that is to receive notifications must implement IAdviseSink, and the objects in which it is interested must implement IOleObject::Advise and IDataObject::DAdvise. In-process objects and handlers may also implement IViewObject::SetAdvise. Objects implementing IOleObject must support all reasonable advisory methods. To simplify advisory notifications, OLE supplies implementations of the IDataAdviseHolder and IOleAdviseHolder, which keep track of advisory connections and send notifications to the proper sinks through pointers to their IAdviseSink interfaces. IViewObject (and its advisory methods) is implemented in the default handler.

As shown in the following table, an object that has implemented an advise sink registers its interest in receiving certain types of notifications by calling the appropriate method:

Call This Method | To Register for These Notifications
IOleObject::Advise | When a document is saved, closed, or renamed.
IDataObject::DAdvise | When a document's data changes.
IViewObject::SetAdvise | When a document's presentation changes.

When an event occurs that applies to a registered notification type, the object application calls the appropriate IAdviseSink method. For example, when an embedded object closes, it calls the IAdviseSink::OnClose method to notify its container. These notifications are asynchronous, occurring after the events that trigger them.

When to Implement

Objects, such as container applications and compound documents, implement IAdviseSink to receive notification of changes in data, presentation, name, or state of their linked and embedded objects. Implementers register for one or more types of notification, depending on their needs.

Notifications of changes to an embedded object originate in the server and flow to the container by way of the object handler. If the object is a linked object, the OLE link object intercepts the notifications from the object handler and notifies the container directly. All containers, the object handler, and the OLE link object register for OLE notifications. The typical container also registers for view notifications. Data notifications are usually sent to the OLE link object and object handler.

When to Use

Servers call the methods of IAdviseSink to notify objects with which they have an advisory connection of changes in an object's data, view, name, or state.

Note OLE does not permit synchronous calls in the implementation of asynchronous methods, so you cannot make synchronous calls within any of the the IAdviseSink interface's methods. For example, an implementation of IAdviseSink::OnDataChange cannot contain a call to IDataObject::GetData.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IAdviseSink Methods | Description
OnDataChange | Advises that data has changed.
OnViewChange | Advises that view of object has changed.
OnRename | Advises that name of object has changed.
OnSave | Advises that object has been saved to disk.
OnClose | Advises that object has been closed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IAdviseSink2, IDataAdviseHolder, IOleAdviseHolder, IOleObject::Advise, IOleObject::Unadvise, IOleObject::EnumAdvise


IAdviseSink::OnClose

Called by the server to notify all registered advisory sinks that the object has changed from the running to the loaded state.

Void OnClose();

Remarks

OnClose notification indicates that an object is making the transition from the running to the loaded state, so its container can take appropriate measures to ensure orderly shutdown. For example, an object handler must release its pointer to the object.

If the object that is closing is the last open object supported by its OLE server application, the application can also shut down.

In the case of a link object, the notification that the object is closing should always be interpreted to mean that the connection to the link source has broken.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IAdviseSink::OnDataChange

Called by the server to notify a data object's currently registered advise sinks that data in the object has changed.

void OnDataChange(
  FORMATETC * pFormatetc,  //Pointer to format information
  STGMEDIUM * pStgmed  //Pointer to storage medium
);

Parameters

pFormatetc
[in] Pointer to the FORMATETC structure, which describes the format, target device, rendering, and storage information of the calling data object.
pStgmed
[in] Pointer to the STGMEDIUM structure, which defines the storage medium (global memory, disk file, storage object, stream object, GDI object, or undefined) and ownership of that medium for the calling data object.

Remarks

Object handlers and containers of link objects implement IAdviseSink::OnDataChange to take appropriate steps when notified that data in the object has changed. They also must call IDataObject::DAdvise to set up advisory connections with the objects in whose data they are interested. (See IDataObject::DAdvise for more information on how to specify an advisory connection for data objects.)

Containers that take advantage of OLE's caching support do not need to register for data-change notifications, because the information necessary to update the container's presentation of the object, including any changes in its data, are maintained in the object's cache.

Notes to Implementers

If you implement IAdviseSink::OnDataChange for a container, remember that this method is asynchronous and that making synchronous calls within asynchronous methods is not valid. Therefore, you cannot call IDataObject::GetData to obtain the data you need to update your object. Instead, you either post an internal message, or invalidate the rectangle for the changed data by calling InvalidateRect and waiting for a WM_PAINT message, at which point you are free to get the data and update the object.

The data itself, which is valid only for the duration of the call, is passed using the storage medium pointed to by pStgmed. Since the caller owns the medium, the advise sink should not free it. Also, if pStgmed points to an IStorage or IStream interface, the sink must not increment the reference count.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::DAdvise


IAdviseSink::OnRename

Called by the server to notify all registered advisory sinks that the object has been renamed.

Void OnRename(
  IMoniker * pmk  //Pointer to the new moniker of the object
);

Parameter

pmk
[in] Pointer to the IMoniker interface on the new full moniker of the object.

Remarks

OLE link objects normally implement IAdviseSink::OnRename to receive notification of a change in the name of a link source or its container. The object serving as the link source calls OnRename and passes its new full moniker to the object handler, which forwards the notification to the link object. In response, the link object must update its moniker. The link object, in turn, forwards the notification to its own container.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IAdviseSink::OnSave

Called by the server to notify all registered advisory sinks that the object has been saved.

Void OnSave();

Remarks

Object handlers and link objects normally implement IAdviseSink::OnSave to receive notifications of when an object is saved to disk, either to its original storage (through a Save operation) or to new storage (through a Save As operation). Object Handlers and link objects register to be notified when an object is saved for the purpose of updating their caches, but then only if the advise flag passed during registration specifies ADVFCACHE_ONSAVE. Object handlers and link objects forward these notifications to their containers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IAdviseSink::OnViewChange

Notifies an object's registered advise sinks that its view has changed.

Void OnViewChange(
  DWORD dwAspect,  //Value specifying aspect of object
  LONG lindex      //Currently must be -1
);

Parameters

dwAspect
[in] The aspect, or view, of the object. Contains a value taken from the enumeration, DVASPECT.
lindex
[in] The portion of the view that has changed. Currently only -1 is valid.

Remarks

Containers register to be notified when an object's view changes by calling IViewObject::SetAdvise. Once registered, the object will call the sink's IAdviseSink::OnViewChange method when appropriate. OnViewChange can be called when the object is in either the loaded or running state.

Even though DVASPECT values are individual flag bits, dwAspect may represent only one value. That is, dwAspect cannot contain the result of an OR operation combining two or more DVASPECT values.

The lindex member represents the part of the aspect that is of interest. The value of lindex depends on the value of dwAspect. If dwAspect is either DVASPECT_THUMBNAIL or DVASPECT_ICON, lindex is ignored. If dwAspect is DVASPECT_CONTENT, lindex must be -1, which indicates that the entire view is of interest and is the only value that is currently valid.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IViewObject::SetAdvise


IAdviseSink2

The IAdviseSink2 interface is an extension of IAdviseSink, adding the method OnLinkSrcChange to the contract to handle a change in the moniker of a linked object. This avoids overloading the implementation IAdviseSink::OnRename to handle the renaming of both embedded objects and linked objects. In applications where different blocks of code might execute depending on which of these two similar events has occurred, using the same method for both events complicates testing and debugging.

When to Implement

If your application supports links, you should definitely implement IAdviseSink2. Even if your application does not support links, it may do so in future releases. In general, your code would implement just IAdviseSink2, which, because of contract inheritance, must include implementations of all of the IAdviseSink methods, along with the single additional method of IAdviseSink2.

When to Use

When a link source is renamed, the link object should notify its container by calling IAdviseSink::OnLinkSrcChange. If you are using the default handler, containing the OLE link object, you can still have both embedded and linked objects call IAdviseSink::OnRename, but for linked objects, the OLE link object maps the notification to IAdviseSink2::OnLinkSrcChange.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IAdviseSink Methods | Description
OnDataChange | Advises that data has changed.
OnViewChange | Advises that view of object has changed.
OnRename | Advises that name of object has changed.
OnSave | Advises that object has been saved to disk.
OnClose | Advises that object has been closed.
IAdviseSink2 Method | Description
OnLinkSrcChange | Advises that link source has changed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IAdviseSink2::OnLinkSrcChange

Notifies the container that registered the advise sink that a link source has changed (either name or location), enabling the container to update the link's moniker.

void OnLinkSrcChange(
  IMoniker *pmk  //Pointer to the moniker of the new link source
);

Parameter

pmk
[in] Pointer to the IMoniker interface identifying the source of a linked object.

Remarks

A container of linked objects implements this method to receive notification in the event of a change in the moniker of its link source.

IAdviseSink2::OnLinkSrcChange is called by the OLE link object when it receives the OnRename notification from the link-source (object) application. The link object updates its moniker and sends the OnLinkSrcChange notification to containers that have implemented IAdviseSink2.

Notes to Implementers

Nothing prevents a link object from notifying its container of the moniker change by calling IAdviseSink::OnRename instead of OnLinkSrcChange. In practice, however, overloading OnRename to mean either that a link object's moniker has changed or that an embedded object's server name has changed makes it difficult for applications to determine which of these events has occurred. If the two events trigger different processing, as will often be the case, calling a different method for each makes the job of determining which event occurred much easier.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IAdviseSink::OnRename


IAdviseSinkEx

The IAdviseSinkEx interface is derived from IAdviseSink to provide extensions for notifying the sink of changes in an object's view status.

When to Implement

Container applications and compound documents implement a site object with the IAdviseSinkEx interface to receive notification of changes in the view status of a contained object.

When to Use

A contained object, such as a control, calls the method of IAdviseSinkEx to notify its container of changes in its view status.

To determine which interface the sink supports, an object must call QueryInterface using the pointer that was passed to IViewObject::SetAdvise.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IAdviseSink Methods | Description
OnDataChange | Advises that data has changed.
OnViewChange | Advises that view of object has changed.
OnRename | Advises that name of object has changed.
OnSave | Advises that object has been saved to disk.
OnClose | Advises that object has been closed.
IAdviseSinkEx Methods | Description
OnViewStatusChange | Notifies the sink that a view status of an object has changed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IAdviseSink, IViewObject::SetAdvise


IAdviseSinkEx::OnViewStatusChange

Notifies the sink that a view status of an object has changed.

HRESULT OnViewStatusChange(
  DWORD dwViewStatus  //New view status
);

Parameters

dwViewStatus
[in] New view status specified in VIEWSTATUS enumeration values.

Return Values

S_OK
The sink was successfully notified of the new view status.

Remarks

It is important that objects call the IAdviseSink:OnViewChange method whenever the object's view changes even when the object is in place active. Containers rely on this notification to keep an object's view up-to-date.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IAdviseSink:OnViewChange, VIEWSTATUS


IDataAdviseHolder

The IDataAdviseHolder interface contains methods that create and manage advisory connections between a data object and one or more advise sinks. Its methods are intended to be used to implement the advisory methods of IDataObject. IDataAdviseHolder is implemented on an advise holder object. Its methods establish and delete data advisory connections and send notification of change in data from a data object to an object that requires this notification, such as an OLE container, which must contain an advise sink.

Advise sinks are objects that require notification of change in the data the object contains and implement the IAdviseSink interface. Advise sinks are also usually associated with OLE compound document containers.

When to implement

Typically, you use the OLE-provided implementation of IDataAdviseHolder to simplify your implementation of the DAdvise, DUnadvise, and EnumDAdvise methods in the IDataObject interface, and to send notification of data change as appropriate. It would be necessary to implement IDataAdviseHolder only in the case where there may be a need for a custom data advise holder object, whose methods are to be used to implement the IDataObject methods in a set of servers.

When to use

Your implementation of the advisory methods of IDataObject can call the methods in IDataAdviseHolder. The first time you receive a call to IDataObject::DAdvise, call the function CreateDataAdviseHolder to create an instance of the OLE-provided advise holder and get a pointer to its IDataAdviseHolder interface. Then, in implementing the IDataObject interface on the data object, you delegate implementations of the DAdvise, DUnadvise, and EnumDAdvise methods to the corresponding methods in IDataAdviseHolder.

When the data of interest to an advise sink actually changes, you call IDataAdviseHolder::SendOnDataChange from the data object to carry out the necessary notifications.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDataAdviseHolder Methods | Description
Advise | Creates a connection between an advise sink and a data object so the advise sink can receive notification of change in the data object.
Unadvise | Destroys a notification connection previously set up with the Advise method.
EnumAdvise | Returns an object that can be used to enumerate the current advisory connections.
SendOnDataChange | Sends a change notification back to each advise sink that is currently being managed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject, IAdviseSink


IDataAdviseHolder::Advise

Creates a connection between an advise sink and a data object for receiving notifications.

HRESULT Advise(
  IDataObject * pDataObject,  //Pointer to the data object for which
                              // notifications are requested
  FORMATETC * pFormatetc,     //Pointer to the description of data to
                              // the advise sink
  DWORD advf,                 //Flags that specify how the notification
                              // takes place
  IAdviseSink * pAdvSink,     //Pointer to the advise sink requesting
                              // notification
  DWORD * pdwConnection       //Pointer to the connection token
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object for which notifications are requested. If data in this object changes, a notification is sent to the advise sinks that have requested notification.
pFormatetc
[in] Pointer to the specified format, medium, and target device that is of interest to the advise sink requesting notification. For example, one sink may want to know only when the bitmap representation of the data in the data object changes. Another sink may be interested in only the metafile format of the same object. Each advise sink is notified when the data of interest changes. This data is passed back to the advise sink when notification occurs.
advf
[in] Contains a group of flags for controlling the advisory connection. Valid values are from the enumeration ADVF. However, only some of the possible ADVF values are relevant for this method. The following table briefly describes the relevant values; a more detailed description can be found in the description of the ADVF enumeration.
ADVF Value | Description
ADVF_NODATA | Asks that no data be sent along with the notification.
ADVF_ONLYONCE | Causes the advisory connection to be destroyed after the first notification is sent. An implicit call to IDataAdviseHolder::Unadvise is made on behalf of the caller to remove the connection.
ADVF_PRIMEFIRST | Causes an initial notification to be sent regardless of whether or not data has changed from its current state.
ADVF_DATAONSTOP | When specified with ADVF_NODATA, this flag causes a last notification with the data included to be sent before the data object is destroyed. When ADVF_NODATA is not specified, this flag has no effect.
pAdvSink
[in] Pointer to the IAdviseSink interface on the advisory sink that receives the change notification.
pdwConnection
[out] Pointer to a DWORD token that identifies this connection. The calling object can later delete the advisory connection by passing this token to IDataAdviseHolder::Unadvise. If this value is zero, the connection was not established.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The advisory connection was created.

Remarks

Through the connection established through this method, the advisory sink can receive future notifications in a call to IAdviseSink::OnDataChange.

An object issues a call to IDataObject::DAdvise to request notification on changes to the format, medium, or target device of interest. This data is specified in the pFormatetc parameter. The DAdvise method is usually implemented to call IDataAdviseHolder::Advise to delegate the task of setting up and tracking a connection to the advise holder. When the format, medium, or target device in question changes, the data object calls IDataAdviseHolder::SendOnDataChange to send the necessary notifications.

The established connection can be deleted by passing the value in pdwConnection in a call to IDataAdviseHolder::Unadvise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ADVF, CreateDataAdviseHolder, FORMATETC, IDataAdviseHolder::Unadvise, IDataObject::DAdvise


IDataAdviseHolder::EnumAdvise

Returns a pointer to an IEnumStatdata interface on an enumeration object that can be used to enumerate the current advisory connections.

HRESULT EnumAdvise(
  IEnumSTATDATA ** ppenumAdvise  //Address of output variable that
                                 // receives the IEnumSTATDATA
                                 // interface pointer
);

Parameter

ppenumAdvise
[out] Address of IEnumSTATDATA* pointer variable that receives the interface pointer to the new enumerator object. If the implementation returns NULL in *ppenumAdvise, there are no connections to advise sinks at this time.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The enumerator object is successfully instantiated or there are no connections.

Remarks

This method must supply a pointer to an implementation of the IEnumSTATDATA interface, one of the standard enumerator interfaces that contain the Next, Reset, Clone, and Skip methods, on an enumerator object. Its methods allow you to enumerate the data stored in an array of STATDATA structures. You get a pointer to the OLE implementation of IDataAdviseHolder through a call to CreateDataAdviseHolder, and then call IDataAdviseHolder::EnumAdvise to implement IDataObject::EnumDAdvise.

Adding more advisory connections while the enumerator object is active has an undefined effect on the enumeration that results from this method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumXXXX, IEnumSTATDATA, IDataObject::EnumDAdvise


IDataAdviseHolder::SendOnDataChange

Sends notifications to each advise sink for which there is a connection established by calling the IAdviseSink::OnDataChange method for each advise sink currently being handled by this instance of the advise holder object.

HRESULT SendOnDataChange(
  IDataObject * pDataObject,
                     //Pointer to the data object that has changed
  DWORD dwReserved,  //Reserved
  DWORD advf         //Advise flags
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object in which the data has just changed. This pointer is used in subsequent calls to IAdviseSink::OnDataChange.
dwReserved
[in] Reserved for future use; must be zero.
advf
[in] Container for advise flags that specify how the call to IAdviseSink::OnDataChange is made. These flag values are from the enumeration ADVF. Typically, the value for advf is NULL. The only exception occurs when the data object is shutting down and must send a final notification that includes the actual data to sinks that have specified ADVF_DATAONSTOP and ADVF_NODATA in their call to IDataObject::DAdvise. In this case, advf contains ADVF_DATAONSTOP.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The call to IAdviseSink::OnDataChange was made.

Remarks

The data object must call this method when it detects a change that would be of interest to an advise sink that has previously requested notification.

Most notifications include the actual data with them. The only exception is if the ADVF_NODATA flag was previously specified when the connection was initially set up in the IDataAdviseHolder::Advise method.

Before calling the IAdviseSink::OnDataChange method for each advise sink, this method obtains the actual data by calling the IDataObject::GetData method through the pointer specified in the pDataObject parameter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ADVF, IAdviseSink::OnDataChange


IDataAdviseHolder::Unadvise

Removes a connection between a data object and an advisory sink that was set up through a previous call to IDataAdviseHolder::Advise. IDataAdviseHolder::Unadvise is typically called in the implementation of IDataObject::DUnadvise.

HRESULT Unadvise(
  DWORD dwConnection  //Connection to remove
);

Parameter

dwConnection
[in] DWORD token that specifies the connection to remove. This value was returned by IDataAdviseHolder::Advise when the connection was originally established.

Return Values

S_OK
The specified connection was successfully deleted.
OLE_E_NOCONNECTION
The specified dwConnection is not a valid connection.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataAdviseHolder::Advise, IDataObject::DUnadvise


IDataObject

The IDataObject interface specifies methods that enable data transfer and notification of changes in data. Data transfer methods specify the format of the transferred data along with the medium through which the data is to be transferred. Optionally, the data can be rendered for a specific target device. In addition to methods for retrieving and storing data, the IDataObject interface specifies methods for enumerating available formats and managing connections to advisory sinks for handling change notifications.

The term "data object" is used to mean any object that supports an implementation of the IDataObject interface. Implementations vary, depending on what the data object is required to do; in some data objects, the implementation of certain methods not supported by the object could simply be the return of E_NOTIMPL. For example, some data objects do not allow callers to send them data. Other data objects do not support advisory connections and change notifications. However, for those data objects that do support change notifications, OLE provides an object called a data advise holder. An interface pointer to this holder is available through a call to the helper function CreateDataAdviseHolder. A data object can have multiple connections, each with its own set of attributes. The OLE data advise holder simplifies the task of managing these connections and sending the appropriate notifications.

When to Implement

Implement the IDataObject interface if you are developing a container or server application that is capable of transferring data. For example, if your application allows its data to be pasted or dropped into another application, you must implement the IDataObject interface. OLE compound document object servers that support objects that can be embedded or linked must implement IDataObject.

OLE provides implementations in its default object handler and its cache.

When to Use

Any object that can receive data calls the methods in the IDataObject interface.

When you call the data transfer methods in the IDataObject interface, you specify a format, a medium, and, optionally, a target device for which the data should be rendered. Objects, such as containers, that want to be notified through their advise sinks when the data in the data object changes call the IDataObject advisory methods to set up an advisory connection through which notifications can be sent.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDataObject Methods | Description
GetData | Renders the data described in a FORMATETC structure and transfers it through the STGMEDIUM structure.
GetDataHere | Renders the data described in a FORMATETC structure and transfers it through the STGMEDIUM structure allocated by the caller.
QueryGetData | Determines whether the data object is capable of rendering the data described in the FORMATETC structure.
GetCanonicalFormatEtc | Provides a potentially different but logically equivalent FORMATETC structure.
SetData | Provides the source data object with data described by a FORMATETC structure and an STGMEDIUM structure.
EnumFormatEtc | Creates and returns a pointer to an object to enumerate the FORMATETC supported by the data object.
DAdvise | Creates a connection between a data object and an advise sink so the advise sink can receive notifications of changes in the data object.
DUnadvise | Destroys a notification previously set up with the DAdvise method.
EnumDAdvise | Creates and returns a pointer to an object to enumerate the current advisory connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.


IDataObject::DAdvise

Called by an object supporting an advise sink to create a connection between a data object and the advise sink. This enables the advise sink to be notified of changes in the data of the object.

HRESULT DAdvise(
  FORMATETC * pFormatetc,  //Pointer to data of interest to the
                           // advise sink
  DWORD advf,              //Flags that specify how the notification
                           // takes place
  IAdviseSink * pAdvSink,  //Pointer to the advise sink
  DWORD * pdwConnection    //Pointer to a token that identifies this
                           // connection
);

Parameters

pFormatetc
[in] Pointer to a FORMATETC structure that defines the format, target device, aspect, and medium that will be used for future notifications. For example, one sink may want to know only when the bitmap representation of the data in the the data object changes. Another sink may be interested in only the metafile format of the same object. Each advise sink is notified when the data of interest changes. This data is passed back to the advise sink when notification occurs.
advf
[in] DWORD that specifies a group of flags for controlling the advisory connection. Valid values are from the enumeration ADVF. However, only some of the possible ADVF values are relevant for this method. The following table briefly describes the relevant values. Refer to ADVF for a more detailed description.
ADVF Value | Description
ADVF_NODATA | Asks the data object to avoid sending data with the notifications. Typically data is sent. This flag is a way to override the default behavior. When ADVF_NODATA is used, the TYMED member of the STGMEDIUM structure that is passed to OnDataChange will usually contain TYMED_NULL. The caller can then retrieve the data with a subsequent IDataObject::GetData call.
ADVF_ONLYONCE | Causes the advisory connection to be destroyed after the first change notification is sent. An implicit call to IDataObject::DUnadvise is made on behalf of the caller to remove the connection.
ADVF_PRIMEFIRST | Asks for an additional initial notification. The combination of ADVF_ONLYONCE and ADVF_PRIMEFIRST provides, in effect, an asynchronous IDataObject::GetData call.
ADVF_DATAONSTOP When specified with ADVF_NODATA, this flag causes a last notification with the data included to to be sent before the data object is destroyed.

If used without ADVF_NODATA, IDataObject::DAdvise can be implemented in one of the following ways:

  • the ADVF_DATAONSTOP can be ignored.
  • the object can behave as if ADVF_NODATA was specified.

a change notification is sent only in the shutdown case. Data changes prior to shutdown do not cause a notification to be sent.
pAdvSink
[in] Pointer to the IAdviseSink interface on the advisory sink that will receive the change notification.
pdwConnection
[out] Pointer to a DWORD token that identifies this connection. You can use this token later to delete the advisory connection (by passing it to IDataObject::DUnadvise). If this value is zero, the connection was not established.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
The advisory connection was created.
E_NOTIMPL
This method is not implemented on the data object.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
OLE_E_ADVISENOTSUPPORTED
The data object does not support change notification.

Remarks

IDataObject::DAdvise creates a change notification connection between a data object and the caller. The caller provides an advisory sink to which the notifications can be sent when the object's data changes.

Objects used simply for data transfer typically do not support advisory notifications and return OLE_E_ADVISENOTSUPPORTED from IDataObject::DAdvise.

Notes to Callers

The object supporting the advise sink calls IDataObject::DAdvise to set up the connection, specifying the format, aspect, medium, and/or target device of interest in the FORMATETC structure passed in. If the data object does not support one or more of the requested attributes or the sending of notifications at all, it can refuse the connection by returning OLE_E_ADVISENOTSUPPORTED.

Containers of linked objects can set up advisory connections directly with the bound link source or indirectly through the standard OLE link object that manages the connection. Connections set up with the bound link source are not automatically deleted. The container must explicitly call IDataObject::DUnAdvise on the bound link source to delete an advisory connection. The OLE link object, manipulated through the IOleLink interface, is implemented in the default handler. Connections set up through the OLE link object are destroyed when the link object is deleted.

The OLE default link object creates a "wildcard advise" with the link source so OLE can maintain the time of last change. This advise is specifically used to note the time that anything changed. OLE ignores all data formats that may have changed, noting only the time of last change. To allow wildcard advises, set the FORMATETC members as follows before calling IDataObject::DAdvise:

cf == 0;
ptd == NULL;
dwAspect == -1;
lindex == -1
tymed == -1;

The advise flags should also include ADVF_NODATA. Wildcard advises from OLE should always be accepted by applications.

Notes to Implementers

To simplify the implementation of DAdvise and the other notification methods in IDataObject (DUnadvise and EnumAdvise) that supports notification, OLE provides an advise holder object that manages the registration and sending of notifications. To get a pointer to this object, call the helper function CreateDataAdviseHolder on the first invocation of DAdvise. This supplies a pointer to the object's IDataAdviseHolder interface. Then, delegate the call to the IDataAdviseHolder::Advise method in the data advise holder, which creates, and subsequently manages, the requested connection.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

ADVF, FORMATETC, CreateDataAdviseHolder, IAdviseSink::OnDataChange, IDataObject::DUnAdvise


IDataObject::DUnadvise

Destroys a notification connection that had been previously set up.

HRESULT DUnadvise(
  DWORD dwConnection  //Connection to remove
);

Parameter

dwConnection
[in] DWORD token that specifies the connection to remove. Use the value returned by IDataObject::DAdvise when the connection was originally established.

Return Values

S_OK
The specified connection was successfully deleted.
OLE_E_NOCONNECTION
The specified dwConnection is not a valid connection.
OLE_E_ADVISENOTSUPPORTED
This IDataObject implementation does not support notification.

Remarks

This methods destroys a notification created with a call to the IDataObject::DAdvise method.

If the advisory connection being deleted was initially set up by delegating the IDataObject::DAdvise call to IDataAdviseHolder::Advise, you must delegate this call to IDataAdviseHolder::Unadvise to delete it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::DAdvise, IDataAdviseHolder::Unadvise


IDataObject::EnumDAdvise

Creates an object that can be used to enumerate the current advisory connections.

HRESULT EnumDAdvise(
  IEnumSTATDATA ** ppenumAdvise  //Address of output variable that
                                 // receives the IEnumSTATDATA
                                 // interface pointer
);

Parameter

ppenumAdvise
[out] Address of IEnumSTATDATA* pointer variable that receives the interface pointer to the new enumerator object. If the implementation sets *ppenumAdvise to NULL, there are no connections to advise sinks at this time.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The enumerator object is successfully instantiated or there are no connections.
OLE_E_ADVISENOTSUPPORTED
Advisory notifications are not supported by this object.

Remarks

The enumerator object created by this method implements the IEnumSTATDATA interface, one of the standard enumerator interfaces that contain the Next, Reset, Clone, and Skip methods. IEnumSTATDATA permits the enumeration of the data stored in an array of STATDATA structures. Each of these structures provides information on a single advisory connection, and includes FORMATETC and ADVF information, as well as the pointer to the advise sink and the token representing the connection.

Notes to Callers

After getting a pointer through this method, the data object can call the appropriate enumeration methods. While the enumeration is in progress, the effect of adding more advisory connections on the subsequent enumeration is undefined.

Notes to Implementers

It is recommended that you use the OLE data advise holder object to handle advisory connections. With the pointer obtained through a call to CreateDataAdviseHolder, implementing IDataObject::EnumDAdvise becomes a simple matter of delegating the call to IDataAdviseHolder::EnumAdvise. This creates the enumerator and supplies the pointer to the OLE implementation of IEnumSTATDATA. At that point, you can call its methods to enumerate the current advisory connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumSTATDATA, IDataAdviseHolder::EnumAdvise


IDataObject::EnumFormatEtc

Creates an object for enumerating the FORMATETC structures for a data object. These structures are used in calls to IDataObject::GetData or IDataObject::SetData.

HRESULT EnumFormatEtc(
  DWORD dwDirection,  //Specifies a value from the enumeration
                      // DATADIR
  IEnumFORMATETC ** ppenumFormatetc
                      //Address of output variable that receives the
                      // IEnumFORMATETC interface pointer
);

Parameters

dwDirection
[in] Direction of the data through a value from the enumeration DATADIR.
typedef enum tagDATADIR
{
    DATADIR_GET = 1,
    DATADIR_SET = 2,
} DATADIR;

The value DATADIR_GET enumerates the formats that can be passed in to a call to IDataObject::GetData. The value DATADIR_SET enumerates those formats that can be passed in to a call to IDataObject::SetData.

ppenumFormatetc
[out] Address of IEnumFORMATETC* pointer variable that receives the interface pointer to the new enumerator object.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
Enumerator object was successfully created.
E_NOTIMPL
The direction specified by dwDirection is not supported.
OLE_S_USEREG
Requests that OLE enumerate the formats from the registry.

Remarks

IDataObject::EnumFormatEtc creates an enumerator object that can be used to determine all of the ways the data object can describe data in a FORMATETC structure, and supplies a pointer to its IEnumFORMATETC interface. This is one of the standard enumerator interfaces.

Notes to Callers

Having obtained the pointer, the caller can enumerate the FORMATETC structures by calling the enumeration methods of IEnumFORMATETC. Because the formats can change over time, there is no guarantee that an enumerated format is currently supported because the formats can change over time. Accordingly, applications should treat the enumeration as a hint of the format types that can be passed. The caller is responsible for calling IEnumFormatEtc::Release when it is finished with the enumeration.

IDataObject::EnumFormatEtc is called when one of the following actions occurs:

  • An application calls OleSetClipboard. OLE must determine what data to place on the Clipboard and whether it is necessary to put OLE 1 compatibility formats on the Clipboard.
  • Data is being pasted from the Clipboard or dropped. An application uses the first acceptable format.
  • The Paste Special dialog box is displayed. The target application builds the list of formats from the FORMATETC entries.

Notes to Implementers

Formats can be registered statically in the registry or dynamically during object initialization. If an object has an unchanging list of formats and these formats are registered in the registry, OLE provides an implementation of a FORMATETC enumeration object that can enumerate formats registered under a specific CLSID in the registry. A pointer to its IEnumFORMATETC interface is available through a call to the helper function OleRegEnumFormatEtc. In this situation, therefore, you can implement the EnumFormatEtc method simply with a call to this function.

EXE applications can effectively do the same thing by implementing the method to return the value OLE_S_USEREG. This return value instructs the default object handler to call OleRegEnumFormatEtc. Object applications that are implemented as DLL object applications cannot return OLE_S_USEREG, so must call OleRegEnumFormatEtc directly.

Private formats can be enumerated for OLE 1 objects, if they are registered with the RequestDataFormats or SetDataFormats keys in the registry. Also, private formats can be enumerated for OLE objects (all versions after OLE 1), if they are registered with the GetDataFormats or SetDataFormats keys.

For OLE 1 objects whose servers do not have RequestDataFormats or SetDataFormats information registered in the registry, a call to IDataObject::EnumFormatEtc passing DATADIR_GET only enumerates the Native and Metafile formats, regardless of whether they support these formats or others. Calling EnumFormatEtc passing DATADIR_SET on such objects only enumerates Native, regardless of whether the object supports being set with other formats.

The FORMATETC structure returned by the enumeration usually indicates a NULL target device (ptd). This is appropriate because, unlike the other members of FORMATETC, the target device does not participate in the object's decision as to whether it can accept or provide the data in either a SetData or GetData call.

The TYMED member of FORMATETC often indicates that more than one kind of storage medium is acceptable. You should always mask and test for this by using a Boolean OR operator.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

FORMATETC, OleRegEnumFormatEtc, IEnumFormatEtc, IDataObject::SetData, IDataObject::GetData


IDataObject::GetCanonicalFormatEtc

Provides a standard FORMATETC structure that is logically equivalent to one that is more complex. You use this method to determine whether two different FORMATETC structures would return the same data, removing the need for duplicate rendering.

HRESULT GetCanonicalFormatEtc(
  FORMATETC * pFormatetcIn,  //Pointer to the FORMATETC structure
  FORMATETC * pFormatetcOut  //Pointer to the canonical equivalent
                             // FORMATETC structure
);

Parameters

pFormatetcIn
[in] Pointer to the FORMATETC structure that defines the format, medium, and target device that the caller would like to use to retrieve data in a subsequent call such as IDataObject::GetData. The TYMED member is not significant in this case and should be ignored.
pFormatetcOut
[out] Pointer to a FORMATETC structure that contains the most general information possible for a specific rendering, making it canonically equivalent to pFormatetcIn. The caller must allocate this structure and the GetCanonicalFormatEtc method must fill in the data. To retrieve data in a subsequent call like IDataObject::GetData, the caller uses the supplied value of pFormatetcOut, unless the value supplied is NULL. This value is NULL if the method returns DATA_S_SAMEFORMATETC. The TYMED member is not significant in this case and should be ignored.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
The returned FORMATETC structure is different from the one that was passed.
DATA_S_SAMEFORMATETC
The FORMATETC structures are the same and NULL is returned in pFormatetcOut.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
OLE_E_NOTRUNNING
Object application is not running.

Remarks

If a data object can supply exactly the same data for more than one requested FORMATETC structure, IDataObject::GetCanonicalFormatEtc can supply a "canonical", or standard FORMATETC that gives the same rendering as a set of more complicated FORMATETC structures. For example, it is common for the data returned to be insensitive to the target device specified in any one of a set of otherwise similar FORMATETC structures.

Notes to Callers

A call to this method can determine whether two calls to IDataObject::GetData on a data object, specifying two different FORMATETC structures, would actually produce the same renderings, thus eliminating the need for the second call and improving performance. If the call to GetCanonicalFormatEtc results in a canonical format being written to the pFormatetcOut parameter, the caller then uses that structure in a subsequent call to IDataObject::GetData.

Notes to Implementers

Conceptually, it is possible to think of FORMATETC structures in groups defined by a canonical FORMATETC that provides the same results as each of the group members. In constructing the canonical FORMATETC, you should make sure it contains the most general information possible that still produces a specific rendering.

For data objects that never provide device-specific renderings, the simplest implementation of this method is to copy the input FORMATETC to the output FORMATETC, store a NULL in the ptd field of the output FORMATETC, and return DATA_S_SAMEFORMATETC.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::GetData, FORMATETC


IDataObject::GetData

Called by a data consumer to obtain data from a source data object. The GetData method renders the data described in the specified FORMATETC structure and transfers it through the specified STGMEDIUM structure. The caller then assumes responsibility for releasing the STGMEDIUM structure.

HRESULT GetData(
  FORMATETC * pFormatetc,  //Pointer to the FORMATETC structure
  STGMEDIUM * pmedium  //Pointer to the STGMEDIUM structure
);

Parameters

pFormatetc
[in] Pointer to the FORMATETC structure that defines the format, medium, and target device to use when passing the data. It is possible to specify more than one medium by using the Boolean OR operator, allowing the method to choose the best medium among those specified.
pmedium
[out] Pointer to the STGMEDIUM structure that indicates the storage medium containing the returned data through its tymed member, and the responsibility for releasing the medium through the value of its pUnkForRelease member. If pUnkForRelease is NULL, the receiver of the medium is responsible for releasing it; otherwise, pUnkForRelease points to the IUnknown on the appropriate object so its Release method can be called. The medium must be allocated and filled in by IDataObject::GetData.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
Data was successfully retrieved and placed in the storage medium provided.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
STG_E_MEDIUMFULL
An error occurred when allocating the medium.

Remarks

A data consumer calls IDataObject::GetData to retrieve data from a data object, conveyed through a storage medium (defined through the STGMEDIUM structure).

Notes to Callers

You can specify more than one acceptable TYMED medium with the Boolean OR operator. IDataObject::GetData must choose from the OR'd values the medium that best represents the data, do the allocation, and indicate responsibility for releasing the medium.

Data transferred across a stream extends from position zero of the stream pointer through to the position immediately before the current stream pointer (that is, the stream pointer position upon exit).

Notes to Implementers

IDataObject::GetData must check all fields in the FORMATETC structure. It is important that IDataObject::GetData render the requested aspect and, if possible, use the requested medium. If the data object cannot comply with the information specified in the FORMATETC, the method should return DV_E_FORMATETC. If an attempt to allocate the medium fails, the method should return STG_E_MEDIUMFULL. It is important to fill in all of the fields in the STGMEDIUM structure.

Although the caller can specify more than one medium for returning the data, IDataObject::GetData can supply only one medium. If the initial transfer fails with the selected medium, this method can be implemented to try one of the other media specified before returning an error.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::GetDataHere, IDataObject::SetData, FORMATETC, STGMEDIUM


IDataObject::GetDataHere

Called by a data consumer to obtain data from a source data object. This method differs from the GetData method in that the caller must allocate and free the specified storage medium.

HRESULT GetDataHere(
  FORMATETC * pFormatetc,  //Pointer to the FORMATETC structure
  STGMEDIUM * pmedium  //Pointer to the STGMEDIUM structure
);

Parameters

pFormatetc
[in] Pointer to the FORMATETC structure that defines the format, medium, and target device to use when passing the data. Only one medium can be specified in TYMED, and only the following TYMED values are valid: TYMED_STORAGE, TYMED_STREAM, TYMED_HGLOBAL, or TYMED_FILE.
pmedium
[in, out] Pointer to the STGMEDIUM structure that defines the storage medium containing the data being transferred. The medium must be allocated by the caller and filled in by IDataObject::GetDataHere. The caller must also free the medium. The implementation of this method must always supply a value of NULL for the punkForRelease member of the STGMEDIUM structure to which this parameter points.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
Data was successfully retrieved and placed in the storage medium provided.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
STG_E_MEDIUMFULL
The medium provided by the caller is not large enough to contain the data.

Remarks

The IDataObject::GetDataHere method is similar to IDataObject::GetData, except that the caller must both allocate and free the medium specified in pmedium. GetDataHere renders the data described in a FORMATETC structure and copies the data into that caller-provided STGMEDIUM structure. For example, if the medium is TYMED_HGLOBAL, this method cannot resize the medium or allocate a new hGlobal.

Some media are not appropriate in a call to GetDataHere, including GDI types such as metafiles. The GetDataHere method cannot put data into a caller-provided metafile. In general, the only storage media it is necessary to support in this method are TYMED_ISTORAGE, TYMED_ISTREAM, and TYMED_FILE.

When the transfer medium is a stream, OLE makes assumptions about where the data is being returned and the position of the stream's seek pointer. In a GetData call, the data returned is from stream position zero through just before the current seek pointer of the stream (that is, the position on exit). For GetDataHere, the data returned is from the stream position on entry through just before the position on exit.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::GetData, FORMATETC, STGMEDIUM


IDataObject::QueryGetData

Determines whether the data object is capable of rendering the data described in the FORMATETC structure. Objects attempting a paste or drop operation can call this method before calling IDataObject::GetData to get an indication of whether the operation may be successful.

HRESULT QueryGetData(
  FORMATETC * pFormatetc  //Pointer to the FORMATETC structure
);

Parameter

pFormatetc
[in] Pointer to the FORMATETC structure defining the format, medium, and target device to use for the query.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
Subsequent call to IDataObject::GetData would probably be successful.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.

Remarks

The client of a data object calls IDataObject::QueryGetData to determine whether passing the specified FORMATETC structure to a subsequent call to IDataObject::GetData is likely to be successful. A successful return from this method does not necessarily ensure the success of the subsequent paste or drop operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::GetData, FORMATETC


IDataObject::SetData

Called by an object containing a data source to transfer data to the object that implements this method.

HRESULT SetData(
  FORMATETC * pFormatetc,  //Pointer to the FORMATETC structure
  STGMEDIUM * pmedium,     //Pointer to STGMEDIUM structure
  BOOL fRelease            //Indicates which object owns the storage
                           // medium after the call is completed
);

Parameters

pFormatetc
[in] Pointer to the FORMATETC structure defining the format used by the data object when interpreting the data contained in the storage medium.
pmedium
[in] Pointer to the STGMEDIUM structure defining the storage medium in which the data is being passed.
fRelease
[in] If TRUE, the data object called, which implements IDataObject::SetData, owns the storage medium after the call returns. This means it must free the medium after it has been used by calling the ReleaseStgMedium function. If FALSE, the caller retains ownership of the storage medium and the data object called uses the storage medium for the duration of the call only.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
Data was successfully transferred.
E_NOTIMPL
This method is not implemented for the data object.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.

Remarks

IDataObject::SetData allows another object to attempt to send data to the implementing data object. A data object implements this method if it supports receiving data from another object. If it does not support this, it should be implemented to return E_NOTIMPL.

The caller allocates the storage medium indicated by the pmedium, in which the data is passed. The data object called does not take ownership of the data until it has successfully received it and no error code is returned. The value of the fRelease parameter indicates the ownership of the medium after the call returns. FALSE indicates the caller still owns the medium, and the data object only has the use of it during the call; TRUE indicates that the data object now owns it and must release it when it is no longer needed.

The type of medium (TYMED) specified in the pformatetc and pmedium parameters must be the same. For example, one cannot be an hGlobal (global handle) and the other a stream.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject::GetData, FORMATETC, STGMEDIUM


IDropSource

The IDropSource interface is one of the interfaces you implement to provide drag-and-drop operations in your application. It contains methods used in any application used as a data source in a drag-and-drop operation. The data source application in a drag-and-drop operation is responsible for:

  • Determining the data being dragged based on the user's selection.
  • Initiating the drag-and-drop operation based on the user's mouse actions.
  • Generating some of the visual feedback during the drag-and-drop operation, such as setting the cursor and highlighting the data selected for the drag-and-drop operation.
  • Canceling or completing the drag-and-drop operation based on the user's mouse actions.
  • Performing any action on the original data caused by the drop operation, such as deleting the data on a drag move.

IDropSource contains the methods for generating visual feedback to the end user and for canceling or completing the drag-and-drop operation. You also need to call the DoDragDrop, RegisterDragDrop, and RevokeDragDrop functions in drag-and-drop operations.

When to Implement

Implement IDropSource if you are developing a container or server application that can act as a data source for a drag-and-drop operation. The IDropSource interface is only required during the drag-and-drop operation.

If you implement the IDropSource interface, you must also implement the IDataObject interface on the same object to represent the data being transferred.

You can use the same implementation of IDataObject for drag-and-drop data as for the data object offered to the clipboard. Once you have implemented clipboard operations in your application, you can add drag-and-drop operations with only a little extra work.

When to Use

You don't usually call IDropSource methods directly. Instead, your data source calls the DoDragDrop function when it detects that the user has initiated a drag-and-drop operation. Then, DoDragDrop calls the IDropSource methods during the drag-and-drop operation.

For example, DoDragDrop calls IDropSource::GiveFeedback when you need to change the cursor shape or when you need to provide some other visual feedback. DoDragDrop calls IDropSource::QueryContinueDrag when there is a change in the mouse button state to determine if the drag-and-drop operation was canceled or completed.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDropSource Methods | Description
QueryContinueDrag | Determines whether a drag-and-drop operation should continue.
GiveFeedback | Gives visual feedback to an end user during a drag-and-drop operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDataObject, IDropTarget


IDropSource::GiveFeedback

Enables a source application to give visual feedback to the end user during a drag-and-drop operation by providing the DoDragDrop function with an enumeration value specifying the visual effect.

HRESULT GiveFeedback(
  DWORD dwEffect  //Effect of a drop operation
);

Parameter

dwEffect
[in] The DROPEFFECT value returned by the most recent call to IDropTarget::DragEnter, IDropTarget::DragOver, or IDropTarget::DragLeave. For a list of values, see the DROPEFFECT enumeration.

Return Values

This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and E_OUTOFMEMORY, as well as the following:

S_OK
The method completed its task successfully, using the cursor set by the source application.
DRAGDROP_S_USEDEFAULTCURSORS
Indicates successful completion of the method, and requests OLE to update the cursor using the OLE-provided default cursors.

Remarks

When your application detects that the user has started a drag-and-drop operation, it should call the DoDragDrop function. DoDragDrop enters a loop, calling IDropTarget::DragEnter when the mouse first enters a drop target window, IDropTarget::DragOver when the mouse changes its position within the target window, and IDropTarget::DragLeave when the mouse leaves the target window.

For every call to either IDropTarget::DragEnter or IDropTarget::DragOver, DoDragDrop calls IDropSource::GiveFeedback, passing it the DROPEFFECT value returned from the drop target call.

DoDragDrop calls IDropTarget::DragLeave when the mouse has left the target window. Then, DoDragDrop calls IDropSource::GiveFeedback and passes the DROPEFFECT_NONE value in the dwEffect parameter.

The dwEffect parameter can include DROPEFFECT_SCROLL, indicating that the source should put up the drag-scrolling variation of the appropriate pointer.

OLE defines a recommended set of cursor shapes that your application should use. See the User Interface Guidelines for more information.

Notes to Implementers

This function is called frequently during the DoDragDrop loop, so you can gain performance advantages if you optimize your implementation as much as possible.

IDropSource::GiveFeedback is responsible for changing the cursor shape or for changing the highlighted source based on the value of the dwEffect parameter. If you are using default cursors, you can return DRAGDROP_S_USEDEFAULTCURSORS, which causes OLE to update the cursor for you, using its defaults.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropTarget


IDropSource::QueryContinueDrag

Determines whether a drag-and-drop operation should be continued, canceled, or completed. You do not call this method directly. The OLE DoDragDrop function calls this method during a drag-and-drop operation.

HRESULT QueryContinueDrag(
  BOOL fEscapePressed,  //Status of escape key since previous call
  DWORD grfKeyState     //Current state of keyboard modifier keys
);

Parameters

fEscapePressed
[in] Specifies whether the Esc key has been pressed since the previous call to IDropSource::QueryContinueDrag or to DoDragDrop if this is the first call to QueryContinueDrag. A TRUE value indicates the end user has pressed the escape key; a FALSE value indicates it has not been pressed.
grfKeyState
[in] Current state of the keyboard modifier keys on the keyboard. Valid values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.

Return Values

This method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following:

S_OK
The drag operation should continue. This result occurs if no errors are detected, the mouse button starting the drag-and-drop operation has not been released, and the Esc key has not been detected.
DRAGDROP_S_DROP
The drop operation should occur completing the drag operation. This result occurs if grfKeyState indicates that the key that started the drag-and-drop operation has been released.
DRAGDROP_S_CANCEL
The drag operation should be canceled with no drop operation occurring. This result occurs if fEscapePressed is TRUE, indicating the Esc key has been pressed.

Remarks

The DoDragDrop function calls IDropSource::QueryContinueDrag whenever it detects a change in the keyboard or mouse button state during a drag-and-drop operation. IDropSource::QueryContinueDrag must determine whether the drag-and-drop operation should be continued, canceled, or completed based on the contents of the parameters grfKeyState and fEscapePressed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop


IDropTarget

The IDropTarget interface is one of the interfaces you implement to provide drag-and-drop operations in your application. It contains methods used in any application that can be a target for data during a drag-and-drop operation. A drop-target application is responsible for:

  • Determining the effect of the drop on the target application.
  • Incorporating any valid dropped data when the drop occurs.
  • Communicating target feedback to the source so the source application can provide appropriate visual feedback such as setting the cursor.
  • Implementing drag scrolling.
  • Registering and revoking its application windows as drop targets.

The IDropTarget interface contains methods that handle all these responsibilities except registering and revoking the application window as a drop target, for which you must call the RegisterDragDrop and the RevokeDragDrop functions.

When to Implement

Implement the IDropTarget interface if you are developing an application that can act as a target for a drag-and-drop operation. The IDropTarget interface is associated with your application windows and is implemented on your window objects. Call the RegisterDragDrop function to register your window objects as drop targets.

When to Use

You do not call the methods of IDropTarget directly. The DoDragDrop function calls the IDropTarget methods during the drag-and-drop operation.

For example, DoDragDrop calls IDropTarget::DragEnter when it detects the mouse has moved over a window that is registered as a drag target. Once the mouse has entered a drag-target window, DoDragDrop calls IDropTarget::DragOver as the mouse moves through the window and calls IDropTarget::DragLeave if the mouse leaves the target window or if the user cancels or completes the drag-and-drop operation. DoDragDrop calls IDropTarget::Drop if the drop finally occurs.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IDropTarget Methods | Description
DragEnter | Determines whether a drop can be accepted and its effect if it is accepted.
DragOver | Provides target feedback to the user through the DoDragDrop function.
DragLeave | Causes the drop target to suspend its feedback actions.
Drop | Drops the data into the target window.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, RegisterDragDrop, RevokeDragDrop


IDropTarget::DragEnter

Indicates whether a drop can be accepted, and, if so, the effect of the drop.

HRESULT DragEnter(
  IDataObject * pDataObject,
                     //Pointer to the interface of the source data
                     // object
  DWORD grfKeyState, //Current state of keyboard modifier keys
  POINTL pt,         //Current cursor coordinates
  DWORD * pdwEffect  //Pointer to the effect of the drag-and-drop
                     // operation
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object. This data object contains the data being being transferred in the drag-and-drop operation. If the drop occurs, this data object will be incorporated into the target.
grfKeyState
[in] Current state of the keyboard modifier keys on the keyboard. Valid values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
pt
[in] POINTL structure containing the current cursor coordinates in screen coordinates.
pdwEffect
[in, out] On entry, pointer to the value of the pdwEffect parameter of the DoDragDrop function. On return, must contain one of the effect flags from the DROPEFFECT enumeration, which indicates what the result of the drop operation would be.

Return Values

This method supports the standard return values E_OUTOFMEMORY, E_INVALIDARG, and E_UNEXPECTED, as well as the following:

S_OK
The method completed its task successfully.

Remarks

You do not call IDropTarget::DragEnter directly; instead the DoDragDrop function calls it to determine the effect of a drop the first time the user drags the mouse into the registered window of a drop target.

To implement IDropTarget::DragEnter, you must determine whether the target can use the data in the source data object by checking three things:

  • The format and medium specified by the data object
  • The input value of pdwEffect
  • The state of the modifier keys

To check the format and medium, use the IDataObject pointer passed in the pDataObject parameter to call IDataObject::EnumFormatEtc so you can enumerate the FORMATETC structures the source data object supports. Then call IDataObject::QueryGetData to determine whether the data object can render the data on the target by examining the formats and medium specified for the data object.

On entry to IDropTarget::DragEnter, the pdwEffect parameter is set to the effects given to the pdwOkEffect parameter of the DoDragDrop function. The IDropTarget::DragEnter method must choose one of these effects or disable the drop.

The following modifier keys affect the result of the drop:

Key Combination | User-Visible Feedback | Drop Effect
CTRL + SHIFT | = | DROPEFFECT_LINK
CTRL | + | DROPEFFECT_COPY
No keys or SHIFT | None | DROPEFFECT_MOVE

On return, the method must write the effect, one of the members of the DROPEFFECT enumeration, to the pdwEffect parameter. DoDragDrop then takes this parameter and writes it to its pdwEffect parameter. You communicate the effect of the drop back to the source through DoDragDrop in the pdwEffect parameter. The DoDragDrop function then calls IDropSource::GiveFeedback so that the source application can display the appropriate visual feedback to the user through the target window.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, IDropTarget, RegisterDragDrop, RevokeDragDrop


IDropTarget::DragLeave

Removes target feedback and releases the data object.

HRESULT DragLeave(void);

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The method completed its task successfully.

Remarks

You do not call this method directly. The DoDragDrop function calls this method in either of the following cases:

  • When the user drags the cursor out of a given target window.
  • When the user cancels the current drag-and-drop operation.

To implement IDropTarget::DragLeave, you must remove any target feedback that is currently displayed. You must also release any references you hold to the data transfer object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, IDropTarget, RegisterDragDrop, RevokeDragDrop


IDropTarget::DragOver

Provides target feedback to the user and communicates the drop's effect to the DoDragDrop function so it can communicate the effect of the drop back to the source.

HRESULT DragOver(
  DWORD grfKeyState, //Current state of keyboard modifier keys
  POINTL pt,         //Current cursor coordinates
  DWORD * pdwEffect  //Pointer to the effect of the drag-and-drop
                     // operation
);

Parameters

grfKeyState
[in] Current state of the keyboard modifier keys on the keyboard. Valid values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
pt
[in] POINTL structure containing the current cursor coordinates in screen coordinates.
pdwEffect
[in, out] Pointer to the current effect flag. Valid values are from the enumeration DROPEFFECT.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The method completed its task successfully.

Remarks

You do not call IDropTarget::DragOver directly. The DoDragDrop function calls this method each time the user moves the mouse across a given target window. DoDragDrop exits the loop if the drag-and-drop operation is canceled, if the user drags the mouse out of the target window, or if the drop is completed.

In implementing IDropTarget::DragOver, you must provide features similar to those in IDropTarget::DragEnter. You must determine the effect of dropping the data on the target by examining the FORMATETC defining the data object's formats and medium, along with the state of the modifier keys. The mouse position may also play a role in determining the effect of a drop. The following modifier keys affect the result of the drop:

Key Combination | User-Visible Feedback | Drop Effect
CTRL + SHIFT | = | DROPEFFECT_LINK
CTRL | + | DROPEFFECT_COPY
No keys or SHIFT | None | DROPEFFECT_MOVE

You communicate the effect of the drop back to the source through DoDragDrop in pdwEffect. The DoDragDrop function then calls IDropSource::GiveFeedback so the source application can display the appropriate visual feedback to the user.

On entry to IDropTarget::DragOver, the pdwEffect parameter must be set to the allowed effects passed to the pdwOkEffect parameter of the DoDragDrop function. The IDropTarget::DragOver method must be able to choose one of these effects or disable the drop.

Upon return, pdwEffect is set to one of the members of the DROPEFFECT enumeration. This value is then passed to the pdwEffect parameter of DoDragDrop. Reasonable values are DROPEFFECT_COPY to copy the dragged data to the target, DROPEFFECT_LINK to create a link to the source data, or DROPEFFECT_MOVE to allow the dragged data to be permanently moved from the source application to the target.

You may also wish to provide appropriate visual feedback in the target window. There may be some target feedback already displayed from a previous call to IDropTarget::DragOver or from the initial IDropTarget::DragEnter. If this feedback is no longer appropriate, you should remove it.

For efficiency reasons, a data object is not passed in IDropTarget::DragOver. The data object passed in the most recent call to IDropTarget::DragEnter is available and can be used.

When IDropTarget::DragOver has completed its operation, the DoDragDrop function calls IDropSource::GiveFeedback so the source application can display the appropriate visual feedback to the user.

Notes to Implementers

This function is called frequently during the DoDragDrop loop so it makes sense to optimize your implementation of the DragOver method as much as possible.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, IDropTarget, RegisterDragDrop, RevokeDragDrop


IDropTarget::Drop

Incorporates the source data into the target window, removes target feedback, and releases the data object.

HRESULT Drop(
  IDataObject * pDataObject,
                     //Pointer to the interface for the source data
  DWORD grfKeyState, //Current state of keyboard modifier keys
  POINTL pt,         //Current cursor coordinates
  DWORD * pdwEffect  //Pointer to the effect of the drag-and-drop
                     // operation
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object being transferred in the drag-and-drop operation.
grfKeyState
[in] Current state of the keyboard modifier keys on the keyboard. Valid values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
pt
[in] POINTL structure containing the current cursor coordinates in screen coordinates.
pdwEffect
[in, out] Pointer to the current effect flag. Valid values are from the enumeration DROPEFFECT.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The method completed its tasks successfully.

Remarks

You do not call this method directly. The DoDragDrop function calls this method when the user completes the drag-and-drop operation.

In implementing IDropTarget::Drop, you must incorporate the data object into the target. Use the formats available in IDataObject, available through pDataObject, along with the current state of the modifier keys to determine how the data is to be incorporated, such as linking or embedding.

In addition to incorporating the data, you must also clean up as you do in the IDropTarget::DragLeave method:

  • Remove any target feedback that is currently displayed.
  • Release any references to the data object.

You also pass the effect of this operation back to the source application through DoDragDrop, so the source application can clean up after the drag-and-drop operation is complete:

  • Remove any source feedback that is being displayed.
  • Make any necessary changes to the data, such as removing the data if the operation was a move.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, IDropTarget, RegisterDragDrop, RevokeDragDrop


IEnumFORMATETC

The IEnumFORMATETC interface is used to enumerate an array of FORMATETC structures. IEnumFORMATETC has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

IEnumFORMATETC must be implemented by all data objects to support calls to IDataObject::EnumFormatEtc, which supplies a pointer to the enumerator's IEnumFORMATETC interface. If the data object supports a different set of FORMATETC information depending on the direction of the data (whether a call is intended for the SetData or GetData method of IDataObject), the implementation of IEnumFORMATETC must be able to operate on both.

The order of formats enumerated through the IEnumFORMATETC object should be the same as the order that the formats would be in when placed on the clipboard. Typically, this order starts with private data formats and ends with presentation formats such as CF_METAFILEPICT.

When to Use

Call the methods of IEnumFORMATETC when you need to enumerate the FORMATETC structures defining the formats and media supported by a given data object. This is necessary in most data transfer operations, such as clipboard and drag-and-drop, so the object on the other end of the data transfer can determine whether the appropriate format and media for the data is supported.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  FORMATETC_com_FORMATETC * rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumFORMATETC ** ppenum
);

Note To Implementers

If the IEnumFORMATETC::Next method returns a non-NULL DVTARGETDEVICE pointer in the ptd member of the FORMATETC structure, it must be allocated with the CoTaskMalloc function (or its equivalent).

Note To Callers

If the IEnumFORMATETC::Next method returns a non-NULL DVTARGETDEVICE pointer in the ptd member of the FORMATETC structure, the memory must be freed with the CoTaskMemFree function (or its equivalent). Failure to do so will result in a memory leak.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

OleRegEnumFormatEtc, FORMATETC, IEnumXXXX


IEnumMoniker

The IEnumMoniker interface is used to enumerate the components of a moniker or to enumerate the monikers in a table of monikers. IEnumMoniker has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

You need to implement IEnumMoniker if you are writing a new type of moniker and your monikers have an internal structure that can be enumerated. Your implementation of IMoniker::Enum must return an enumerator that implements IEnumMoniker and can enumerate your moniker's components. If your moniker has no structure that can be enumerated, your IMoniker::Enum method can simply return a NULL pointer.

When to Use

Call the methods of the IEnumMoniker interface if you need to enumerate the components of a composite moniker, or to enumerate the monikers in a table.

OLE defines two interfaces that supply an IEnumMoniker interface pointer:

  • The IMoniker::Enum method gets a pointer to an IEnumMoniker implementation that can enumerate forwards or backwards through the components of the moniker. For a description of how two of the system-supplied types of monikers enumerate their components, see IMoniker – File Moniker Implementation and IMoniker – Generic Composite Moniker Implementation.
  • The IRunningObjectTable::EnumRunning method returns a pointer to an IEnumMoniker implementation that can enumerate the monikers registered in a Running Object Table.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  IMoniker_com_IMoniker * rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumMoniker ** ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumXXXX, IMoniker::Enum, IRunningObjectTable::EnumRunning


IEnumOleDocumentViews

The IEnumOleDocumentViews interface is used to enumerate the views supported by a document object. IEnumOleDocumentViews has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, see IEnumXXXX.

When to Implement

Implement IEnumOleDocumentViews on enumerator objects associated with document objects that support more than one view of their data.

When to Use

Use the IEnumOleDocumentViews interface to enumerate all the views supported by a document object. The usual procedure is to first call IOleDocument::EnumViews. If the document object supports only one view, EnumViews will obtain a pointer to that view. If the document object supports two or more views, EnumViews will obtain a pointer to IEnumOleDocumentViews. Using this pointer, the container can then ask the document object to enumerate the views it supports.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG cViews,
  IOleDocumentView * rgpView,
  [out]ULONG * pcFetched
);

HRESULT Skip(
  [in]ULONG cViews
);

HRESULT Reset(void)

HRESULT Clone(
  [out]IEnumOleDocumentViews ** ppenum
);

Remarks

IEnumOleDocumentViews::Next
Enumerates the next cViews elements in the enumerator's list, returning them in rgpView, along with the actual number of enumerated elements in pcFetched. The enumerator object is responsible for calling IOleDocumentView::AddRef, and the caller is responsible for calling IOleDocumentView::Release, on each pointer returned in rgpView.

E_NOTIMPL is not allowed as a return value. If an error value is returned, no entries in the rgpView array are valid and no calls to Release are required.

cViews
[in] The number of IOleDocumentView pointers to be written to the array pointed to by rgpView. If pcFetched is NULL, this argument must be 1.
rgpView
[out, max_is(cViews)] Pointer to a caller-allocated IOleDocumentView * array of size cViews, to which the enumerated connection points are written. The caller is responsible for calling IOleDocumentView::Release through each pointer enumerated into the array once this method returns successfully. If cViews is greater than 1, the caller must also pass a non-NULL pointer passed to pcFetched to know how many pointers to release.
pcFetched
[out] Pointer to the actual number of views enumerated in rgpView. This argument can be NULL, in which case the cViews argument must be 1.
IEnumOleDocumentViews::Skip
Instructs the enumerator to skip the next cViews elements in the enumeration so that the next call to IEnumOleDocumentViews::Next will not return those elements.
cViews
[in] Number of elements to skip in the enumeration.
IEnumOleDocumentViews::Reset
Instructs the enumerator to position itself at the beginning of the list of elements. There is no guarantee that the same set of elements will be enumerated on each pass through the list: it depends on the collection being enumerated. It is too expensive for some collections, such as files in a directory, to maintain this condition.
IEnumOleDocumentViews::Clone
Creates another view enumerator with the same state as the current enumerator, which iterates over the same list. This makes it possible to record a point in the enumeration sequence in order to return to that point at a later time.
ppEnum
[out] Pointer to the IEnumOleDocumentViews interface pointer on the newly created enumerator. The caller must release this enumerator separately from the one from which it was cloned.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IEnumOleUndoUnits

The IEnumOleUndoUnits interface enumerates the undo units on the undo or redo stack.

When to Implement

The undo manager implements this method to enumerate a list of undo units on the undo or redo stack.

When to Use

Use the IEnumOleUndoUnits interface to enumerate all the top level undo units on the undo or redo stack.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG cUndoUnits, //[in]Number of undo units returned in rgpcd
  IOleUndoUnit **rgpcd,
                    //[out]Array to receive enumerated undo units
  ULONG *pcFetched  //[out]Pointer to actual number of undo units
);

HRESULT Skip(
  ULONG cUndoUnits  //[in]Number of undo units to skip
);

HRESULT Reset(void);

HRESULT Clone(
  IEnumOleUndoUnits** ppEnum  //[out]Address of output variable that
                              // receives the IEnumOleUndoUnits
                              // interface pointer
);

Remarks

IEnumOleUndoUnits::Next
Enumerates the next specified number of undo units in the enumerator's list, returning them in rgpcd along with the actual number of enumerated elements in pcFetched.

The caller is responsible for calling the Release method for each element in the array once this method returns successfully. If cUndoUnits is greater than one, the caller must also pass a non-NULL pointer to pcFetched to get the number of pointers it has to release.

E_NOTIMPL is not allowed as a return value. If an error value is returned, no entries in the rgpcd array are valid on exit and require no release.

IEnumOleUndoUnits::Skip
Instructs the enumerator to skip the next specified number of elements in the enumerator so that the next call to IEnumOleUndoUnits::Next will not return those elements.
IEnumOleUndoUnits::Reset
Instructs the enumerator to position itself at the beginning of the list of elements.

There is no guarantee that the same set of elements will be enumerated on each pass through the list. It depends on the collection being enumerated. It is too expensive for some collections, such as files in a directory, to maintain a specific state.

IEnumOleUndoUnits::Clone
Creates another undo unit enumerator with the same state as the current enumerator to iterate over the same list. This method makes it possible to record a point in the enumeration sequence in order to return to that point at a later time.

The caller must release this new enumerator separately from the first enumerator.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager, IOleUndoUnit


IEnumOLEVERB

The IEnumOLEVERB interface enumerates the different verbs available for an object in order of ascending verb number. An enumerator that implements the IEnumOLEVERB interface is returned by IOleObject::EnumVerbs. IEnumOLEVERB has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

You typically do not have to implement this interface. The OLE default handler provides an implementation that supplies the entries in the registry. Because calls to IOleObject::EnumVerb are always routed through the default handler, an OLE application can let the default handler do the work by implementing IOleObject::EnumVerb as a stub that simply returns OLE_S_USEREG. This informs the default handler that it should create the enumerator for you.

When to Use

Call this interface if you need to list the verbs than an OLE object supports.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  LPOLEVERB rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumOLEVERB ** ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OLEVERB, IEnumXXXX


IEnumSTATDATA

The IEnumSTATDATA interface is used to enumerate through an array of STATDATA structures, which contain advisory connection information for a data object. IEnumSTATDATA has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

IEnumSTATDATA is implemented to enumerate advisory connections. Most applications will not implement this directly, but will use the OLE-provided implementation. Pointers to this implementation are available in two ways:

  • In a data object, call CreateDataAdviseHolder to get a pointer to the OLE data advise holder object, and then, to implement IDataObject::EnumDAdvise, call IDataAdviseHolder::EnumAdvise, which creates the enumeration object and supplies a pointer to the implementation of IEnumSTATDATA.
  • In a compound document object, call CreateOLEAdviseHolder to get a pointer to the OLE advise holder object, and then, to implement IOleObject::EnumAdvise, call IOleAdviseHolder::EnumAdvise, which creates the enumeration object and supplies a pointer to the implementation of IEnumSTATDATA.

When to Use

Containers usually call methods that return a pointer to IEnumSTATDATA so the container can use its methods to enumerate the existing advisory connections, and use this information to instruct an object to release each of its advisory connections prior to closing down. IDataObject::EnumDAdvise, IDataAdviseHolder::EnumAdvise, IOleAdviseHolder::EnumAdvise, and IOleCache::EnumCache methods all supply a pointer to IEnumSTATDATA.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  STATDATA * rgelt,
  ULONG * pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumSTATDATA ** ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

STATDATA, IEnumXXXX, IOleCache::EnumCache, IDataObject::EnumDAdvise, IDataAdviseHolder::EnumAdvise, IOleObject::EnumAdvise


IObjectWithSite

The IObjectWithSite interface provides a simple way to support communication between an object and its site in the container.

Often an object needs to communicate directly with a container site object and, in effect, manage the site object itself. Outside of IOleObject::SetClientSite, there is no generic means through which an object becomes aware of its site. IObjectWithSite provides simple objects with a simple siting mechanism (lighter than IOleObject) This interface should only be used when IOleObject is not already in use.

Through IObjectWithSite, a container can pass the IUnknown pointer of its site to the object through IObjectWithSite::SetSite. Callers can also retrieve the latest site passed to IObjectWithSite::SetSite through IObjectWithSite::GetSite. This latter method is included as a hooking mechanism, allowing a third party to intercept calls from the object to the site.

When to Implement

An object implements this interface so it's container can supply it with an interface pointer for its site object. Then, the object can communicate directly with its site.

When to Use

A container calls the SetSite method on this interface to provide an object with an interface pointer for its site.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IObjectWithSite Methods | Description
SetSite | Provides the site's IUnknown pointer to the object being managed.
GetSite | Retrieves the last site set with IObjectWithSite::SetSite.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IObjectWithSite::GetSite

Retrieves the last site set with IObjectWithSite::SetSite. If there's no known site, the object return a failure code.

HRESULT GetSite(
  REFIID riid,    //IID of interface pointer being requested
  void** ppvSite  //Address of output variable that receives the
                  // interface pointer requested in riid
);

Parameters

riid
[in] The IID of the interface pointer that should be returned in ppvSite.
ppvSite
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvSite contains the requested interface pointer to the site last seen in IObjectWithSite::SetSite. The specific interface returned depends on the riid argument—in essence, the two arguments act identically to those in IUnknown::QueryInterface. If the appropriate interface pointer is available, the object must call IUnknown::AddRef on that pointer before returning successfully. If no site is available, or the requested interface is not supported, this method must *ppvSite to NULL and return a failure code.

Return Values

S_OK
The site was returned successfully and the caller is responsible for calling IUnknown::Release when the site is no longer needed.
E_FAIL
There is no site in which case *ppvSite contains NULL on return.
E_NOINTERFACE
There is a site but it does not support the interface requested by riid.

Remarks

E_NOTIMPL is not allowed—any object implementing this interface must be able to return the last site seen in IObjectWithSite::SetSite.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IObjectWithSite::SetSite

Provides the site's IUnknown pointer to the object. The object should hold onto this pointer, calling IUnknown::AddRef in doing so. If the object already has a site, it should call that existing site's IUnknown::Release, save the new site pointer, and call the new site's IUnknown::AddRef.

HRESULT SetSite(
  IUnknown* pUnkSite  //Pointer to IUnknown of the site managing
                      // this object
);

Parameter

pUnkSite
[in] Pointer to the IUnknown interface pointer of the site managing this object. If NULL, the object should call IUnknown::Release on any existing site at which point the object no longer knows its site.

Return Value

S_OK
Returned in all circumstances.

Remarks

E_NOTIMPL is not allowed—without implementation of the SetSite method, the IObjectWithSite interface is unnecessary.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IObjectWithSite::GetSite


IOleAdviseHolder

The IOleAdviseHolder interface contains methods that manage advisory connections and compound document notifications in an object server. Its methods are intended to be used to implement the advisory methods of IOleObject. IOleAdviseHolder is implemented on an advise holder object. Its methods establish and delete advisory connections from the object managed by the server to the object's container, which must contain an advise sink (support the IAdviseSink interface). The advise holder object must also keep track of which advise sinks are interested in which notifications and pass along the notifications as appropriate.

When to Implement

It is unlikely that you would choose to implement this interface. OLE provides an implementation of the OLE advise holder as a convenience to programmers. Few applications will require notification capabilities beyond those which the default advise holder provides. In general, a single server application that requires different notification capabilities would simply implement the advisory functionality in its IOleObject advisory methods. It would be necessary to implement IOleAdviseHolder only in the case where there may be a need for a custom advise holder object, whose methods are to be used to implement the IOleObject methods in a set of servers.

When to Use

Call the methods of IOleAdviseHolder to implement the advisory methods of IOleObject. Applications instantiate an OLE advise holder by calling the OLE function CreateOleAdviseHolder. (OLE also provides a data advise holder to manage data notifications. Applications create a data advise holder by calling the OLE function CreateDataAdviseHolder.)

Containers and other objects that need to receive compound document notifications must implement the IAdviseSink interface to receive those notifications, and call the IOleAdviseHolder interface methods to establish an advisory connection and inform the object of what specific notifications it wishes to receive.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleAdviseHolder Methods | Description
Advise | Establishes advisory connection with sink.
Unadvise | Deletes advisory connection with sink.
EnumAdvise | Supplies an IEnumSTATDATA pointer to an enumeration object that can be used to determine current advisory connections.
SendOnRename | Advises sink that name of object has changed.
SendOnSave | Advises sink that object has been saved.
SendOnClose | Advises sink that object has been closed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CreateOleAdviseHolder, IDataAdviseHolder, CreateDataAdviseHolder, IAdviseSink, IOleObject


IOleAdviseHolder::Advise

Establishes an advisory connection between an OLE object and the calling object's advise sink. Through that sink, the calling object can receive notification when the OLE object is renamed, saved, or closed.

HRESULT Advise(
  IAdviseSink * pAdvise, //Pointer to the advise sink on the calling
                         // object
  DWORD * pdwConnection  //Pointer to a token
);

Parameters

pAdvise
[in] Pointer to the IAdviseSink interface on the advisory sink that should be informed of changes.
pdwConnection
[out] Pointer to a token that can be passed to the IOleAdviseHolder::Unadvise method to delete the advisory connection. The calling object is responsible for calling both IUnknown::AddRef and IUnknown::Release on this pointer.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Advisory connections set up successfully.

Remarks

Containers, object handlers, and link objects all create advise sinks to receive notification of changes in compound-document objects of interest, such as embedded or linked objects. OLE objects of interest to these objects must implement the IOleObject interface, which includes several advisory methods, including IOleObject::Advise. A call to this method must set up an advisory connection with any advise sink that calls it, and maintain each connection until it is closed. It must be able to handle more than one advisory connection at a time.

IOleAdviseHolder::Advise is intended to be used to simplify the implementation of IOleObject::Advise. You can get a pointer to the OLE implementation of IOleAdviseHolder by calling CreateOleAdviseHolder, and then, to implement IOleObject::Advise, just delegate the call to IOleAdviseHolder::Advise. Other IOleAdviseHolder methods are intended to implement other IOleObject advisory methods.

If the attempt to establish an advisory connection is successful, the object receiving the call returns a nonzero value through pdwConnection. If the attempt fails, the object returns a zero. To delete an advisory connection, the object with the advise sink passes this nonzero token back to the object by calling IOleAdviseHolder::Unadvise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleAdviseHolder::UnAdvise, IOleAdviseHolder::EnumAdvise, IOleObject::Advise


IOleAdviseHolder::EnumAdvise

Creates an enumerator that can be used to enumerate the advisory connections currently established for an object, and supplies a pointer to its IEnumSTATDATA interface.

HRESULT EnumAdvise(
  IEnumSTATDATA ** ppenumAdvise  //Address of output variable that
                                 // receives the IEnumSTATDATA
                                 // interface pointer
);

Parameter

ppenumAdvise
[out] Address of IEnumSTATDATA* pointer variable that receives the interface pointer to the new enumerator. A NULL value for *ppenumAdvise indicates that there are presently no advisory connections on the object, or that an error occurred. The advise holder is responsible for incrementing the reference count on the IEnumSTATDATA pointer this method supplies. It is the caller's responsibility to call IUnknown::Release when it is done with the pointer.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Enumerator created successfully.
E_NOTIMPL
EnumAdvise is not implemented.

Remarks

IOleAdviseHolder::EnumAdvise creates an enumerator that can be used to enumerate an object's established advisory connections. The method supplies a pointer to the IEnumSTATDATA interface on this enumerator. Advisory connection information for each connection is stored in the STATDATA structure, and the enumerator must be able to enumerate these structures, defined as follows:

typedef struct tagSTATDATA {
    FORMATETC         Formatetc;
    DWORD             grfAdvf;
    IAdviseSink *     pAdvise;
    DWORD             dwConnection;
    }STATDATA;

For this method, the only relevant structure members are pAdvise and dwConnection. Other members contain data advisory information. When you call the enumeration methods, and while an enumeration is in progress, the effect of registering or revoking advisory connections on what is to be enumerated is undefined.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleAdviseHolder::Advise, IOleAdviseHolder::UnAdvise, IOleObject::EnumAdvise, IDataAdviseHolder::EnumAdvise, STATDATA structure


IOleAdviseHolder::SendOnClose

Sends notification that the object has closed to all advisory sinks currently registered with the advise holder.

HRESULT SendOnClose();

Return Value

S_OK
Advise sinks were notified of the close operation through a call to the IAdviseSink::OnClose method.

Remarks

IOleAdviseHolder::SendOnClose must call IAdviseSink::OnClose on all advise sinks that have a valid advisory connection with the object, whenever the object goes from the running state to the loaded state. This occurs through a call to IOleObject::Close, so you can call IOleAdviseHolder::SendOnClose when you determine that a Close operation has been successful.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IAdviseSink::OnClose


IOleAdviseHolder::SendOnRename

Sends IAdviseSink::OnRename notifications to all advisory sinks currently registered with the advise holder.

HRESULT SendOnRename(
  IMoniker *pmk  //Pointer to an interface on the new moniker
);

Parameter

pmk
[in] Pointer to the new full moniker of the object.

Return Value

S_OK
Advise sinks were sent IAdviseSink::OnRename notifications.

Remarks

IOleAdviseHolder::SendOnRename calls IAdviseSink::OnRename to advise the calling object, which must have already established an advisory connection, that the object has a new moniker. If you are using the OLE advise holder (having obtained a pointer through a call to CreateOleAdviseHolder), you can call IOleAdviseHolder::SendOnRename in the implementation of IOleObject::SetMoniker, when you have determined that the operation is successful.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IAdviseSink::OnRename


IOleAdviseHolder::SendOnSave

Sends IAdviseSink::OnSave notifications to all advisory sinks currently registered with the advise holder.

HRESULT SendOnSave();

Return Value

S_OK
Advise sinks were sent IAdviseSink::OnSave notifications.

Remarks

IOleAdviseHolder::SendOnSave calls IAdviseSink::OnSave to advise the calling object (client), which must have already established an advisory connection, that the object has been saved. If you are using the OLE advise holder (having obtained a pointer through a call to CreateOleAdviseHolder), you can call IOleAdviseHolder::SendOnSave whenever you save the object the advise holder is associated with.

To take the object from the running state to the loaded state, the client calls IOleObject::Close. Within that implementation, if the user wants to save the object to persistent storage, the object calls IOleClientSite::SaveObject, followed by the call to IOleAdviseHolder::SendOnSave.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IAdviseSink::OnSave


IOleAdviseHolder::Unadvise

Deletes a previously established advisory connection.

HRESULT Unadvise(
  DWORD dwConnection  //Value identifying an established advisory
                      // connection
);

Parameter

dwConnection
[in] Contains a nonzero DWORD previously returned by IOleAdviseHolder::Advise in pdwConnection.

Return Values

S_OK
Advisory connection deleted successfully.
OLE_E_NOCONNECTION
The dwConnection parameter does not represent a valid advisory connection.

Remarks

IOleAdviseHolder::Unadvise is intended to be used to implement IOleObject::Unadvise to delete an advisory connection. In general, you would use the OLE advise holder having obtained a pointer through a call to CreateOleAdviseHolder.

Normally, containers call this method at shutdown or when an object is deleted. In certain cases, containers could call this method on objects that are running but not currently visible, as a way of reducing the overhead of maintaining multiple advisory connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleAdviseHolder::Advise, IOleAdviseHolder::EnumAdvise, IOleObject::Unadvise


IOleCache

The IOleCache interface provides control of the presentation data that gets cached inside of an object. Cached presentation data is available to the container of the object even when the server application is not running or is unavailable.

When to Implement

The IOleCache interface can be implemented by an object handler and an in-process server. In general, however, the methods of IOleCache are implemented as part of the IOleCache2 interface, which inherits the IOleCache definition, and adds methods for selectively updating the cache. Rather than implementing, however, it is typical to use or aggregate the OLE implementation, a pointer to which is available through a call to CreateDataCache.

When to Use

A client calls the methods of IOleCache to control what the data cache holds. A typical client would be an OLE Documents container that would cache an object's presentation so it is available without the object actually being active.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleCache Methods | Description
Cache | Adds a presentation to the data or view cache.
Uncache | Removes a presentation previously added with IOleCache::Cache.
EnumCache | Returns an object to enumerate the current cache connections.
InitCache | Fills the cache with all the presentation data from the data object.
SetData | Fills the cache with specified format of presentation data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleCache::Cache

Specifies the format and other data to be cached inside an embedded object.

HRESULT Cache(
  FORMATETC * pFormatetc,  //Pointer to the format and data to be
                           // cached
  DWORD advf,            //Flags that control the caching
  DWORD * pdwConnection  //Pointer to the connection for future
                         // calls to uncache
);

Parameters

pFormatetc
[in] Pointer to the format and other data to be cached. View caching is specified by passing a zero clipboard format in pFormatetc.
advf
[in] Contains a group of flags that control the caching. Valid values can be derived by using an OR operation on the values in the ADVF enumeration. When used in this context, for a cache, these values have specific meanings, which are outlined in the Remarks. Refer to the ADVF enumeration for a more detailed description.
pdwConnection
[out] Pointer to a returned DWORD token that identifies this connection and can later be used to turn caching off (by passing it to IOleCache::Uncache). If this value is zero, the connection was not established. The OLE-provided implementation uses nonzero numbers for connection identifiers.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
Requested data or view successfully cached.
CACHE_S_FORMATETC_NOTSUPPORTED
The cache was created, but the object application does not support the specified format. Cache creation succeeds even if the format is not supported, allowing the caller to fill the cache. If, however, the caller does not need to keep the cache, call IOleCache::UnCache.
CACHE_S_SAMECACHE
A cache already exists for the FORMATETC passed to IOleCache::Uncache. In this case, the new advise flags are assigned to the cache, and the previously assigned connection identifier is returned.
DV_E_LINDEX
Invalid value for lindex; currently only -1 is supported.
DV_E_TYMED
The value is not valid for pFormatetc->tymed.
DV_E_DVASPECT
The value is not valid for pFormatetc->dwAspect.
DV_E_CLIPFORMAT
The value is not valid for pFormatetc->cfFormat.
CO_E_NOTINITIALIZED
The cache's storage is not initialized.
DV_E_DVTARGETDEVICE
The value is not valid for pFormatetc->ptd.
OLE_E_STATIC
The cache is for a static object and it already has a cache node.

Remarks

IOleCache::Cache can specify either data caching or view (presentation) caching. To specify data caching, a valid data format must be passed in pFormatetc. For view caching, the cache object itself decides on the format to cache, so a caller would pass a zero data format in pFormatetc as follows:

pFormatetc->cfFormat == 0

A custom object handler can choose not to store data in a given format. Instead, it can synthesize it on demand when requested.

The advf value specifies a member of the ADVF enumeration. When one of these values (or an OR'd combination of more than one value) is used in this context, these values mean the following:

ADVF Value | Description
ADVF_NODATA | The cache is not to be updated by changes made to the running object. Instead, the container will update the cache by explicitly calling IOleCache::SetData, IDataObject::SetData, or IOleCache2::UpdateCache. This flag is usually used when the iconic aspect of an object is being cached.
ADVF_ONLYONCE | Update the cache one time only. After the update is complete, the advisory connection between the object and the cache is disconnected. The source object for the advisory connection calls the IAdviseSink::Release method.
ADVF_PRIMEFIRST | The object is not to wait for the data or view to change before updating the cache. OR'd with ADVF_ONLYONCE, this parameter provides an asynchronous GetData call.
ADVFCACHE_NOHANDLER | Synonym for ADVFCACHE_FORCEBUILTIN.
ADVFCACHE_FORCEBUILTIN | Used by DLL object applications and object handlers that draw their objects to cache presentation data to ensure that there is a presentation in the cache. This ensures that the data can be retrieved even when the object or handler code is not available.
ADVFCACHE_ONSAVE | Updates the cached representation only when the object containing the cache is saved. The cache is also updated when the OLE object changes from the running state back to the loaded state (because a subsequent save operation would require running the object again).

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

ADVF, IOleCache::Uncache


IOleCache::EnumCache

Returns a pointer to an enumeration object that can be used to enumerate the current cache connections.

HRESULT EnumCache(
  IEnumSTATDATA ** ppenumSTATDATA  //Address of output variable that
                                   // receives the IEnumSTATDATA
                                   // interface pointer
);

Parameter

ppenumSTATDATA
[out] Address of IEnumSTATDATA* pointer variable that receives the interface pointer to the new enumerator object. If *ppenumSTATDATA is NULL, there are currently no cache connections at this time.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The enumerator object is successfully instantiated or there are no cache connections. Check the ppenumSTATDATA parameter to determine which result occurred. If the ppenumSTATDATA parameter is NULL, then there are no cache connections at this time.

Remarks

The enumerator object returned by this method implements the IEnumSTATDATA interface, one of the standard enumerator interfaces that contain the Next, Reset, Clone, and Skip methods. IEnumSTATDATA enumerates the data stored in an array of STATDATA structures containing information about current cache connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IEnumXXXX, IEnumSTATDATA, IOleCache::Cache


IOleCache::InitCache

Fills the cache as needed using the data provided by the specified data object.

HRESULT InitCache(
  IDataObject * pDataObject  //Pointer to the data object from which
                             // the cache is initialized
);

Parameter

pDataObject
[in] Pointer to the IDataObject interface on the data object from which the cache is to be initialized.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The cache was filled using the data provided.
OLE_E_NOTRUNNING
The cache is not running.
CACHE_E_NOCACHE_UPDATED
None of the caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED
Only some of the existing caches were updated.

Remarks

IOleCache::InitCache is usually used when creating an object from a drag-and-drop operation or from a clipboard paste operation. It fills the cache as needed with presentation data from all the data formats provided by the data object provided on the clipboard or in the drag-and-drop operation. Helper functions like OleCreateFromData or OleCreateLinkFromData call this method when needed. If a container does not use these helper functions to create compound document objects, it can use IOleCache::Cache to set up the cache entries which are then filled by IOleCache::InitCache.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCache::Cache


IOleCache::SetData

Initializes the cache with data in a specified format and on a specified medium.

HRESULT SetData(
  FORMATETC * pFormatetc,  //Pointer to the format of the
                           // presentation data to be placed in the
                           // cache
  STGMEDIUM * pmedium,  //Pointer to the storage medium containing
                        // the data to be placed in the cache
  BOOL fRelease         //Ownership of the storage medium after this
                        // method is completed
);

Parameters

pFormatetc
[in] Pointer to the format of the presentation data being placed in the cache.
pmedium
[in] Pointer to the storage medium that contains the presentation data.
fRelease
[in] Ownership of the storage medium after completion of the method. If fRelease is TRUE, the cache takes ownership, freeing the medium when it is finished using it. When fRelease is FALSE, the caller retains ownership and is responsible for freeing the medium. The cache can only use the storage medium for the duration of the call.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The cache was filled.
DV_E_LINDEX
The value is not valid for pFormatetc->lindex. Currently, only -1 is supported.
DV_E_FORMATETC
The FORMATETC structure is invalid.
DV_E_TYMED
The value is not valid for pFormatetc->tymed.
DV_E_DVASPECT
The value is not valid for pFormatetc->dwAspect.
OLE_E_BLANK
Uninitialized object.
DV_E_TARGETDEVICE
The object is static and pFormatetc->ptd is non-NULL.
STG_E_MEDIUMFULL
The storage medium is full.

Remarks

IOleCache::SetData is usually called when an object is created from the Clipboard or through a drag-and-drop operation, and Embed Source data is used to create the object.

IOleCache::SetData and IOleCache::InitCache are very similar. There are two main differences. The first difference is that while IOleCache::InitCache initializes the cache with the presentation format provided by the data object, IOleCache::SetData initializes it with a single format. Second, the IOleCache::SetData method ignores the ADVF_NODATA flag while IOleCache::InitCache obeys this flag.

A container can use this method to maintain a single aspect of an object, such as the icon aspect of the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IDataObject::SetData, IOleCache::Cache, ADVF enumeration


IOleCache::Uncache

Removes a cache connection created in a prior call to IOleCache::Cache.

HRESULT Uncache(
  DWORD dwConnection  //Cache connection to remove
);

Parameter

dwConnection
[in] Cache connection to remove. This nonzero value was returned by IOleCache::Cache when the cache was originally established.

Return Values

S_OK
The cache connection was deleted.
OLE_E_NOCONNECTION
No cache connection exists for dwConnection.

Remarks

The IOleCache::Uncache method removes a cache connection that was created in a prior call to IOleCache::Cache. It uses the dwConnection parameter that was returned by the prior call to IOleCache::Cache.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCache::Cache


IOleCache2

The IOleCache2 interface allows object clients to selectively update each cache that was created with IOleCache::Cache.

When to Implement

The IOleCache2 interface can be implemented by an object handler and an in-process server. Rather than implementing, however, it is typical to use or aggregate the OLE implementation in the default handler, a pointer to which is available through a call to CreateDataCache.

When to Use

The OLE-provided implementation includes implementations of the IOleCache interface methods, from which IOleCache2 inherits its contract definition. The IOleCache2 interface is called by container applications, object handlers, or in process servers to update one or more of the caches that were created with the IOleCache::Cache method. This extended interface was added so client applications can exercise precise control over updates to the caches being maintained.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleCache Methods | Description
Cache | Adds a presentation to the data or view cache.
Uncache | Removes a presentation previously added with IOleCache::Cache.
EnumCache | Returns an object to enumerate the current cache connections.
InitCache | Fills the cache with all the presentation data from the data object.
SetData | Fills the cache with specified format of presentation data.
IOleCache2 Methods | Description
UpdateCache | Updates the specified cache(s).
DiscardCache | Discards cache(s) found in memory.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleCache2::DiscardCache

Discards the caches in memory.

HRESULT DiscardCache(
  DWORD dwDiscardOptions  //Save options
);

Parameter

dwDiscardOptions
[in] DWORD value from the DISCARDCACHE enumeration that indicates whether data is to be saved prior to being discarded. Containers that have drawn a large object and need to free up memory can specify DISCARDCACHE_SAVEIFDIRTY so that the newest presentation is saved for the next time the object must be drawn.

Containers that have activated an embedded object, made some changes, and then called IOleObject::Close(OLECLOSE_NOSAVE) to roll back the changes can specify DISCARDCACHE_NOSAVE to ensure that the native and presentation data are not out of synchronization.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The cache(s) were discarded according to the value specified in dwDiscardOptions.
OLE_E_NOSTORAGE
There is no storage available for saving the data in the cache.
STG_E_MEDIUMFULL
The storage medium is full.

Remarks

The IOleCache2::DiscardCache method is commonly used to handle low memory conditions by freeing memory currently being used by presentation caches.

Once discarded, the cache will satisfy subsequent IDataObject::GetData calls by reverting to disk-based data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DISCARDCACHE, IOleCache, IOleCacheControl


IOleCache2::UpdateCache

Updates specified cache(s). It updates the cache according to the value of a parameter. This method is used when the application needs precise control over caching.

HRESULT UpdateCache(
  IDataObject * pDataObject,  //Pointer to the data object from
                              // which the cache is updated
  DWORD grfUpdt,              //Type of cache to update
  LPVOID pReserved            //Reserved
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object from which the cache is updated. Object handlers and in-process servers typically pass a non-NULL value. A container application usually passes NULL, and the source is obtained from the currently running object.
grfUpdt
[in] Type of cache to update. The value is obtained by combining values from the following table:
Cache Control Values | Description
UPDFCACHE_NODATACACHE | Updates caches created by using ADVF_NODATA in the call to IOleCache::Cache.
UPDFCACHE_ONSAVECACHE | Updates caches created by using ADVFCACHE_ONSAVE in the call to IOleCache::Cache.
UPDFCACHE_ONSTOPCACHE | Updates caches created by using ADVFCACHE_ONSTOP in the call to IOleCache::Cache.
UPDFCACHE_NORMALCACHE | Dynamically updates the caches (as is normally done when the object sends out OnDataChange notices).
UPDFCACHE_IFBLANK | Updates the cache if blank, regardless of any other flag specified.
UPDFCACHE_ONLYIFBLANK | Updates only caches that are blank.
UPDFCACHE_
IFBLANKORONSAVECACHE 		The equivalent of using an OR operation to combine UPDFCACHE_IFBLANK and UPDFCACHE_ONSAVECACHE.
UPDFCACHE_ALL | Updates all caches.
UPDFCACHE_
ALLBUTNODATACACHE 		Updates all caches except those created with ADVF_NODATA in the call to IOleCache::Cache.
Thus, you can control updates to the caches created with the ADVF_NODATA flag and only update these caches explicitly.
pReserved
[in] Reserved for future use; must be NULL.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The cache(s) were updated according to the value specified in grfUpdt.
OLE_E_NOTRUNNING
The specified pDataObject is not running.
CACHE_E_NOCACHE_UPDATED
None of the caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED
Some of the caches were updated.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCache, IOleCacheControl, IDataObject


IOleCacheControl

The IOleCacheControl interface provides proper maintenance of caches. It maintains the caches by connecting the running object's IDataObject implementation to the cache, allowing the cache to receive notifications from the running object.

When to Implement

The OLE-provided implementation is used by most handlers and in-process servers. You can get a pointer to the OLE data cache object through a call to CreateDataCache.

When to Use

Object handlers and in-process servers use this interface internally to connect the cache part of the handler to the IDataObject implementation on the running object. Container applications have no need for this interface; they use IRunnableObject or OleRun instead.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleCacheControl Methods | Description
OnRun | Notifies the cache when the data object is running so the cache object can establish advise sinks as needed.
OnStop | Notifies the cache to terminate any existing advise sinks.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleCacheControl::OnRun

Notifies the cache that the data source object has now entered its running state.

HRESULT OnRun(
  DATAOBJECT * pDataObject  //Pointer to the object that is now
                            // running
);

Parameter

pDataObject
[in] Pointer to the IDataObject interface on the object that is entering the running state.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The cache was notified and pDataObject is valid.

Remarks

When IOleCacheControl::OnRun is called, the cache sets up advisory connections as necessary with the source data object so it can receive notifications. The advisory connection created between the running object and the cache is destroyed when IOleCacheControl::OnStop is called.

Some object handlers or in-process servers might use the cache passively, and not call IOleCacheControl::OnRun. These applications must call IOleCache2::UpdateCache, IOleCache::InitCache, or IOleCache::SetData to fill the cache when necessary to ensure that the cache gets updated.

IOleCacheControl::OnRun does not add a reference count on the pointer to IDataObject passed in pDataObject. Because it is the responsibility of the caller of OleRun to ensure that the lifetime of the pDataObject pointer lasts until OnStop is called, the caller must be holding a pointer to IDataObject on the data object of interest.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCache2::UpdateCache, IOleCacheControl::OnStop


IOleCacheControl::OnStop

Notifies the cache it should terminate any existing connection previously given to it by using IOleCacheControl::OnRun. No indication is given as to whether a connection existed or not.

HRESULT OnStop( );

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The cache was notified and the advisory connection was successfully removed.

Remarks

The data advisory connection between the running object and the cache is destroyed as part of calling IOleCacheControl::OnStop.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCacheControl::OnRun


IOleClientSite

The IOleClientSite interface is the primary means by which an embedded object obtains information about the location and extent of its display site, its moniker, its user interface, and other resources provided by its container. An object server calls IOleClientSite to request services from the container. A container must provide one instance of IOleClientSite for every compound-document object it contains.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleClientSite Methods | Description
SaveObject | Saves embedded object.
GetMoniker | Requests object's moniker.
GetContainer | Requests pointer to object's container.
ShowObject | Asks container to display object.
OnShowWindow | Notifies container when object becomes visible or invisible
RequestNewObjectLayout | Asks container to resize display site.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleClientSite::GetContainer

Returns a pointer to the container's IOleContainer interface.

HRESULT GetContainer(
  LPOLECONTAINER FAR* ppContainer  //Address of output variable that
                                   // receives the IOleContainer
                                   // interface pointer
);

Parameter

ppContainer
[out] Address of IOleContainer* pointer variable that receives the interface pointer to the container object. If an error occurs, the implementation must set *ppContainer to NULL.

Return Values

S_OK
The pointer to the container's IOleContainer interface was successfully returned.
OLE_E_NOT_SUPPORTED
Client site is in OLE 1 container.
E_NOINTERFACE
The container does not implement the IOleContainer interface.

Remarks

If a container supports links to its embedded objects, implementing IOleClientSite::GetContainer enables link clients to enumerate the container's objects and recursively traverse a containment hierarchy. This method is optional but recommended for all containers that expect to support links to their embedded objects.

Link clients can traverse a hierarchy of compound-document objects by recursively calling IOleClientSite::GetContainer to get a pointer to the link source's container; followed by IOleContainer::QueryInterface to get a pointer to the container's IOleObject interface and, finally, IOleObject::GetClientSite to get the container's client site in its container.

Simple containers that do not support links to their embedded objects probably do not need to implement this method. Instead, they can return E_NOINTERFACE and set ppContainer to NULL.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleClientSite::GetMoniker

Returns a moniker to an object's client site. An object can force the assignment of its own or its container's moniker by specifying a value for dwAssign.

HRESULT GetMoniker(
  DWORD dwAssign,  //Value specifying how moniker is assigned
  DWORD dwWhichMoniker,
                   //Value specifying which moniker is assigned
  IMoniker ** ppmk //Address of output variable that receives the
                   // IMoniker interface pointer
);

Parameters

dwAssign
[in] Specifies whether to get a moniker only if one already exists, force assignment of a moniker, create a temporary moniker, or remove a moniker that has been assigned. In practice, you will usually request that the container force assignment of the moniker. Values defining these choices are contained in the enumeration OLEGETMONIKER.
dwWhichMoniker
[in] DWORD that specifies whether to return the container's moniker, the object's moniker relative to the container, or the object's full moniker. In practice, you will usually request the object's full moniker. Values defining these choices are contained in the enumeration OLEWHICHMK.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the moniker for the object's client site. If an error occurs, the implementation must set *ppmk to NULL. Each time a container receives a call to IOleClientSite::GetMoniker, it must increase the reference count on the *ppmk pointer it returns. It is the caller's responsibility to call Release when it is done with the pointer.

Return Values

This method supports the standard return values E_FAIL and E_UNEXPECTED, as well as the following:

S_OK
Requested moniker returned successfully.
E_NOTIMPL
This container cannot assign monikers to objects. This is the case with OLE 1 containers.

Remarks

Containers implement IOleClientSite::GetMoniker as a way of passing out monikers for their embedded objects to clients wishing to link to those objects.

When a link is made to an embedded object or to a pseudo-object within it (a range of cells in a spreadsheet, for example), the object needs a moniker to construct the composite moniker indicating the source of the link. If the embedded object does not already have a moniker, it can call IOleClientSite::GetMoniker to request one.

Every container that expects to contain links to embeddings should support IOleClientSite::GetMoniker to give out OLEWHICHMK_CONTAINER, thus enabling link tracking when the link client and link source files move, but maintain the same relative position.

An object must not persistently store its full moniker or its container's moniker, because these can change while the object is not loaded. For example, either the container or the object could be renamed, in which event, storing the container's moniker or the object's full moniker would make it impossible for a client to track a link to the object.

In some very specialized cases, an object may no longer need a moniker previously assigned to it and may wish to have it removed as an optimization. In such cases, the object can call IOleClientSite::GetMoniker with OLEGETMONIKER_UNASSIGN to have the moniker removed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetMoniker, IOleObject::SetMoniker


IOleClientSite::OnShowWindow

Notifies a container when an embedded object's window is about to become visible or invisible. This method does not apply to an object that is activated in place and therefore has no window separate from that of its container.

HRESULT OnShowWindow(
  BOOL fShow  //Value indicating if window is becoming visible
);

Parameter

fShow
[in] Value that indicates whether an object's window is open (TRUE) or closed (FALSE).

Return Value

S_OK
Shading or hatching has been successfully added or removed.

Remarks

An embedded object calls IOleClientSite::OnShowWindow to keep its container informed when the object is open in a window. This window may or may not be currently visible to the end user. The container uses this information to shade the object's client site when the object is displayed in a window, and to remove the shading when the object is not. A shaded object, having received this notification, knows that it already has an open window and therefore can respond to being double-clicked by bringing this window quickly to the top, instead of launching its application in order to obtain a new one.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleClientSite::RequestNewObjectLayout

Asks container to allocate more or less space for displaying an embedded object.

HRESULT RequestNewObjectLayout();

Return Values

S_OK
Request for new layout succeeded.
E_NOTIMPL
Client site does not support requests for new layout.

Remarks

Currently, there is no standard mechanism by which a container can negotiate how much room an object would like. When such a negotiation is defined, responding to this method will be optional for containers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleClientSite::SaveObject

Saves the object associated with the client site. This function is synchronous; by the time it returns, the save will be completed.

HRESULT SaveObject();

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The object was saved.

Remarks

An embedded object calls IOleClientSite::SaveObject to ask its container to save it to persistent storage when an end user chooses the File Update or Exit commands. The call is synchronous, meaning that by the time it returns, the save operation will be completed.

Calls to IOleClientSite::SaveObject occur in most implementations of IOleObject::Close. Normally, when a container tells an object to close, the container passes a flag specifying whether the object should save itself before closing, prompt the user for instructions, or close without saving itself. If an object is instructed to save itself, either by its container or an end user, it calls IOleClientSite::SaveObject to ask the container application to save the object's contents before the object closes itself. If a container instructs an object not to save itself, the object should not call SaveObject.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::Close


IOleClientSite::ShowObject

Tells the container to position the object so it is visible to the user. This method ensures that the container itself is visible and not minimized.

HRESULT ShowObject();

Return Values

S_OK
Container has tried to make the object visible.
OLE_E_NOT_SUPPORTED
Client site is in an OLE 1 container.

Remarks

After a link client binds to a link source, it commonly calls IOleObject::DoVerb on the link source, usually requesting the source to perform some action requiring that it display itself to the user. As part of its implementation of DoVerb, the link source can call IOleClientSite::ShowObject, which forces the client to show the link source as best it can. If the link source's container is itself an embedded object, it will recursively invoke IOleClientSite::ShowObject on its own container.

Having called the ShowObject method, a link source has no guarantee of being appropriately displayed because its container may not be able to do so at the time of the call. The ShowObject method does not guarantee visibility, only that the container will do the best it can.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::DoVerb


IOleCommandTarget

The IOleCommandTarget interface enables objects and their containers to dispatch commands to each other. For example, an object's toolbars may contain buttons for commands such as Print, Print Preview, Save, New, and Zoom.

Normal in-place activation guidelines recommend that you remove or disable such buttons because no efficient, standard mechanism has been available to dispatch them to the container. Similarly, a container has heretofore had no efficient means to send commands such as Print, Page Setup, and Properties to an in-place active object. Such simple command routing could have been handled through existing OLE Automation standards and the IDispatch interface, but the overhead with IDispatch is more than is required in the case of document objects. The IOleCommandTarget interface provides a simpler means to achieve the same ends.

Available commands are defined by integer identifiers in a group. The group itself is identified with a GUID. The interface allows a caller both to query for support of one or more commands within a group and to issue a supported command to the object.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleCommandTarget Methods | Description
QueryStatus | Queries object for status of commands
Exec | Execute a command

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IOleCommandTarget::Exec

Executes a specified command or displays help for a command.

HRESULT Exec(
  const GUID *pguidCmdGroup,  // Pointer to command group
  DWORD nCmdID,               // Identifier of command to execute
  DWORD nCmdExecOpt,          // Options for executing the command
  VARIANTARG *pvaIn,          // Pointer to input arguments
  VARIANTARG *pvaOut          // Pointer to command output
);

Parameters

pguidCmdGroup
[unique][in] Pointer to unique identifier of the command group; can be NULL to specify the standard group.
nCmdID
[in] The command to be executed. This command must belong to the group specified with pguidCmdGroup.
nCmdExecOpt
[in] Values taken from the OLECMDEXECOPT enumeration, which describe how the object should execute the command.
pvaIn
[unique][in] Pointer to a VARIANTARG structure containing input arguments. Can be NULL.
pvaOut
[unique][in,out] Pointer to a VARIANTARG structure to receive command output. Can be NULL.

Return Values

This method supports the standard return values E_FAIL and E_UNEXPECTED, as well as the following:

S_OK
The command was executed successfully.
OLECMDERR_E_UNKNOWNGROUP
The pguidCmdGroup parameter is not NULL but does not specify a recognized command group.
OLECMDERR_E_NOTSUPPORTED
The nCmdID parameter is not a valid command in the group identified by pguidCmdGroup.
OLECMDERR_E_DISABLED
The command identified by nCmdID is currently disabled and cannot be executed.
OLECMDERR_E_NOHELP
The caller has asked for help on the command identified by nCmdID, but no help is available.
OLECMDERR_E_CANCELED
The user canceled the execution of the command.

Remarks

The list of input and output arguments of a command and how they are packaged is unique to each command. Such information should be documented with the specification of the command group. (See the description of OLECMDID_ZOOM in the OLECMDID enumeration.) In the absence of any specific information the command is assumed to take no arguments and have no return value.

Notes to Callers

The pguidCmdGroup and nCmdID parameters together uniquely identify the command to invoke. The nCmdExecOpt parameter specifies the exact action to be taken. (See the OLECMDEXECOPT enumeration for more details.)

Most commands neither take arguments nor return values. For such commands, the caller can pass NULL in pvaIn and pvaOut. For commands that expect one or more input values, the caller can declare and initialize a VARIANTARG variable and pass a pointer to that variable in pvaIn. If the input to the command is a single value, the argument can be stored directly in the VARIANTARG structure and passed to the function. If the command expects multiple arguments, those arguments must be packaged appropriately within the VARIANTARG, using one of the supported types (such as IDispatch or SAFEARRAY).

If a command returns one or more arguments, the caller is expected to declare a VARIANTARG, initialize it to VT_EMPTY, and pass its address in pvaOut. If the command returns a single value, then the object can store that value directly in pvaOut. If the command has multiple output values, then it will package those in some way appropriate for the VARIANTARG.

Because pvaIn and pvOut are both caller-allocated, stack variables are permitted for both the caller and the object receiving the call. For commands that take zero or one argument on input and return zero or one value, no additional memory allocation is necessary. Most of the types supported by VARIANTARG do not require memory allocation. Exceptions include SAFEARRAY and BSTR.

Notes to Implementers

A command target must implement this function; E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

OLECMDEXECOPT


IOleCommandTarget::QueryStatus

Queries the object for the status of one or more commands generated by user interface events.

[input_sync] HRESULT QueryStatus(
  const GUID *pguidCmdGroup,
                        // Pointer to command group
  ULONG cCmds,          // Number of commands in prgCmds array
  OLECMD *prgCmds,      // Array of commands
  OLECMDTEXT *pCmdText  // Pointer to name or status of command
);

Parameters

pguidCmdGroup
[unique][in] Unique identifier of the command group; can be NULL to specify the standard group. All the commands that are passed in the prgCmds array must belong to the group specified by pguidCmdGroup.
cCmds
[in] The number of commands in the prgCmds array.
prgCmds
[in,out] A caller-allocated array of OLECMD structures that indicate the commands for which the caller needs status information. This method fills the cmdf member of each structure with values taken from the OLECMDF enumeration.
pCmdText
[unique][in,out] Pointer to an OLECMDTEXT structure in which to return name and/or status information of a single command. Can be NULL to indicate that the caller does not need this information.

Return Values

This method supports the standard return values E_FAIL and E_UNEXPECTED, as well as the following:

S_OK
The command status as any optional strings were returned successfully.
E_POINTER
The prgCmds argument is NULL.
OLECMDERR_E_UNKNOWNGROUP
The pguidCmdGroup parameter is not NULL but does not specify a recognized command group.

Remarks

Callers use IOleCommandTarget::QueryStatus to determine which commands are supported by a target object. The caller can then disable unavailable commands that would otherwise be routed to the object. The caller can also use this method to get the name or status of a single command.

Notes to Callers

The caller passes an array of OLECMD structures in prgCmds that describe the commands of interest from the group specified in pguidCmdGroup, where each structure's cmdID is set to a command identifier and the cmdf field is set to zero.

Notes to Implementers

The object receiving the call the fills the cmdf field for each command with values taken from the OLECMDF enumeration to describe the status of each command.

The called object should first mark the command as described above. Then, if the command is supported (OLECMDF_SUPPORTED) the object should check the OLECMDTEXTF flags in the OLECMDTEXT structure. If the OLECMDFTEXF_NAME flag is specified, then the object should copy the localized name of the command (for example, "Open", "Copy", etc.) into the rgwz field of OLECMDTEXT, paying attention to the size specified by the cwBuf field in that same structure.

If the caller sets the OLECMDFTEXTF_STATUS flag, the object should instead write a localized status string for the command into the rgwz field. The status string is typically contextual and depends on the state of the command—enabled/disabled, for example. If the buffer is not big enough, then the object should zero-terminate the buffer. Whether the buffer is big enough or not, the object must return the total actual size of the string that it attempted to copy in the cwActual member of the OLECMDTEXT structure.

If the command array contains more than one command, the textual information should be returned for the first command in the array that the object supports. Typically, this functionality is used to show the status text of a command. The caller can use either a stack or a global variable for rgwz, because memory for this parameter is not dynamically allocated.

Because QueryStatus is defined with the [input_sync] attribute, the implementing object cannot yield or make another non input_sync RPC call while executing it.

A command target must implement this function; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

OLECMD, OLECMDF, OLECMDTEXT


IOleContainer

The IOleContainer interface is used to enumerate objects in a compound document or lock a container in the running state. Container and object applications both implement this interface.

When to Implement

Applications that support links and links to embedded objects implement this interface to provide object enumeration, name parsing, and silent updates of link sources. Simple, nonlinking containers do not need to implement IOleContainer if it is useful mainly to support links to embedded objects.

When to Use

Call IOleContainer to enumerate the objects in a compound document or to lock a container so that silent updates of link sources can be carried out safely.

Many applications inherit the functions of IOleContainer by implementing IOleItemContainer, which is used to bind item monikers.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IParseDisplayName | Method Description
ParseDisplayName | Parses object's display name to form moniker.
IOleContainer Methods | Description
EnumObjects | Enumerates objects in a container.
LockContainer | Keeps container running until explicitly released.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleItemContainer, IParseDisplayName


IOleContainer::EnumObjects

Enumerates objects in the current container.

HRESULT EnumObjects(
  DWORD grfFlags,  //Value specifying what is to be enumerated
  IEnumUnknown **ppenum
                   //Address of output variable that receives the
                   // IEnumUnknown interface pointer
);

Parameters

grfFlags
[in] Value that specifies which objects in a container are to be enumerated, as defined in the enumeration OLECONTF.
ppenum
[out] Address of IEnumUnknown* pointer variable that receives the interface pointer to the enumerator object. Each time a container receives a successful call to EnumObjects, it must increase the reference count on the *ppenum pointer the method returns. It is the caller's responsibility to call IUnknown::Release when it is done with the pointer. If an error is returned, the implementation must set *ppenum to NULL.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Enumerator successfully returned.
E_NOTIMPL
Object enumeration not supported.

Remarks

A container should implement EnumObjects to enable programmatic clients to find out what objects it holds. This method, however, is not called in standard linking scenarios.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IEnumUnknown, IOleItemContainer, OLECONTF


IOleContainer::LockContainer

Keeps an embedded object's container running.

HRESULT LockContainer(
  BOOL fLock  //Value indicating lock or unlock
);

Parameter

fLock
[in] Value that specifies whether to lock (TRUE) or unlock (FALSE) a container.

Return Values

This method supports the standard return values E_FAIL and E_OUTOFMEMORY, as well as the following:

S_OK
Container was locked successfully.

Remarks

An embedded object calls IOleContainer::LockContainer to keep its container running when the object has link clients that require an update. If an end user selects File Close from the container's menu, however, the container ignores all outstanding LockContainer locks and closes the document anyway.

Notes to Callers

When an embedded object changes from the loaded to the running state, it should call IOleContainer::LockContainer with the fLock parameter set to TRUE. When the embedded object shuts down (transitions from running to loaded), it should call IOleContainer::LockContainer with the fLock parameter set to FALSE.

Each call to LockContainer with fLock set to TRUE must be balanced by a call to LockContainer with fLock set to FALSE. Object applications typically need not call LockContainer; the default handler makes these calls automatically for object applications implemented as .EXEs as the object makes the transition to and from the running state. Object applications not using the default handler, such as DLL object applications, must make the calls directly.

An object should have no strong locks on it when it registers in the Running Object Table, but it should be locked as soon as the first external client connects to it. Therefore, following registration of the object in the Running Object Table, object handlers and DLL object applications, as part of their implementation of IRunnableObject::Run, should call IOleContainer::LockContainer(TRUE) to lock the object.

Notes to Implementers

The container must keep track of whether and how many calls to LockContainer(TRUE) have been made. To increment or decrement the reference count, IOleContainer::LockContainer calls CoLockObjectExternal with a flag set to match fLock.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CoLockObjectExternal, IRunnableObject::Run


IOleDocument

The IOleDocument interface enables a document object to communicate to containers its ability to create views of its data. This interface also enables a document object to enumerate its views and to provide containers with miscellaneous information about itself, such as whether it supports multiple views or complex rectangles.

When to Implement

An OLE document that is to be activated as a document object at the very least must implement this interface. If an application exposes more than one type of document object, each type must implement IOleDocument.

When to Use

A container of document objects calls the methods of this interface to ask a document object to create views of itself, enumerate the views it supports, or provide miscellaneous information about its capabilities.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleDocument Methods | Description
CreateView | Creates a new view object.
GetDocMiscStatus | Returns status information about the document object.
EnumViews | Enumerates views supported by the document object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleCommandTarget, IOleDocumentSite, IOleDocumentView, IPrint


IOleDocument::CreateView

Creates a document view object in the caller's process and obtains a pointer to that object's IOleDocumentView interface.

HRESULT CreateView(
  IOleInPlaceSite * pIPSite,
                     // Pointer to container's view-site object
  IStream * pstm,    // Pointer to a stream object
  DWORD dwReserved,  // Reserved for future use
  IOleDocumentView ** ppView
                     //Address of output variable that receives the
                     // IOleDocumentView interface pointer
);

Parameters

pIPSite
[in] Pointer to the view site object to be associated with the new document view object. May be NULL, as when the view is contained in a new, uninitialized document object, in which case the caller must initialize the view with a subsequent call to IOleDocumentView::SetInPlaceSite.
pstm
[in] Pointer to a stream containing data from which the new document view object should initialize itself. If NULL, the document object initializes the new document view object with a default state.
dwReserved
[in] Reserved for future use. Must be zero.
ppView
[out] Address of IOleDocumentView* pointer variable that receives the interface pointer to the new document view object. When successful, the caller is responsible for calling Release on the *ppview pointer when the view object is no longer needed.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The view object was created successfully.
E_POINTER
The address in ppView is NULL.

Remarks

A document object container's document site calls IOleDocument::CreateView to instruct a document object to create a new view of itself in the container's process, either from default data or using the contents of an existing stream.

Calling CreateView does not cause the new view to display itself. To do so requires a call to either IOleDocumentView::Show or IOleDocumentView::UIActivate.

Notes to Callers

CreateView is normally called by the container's implementation of IOleDocumentSite::ActivateMe. If a document object passes an IOleDocumentView pointer in its call to ActivateMe, the container has no need to call CreateView and instead can call IOleDocumentView::SetInPlaceSite, followed by AddRef. If a document object passes a NULL pointer in its call to ActivateMe, then the container calls CreateView to retrieve a view pointer.

The container has the option of passing a pointer to a stream containing data used to initialize the requested view. If the container passes a NULL stream pointer, the document object will initialize the new view using its default settings.

Notes to Implementers

This method must be completely implemented for any document object; E_NOTIMPL is not an acceptable return value.

If pIPSite is not NULL, then the document object should pass the pointer to the new view in a call to IOleDocumentView::SetInPlaceSite. If pIPSite is NULL, the caller is responsible for making this same call. In addition, if pstm is not NULL, then the document object should initialize the view object by passing pstm in a call to IOleDocumentView::ApplyViewState.

As with all new interface pointers, CreateView should call AddRef on the pointer in *ppView before returning. The caller is responsible for calling Release on this pointer when it is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentSite::ActivateMe, IOleDocumentView::ApplyViewState, IOleDocumentView::SetInPlaceSite, IOleDocumentView::Show, IOleDocumentView::UIActivate


IOleDocument::EnumViews

Creates an object that enumerates the views supported by a document object or, if only one view is supported, returns a pointer to that view.

HRESULT EnumViews(
  IEnumOleDocumentViews ** ppEnum,
                              //Address of output variable that
                              // receives the IEnumOleDocumentViews
                              // interface pointer
  IOleDocumentView ** ppView  //Address of output variable that
                              // receives the IOleDocumentView
                              // interface pointer
);

Parameters

ppEnum
[out] Address of IEnumOleDocumentViews* pointer variable that receives the interface pointer to the enumerator object.
ppView
[out] Address of IOleDocumentView* pointer variable that receives the interface pointer to a single view object.

Return Value

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
If the object supports multiple views, then *ppEnum contains a pointer to the enumerator object, and *ppView is NULL. Otherwise, *ppEnum is NULL, and *ppView contains an interface pointer on the single view.
E_POINTER
The address in ppEnum or ppView is invalid. The caller must pass valid pointers for both arguments.

Remarks

If a document object supports multiple views of its data, it must also implement IEnumOleDocumentViews and pass that interface's pointer in the out parameter *ppEnum. Using this pointer, the container can enumerate the views supported by the document object.

If the document object supports only a single view, IOleDocument::EnumViews passes that view's IOleDocumentView pointer in the out parameter *ppView.

Notes to Callers

Call this method to determine if a document object supports more than one view of its data. If it does, the caller can use the pointer written to *ppEnum to specify which view to activate. When finished with the pointer, the caller must free it by calling Release.

Notes to Implementers

This method must be completely implemented on all document objects; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IEnumOleDocumentViews, IOleDocumentView


IOleDocument::GetDocMiscStatus

Obtains information about whether a document object supports behaviors specified in the DOCMISC enumerator.

HRESULT GetDocMiscStatus(
  DWORD *pdwStatus  // Pointer to information about the document
                    // object
);

Parameters

pdwStatus
[out] Pointer to the information on supported behaviors. Values written to the address specified by this pointer are taken from the DOCMISC enumeration.

Return Values

S_OK
The information was obtained successfully.
E_POINTER
The address in pdwStatus is NULL.

Remarks

This method provides a way for containers to ascertain whether a document object supports multiple views, complex rectangles, opening in a pop-up window, or file read/write.

Values from this enumerator are also stored in the registry as the value of the DocObject key.

Notes to Callers

By calling this method prior to activating a document object, containers can take whatever steps are necessary to support or otherwise accommodate the specified behaviors.

Notes to Implementers

This method must be completely implemented in any document object, even if the dereferenced value of pdwStatus is zero. E_NOTIMPL is not an acceptable return value. Normally, the returned DOCMISC value should be hard-coded for performance.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

DOCMISC


IOleDocumentSite

The IOleDocumentSite interface enables a document that has been implemented as a document object to bypass the normal activation sequence for in-place-active objects and to directly instruct its client site to activate it as a document object. A client site with this ability is called a document site.

For each document object to be hosted, a container must provide a corresponding document site, which is an OLE Documents client site that, in addition to implementing IOleClientSite and IAdviseSink, also implements IOleDocumentSite. Each document site implements a separate document view site object for each view of a document to be activated. The document view site implements IOleInPlaceSite and, optionally, IContinueCallback.

When to Implement

Implement IOleDocumentSite on each client site that is to host a document object. In addition to this interface, a document site must also implement IOleClientSite, IOleInPlaceSite, and IAdviseSink. IContinueCallback is optional.

When to Use

A document object calls IOleDocumentSite::ActivateMe to ask its document site to activate it, typically in response to the container's call to IOleObject::DoVerb.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleDocumentSite Methods | Description
ActivateMe | Activates an OLE Document Object

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IOleDocumentSite::ActivateMe

Asks a document site to activate the document making the call as a document object rather than an in-place-active object and, optionally, specifies which view of the object document to activate.

HRESULT ActivateMe(
  IOleDocumentView * pViewToActivate  // Pointer to the view to be
                                      // activated
);

Parameters

pViewToActivate
[in] Pointer to the document view to be used in activating the document object. Can be NULL, in which case the container should call IOleDocument::CreateView to obtain a document view pointer.

Return Value

This method supports the standard return values E_OUTOFMEMORY and E_FAIL, as well as the following:

S_OK
The container activated the view successfully.

Remarks

When a container calls IOleObject::DoVerb to activate a document, a document object bypasses the usual in-place activation sequence by calling IOleDocumentSite::ActivateMe.

When calling IOleObject::DoVerb on a document object, the most appropriate activation verb is usually OLEIVERB_SHOW. Other allowable verbs include OLEIVERB_PRIMARY and OLEIVERB_UIACTIVATE. OLEIVERB_OPEN is discouraged because it means opening an embedded object in a separate window, which is contrary to the intent of document object activation.

Notes to Callers

Only document objects should call this method. A normal in-place active document should respond to a container's call to IOleObject::DoVerb by calling IOleInPlaceSite.

A document object should initiate its activation by calling IOleDocumentSite::ActivateMe. If the container does not implement IOleDocumentSite, then the document should default to the normal in-place activation sequence.

A document object that supports more than one view of its data can specify which view to activate by passing a pointer to that view's IOleDocumentView interface in pViewToActivate.

However the IOleDocumentView pointer is obtained, the container should release the pointer when it is no longer needed.

Notes to Implementers

This function must be completely implemented in a document object container; E_NOTIMPL is not an acceptable return value.

If a document object passes an IOleDocumentView pointer in pViewToActivate, the container's implementation of ActivateMe should call IOleDocumentView::SetInPlaceSite and pass a pointer to its IOleInPlaceSite interface back to the view object. If the container is holding onto the IOleDocumentView pointer, which will normally be the case, it should follow the call to SetInPlaceSite with a call to AddRef.

If pViewToActivate is NULL, the container can obtain a pointer to a document view by querying the document for IOleDocument, then calling IOleDocument::CreateView and passing its IOleInPlaceSite pointer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleClientSite, IOleDocument::CreateView, IOleDocumentView::SetInPlaceSite, IOleInPlaceSite, IOleObject::DoVerb


IOleDocumentView

The IOleDocumentView interface enables a container to communicate with each view supported by a document object.

A document object that supports multiple views of its data represents each view as a separate object. Each document view object implements IOleDocumentView, along with IOleInPlaceObject, IOleInPlaceActiveObject, and optional interfaces such as IPrint and IOleCommandTarget. A document object that supports only a single view does not require that view to be implemented as a separate object. Instead, both document and view can be implemented as a single class.

When to Implement

All document objects must implement IOleDocumentView for each view they support. If a document object supports multiple views, each view must be implemented as a separate class object. If a document object supports only a single view, the document object and its view can both be implemented as a single class.

When to Use

A container calls the methods of this interface to activate, deactivate, close, and generally communicate with a document view object.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleDocumentView Methods | Description
SetInPlaceSite | Associates a container view site with this view.
GetInPlaceSite | Retrieves the view site associated with this view.
GetDocument | Obtains the IUnknown pointer of the document object that owns this view.
SetRect | Sets the coordinates of the view port.
GetRect | Retrieves the coordinates last passed in SetRect.
SetRectComplex | Sets the coordinates of the view port, scroll bars, and size box.
Show | In-place activates or deactivates a view.
UIActivate | In-place activates or deactivates a view's user-interface elements.
Open | Displays view in a separate pop-up window.
Close | Instructs view to close.
SaveViewState | Saves view state into stream.
ApplyViewState | Initializes view with view state previously saved in call to SaveViewState.
Clone | Creates a duplicate view object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IOleDocumentView::ApplyViewState

Asks a view to initialize itself to a previously saved state.

HRESULT ApplyViewState(
  IStream *pstm  // Pointer to stream containing state data
);

Parameters

pstm
[in] Pointer to a stream containing data from which the view should initialize itself.

Return Values

S_OK
The view successfully loaded its state from the specified stream.
E_POINTER
The value in pstm is NULL.
E_NOTIMPL
This view has no meaningful state to load. This error should be rare because most views will have at least some state information worth loading.

Remarks

Typically, this function is called after an existing view has been created in the container but before that view has been displayed. It is the responsibility of the view to validate the data in the view stream; the container does not attempt to interpret the view's state data.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocument::CreateView, IOleDocumentView::SaveViewState


IOleDocumentView::Clone

Creates a duplicate view object with an internal state identical to that of the current view.

HRESULT Clone(
  IOleInPlaceSite *pIPSiteNew  // Pointer to view site of new view
                               // object
  IOleDocumentView **ppViewNew  //Address of output variable that
                                // receives the IOleDocumentView
                                // interface pointer
);

Parameters

pIPSiteNew
[in] Pointer to the view site in which the new view object will be activated. On receiving this pointer, the view being cloned should pass it to the new view's SetInPlaceSite method. This pointer can be NULL, in which case the caller is responsible for calling SetInPlaceSite on the new view directly.
ppViewNew
[out] Address of IOleDocumentView* pointer variable that receives the interface pointer to the new view object. The caller is responsible for releasing *ppViewNew when it is no longer needed.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The view was successfully cloned.
E_POINTER
The value in ppViewNew is NULL.
E_NOTIMPL
The view object does not implement this interface.

Remarks

This method is useful for creating a new view with a different view port and view site but with the same view context as the view being cloned. Typically, containers hosting an MDI application will call this method to provide "Window/New window" capability.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView, IOleDocumentView::SetInPlaceSite


IOleDocumentView::Close

Asks a document view to close itself and release its IOleInPlaceSite pointer.

HRESULT Close(
  DWORD dwReserved  // Reserved. Must be zero
);

Return Values

S_OK
The view successfully closed itself.

Remarks

When a separate window is no longer needed, the container calls Close, whereupon the view releases its site pointer to the separate window and destroys the window. Unlike the normal in-place deactivation sequence for active documents, a document view continues to hold the IOleInPlaceSite pointer. This pointer is released only when the view's container calls SetInPlaceSite, with pIPSite set to NULL, or calls Close.

When a user closes a view's separate window, the view should not shut itself down. Instead, it should call IOleInPlaceSite::OnInPlaceActivate. The view site then decides whether to call UIActivate(FALSE) immediately or later. In this way, a document view displayed in a separate window remains available for activation in the container's own window.

The container must call this method before it deletes the view, that is, releases its last reference to the view. In general, implementation of this method will call Show(FALSE) to hide the view if it is not already hidden, then call SetInPlaceSite(NULL) to deactivate itself and release the view site pointer.

Because Close is called when a container is going to completely shut down a view, this method must be implemented and has no reason to fail.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::SetInPlaceSite, IOleDocumentView::Show


IOleDocumentView::GetDocument

Obtains the IUnknown interface pointer on the document object that owns this view.

HRESULT GetDocument(
  IUnknown **ppunk  // On return, pointer to the IUnknown pointer of
                    // the document object that owns this view
);

Parameters

ppunk
[out] Pointer to the IUnknown pointer of the document object that owns this view.

Return Value

S_OK
The document object's interface pointer was successfully obtained. S_OK is the only valid return value for this function.

Remarks

The caller is responsible for incrementing the reference count on the interface pointer obtained by this method. The caller must call Release on this pointer when it is no longer needed.

Because a view object must always be contained or aggregated in a document object, this method will always succeed. Before returning, this method should call AddRef on the pointer stored in *ppunk.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IUnknown


IOleDocumentView::GetInPlaceSite

Retrieves the view site associated with this view object.

HRESULT GetInPlaceSite(
  IOleInPlaceSite **ppIPSite  //Address of output variable that
                              // receives the IOleInPlaceSite interface
                              // pointer
);

Parameters

ppIPSite
[out] Address of IOleInPlaceSite* pointer variable that receives the interface pointer to the document's view site.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The interface pointer was returned successfully.

Remarks

GetInPlaceSite obtains the most recent IOleInPlaceSite pointer passed by IOleDocumentView::SetInPlaceSite, or NULL if SetInPlaceSite has not yet been called. If this pointer is not NULL, this method will call AddRef on the pointer. The caller is responsible for releasing it. A document view must implement this method completely; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::SetInPlaceSite, IOleInPlaceSite


IOleDocumentView::GetRect

Obtains the rectangular coordinates of the view port in which the view is or will be activated.

HRESULT GetRect(
  LPRECT prcView  // Pointer to the current view coordinates
);

Parameters

prcView
[out] Pointer to a RECT structure to contain the coordinates of the current view port.

Return Value

S_OK
The view was successfully resized to the rectangle.
E_UNEXPECTED
This view has not yet seen a call to IOleDocumentView::SetRect or IOleDocumentView::SetRectComplex and therefore has no rectangle to return.

Remarks

For a single document interface (SDI) application, the view port is the client area of the frame window minus the space allocated for tool bars, status bar, and such. For a multiple document interface (MDI) window, the view port is the client area of the MDI document window minus any other frame-level user-interface elements.

The view port coordinates obtained by this method are those set in the most recent call to either IOleDocumentView::SetRect or IOleDocumentView::SetRectComplex.

A document view must implement this method completely; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::SetRect, IOleDocumentView::SetRectComplex


IOleDocumentView::Open

Asks a document view to display itself in a separate pop-up window with semantics equivalent to IOleObject::DoVerb(OLEIVERB_OPEN).

HRESULT Open(Void);

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The view successfully created its separate window.
E_NOTIMPL
The document object that owns this view does not support separate window activation.

Remarks

A user viewing a document object in a container application such as a browser or "binder" may want to see two or more views or documents at once. Because the browser displays only one view at a time, the container needs a way to ask the other views or documents to display themselves, as required, in separate windows. The IOleDocumentView::Open method provides that way.

Notes to Callers

A successful call to IOleDocumentView::Open should be followed by a call to IOleDocumentView::Show to hide the window or to show the window and bring it to the foreground. While the view is active in its separate window, a container can show or hide the window as many times as it may require.

Notes to Implementers

A document object indicates that it does not support activation in a separate window by setting the DOCMISC_CANTOPENEDIT status flag and returning E_NOTIMPL to containers that call this method.

Implementation consists mainly of the view object calling its own IOleInPlaceObject::InPlaceDeactivate method, which leaves the document object in a running state but without in-place activation. The document object's user interface is not visible until the container calls IOleDocumentView::Show (see "Notes to Callers" above).

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::Close, IOleDocumentView::Show, IOleDocumentView::UIActivate, IOleInPlaceObject::InPlaceDeactivate, IOleInPlaceSite, IOleInPlaceSite::OnInPlaceActivate


IOleDocumentView::SaveViewState

Saves the view's state into the specified stream.

HRESULT SaveViewState(
  IStream *pstm  // Pointer to stream containing the view's state
                 // data
);

Parameters

pstm
[in] Pointer to the stream in which the view is to save its state data.

Return Values

S_OK
The view successfully saved its state to the specified stream.
E_POINTER
The value in pstm is NULL.
E_NOTIMPL
This view has no meaningful state to save. This error should be rare because most views have at least some state information worth saving.

Remarks

The view's state includes properties such as the view type, zoom factor, and location of insertion point. The container typically calls this function before deactivating the view. The stream can then later be used to reinitialize a view of the same document to this saved state through ApplyViewState.

According to the rules governing IPersistStream, a view must write its CLSID as the first element in the stream. Any cross-platform file format compatibility issues that apply to the document's storage representation also apply to this context.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::ApplyViewState, IPersistStream


IOleDocumentView::SetInPlaceSite

Associates a container's document view site with a document's document view object.

HRESULT SetInPlaceSite(
  IOleInPlaceSite * pIPSite  // Pointer to document view site's
                             // IOleInPlaceSite interface
);

Parameters

pIPSite
[in] Pointer to the document view site's IOleInPlaceSite interface. Can be NULL, in which case the document view object loses all asociation with the container.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
A document view site was successfully associated (or disassociated if pIPSite is NULL) with a document view object.

Remarks

As part of activating a document object, a container must pass the object a pointer to the container's implementation of IOleInPlaceSite. This pointer designates the document view site that is to be associated with the view on which this method is called.

A container normally passes this pointer in response to a document's request to be activated. A document makes such a request by calling IOleDocumentSite::ActivateMe and passing the container a pointer to the view to be activated. The container, in turn, uses this pointer to call SetInPlaceSite.

Notes to Callers

If the container is requesting creation and activation of a new instance of a document object, rather than merely the activation of a loaded instance of a document object, the view site is passed in the pIPSite argument of IOleDocument::CreateView. Therefore, an explicit call to SetInPlaceSite is unnecessary.

Notes to Implementers

If this method is called on a view that is already associated with a view site, the view must do some housekeeping in preparation for activating itself in the new site. First, the view must deactivate itself in the current site and release its pointer to that site. Next, if the new IOleInPlaceSite pointer is not NULL, the view should both save the pointer and call AddRef on it. The view should then wait for the container to tell it when to activate itself in the new view site.

A document view must implement this method completely; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::GetInPlaceSite


IOleDocumentView::SetRect

Sets the rectangular coordinates of the view port in which a view is to be activated or resets the coordinates of the view port in which a view is currently activated.

[input_sync] HRESULT SetRect(
  LPRECT prcView  // Pointer to the coordinates of the view port
);

Parameters

prcView
[in] Pointer to a RECT structure containing the coordinates of the view port.

Return Value

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The view was successfully resized.

Remarks

For a single document interface (SDI) application, the view port is the client area of the frame window minus the space allocated for tool bars, status bar, and such. For a multiple document interface (MDI) window, the view port is the client area of the MDI document window minus any other frame-level user-interface elements.

Notes to Callers

Calling IOleDocumentView::SetRect or SetRectComplex is part of the normal activation sequece for document objects, usually following a call to IOleDocumentView::UIActivate and preceding a call to IOleDocumentView::Show.

Whenever the window used to display a document object is resized, the container should call SetRect (or SetRectComplex) to tell the document view object to resize itself to the new window dimensions.

Notes to Implementers

The coordinates of the view port are within the coordinates of the view window, which is obtained through IOleInPlaceSite::GetWindow. The view must resize itself to fit the new coordinates passed in *prcView.

This method is defined with the [input_sync] attribute, which means that the view object cannot yield or make another, non input_sync RPC call while executing this method.

A document view must implement this method completely; E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IOleDocumentView::SetRectComplex

Sets the rectangular coordinates of the view port, horizontal and vertical scroll bars, and the size box.

[input_sync] HRESULT SetRectComplex(
  LPRECT prcView,    // Pointer to coordinates of view port
  LPRECT prcHScroll, // Pointer to coordinates of horizontal scroll
                     // bar
  LPRECT prcVScroll, // Pointer to coordinates of vertical scroll bar
  LPRECT prcSizeBox  // Pointer to coordinates of size box
);

Parameters

prcView
[in] Pointer to a RECT structure containing the coordinates of the view port.
prcHScroll
[in] Pointer to a RECT structure containing the coordinates of the horizontal scroll bar.
prcVScroll
[in] Pointer to a RECT structure containing the coordinates of the vertical scroll bar.
prcSizeBox
[in] Pointer to a RECT structure containing the coordinates of the size box.

Return Value

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The view was successfully resized to the rectangle.
E_NOTIMPL
The document object that owns this view does not support complex rectangles.

Remarks

View frames that support a workbook metaphor, in which a single document comprises multiple sheets or pages, typically call this method to set the coordinates to be used in common by all the sheets or pages.

Notes to Callers

Calling IOleDocumentView::SetRectComplex is part of the normal activation sequence for document objects that support complex rectangles, usually following a call to IOleDocumentView::UIActivate and preceding a call to IOleDocumentView::Show.

Whenever the window used to display a document object is resized, the container should call SetRectComplex or IOleDocumentView::SetRect to tell the view object to resize itself to the new window dimensions.

Notes to Implementers

Document objects that support complex rectangles mark themselves with DOCMISC_SUPPORTCOMPLEXRECTANGLES, as described in DOCMISC and IOleDocument::GetDocMiscStatus . Document objects that do not support this method can return E_NOTIMPL.

Upon receiving a call to this method, a view should resize itself to fit the coordinates specified in prcView and fit its scrollbars and size box to the areas described in prcHScroll, prcVScroll, and prcSizeBox.

This method is defined with the [input_sync] attribute, which means that the implementing object cannot yield or make another, non input_sync RPC call while executing this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

DOCMISC, IOleDocument::GetDocMiscStatus


IOleDocumentView::Show

Requests a view to show or hide itself.

HRESULT Show(
  BOOL fShow  // Boolean value specifying whether to activate or
              // deactivate the view
);

Parameters

fShow
[in] If TRUE, the view is to show itself. If FALSE, the view is to hide itself.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The view was successfully shown or hidden.

Remarks

Calling IOleDocumentView::Show is the last step in the activation sequence, because before showing itself a document object must know exactly what space it occupies and have all its tools available.

Notes to Callers

A call to this method for the purpose of activating a view should follow calls to IOleDocumentView::SetInPlaceSite, IOleDocumentView::UIActivate, and IOleDocumentView::SetRect (or SetRectComplex).

Notes to Implementers

Implementations of this method should embody the following pseudocode:

if (fShow)
    {
    In-place activate the view but do not UI activate it.
    Show the view window.
    }
else
    {
    Call IOleDocumentView::UIActivate(FALSE) on this view
    Hide the view window
    }

All views of a document object must at least support in-place activation; E_NOTIMPL is not an acceptable value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::SetInPlaceSite, IOleDocumentView::SetRect, IOleDocumentView::SetRectComplex, IOleDocumentView::UIActivate


IOleDocumentView::UIActivate

Activates or deactivates a document view's user interface elements, such as menus, toolbars, and accelerators.

HRESULT UIActivate(
  BOOL fUIActivate  // Boolean value specifying whether to activate
                    // or deactivate view
);

Parameters

fUIActivate
[in] If TRUE, the view is to activate its user interface. If FALSE, the view is to deactivate its user interface.

Return Value

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The view's user interface was successfully activated or deactivated.

Remarks

Notes to Callers

Calling this method before calling IOleDocumentView::SetInPlaceSite returns E_UNEXPECTED, because the view must be associated with a view site before it can activate itself.

When IOleDocumentView::UIActivate is called as part of the activation sequence, the call should precede a call to IOleDocumentView::SetRect or SetRectComplex, because otherwise the view dimensions would not account for toolbar space.

To deactivate a view, the container should call IOleDocumentView::Show(FALSE), followed by IOleDocumentView::UIActivate(FALSE).

Notes to Implementers

Implementations of this method should embody the following pseudo-code:

if (fActivate)
    {
    UI activate the view (do menu merging, show frame level tools, process accelerators)
    Take focus, and bring the view window forward.
    }
else
    call IOleInPlaceObject::UIDeactivate on this view

In addition, the view can and should participate in extended Help menu merging.

All views of a document object must support in-place activation. E_NOTIMPL is not an acceptable return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.

See Also

IOleDocumentView::SetInPlaceSite, IOleDocumentView::SetRect, IOleDocumentView::SetRectComplex, IOleDocumentView::Show, IOleInPlaceObject::UIDeactivate


IOleInPlaceActiveObject

The IOleInPlaceActiveObject interface provides a direct channel of communication between an in-place object and the associated application's outer-most frame window and the document window within the application that contains the embedded object. The communication involves the translation of messages, the state of the frame window (activated or deactivated), and the state of the document window (activated or deactivated). Also, it informs the object when it needs to resize its borders, and manages modeless dialog boxes.

When to Implement

This interface is implemented by object applications in order to provide support for their objects while they are active in-place.

When to Use

These methods are used by the in-place object's top-level container to manipulate objects while they are active.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceActiveObject Methods | Description
TranslateAccelerator | Translates messages.
OnFrameWindowActivate | State of container's top-level frame.
OnDocWindowActivate | State of container document window.
ResizeBorder | Alert object of need to resize border space.
EnableModeless | Enable or disable modeless dialog boxes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleWindow


IOleInPlaceActiveObject::EnableModeless

Enables or disables modeless dialog boxes when the container creates or destroys a modal dialog box.

HRESULT EnableModeless(
  BOOL fEnable  //Enable or disable modeless dialog box windows
);

Parameter

fEnable
[in] TRUE to enable modeless dialog box windows; FALSE to disable them.

Return Value

S_OK
The method completed successfully.

Notes to Callers

IOleInPlaceActiveObject::EnableModeless is called by the top-level container to enable and disable modeless dialog boxes that the object displays. For the container to display a modal dialog box, it must first call IOleInPlaceActiveObject::EnableModeless, specifying FALSE to disable the object's modeless dialog box windows. When the container is through displaying its modal dialog box, it calls IOleInPlaceActiveObject::EnableModeless, specifying TRUE to reenable the object's modeless dialog boxes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceFrame::EnableModeless


IOleInPlaceActiveObject::OnDocWindowActivate

Notifies the active in-place object when the container's document window is activated or deactivated.

HRESULT OnDocWindowActivate(
  BOOL fActivate  //State of MDI child document window
);

Parameter

fActivate
[in] State of the MDI child document window. It is TRUE if the window is in the act of activating; FALSE if it is in the act of deactivating.

Return Value

S_OK
The method completed successfully.

Notes to Callers

Call IOleInPlaceActiveObject::OnDocWindowActivate when the MDI child document window is activated or deactivated and the object is currently the active object for the document.

Notes to Implementers

You should include code in this method that installs frame-level tools during object activation. These tools include the shared composite menu and/or optional toolbars and frame adornments. You should then take focus. When deactivating, the object should remove the frame-level tools. Note that if you do not call IOleInPlaceUIWindow::SetBorderSpace with pborderwidths set to NULL, you can avoid having to renegotiate border space.

Note While executing IOleInPlaceActiveObject::OnDocWindowActivate, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within OnDocWindowActivate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

PeekMessage, GetMessage


IOleInPlaceActiveObject::OnFrameWindowActivate

Notifies the object when the container's top-level frame window is activated or deactivated.

HRESULT OnFrameWindowActivate(
  BOOL fActivate  //State of container's top-level window
);

Parameter

fActivate
[in] State of the container's top-level frame window. TRUE if the window is activating; FALSE if it is deactivating.

Return Value

S_OK
The method notified the object successfully.

Notes to Callers

The container must call IOleInPlaceActiveObject::OnFrameWindowActivate when the container's top-level frame window is either being activated or deactivated and the object is the current, active object for the frame.

Note While executing IOleInPlaceActiveObject::OnFrameWindowActivate, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within OnFrameWindowActivate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

PeekMessage, GetMessage


IOleInPlaceActiveObject::ResizeBorder

Alerts the object that it needs to resize its border space.

HRESULT ResizeBorder(
  LPCRECT prcBorder,              //Pointer to new outer rectangle
                                  // for border space
  IOleInPlaceUIWindow pUIWindow,  //Pointer to frame or document
                                  // window border change
  BOOL fFrameWindow               //Indicates whether frame window
                                  // object calls ResizeBorder
);

Parameters

prcBorder
[in] Pointer to a RECT structure containing the new outer rectangle within which the object can request border space for its tools.
pUIWindow
[in] Pointer to the frame or document window object whose border has changed.
fFrameWindow
[in] TRUE if the frame window object is calling ResizeBorder; otherwise, FALSE.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The method alerted the object successfully.

Notes to Callers

IOleInPlaceActiveObject::ResizeBorder is called by the top-level container's document or frame window object when the border space allocated to the object should change. Because the active in-place object is not informed about which window has changed (the frame- or document-level window), IOleInPlaceActiveObject::ResizeBorder must be passed the pointer to the window's IOleInPlaceUIWindow interface.

Notes to Implementers

In most cases, resizing only requires that you grow, shrink, or scale your object's frame adornments. However, for more complicated adornments, you may be required to renegotiate for border space with calls to IOleInPlaceUIWindow::RequestBorderSpace and IOleInPlaceUIWindow::SetBorderSpace.

Note While executing IOleInPlaceActiveObject::ResizeBorder, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within ResizeBorder.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceUIWindow::GetBorder, PeekMessage, GetMessage


IOleInPlaceActiveObject::TranslateAccelerator

Processes menu accelerator-key messages from the container's message queue. This method should only be used for objects created by a DLL object application.

HRESULT TranslateAccelerator(
  LPMSG lpmsg  //Pointer to message that may need translating
);

Parameter

lpmsg
[in] Pointer to the message that might need to be translated.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The message was translated successfully.
S_FALSE
The message was not translated.

Notes to Callers

Active in-place objects must always be given the first chance at translating accelerator keystrokes. You can provide this opportunity by calling IOleInPlaceActiveObject::TranslateAccelerator from your container's message loop before doing any other translation. You should apply your own translation only when this method returns S_FALSE.

If you call IOleInPlaceActiveObject::TranslateAccelerator for an object that is not created by a DLL object application, the default object handler returns S_FALSE.

Notes to Implementers

An object created by an EXE object application gets keystrokes from its own message pump, so the container does not get those messages.

If you need to implement this method, you can do so by simply wrapping the call to the Window's TranslateAccelerator function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OleTranslateAccelerator, TranslateAccelerator


IOleInPlaceFrame

The IOleInPlaceFrame interface controls the container's top-level frame window. This control involves allowing the container to insert its menu group into the composite menu, install the composite menu into the appropriate window frame, and remove the container's menu elements from the composite menu. It sets and displays status text relevant to the in-place object. It also enables or disables the frame's modeless dialog boxes, and translates accelerator keystrokes intended for the container's frame.

When to Implement

You will need to implement this interface if you are writing a container application that will be participating in in-place activation.

When to Use

This interface is used by object applications to control the display and placement of composite menus, keystroke accelerator translation, context-sensitive help mode, and modeless dialog boxes.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceUIWindow Methods | Description
GetBorder | Translates messages.
RequestBorderSpace | State of container's top-level frame.
SetBorderSpace | State of container document window.
SetActiveObject | Alert object of need to resize border space.
IOleInPlaceFrame Methods | Description
InsertMenus | Allows container to insert menus.
SetMenu | Adds a composite menu to window frame.
RemoveMenus | Removes a container's menu elements.
SetStatusText | Sets and displays status text about.
EnableModeless | Enables or disables modeless dialog boxes.
TranslateAccelerator | Translates keystrokes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleWindow, IOleInPlaceUIWindow


IOleInPlaceFrame::EnableModeless

Enables or disables a frame's modeless dialog boxes.

HRESULT EnableModeless(
  BOOL fEnable  //Enable or disable modeless dialog box windows
);

Parameter

fEnable
[in] Specifies whether the modeless dialog box windows are to be enabled by specifying TRUE or disabled by specifying FALSE.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The dialog box was either enabled or disabled successfully, depending on the value for fEnable.

Notes to Callers

The active in-place object calls IOleInPlaceFrame::EnableModeless to enable or disable modeless dialog boxes that the container might be displaying. To display a modal dialog box, the object first calls IOleInPlaceFrame::EnableModeless, specifying FALSE to disable the container's modeless dialog box windows. After completion, the object calls IOleInPlaceFrame::EnableModeless, specifying TRUE to reenable them.

Notes to Implementers

You should track the value of EnableModeless and check it before displaying a dialog box.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceActiveObject::EnableModeless


IOleInPlaceFrame::InsertMenus

Allows the container to insert its menu groups into the composite menu to be used during the in-place session.

HRESULT InsertMenus(
  HMENU hmenuShared,                 //Handle to empty menu
  LPOLEMENUGROUPWIDTHS lpMenuWidths  //Pointer to array
);

Parameters

hmenuShared
[in] Handle to an empty menu.
lpMenuWidths
[in, out] Pointer to an OLEMENUGROUPWIDTHS array of six LONG values. The container fills in elements 0, 2, and 4 to reflect the number of menu elements it provided in the File, View, and Window menu groups.

Return Values

S_OK

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The menu groups were inserted successfully.

Notes to Callers

This method is called by object applications when they are first being activated. They call it in order to insert their menus into the frame-level user interface.

The object application asks the container to add its menus to the menu specified in hmenuShared and to set the group counts in the OLEMENUGROUPWIDTHS array pointed to by lpMenuWidths. The object application then adds its own menus and counts. Objects can call IOleInPlaceFrame::InsertMenus as many times as necessary to build up the composite menus. The container should use the initial menu handle associated with the composite menu for all menu items in the drop-down menus.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceFrame::RemoveMenus

Gives the container a chance to remove its menu elements from the in-place composite menu.

HRESULT RemoveMenus(
  HMENU hmenuShared  //Handle to in-place composite menu
);

Parameter

hmenuShared
[in] Handle to the in-place composite menu that was constructed by calls to IOleInPlaceFrame::InsertMenus and the Windows InsertMenu function.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTEDs, as well as the following:

S_OK
The method completed successfully.

Remarks

The object should always give the container a chance to remove its menu elements from the composite menu before deactivating the shared user interface.

Notes to Callers

Called by the object application while it is being UI-deactivated in order to remove its menus.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceFrame::SetMenu, InsertMenu


IOleInPlaceFrame::SetMenu

Installs the composite menu in the window frame containing the object being activated in place.

HRESULT SetMenu(
  HMENU hmenuShared,     //Handle to composite menu
  HOLEMENU holemenu,     //Handle to menu descriptor
  HWND hwndActiveObject  //Handle to object's window
);

Parameters

hmenuShared
[in] Handle to the composite menu constructed by calls to IOleInPlaceFrame::InsertMenus and the Windows InsertMenu function.
holemenu
[in] Handle to the menu descriptor returned by the OleCreateMenuDescriptor function.
hwndActiveObject
[in] Handle to a window owned by the object and to which menu messages, commands, and accelerators are to be sent.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Notes to Callers

The object calls IOleInPlaceFrame::SetMenu to ask the container to install the composite menu structure set up by calls to IOleInPlaceFrame::InsertMenus.

Notes to Implementers

An SDI container's implementation of this method should call the Windows SetMenu function. An MDI container should send a WM_MDISETMENU message, using hmenuShared as the menu to install. The container should call OleSetMenuDescriptor to install the OLE dispatching code.

When deactivating, the container must call IOleInPlaceFrame::SetMenu, specifying NULL to remove the shared menu. This is done to help minimize window repaints. The container should also call OleSetMenuDescriptor, specifying NULL to unhook the dispatching code. Finally, the object application calls OleDestroyMenuDescriptor to free the data structure.

Note While executing IOleInPlaceFrame::SetMenu, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within SetMenu.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceFrame::InsertMenus, OleSetMenuDescriptor, OleDestroyMenuDescriptor, PeekMessage, GetMessage


IOleInPlaceFrame::SetStatusText

Sets and displays status text about the in-place object in the container's frame window status line.

HRESULT SetStatusText(
  LPCOLESTR pszStatusText  //Pointer to message to display
);

Parameter

pszStatusText
[in] Pointer to a null-terminated character string containing the message to display.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_UNEXPECTED, as well as the following:

S_OK
The text was displayed.
S_TRUNCATED
Some text was displayed but the message was too long and was truncated.

Notes to Callers

You should call SetStatusText when you need to ask the container to display object text in its frame's status line, if it has one. Because the container's frame window owns the status line, calling IOleInPlaceFrame::SetStatusText is the only way an object can display status information in the container's frame window. If the container refuses the object's request, the object application can, however, negotiate for border space to display its own status window.

Note When switching between menus owned by the container and the in-place active object, the status bar text is not reflected properly if the object does not call the container's IOleInPlaceFrame::SetStatusText method. For example, if, during an in-place session, the user were to select the File menu, the status bar would reflect the action that would occur if the user selected this menu. If the user then selects the Edit menu (which is owned by the in-place object), the status bar text would not change unless the IOleInPlaceFrame::SetStatusText happened to be called. This is because there is no way for the container to recognize that one of the object's menus has been made active because all the messages that the container would trap are now going to the object.

Notes to Implementers

To avoid potential problems, all objects being activated in place should process the WM_MENUSELECT message and call IOleInPlaceFrame::SetStatusText — even if the object does not usually provide status information (in which case the object can just pass a NULL string for the requested status text).

Note While executing IOleInPlaceFrame::SetStatusText, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within GetBorder.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

PeekMessage, GetMessage


IOleInPlaceFrame::TranslateAccelerator

Translates accelerator keystrokes intended for the container's frame while an object is active in place.

HRESULT TranslateAccelerator(
  LPMSG lpmsg,  //Pointer to structure
  WORD wID      //Command identifier value
);

Parameters

lpmsg
[in] Pointer to the MSG structure containing the keystroke message.
wID
[in] Command identifier value corresponding to the keystroke in the container-provided accelerator table. Containers should use this value instead of translating again.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The keystroke was used.
S_FALSE
The keystroke was not used.

Notes to Callers

The IOleInPlaceFrame::TranslateAccelerator method is called indirectly by OleTranslateAccelerator when a keystroke accelerator intended for the container (frame) is received.

Notes to Implementers

The container application should perform its usual accelerator processing, or use wID directly, and then return, indicating whether the keystroke accelerator was processed. If the container is an MDI application and the Windows TranslateAccelerator call fails, the container can call the Windows TranslateMDISysAccel function, just as it does for its usual message processing.

In-place objects should be given first chance at translating accelerator messages. However, because objects implemented by DLL object applications do not have their own message pump, they receive their messages from the container's message queue. To ensure that the object has first chance at translating messages, a container should always call IOleInPlaceActiveObject::TranslateAccelerator before doing its own accelerator translation. Conversely, an executable object application should call OleTranslateAccelerator after calling TranslateAccelerator, calling TranslateMessage and DispatchMessage only if both translation functions fail.

Note You should define accelerator tables for containers so they will work properly with object applications that do their own accelerator keystroke translations. Tables should be defined as follows:

"char", wID, VIRTKEY, CONTROL

This is the most common way to describe keyboard accelerators. Failure to do so can result in keystrokes being lost or sent to the wrong object during an in-place session.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OleTranslateAccelerator, IOleInPlaceActiveObject::TranslateAccelerator, TranslateAccelerator, TranslateMessage, DispatchMessage, TranslateMDISysAccel


IOleInPlaceObject

The IOleInPlaceObject interface manages the activation and deactivation of in-place objects, and determines how much of the in-place object should be visible.

You can obtain a pointer to IOleInPlaceObject by calling QueryInterface on IOleObject.

When to Implement

You must implement this interface if you are writing an object application that will participate in in-place activation.

When to Use

Used by an object's immediate container to activate or deactivate the object.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context sensitive help.
IOleInPlaceObject Methods | Description
InPlaceDeactivate | Deactivate active in-place object.
UIDeactivate | Deactivate and remove UI of active object.
SetObjectRects | Portion of in-place object to be visible.
ReactivateAndUndo | Reactivate previously deactivated object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject, IOleWindow


IOleInPlaceObject::InPlaceDeactivate

Deactivates an active in-place object and discards the object's undo state.

HRESULT InPlaceDeactivate();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The object was successfully deactivated.

Notes to Callers

This method is called by an active object's immediate container to deactivate the active object and discard its undo state.

Notes to Implementers

On return from IOleInPlaceObject::InPlaceDeactivate, the object discards its undo state. The object application should not shut down immediately after this call. Instead, it should wait for an explicit call to IOleObject::Close or for the object's reference count to reach zero.

Before deactivating, the object application should give the container a chance to put its user interface back on the frame window by calling IOleInPlaceSite::OnUIDeactivate.

If the in-place user interface is still visible during the call to InPlaceDeactivate, the object application should call its own IOleInPlaceObject::UIDeactivate method to hide the user interface. The in-place user interface can be optionally destroyed during calls to IOleInPlaceObject::UIDeactivate and IOleInPlaceObject::InPlaceDeactivate. But if the user interface has not already been destroyed when the container calls IOleObject::Close, then it must be destroyed during the call to IOleObject::Close.

During the call to IOleObject::Close, the object should check to see whether it is still active in place. If so, it should call InPlaceDeactivate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceSite::OnInPlaceDeactivate, IOleInPlaceSite::OnUIDeactivate, IOleObject::Close


IOleInPlaceObject::ReactivateAndUndo

Reactivates a previously deactivated object, undoing the last state of the object.

HRESULT ReactivateAndUndo();

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The object was successfully reactivated.
E_NOTUNDOABLE
Called when the Undo state is not available.

Remarks

If the user chooses the Undo command before the Undo state of the object is lost, the object's immediate container calls IOleInPlaceObject::ReactivateAndUndo to activate the user interface, carry out the Undo operation, and return the object to the active state.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceObject::SetObjectRects

Indicates how much of the in-place object is visible.

HRESULT SetObjectRects(
  LPCRECT lprcPosRect,  //Pointer to the position of the in-place
                        // object
  LPCRECT lprcClipRect  //Pointer to the outer rectangle containing
                        // the in-place object's position rectangle
);

Parameters

lprcPosRect
[in] Pointer to the rectangle containing the position of the in-place object using the client coordinates of its parent window.
lprcClipRect
[in] Pointer to the outer rectangle containing the in-place object's position rectangle (PosRect). This rectangle is relative to the client area of the object's parent window.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The operation successfully indicated the rectangle.

Remarks

It is possible for lprcClipRect to change without lprcPosRect changing.

The size of an in-place object's rectangle is always calculated in pixels. This is different from other OLE object's visualizations, which are in HIMETRIC.

Note While executing IOleInPlaceObject::SetObjectRects, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within SetObjectRects.

Notes to Callers

The container should call IOleInPlaceObject::SetObjectRects whenever the window position of the in-place object and/or the visible part of the in-place object changes.

Notes to Implementers

The object must size its in-place window to match the intersection of lprcPosRect and lprcClipRect. The object must also draw its contents into the object's in-place window so that proper clipping takes place.

The object should compare its width and height with those provided by its container (conveyed through lprcPosRect). If the comparison does not result in a match, the container is applying scaling to the object. The object must then decide whether it should continue the in-place editing in the scale/zoom mode or deactivate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceSite::OnPosRectChange, PeekMessage, GetMessage


IOleInPlaceObject::UIDeactivate

Deactivates and removes the user interface that supports in-place activation.

HRESULT UIDeactivate();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The in-place UI was deactivated and removed.

Notes to Callers

This method is called by the object's immediate container when, for example, the user has clicked in the client area outside the object.

If the container has called IOleInPlaceObject::UIDeactivate, it should later call IOleInPlaceObject::InPlaceDeactivate to properly clean up resources. The container can assume that stopping or releasing the object cleans up resources if necessary. The object must be prepared to do so if IOleInPlaceObject::InPlaceDeactivate has not been called. but either IOleInPlaceObject::UIDeactivate or IOleObject::Close has been called.

Notes to Implementers

Resources such as menus and windows can be either cleaned up or kept in a hidden state until your object is completely deactivated by calls to either IOleInPlaceObject::InPlaceDeactivate or IOleObject::Close. The object application must call IOleInPlaceSite::OnUIDeactivate before doing anything with the composite menus so that the container can first be detached from the frame window. On deactivating the in-place object's user interface, the object is left in a ready state so it can be quickly reactivated. The object stays in this state until the undo state of the document changes. The container should then call IOleInPlaceObject::InPlaceDeactivate to tell the object to discard its undo state.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::InPlaceDeactivate, IOleInPlaceSite::OnUIDeactivate, IOleInPlaceObject::ReactivateAndUndo, IOleObject::Close


IOleInPlaceObjectWindowless

The IOleInPlaceObjectWindowless interface enables a windowless object to process window messages and participate in drag and drop operations. It is derived from and extends the IOleInPlaceObject interface.

A small object, such as a control, does not need a window of its own. Instead, it can rely on its container to dispatch window messages and help the object to participate in drag and drop operations. The container must implement the IOleInPlaceSiteWindowless interface. Otherwise, the object must act as a normal compound document object and create a window when it is activated.

When to Implement

Implement this interface on an object that can be in place activated without a window. This interface is derived from IOleInPlaceObject which, in turn, is derived from IOleWindow.

When to Use

A container calls the methods in this interface to dispatch window messages to an in-place-active windowless object and to assist the object in participating in drag and drop operations. The container must implement a site object with the IOleInPlaceSiteWindowless interface to support these activities. See the IOleInPlaceSiteWindowless interface for more information on operations involving windowless objects, such as drawing, drag and drop, and processing window messages.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context sensitive help.
IOleInPlaceObject Methods | Description
InPlaceDeactivate | Deactivate active in-place object.
UIDeactivate | Deactivate and remove UI of active object.
SetObjectRects | Portion of in-place object to be visible.
ReactivateAndUndo | Reactivate previously deactivated object.
IOleInPlaceObjectWindowless Methods | Description
OnWindowMessage | Dispatches a message from the container to a windowless object.
GetDropTarget | Supplies the IDropTarget interface for a windowless object that supports drag and drop.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless


IOleInPlaceObjectWindowless::GetDropTarget

Supplies the IDropTarget interface for an in-place active, windowless object that supports drag and drop.

HRESULT GetDropTarget(
  IDropTarget** ppDropTarget   //Address of output variable that
                               // receives the IDropTarget interface
                               // pointer
);

Parameters

ppDropTarget
[out] Address of IDropTarget* pointer variable that receives the interface pointer to the windowless object.

Return Values

S_OK
The IDropTarget interface was successfully returned.
E_NOTIMPL
The windowless object does not support drag and drop.

Remarks

A windowed object registers its IDropTarget interface by calling the RegisterDragDrop function and supplying its window handle as a parameter. Registering its IDropTarget interface enables the object to participate in drag and drop operations. Because it does not have a window when active, a windowless object cannot register its IDropTarget interface. Therefore, it cannot directly participate in drag and drop operations without support from its container.

The following events occur during a drag and drop operation involving windowless objects:

  • The container registers its own IDropTarget interface through the RegisterDragDrop function.
  • In the container's implementation of its own IDropTarget::DragEnter or IDropTarget::DragOver methods, the container detects whether the mouse pointer just entered an embedded object.
  • If the object is inactive, the container calls the object's IPointerInactive::GetActivationPolicy method. The object returns the POINTERINACTIVE_ACTIVATEONDRAG flag. The container then activates the object in place. If the object was already active, the container does not have to do this step.
  • Once the object is active, the container must then obtain the object's IDropTarget interface.
  • A windowless object that wishes to be a drop target still implements the IDropTarget interface, but does not register it and does not return it through calls to QueryInterface. Instead, the container can obtain this interface by calling the object's IOleInPlaceObjectWindowless::GetDropTarget method. The object returns a pointer to its own IDropTarget interface if it wants to participate in drag and drop operations. The container can cache this interface pointer for later use. For example, on subsequent calls to the container's IDropTarget::DragEnter or IDropTarget::DragLeave methods, the container can use the cached pointer instead of calling the object's GetDropTarget method again.
  • The container then calls the object's IDropTarget::DragEnter and passes the returned value for *pdwEffect from its own DragOver or DragEnter methods. From this point on, the container forwards all subsequent DragOver calls to the windowless object until the mouse leaves the object or a drop occurs on the object. If the mouse leaves the object, the container calls the object's IDropTarget::DragLeave and then releases the object's IDropTarget interface. If the drop occurs, the container forwards the IDropTarget::DragDrop call to the object.
  • Finally, the container in-place deactivates the object.

An object can return S_FALSE from its own IDropTarget::DragEnter to indicate that it does not accept any of the data formats in the data object. In that case, the container can decide to accept the data for itself and return an appropriate dwEffect from its own DragEnter or DragOver methods. Note that an object that returns S_FALSE from DragEnter should be prepared to receive subsequent calls to DragEnter without any DragLeave in between. Indeed, if the mouse is still over the same object during the next call to the container's DragOver, the container may decide to try and call DragEnter again on the object.

Note to Callers

A container can cache the pointer to the object's IDropTarget interface for later use.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IDropTarget, IPointerInactive::GetActivationPolicy, RegisterDragDrop


IOleInPlaceObjectWindowless::OnWindowMessage

Dispatches a message from a container to a windowless object that is in-place active.

HRESULT OnWindowMessage(
  UINT msg,           //Message Identifier as provided by Windows
  WPARAM wParam,      //Message parameter as provided by Windows
  LPARAM lParam,      //Message parameter as provided by Windows
  LRESULT* plResult   //Pointer to message result code
);

Parameters

msg
[in] Identifier for the window message provided to the container by Windows.
wParam
[in] Parameter for the window message provided to the container by Windows.
lParam
[in] Parameter for the window message provided to the container by Windows.
plResult
[out] Pointer to result code for the window message as defined in the Windows API.

Return Values

S_OK
The window message was successfully dispatched to the windowless object.
S_FALSE
The windowless object did not process the window message. The container should call the DefWindowProc for the message or process the message itself as described below.

Remarks

A container calls this method to send window messages to a windowless object that is in-place active. The container should dispatch messages according to the following guidelines:

  • For the following messages, the container should first dispatch the message to the windowless object that has captured the mouse, if any. Otherwise, the container should dispatch the message to the windowless object under the mouse cursor. If there is no such object, the container is free to process the message for itself.
    WM_MOUSEMOVE
    WM_SETCURSOR
    WM_XBUTTONDOWN
    WM_XBUTTONUP
    WM_XBUTTONDBLCLK
  • For the following messages, the container should dispatch the message to the windowless object with the keyboard focus.
    WM_KEYDOWN | WM_SYSKEYUP
    WM_KEYUP | WM_SYSDEADCHAR
    WM_CHAR | WM_IMExxx
    WM_DEADCHAR | WM_HELP
    WM_SYSKEYDOWN | WM_CANCELMODE
  • For all other messages, the container should process the message on its own.

The windowless object can return S_FALSE to this method to indicate that it did not process the message. Then, the container either performs the default behavior for the message by calling the Windows API function DefWindowProc, or processes the message itself.

The container must pass the following window messages to the default window procedure:

WM_MOUSEMOVE | WM_DEADCHAR
WM_XBUTTONxxx | WM_SYSKEYUP
WM_KEYDOWN | WM_SYSCHAR
WM_KEYUP | WM_SYSDEADCHAR
WM_CHAR | WM_IMExxx

The container must process the following window messages as its own:

WM_SETCURSOR
WM_CONTEXTMENU
WM_HELP

Note For WM_SETCURSOR, the container can either set the cursor itself or do nothing.

Objects can also use IOleInPlaceSiteWindowless::OnDefWindowMessage to explicitly invoke the default message processing from the container. In the case of the WM_SETCURSOR message, this allows an object to take action if the container does not set the cursor.

All coordinates passed to the object in wParam and lParam are specified as client coordinates of the containing window.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless::SetCapture, IOleInPlaceSiteWindowless::OnDefWindowMessage


IOleInPlaceSite

The IOleInPlaceSite interface manages interaction between the container and the object's in-place client site. Recall that the client site is the display site for embedded objects, and provides position and conceptual information about the object.

This interface provides methods that manage in-place objects. With IOleInPlaceSite, you can determine if an object can be activated and manage its activation and deactivation. You can notify the container when one of its objects is being activated and inform the container that a composite menu will replace the container's regular menu. It provides methods that make it possible for the in-place object to retrieve the window object hierarchy, and the position in the parent window where the object should place its in-place activation window. Finally, it determines how the container scrolls the object, manages the object undo state, and notifies the object when its borders have changed.

When to Implement

You must implement this interface if you are writing a container application that will participate in in-place activation.

When to Use

Use this interface to allow your object to control in-place activation from within the container.

The IOleInPlaceSite interface pointer is obtained by calling QueryInterface on the object's IOleClientSite interface.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceSite Methods | Description
CanInPlaceActivate | Determines if the container can activate the object in place.
OnInPlaceActivate | Notifies the container that one of its objects is being activated in place.
OnUIActivate | Notifies the container that the object is about to be activated in place, and that the main menu will be replaced by a composite menu.
GetWindowContext | Enables an in-place object to retrieve window interfaces that form at the window object hierarchy, and the position in the parent window to locate the object's in-place activation window.
Scroll | Specifies the number of pixels by which the container is to scroll the object.
OnUIDeactivate | Notifies the container to reinstall its user interface and take focus.
OnInPlaceDeactivate | Notifies the container that the object is no longer active in place.
DiscardUndoState | Instructs the container to discard its undo state.
DeactivateAndUndo | Deactivate the object and revert to undo state.
OnPosRectChange | Object's extents have changed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleWindow, IOleClientSite


IOleInPlaceSite::CanInPlaceActivate

Determines whether or not the container can activate the object in place.

HRESULT CanInPlaceActivate();

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The container allows in-place activation for this object.
S_FALSE
The container does not allow in-place activation for this object.

Remarks

Only objects being displayed as DVASPECT_CONTENT can be activated in place.

Notes to Callers

IOleInPlaceSite::CanInPlaceActivate is called by the client site's immediate child object when this object must activate in place. This function allows the container application to accept or refuse the activation request.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceSite::DeactivateAndUndo

Causes the container to end the in-place session, deactivate the object, and revert to its own saved undo state.

HRESULT IOleInPlaceSite::DeactivateAndUndo();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Notes to Callers

Called by the active object when the user invokes undo just after activating the object.

Notes to Implementers

Upon completion of this call, the container should call IOleInPlaceObject::UIDeactivate to remove the user interface for the object, activate itself, and undo.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceSite::DiscardUndoState

Tells the container that the object no longer has any undo state and that the container should not call IOleInPlaceObject::ReActivateAndUndo.

HRESULT IOleInPlaceSite::DiscardUndoState();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Remarks

If an object is activated in place and the object's associated object application maintains only one level of undo, there is no need to have more than one entry on the undo stack. That is, once a change has been made to the active object that invalidates its undo state saved by the container, there is no need to maintain this undo state in the container.

Notes to Callers

IOleInPlaceSite::DiscardUndoState is called by the active object while performing some action that would discard the undo state of the object. The in-place object calls this method to notify the container to discard the object's last saved undo state.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceSite::DeactivateAndUndo


IOleInPlaceSite::GetWindowContext

Enables the in-place object to retrieve the window interfaces that form the window object hierarchy, and the position in the parent window where the object's in-place activation window should be placed.

HRESULT GetWindowContext(
  IOleInPlaceFrame **ppFrame,
                       //Address of output variable that receives
                       // the IOleInPlaceFrame interface pointer
  IOleInPlaceUIWindow **ppDoc,
                       //Address of output variable that receives
                       // the IOleInPlaceUIWindow interface pointer
  LPRECT lprcPosRect,  //Points to position of in-place object
  LPRECT lprcClipRect, //Points to in-place object's position
                       // rectangle
  LPOLEINPLACEFRAMEINFO lpFrameInfo
                       //Points to structure
);

Parameters

ppFrame
[out] Address of IOleInPlaceFrame* pointer variable that receives the interface pointer to the frame. If an error occurs, the implementation must set *ppFrame to NULL.
ppDoc
[out] Address of IOleInPlaceUIWindow* pointer variable that receives the interface pointer to the document window. If the document window is the same as the frame window, *ppDoc is set to NULL. In this case, the object can only use *ppFrame or border negotiation. If an error is returned, the implementation must set *ppDoc to NULL.
lprcPosRect
[out] Pointer to the rectangle containing the position of the in-place object in the client coordinates of its parent window. If an error is returned, this parameter must be set to NULL.
lprcClipRect
[out] Pointer to the outer rectangle containing the in-place object's position rectangle (PosRect). This rectangle is relative to the client area of the object's parent window. If an error is returned, this parameter must be set to NULL.
lpFrameInfo
[out] Pointer to an OLEINPLACEFRAMEINFO structure the container is to fill in with appropriate data. If an error is returned, this parameter must be set to NULL.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Remarks

The OLEINPLACEFRAMEINFO structure provides data needed by OLE to dispatch keystroke accelerators to a container frame while an object is active in place.

When an object is activated, it calls GetWindowContext from its container. The container returns the handle to its in-place accelerator table through the OLEINPLACEFRAMEINFO structure. Before calling GetWindowContext, the object must provide the size of the OLEINPLACEFRAMEINFO structure by filling in the cb member, pointed to by lpFrameInfo.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceSite::OnInPlaceActivate

Notifies the container that one of its objects is being activated in place.

HRESULT OnInPlaceActivate();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The container allows the in-place activation.

Notes to Callers

IOleInPlaceSite::OnInPlaceActivate is called by the active embedded object when it is activated in-place for the first time. The container should note that the object is becoming active.

Notes to Implementers

A container that supports linking to embedded objects must properly manage the running of its in-place objects when they are UI-inactive and running in the hidden state. To reactivate the in-place object quickly, a container should not call IOleObject::Close until the container's IOleInPlaceSite::DeactivateAndUndo method is called. To safeguard against the object being left in an unstable state if a linking client updates silently, the container should call OleLockRunning to lock the object in the running state. This prevents the hidden in-place object from shutting down before it can be saved in its container.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleInPlaceSite::OnInPlaceDeactivate

Notifies the container that the object is no longer active in place.

HRESULT OnInPlaceDeactivate();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method successfully notified the container.

Notes to Callers

IOleInPlaceSite::OnInPlaceDeactivate is called by an in-place object when it is fully deactivated. This function notifies the container that the object has been deactivated, and it gives the container a chance to run code pertinent to the object's deactivation. In particular, IOleInPlaceSite::OnInPlaceDeactivate is called as a result of IOleInPlaceObject::InPlaceDeactivate being called. Calling IOleInPlaceSite::OnInPlaceDeactivate indicates that the object can no longer support Undo.

Notes to Implementers

If the container is holding pointers to the IOleInPlaceObject and IOleInPlaceActiveObject interface implementations, it should release them after the IOleInPlaceSite::OnInPlaceDeactivate call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::InPlaceDeactivate


IOleInPlaceSite::OnPosRectChange

Indicates the object's extents have changed.

HRESULT OnPosRectChange(
  LPCRECT lprcPosRect  //Pointer to rectangle containing the
                       // position of in-place object
);

Parameter

lprcPosRect
[in] Pointer to the rectangle containing the position of the in-place object in the client coordinates of its parent window.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Notes to Callers

The IOleInPlaceSite::OnPosRectChange method is called by the in-place object.

Notes to Implementers

When the in-place object calls IOleInPlaceSite::OnPosRectChange, the container must call IOleInPlaceObject::SetObjectRects to specify the new position of the in-place window and the ClipRect. Only then does the object resize its window.

In most cases, the object grows to the right and/or down. There could be cases where the object grows to the left and/or up, as conveyed through lprcPosRect. It is also possible to change the object's position without changing its size.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::SetObjectRects


IOleInPlaceSite::OnUIActivate

Notifies the container that the object is about to be activated in place and that the object is going to replace the container's main menu with an in-place composite menu.

HRESULT IOleInPlaceSite::OnUIActivate();

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The container allows the in-place activation.

Notes to Callers

The in-place object calls IOleInPlaceSite::OnUIActivate just before activating its user interface.

Notes to Implementers

The container should remove any user interface associated with its own activation. If the container is itself an embedded object, it should remove its document-level user interface.

If there is already an object active in place in the same document, the container should call IOleInPlaceObject::UIDeactivate before calling OnUIDeactivate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::UIDeactivate


IOleInPlaceSite::OnUIDeactivate

Notifies the container on deactivation that it should reinstall its user interface and take focus, and whether or not the object has an undoable state.

HRESULT OnUIDeactivate(
  BOOL fUndoable  //Specifies if object can undo changes
);

Parameter

fUndoable
[in] Specifies whether the object can undo changes. TRUE if the object can undo, FALSE if it cannot.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Remarks

The object indicates whether it can undo changes through the fUndoable flag. If the object can undo changes, the container can (by the user invoking the Edit Undo command) call the IOleInPlaceObject::ReactivateAndUndo method to undo the changes.

Notes to Callers

IOleInPlaceSite::OnUIDeactivate is called by the site's immediate child object when it is deactivating to notify the container that it should reinstall its own user interface components and take focus. The container should wait for the call to IOleInPlaceSite::OnUIDeactivate to complete before fully cleaning up and destroying any composite submenus.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::ReactivateAndUndo


IOleInPlaceSite::Scroll

Tells the container to scroll the view of the object by a specified number of pixels.

HRESULT Scroll(
  SIZE scrollExtent  //Number of pixels
);

Parameter

scrollExtent
[in] Number of pixels by which to scroll in the X and Y directions.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The method successfully executed the view scroll instruction.

Remarks

As a result of scrolling, the object's visible rectangle can change. If that happens, the container should give the new ClipRect to the object by calling IOleInPlaceObject::SetObjectRects. The intersection of the ClipRect and PosRect rectangles gives the new visible rectangle. See IOleInPlaceSite::GetWindowContext for a discussion of ClipRect and PosRect.

Notes to Callers

Called by an active, in-place object when it is asking the container to scroll.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject::SetObjectRects


IOleInPlaceSiteEx

The IOleInPlaceSiteEx interface provides an additional set of activation and deactivation notification methods that enable an object to avoid unnecessary flashing on the screen when the object is activated and deactivated.

When an object is activated, it does not know if its visual display is already correct. When the object is deactivated, the container does not know if the visual display is correct. To avoid a redraw and the associated screen flicker in both cases, the container can provide this extension to IOleInPlaceSite.

When to Implement

Implement this interface on the client site in a container application that supports flicker-free activation and deactivation of embedded, in-place active objects.

When to Use

An embedded object, such as a control, calls the methods in this interface to determine if it needs to redraw itself on activation and to notify the container if the object needs to be redrawn on deactivation. By avoiding the redraw when it is not needed, you can reduce the amount of flashing on the screen.

If the site object does not support IOleInPlaceSiteEx, the object must call methods in the IOleInPlaceSite interface instead. In this case, the object must redraw itself on activation and deactivation.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceSite Methods | Description
CanInPlaceActivate | Determines if the container can activate the object in place.
OnInPlaceActivate | Notifies the container that one of its objects is being activated in place.
OnUIActivate | Notifies the container that the object is about to be activated in place, and that the main menu will be replaced by a composite menu.
GetWindowContext | Enables an in-place object to retrieve window interfaces that form at the window object hierarchy, and the position in the parent window to locate the object's in-place activation window.
Scroll | Specifies the number of pixels by which the container is to scroll the object.
OnUIDeactivate | Notifies the container to reinstall its user interface and take focus.
OnInPlaceDeactivate | Notifies the container that the object is no longer active in place.
DiscardUndoState | Instructs the container to discard its undo state.
DeactivateAndUndo | Deactivate the object and revert to undo state.
OnPosRectChange | Object's extents have changed.
IOleInPlaceSiteEx Methods | Description
OnInPlaceActivateEx | Called by the embedded object to determine if it needs to redraw itself upon activation.
OnInPlaceDeactivateEx | Notifies the container of whether the object needs to be redrawn upon deactivation.
RequestUIActivate | Notifies the container that the object is about to enter the UI-active state.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSite


IOleInPlaceSiteEx::OnInPlaceActivateEx

Called by the embedded object to determine if it needs to redraw itself upon activation.

HRESULT OnInPlaceActivateEx(
  BOOL* pfNoRedraw,  //Pointer to current redraw status
  DWORD dwFlags      //Indicates whether the object is windowless
);

Parameters

pfNoRedraw
[out] Pointer to current redraw status. The status is TRUE if the object need not redraw itself upon activation; FALSE if the object needs to redraw upon activation. Windowless objects usually do not need the value returned by this parameter and may pass a NULL pointer to save the container the burden of computing this value.
dwFlags
[in] Indicates whether the object is activated as a windowless object. This parameter takes values from the ACTIVATEFLAGS enumeration. See IOleInPlaceSiteWindowless for more information on windowless objects.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The container allows the in-place activation.

Remarks

This method replaces IOleInPlaceSite::OnInPlaceActivate. If the older method is used, the object must always redraw itself on activation.

Windowless objects are required to use this method instead of IOleInPlaceSite::OnInPlaceActivate to notify the container of whether they are activating windowless or not.

Notes to Implementers

The container should carefully check the invalidation status of the object, its z-order, clipping and any other relevant parameters to determine the appropriate value to return in pfNoRedraw.

A container can cache the value of the ACTIVATEFLAGS enumeration instead of calling the GetWindow method in the IOleInPlaceObjectWindowless interface repeatedly.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

ACTIVATEFLAGS, IOleInPlaceSite::OnInPlaceActivate, IOleInPlaceObjectWindowless IOleInPlaceSiteWindowless


IOleInPlaceSiteEx::OnInPlaceDeactivateEx

Notifies the container if the object needs to be redrawn upon deactivation.

HRESULT OnInPlaceDeactivateEx(
  BOOL fNoRedraw   //Indicates whether the object needs to be redrawn
);

Parameter

fNoRedraw
[in] If TRUE, the container need not redraw the object after completing the deactivation; if FALSE the object must be redrawn after deactivation.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The method successfully notified the container.

Remarks

This method replaces IOleInPlaceSite::OnInPlaceDeactivate. If the older method is used, the object must always be redrawn on deactivation.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSite::OnInPlaceDeactivate


IOleInPlaceSiteEx::RequestUIActivate

Notifies the container that the object is about to enter the UI-active state.

HRESULT RequestUIActivate(void);

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The object can continue the activation process and call IOleInPlaceSite::OnUIActivate.
S_FALSE
The object cannot enter the UI-active state. The object must call IOleInPlaceSite::OnUIDeactivate so the container can perform its the necessary processing to restore the focus.

Remarks

An object calls this method to determine if it can enter the UI-active state and to notify the container that it is about to make this transition. The container can return S_FALSE to deny this request, for example, if the end user has canceled the operation or if the currently active object will not relinquish its active state.

If the object does not call RequestUIActivate, the container handles data validation and fires Enter and Exit events from IOleInPlaceSite::OnUIActivate.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSite::OnUIActivate, IOleInPlaceSite::OnUIDeactivate


IOleInPlaceSiteWindowless

The IOleInPlaceSiteWindowless interface is derived from and extends the IOleInPlaceSiteEx interface. IOleInPlaceSiteWindowless works with IOleInPlaceObjectWindowless which is implemented on the windowless object. Together, these two interfaces provide services to a windowless object from its container allowing the windowless object to:

  • process window messages
  • participate in drag and drop operations
  • perform drawing operations

Having a window can place unnecessary burdens on small objects, such as controls. It prevents an object from being non-rectangular. It prevents windows from being transparent. It prevents the small instance size needed by many small controls.

A windowless object can enter the in-place active state without requiring a window or the resources associated with a window. Instead, the object's container provides the object with many of the services associated with having a window.

Windowless object model

Windowless objects are an extension of normal compound document objects. They follow the same in-place activation model and share the same definitions for the various OLE states, with the difference that they do not consume a window when they enter the in-place and UI active states. They are required to comply with the OLE compound document specification, including in-place and UI activation.

Windowless objects require special support from their container. In other words, the container has to be specifically written to support this new kind of object. However, windowless objects are backward compatible with down level containers. In such containers, they simply create a window when active and behave as a normal compound document object.

As with other compound document objects, windowless objects need to be in-place active to get mouse and keyboard messages. In fact, since an object needs to have the keyboard focus to receive keyboard messages, and having the keyboard focus implies being UI active for an object, only UI active objects will actually get keyboard messages. Non-active objects can still process keyboard mnemonics.

Since windowless objects do not have a window, they rely on their container to receive window messages for them. The container dispatches its own window messages to the appropriate embedded, windowless object through calls to IOleInPlaceObjectWindowless methods. Similarly, windowless objects can obtain services from their container such as capturing the mouse, setting the focus, getting a device context in which to paint, and so on. The control calls IOleInPlaceSiteWindowless methods. In addition, the container is responsible for drawing any border hatching as well as the grab handles for the control.

These two interfaces are derived from existing interfaces. By extending existing interfaces rather than creating new ones, no new VTable pointer is added to the object instance, helping to keep the instance size small.

Client and server negotiations with windowless objects

When a windowless object gets in-place activated, it should query its site for the IOleInPlaceSiteWindowless interface. If this interface is supported, the object calls IOleInPlaceSiteWindowless::CanWindowlessActivate to determine if it can proceed and in-place activate without a window.

If the container does not support IOleInPlaceSiteWindowless or if the IOleInPlaceSiteWindowless::CanWindowlessActivate method returns S_FALSE, the windowless object should behave like a normal compound document object and create a window.

The container can get the window handle for an embedded object by calling IOleWindow::GetWindow. This method should fail (return E_FAIL) for a windowless object. However, a container cannot be sure that the object is windowless by calling this method. The object may have a window but may not have created it yet. Many existing objects only create their window after calling the OnInPlaceActivate method on their site object.

Consequently, a windowless object must call the new IOleInPlaceSiteEx::OnInPlaceActivateEx method on its site object, instead of OnInPlaceActivate. The dwFlags parameter for this new method contains additional information in the ACTIVATEFLAGS enumeration. The ACTIVATE_WINDOWLESS enumeration value indicates that the object is activated without a window. Containers can cache this value instead of calling the GetWindow method on the IOleWindow interface repeatedly.

Message dispatching

Windowless objects rely on their containers to dispatch window messages to them, capture the mouse, and get the keyboard focus. The container calls IOleInPlaceObject::OnWindowMessage to dispatch a window message to a windowless object. This method is similar to the SendMessage Windows API function except that it does not require an HWND parameter and it returns both an HRESULT and a LRESULT.

A windowless object must not call the DefWindowProc Windows API function directly. Instead, it calls IOleInPlaceSiteWindowless::OnDefWindowMessage to invoke the default action, for example with WM_SETCURSOR or WM_HELP that should be propagated back up to the container. Thus, the container has a chance to handle the message before the object processes it.

For mouse messages, the object calls IOleInPlaceSiteWindowless::Set Capture to obtain the mouse capture and IOleInPlaceSiteWindowless::SetFocus to get the keyboard focus.

A windowless object handles accelerators and mnemonics as follows:

Accelerators
The UI active object checks for its own accelerators in IOleInPlaceActiveObject::TranslateAccelerator. A windowless object does the same. However, a windowless object cannot send a WM_COMMAND message to itself, as a windowed object would do. Therefore, instead of translating the key to a command, a windowless object should simply process the key right away.

Except for that one difference, windowless objects should implement the IOleInPlaceActiveObject::TranslateAccelerator method as defined in the OLE specifications. In particular, a windowless object should pass the accelerator message up to its site object if it does not wish to handle it. The windowless object and returns S_OK if the message got translated and S_FALSE if not. In the case of a windowless object, the message is processed instead of translated. Non translated messages will come back to the object through the IOleInPlaceObjectWindowless::OnWindowMessage method.

Note that because the container's window gets all keyboard input, a UI active object should look for messages sent to that window to find those it needs to process in its IOleInPlaceActiveObject::TranslateAccelerator method. An object can get its container's window by calling IOleWindow::GetWindow.

Mnemonics
Control mnemonics are handled the same way whether the control is windowless or not. The container gets the control mnemonic table by calling IOleControl::GetControlInfo and then calls IOleControl::OnMnemonic when it receives a key combination that matches a control mnemonic.

Drag & drop onto windowless objects

Since a windowless object does not have a window when it is active, it cannot register its own IDropTarget interface with the RegisterDragDrop function. However, to participate in drag and drop operations, a windowless object still must implement this interface. The object supplies its container with a pointer to its IDropTarget interface through IOleInPlaceObjectWindowless::GetDropTarget instead of having the container call QueryInterface for it. See IOleInPlaceObjectWindowless::GetDropTarget for more information on drag and drop operations involving windowless objects.

In-place drawing for windowless objects

With windowed objects, the container is only responsible for drawing the object when it is inactive. Windowed objects have their own window when they are active and can draw themselves independently of their container.

A windowless object, however, needs services from its container to redraw itself even when it is active. The container must provide the object with information about its surrounding environment, such as the clipping region, the background, overlapping objects in front of the object being redrawn, and a device context in which to draw.

The IOleInPlaceSiteWindowless interface on the container's site object provides these services: drawing the object, obtaining and releasing the device context, invalidating the object's on-screen display, scrolling the object, or showing a caret when the object is active.

Note All methods of IOleInPlaceSiteWindowless take position information in client coordinates of the containing window, that is, the window in which the object is being drawn.

Drawing windowless objects
To maintain compatibility with windowed objects, the container still uses IViewObject::Draw to redraw the in-place active, windowless object. See IViewObject::Draw for information on how the method is used with windowless objects.
Obtaining and releasing a device context
To draw on its own when in-place active, a windowless object must call its site's IOleInPlaceSiteWindowless::GetDC method to get a device context in which to draw. Then, it draws into the device context and releases it by calling IOleInPlaceSiteWindowless::ReleaseDC.
Display Invalidation
In-place windowless objects may need to invalidate regions of their on-screen image. Even though the notification methods in IAdviseSinkEx can be used for that purpose, they are not ideal for in-place active objects because they take HIMETRIC coordinates. In order to simplify and speed up in-place drawing, the InvalidateRect and InvalidateRgn methods in the IOleInPlaceSiteWindowless interface provide the same functionality.

An object cannot call the Windows API functions InvalidateRect and InvalidateRgn directly on the window handle it gets from calling IOleInPlaceSiteWindowless::GetWindow on its site.

Scrolling
In-place active windowless objects may need to scroll a given rectangle of their on-screen image, for example, with a multi-line text control. Because of transparent and overlapping objects, the Windows API functions ScrollWindow and ScrollDC cannot be used. Instead, the IOleInPlaceSiteWindowless::ScrollRect method enables objects to perform scrolling.
Caret support
A windowless object cannot safely show a caret without first checking whether the caret is partially or totally hidden by overlapping objects. In order to make that possible, an object can submit a rectangle to its site object when it calls the site's IOleInPlaceSiteWindowless::AdjustRect method to get a specified rectangle adjusted (usually, reduced) to ensure the rectangle fits in the clipping region.

When to Implement

Implement this interface on the container's site object to support windowless objects.

When to Use

The windowless object calls the methods in this interface to process window messages, to participate in drag and drop operations, and to perform drawing operations.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceSite Methods | Description
CanInPlaceActivate | Determines if the container can activate the object in place.
OnInPlaceActivate | Notifies the container that one of its objects is being activated in place.
OnUIActivate | Notifies the container that the object is about to be activated in place, and that the main menu will be replaced by a composite menu.
GetWindowContext | Enables an in-place object to retrieve window interfaces that form at the window object hierarchy, and the position in the parent window to locate the object's in-place activation window.
Scroll | Specifies the number of pixels by which the container is to scroll the object.
OnUIDeactivate | Notifies the container to reinstall its user interface and take focus.
OnInPlaceDeactivate | Notifies the container that the object is no longer active in place.
DiscardUndoState | Instructs the container to discard its undo state.
DeactivateAndUndo | Deactivate the object and revert to undo state.
OnPosRectChange | Object's extents have changed.
IOleInPlaceSiteEx Methods | Description
OnInPlaceActivateEx | Called by the embedded object to determine if it needs to redraw itself upon activation.
OnInPlaceDeactivateEx | Notifies the container of whether the object needs to be redrawn upon deactivation.
RequestUIActivate | Notifies the container that the object is about to enter the UI-active state.
IOleInPlaceSiteWindowless Methods | Description
CanWindowlessActivate | Informs an object if its container can support it as a windowless object that can be in-place activated.
GetCapture | Called by an in-place active, windowless object to determine if it still has the mouse capture or not.
SetCapture | Enables an in-place active, windowless object to capture all mouse messages.
GetFocus | Called by an in-place active, windowless object to determine if it still has the keyboard focus or not.
SetFocus | Sets the keyboard focus for a UI-active, windowless object.
GetDC | Provides an object with a handle to a device context for a screen or compatible device from its container.
ReleaseDC | Releases the device context previously obtained by a call to IOleInPlaceSiteWindowless::GetDC.
InvalidateRect | Enables an object to invalidate a specified rectangle of its in-place image on the screen.
InvalidateRgn | Enables an object to invalidate a specified region of its in-place image on the screen.
ScrollRect | Enables an object to scroll an area within its in-place active image on the screen.
AdjustRect | Adjusts a specified rectangle if it is entirely or partially covered by overlapping, opaque objects.
OnDefWindowMessage | Invokes the default processing for all messages passed to an object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IAdviseSinkEx, IOleControl, IOleInPlaceActiveObject::TranslateAccelerator, IOleInPlaceObjectWindowless,


IOleInPlaceSiteWindowless::AdjustRect

Adjusts a specified rectangle if it is entirely or partially covered by overlapping, opaque objects.

HRESULT AdjustRect(
  LPRECT prc   //Rectangle to adjust
);

Parameters

prc
[in,out] Rectangle to adjust.

Return Values

S_OK
The rectangle was adjusted successfully. Note S_OK means that the rectangle was not completely covered.
S_FALSE
The rectangle was adjusted successfully. Note S_FALSE means that the rectangle was completely covered. Its width and height are now NULL.

Remarks

The main use of this method is to adjust the size of the caret. An object willing to create a caret should submit the caret rectangle to its site object by calling this method and using the adjusted rectangle returned from it for the caret. If the caret is entirely hidden, this method will return S_FALSE and the caret should not be shown at all in this case.

In a situation where objects are overlapping this method should return the largest rectangle that is fully visible.

This method can also be used to figure whether a point or a rectangular area is visible or hidden by overlapping objects.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleInPlaceSiteWindowless::CanWindowlessActivate

Informs an object if its container can support it as a windowless object that can be in-place activated.

HRESULT CanWindowlessActivate(void);

Return Values

S_OK
The object can activate in place without a window.

Remarks

If this method returns S_OK, the container can dispatch events to it using IOleInPlaceObjectWindowless.

If this method returns S_FALSE, the object should create a window and behave as a normal compound document object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceObjectWindowless


IOleInPlaceSiteWindowless::GetCapture

Called by an in-place active, windowless object to determine if it still has the mouse capture or not.

HRESULT GetCapture(void);

Return Values

S_OK
The object currently has the mouse capture.
S_FALSE
The object does not currently have the mouse capture.

Remarks

As an alternative to calling this method, the object can cache information about whether it has the mouse capture or not.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleInPlaceSiteWindowless::GetDC

Provides an object with a handle to a device context for a screen or compatible device from its container.

HRESULT GetDC(
  LPCRECT pRect,   //Pointer to rectangle
  DWORD grfFlags,  //OLEDCFLAGS value
  HDC* phDC        //Pointer to device context
);

Parameters

pRect
[in] Pointer to the rectangle that the object wants to redraw, in client coordinates of the containing window. If this parameter is NULL, the object's full extent is redrawn.
grfFlags
[in] A combination of values from the OLEDCFLAGS enumeration.
phDC
[out] Pointer to a returned device context.

Return Values

S_OK
A device context was successfully returned.
OLE_E_NESTEDPAINT
The container is already in the middle of a paint session. That is, this method has already been called, and the ReleaseDC method has not yet been called.

Remarks

A device context obtained by this method should be released by calling IOleInPlaceSiteWindowless::ReleaseDC.

Like other methods in this interface, rectangles are specified in client coordinates of the containing window. The container is expected to intersect this rectangle with the object's site rectangle and clip out everything outside the resulting rectangle. This prevents objects from inadvertently drawing where they are not supposed to.

Containers are also expected to map the device context origin so the object can draw in client coordinates of the containing window, usually the container's window. If the container is merely passing its window device context, this occurs automatically. If it is returning another device context, for example, an offscreen memory device context, then the viewport origin should be set appropriately.

Notes to Implementers

Depending whether it is returning an on-screen or off-screen device context and depending on how sophisticated it is, container can use one of the following algorithms:

  1. On-screen, one pass drawing

    In the GetDC method, the container should:

    • Get the window device context.
    • If OLEDC_PAINTBKGND is set, draw the DVASPECT_CONTENT aspect of every object behind the object requesting the device context.
    • Return the device context.

    In the ReleaseDC method, the container should:

    • Draw the DVASPECT_CONTENT of every overlapping object.
    • Release the device context.
  2. On-screen, two pass drawing

    In the GetDC method, the container should:

    • Get the window device context.
    • Clip out the opaque regions of any overlapping object. These regions do not need to be redrawn since they are already correct on the screen.
    • If OLEDC_PAINTBKGND is not set, return the device context.
    • Otherwise, clip out the opaque parts of the object requesting the device context and draw the opaque parts of every object behind it going front to back.
    • Draw the transparent aspects of every object behind going back to front, setting the clipping region appropriately each time.
    • Finally return the device context.

    In the ReleaseDC method, the container should:

    • Draw the transparent parts of every overlapping object.
    • Release the device context.
  3. Off-screen drawing

    In the GetDC method, the container should:

    • Create a screen compatible memory device context, containing a compatible bitmap of appropriate size.
    • Map the viewport origin of the device context to ensure that the calling object can draw using client area coordinates of the containing window.
    • If OLEDC_PAINTBKGND is set, draw the DVASPECT_CONTENT of every object behind the calling object.
    • Return the device context.

    In the ReleaseDC method, the container should:

    • Draw the DVASPECT_CONTENT aspect of every overlapping object.
    • Copy the off-screen bitmap to the screen at the location the calling object originally requested in GetDC.
    • Delete and release the memory device context.

When this method returns, the clipping region in the device context should be set so that the object can't paint in any area it is not supposed to. If the object is not opaque, the background should have been painted. If the device context is a screen, any overlapping opaque areas should be clipped out.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless::ReleaseDC, OLEDCFLAGS


IOleInPlaceSiteWindowless::GetFocus

Called by an in-place active, windowless object to determine if it still has the keyboard focus or not.

HRESULT GetFocus(void);

Return Values

S_OK
The object currently has the keyboard focus.
S_FALSE
The object does not currently have the keyboard focus.

Remarks

A windowless object calls this method to find out if it currently has the focus or not. As an alternative to calling this method, the object can cache information about whether it has the keyboard focus or not.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleInPlaceSiteWindowless::InvalidateRect

Enables an object to invalidate a specified rectangle of its in-place image on the screen.

HRESULT InvalidateRect(
  LPCRECT pRect,   //Rectangle to be invalidated
  BOOL fErase      //Indicates whether to erase the background
);

Parameters

pRect
[in] Rectangle to invalidate, in client coordinates of the containing window. If this parameter is NULL, the object's full extent is invalidated.
fErase
[in] Specifies whether the background within the update region is to be erased when the region is updated. If this parameter is TRUE, the background is erased. If this parameter is FALSE, the background remains unchanged.

Return Values

S_OK
The specified rectangle was successfully invalidated.

Remarks

An object is only allowed to invalidate pixels contained in its own site rectangle. Any attempt to invalidate an area outside of that rectangle should result in a no-op.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleInPlaceSiteWindowless::InvalidateRgn

Enables an object to invalidate a specified region of its in-place image on the screen.

HRESULT InvalidateRgn(
  HRGN hRGN,   //Region to be invalidated
  BOOL fErase  //Indicates whether to erase the background
);

Parameters

hRGN
[in] Region to invalidate, in client coordinates of the containing window. If this parameter is NULL, the object's full extent is invalidated.
fErase
[in] Specifies whether the background within the update region is to be erased when the region is updated. If this parameter is TRUE, the background is erased. If this parameter is FALSE, the background remains unchanged.

Return Values

S_OK
The specified region was successfully invalidated.

Remarks

An object is only allowed to invalidate pixels contained in its own site region. Any attempt to invalidate an area outside of that region should result in a no-op.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleInPlaceSiteWindowless::OnDefWindowMessage

Invokes the default processing for all messages passed to an object.

HRESULT OnDefWindowMessage(
  UINT msg,           //Message Identifier as provided by Windows
  WPARAM wParam,      //Message parameter as provided by Windows
  LPARAM lParam,      //Message parameter as provided by Windows
  LRESULT* plResult   //Pointer to message result code
);

Parameters

msg
[in] Identifier for the window message provided to the container by Windows.
wParam
[in] Parameter for the window message provided to the container by Windows.
lParam
[in] Parameter for the window message provided to the container by Windows.
plResult
[out] Pointer to result code for the window message as defined in the Windows API.

Return Values

S_OK
The container's default processing for the window message was successfully invoked.
S_FALSE
The container's default processing for the window message was not invoked. See Note to Implementers below.

Remarks

A windowless object can explicitly invoke the default processing for a window message by calling this method. A container dispatches window messages to its windowless objects by calling IOleInPlaceObjectWindowless::OnWindowMessage. The object usually returns S_FALSE to indicate that it did not process the message. Then, the container can perform the default behavior for the message by calling the Windows API function DefWindowProc.

Instead, the object can call this method on the container's site object to explicitly invoke the default processing. Then, the object can take action on its own if the container does not handle the message.

Note to Implementers

The container must pass the following window messages to its default window procedure (the DefWindowProc Windows API function) and return S_OK. Note that *plResult should contain the value returned by DefWindowProc.

WM_MOUSEMOVE | WM_DEADCHAR
WM_XBUTTONxxx | WM_SYSKEYUP
WM_KEYDOWN | WM_SYSCHAR
WM_KEYUP | WM_SYSDEADCHAR
WM_CHAR | WM_IMExxx

The container can either process the window messages as its own and return S_OK or not do anything and return S_FALSE.

WM_SETCURSOR
WM_CONTEXTMENU
WM_HELP

If the container returns S_FALSE, the object can take action to process the window message on its own.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceObjectWindowless::OnWindowMessage


IOleInPlaceSiteWindowless::ReleaseDC

Releases the device context previously obtained by a call to IOleInPlaceSiteWindowless::GetDC.

HRESULT ReleaseDC(
  HDC hDC   //Device context to be released
);

Parameters

hDC
[in] Specifies the device context to be released.

Return Values

S_OK
The device context was successfully released.

Remarks

An object calls this method to notify its container that the object is done with the device context. If the device context was used for drawing, the container should ensure that all overlapping objects are repainted correctly. If the device context was an offscreen device context, its content should also be copied to the screen in the rectangle originally passed to IOleInPlaceSiteWindowless::GetDC. See IOleInPlaceSiteWindowless::GetDC for implementation notes relevant to ReleaseDC.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless::GetDC


IOleInPlaceSiteWindowless::ScrollRect

Enables an object to scroll an area within its in-place active image on the screen.

HRESULT ScrollRect(
  int dx,                //Amount to scroll on the x-axis
  int dy,                //Amount to scroll on the y-axis
  LPCRECT pRectScroll,   //Rectangle to scroll
  LPCRECT pRectClip      //Rectangle to clip
);

Parameters

dx
[in] Amount to scroll on the x-axis.
dy
[in] Amount to scroll on the y-axis.
pRectScroll
[in] Rectangle to scroll, in client coordinates of the containing window. NULL means the full object.
pRectClip
[in] Rectangle to clip to as defined for the Windows API function. Only pixels scrolling into this rectangle are drawn. Pixels scrolling out are not. If this parameter is NULL, the rectangle is not clipped.

Return Values

S_OK
The rectangle was successfully scrolled.

Remarks

This method should take in account the fact that the caller may be transparent and that there may be opaque or transparent overlapping objects. See Notes to Implementers below for suggestions on algorithms this method can use.

Note to Implementers

Containers can implement this method in a variety of ways. However, all of them should account for the possibility that the object requesting scrolling may be transparent or may not have a solid background. Containers should also take into account that there may be overlapping objects.

The simplest way to implement this method consists in simply redrawing the rectangle to scroll.

An added refinement to this simple implementation is to use the ScrollDC Windows API function when the object requesting the scroll is opaque, the object has a solid background, and there are no overlapping objects.

More sophisticated implementations can use the following procedure:

  • Check whether the object is opaque and has a solid background, using IViewObjectEx::GetViewStatus. If not, simply invalidate the rectangle to scroll. An added refinement is to check whether the scrolling rectangle is entirely in the opaque region of a partially transparent object.
  • Get the window device context.
  • Clip out the opaque parts of any overlapping object returned by IViewObjectEx::GetRect.
  • Clip out and invalidate the transparent parts of any overlapping object.
  • Finally, call the Windows API function ScrollDC.
  • Redraw the previously invalidated transparent parts of any overlapping object.

Regardless of the scrolling and clipping rectangle, only pixels contained in the object's site rectangle will be painted. The area uncovered by the scrolling operation is invalidated and redrawn immediately, before this method returns.

All redraw generated by this method should happen synchronously before this method returns.

This method should automatically hide the caret during the scrolling operation and should move the caret by the scrolling amounts if it is inside the clip rectangle.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IViewObjectEx::GetViewStatus, IViewObjectEx::GetRect


IOleInPlaceSiteWindowless::SetCapture

Enables an in-place active, windowless object to capture all mouse messages.

HRESULT SetCapture(
  BOOL fCapture   //Set or release mouse capture
);

Parameters

fCapture
[in] If TRUE, the container should capture the mouse for the object. If FALSE, the container should release mouse capture for the object.

Return Values

S_OK
Mouse capture was successfully granted to the object. If called to release the mouse capture, this method must not fail.
S_FALSE
Mouse capture was denied to the object.

Remarks

A windowless object captures the mouse input, by calling IOleInPlaceSiteWindowless::SetCapture(TRUE) on its site object. The container can deny mouse capture, in which case this method returns S_FALSE. If the capture is granted, the container must set the Windows mouse capture to its own window and dispatch any subsequent mouse message to the object, regardless of whether the mouse cursor position is over this object or not.

The object can later release mouse capture by calling IOleInPlaceSiteWindowless::SetCapture(FALSE) on its site object. The capture can also be released because of an external event, such as the ESC key being pressed. In this case, the object is notified by a WM_CANCELMODE message that the container dispatches along with the keyboard focus.

Containers should dispatch all mouse messages, including WM_SETCURSOR, to the windowless OLE object that has captured the mouse. If no object has captured the mouse, the container should dispatch the mouse message to the object under the mouse cursor.

The container dispatches these window messages by calling IOleInPlaceObjectWindowless::OnWindowMessage on the windowless object. The windowless object can return S_FALSE to this method to indicate that it did not process the mouse message. Then, the container should perform the default behavior for the message by calling the Windows API function DefWindowProc. For WM_SETCURSOR, the container can either set the cursor itself or do nothing.

Objects can also use IOleInPlaceSiteWindowless::OnDefWindowMessage to invoke the default message processing from the container. In the case of the WM_SETCURSOR message, this allows an object to take action if the container does not set the cursor.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless::OnDefWindowMessage


IOleInPlaceSiteWindowless::SetFocus

Sets the keyboard focus for a UI-active, windowless object.

HRESULT SetFocus(
  BOOL fFocus   //Set or release the keyboard focus
);

Parameters

fFocus
[in] If TRUE, sets the keyboard focus to the calling object. If FALSE, removes the keyboard focus from the calling object, provided that the object has the focus.

Return Values

S_OK
Keyboard focus was successfully given to the object. If this method is called to release the focus, it should never fail.
S_FALSE
Keyboard focus was denied to the object.

Remarks

A windowless object calls this method whenever a windowed object would call the Windows API function SetFocus. Through this call, the windowless object obtains the keyboard focus and can respond to window messages. Normally, this call is made during the UI activation process and within the notification methods IOleInPlaceActiveObject::OnDocWindowActivate(TRUE) and IOleInPlaceActiveObject::OnFrameWindowActivate(TRUE).

In response to this call, the container sets the Windows focus to the window being used to get keyboard messages (usually the container window) and redirects any subsequent keyboard messages to the windowless object that requested the focus.

A windowless object also calls the IOleInPlaceSiteWindowless::SetFocus method with the fFocus parameter set to FALSE to release the keyboard focus without assigning it to any other object. In this case, the container must call the Windows API function SetFocus with a NULL parameter so that no window has the focus.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceActiveObject


IOleInPlaceUIWindow

The IOleInPlaceUIWindow interface is implemented by container applications and used by object applications to negotiate border space on the document or frame window. The container provides a RECT structure in which the object can place toolbars and other similar controls, determines if tools can in fact be installed around the object's window frame, allocates space for the border, and establishes a communication channel between the object and each frame and document window.

The document window may not exist in all applications. When this is the case, IOleInPlaceSite::GetWindowContext returns NULL for IOleInPlaceUIWindow.

When to Implement

You must implement this interface if you are writing a container application that will participate in in-place activation.

When to Use

Used by object applications to negotiate border space on the document or frame window when one of its objects is being activated, or to renegotiate border space if the size of the object changes.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.
IOleInPlaceUIWindow Methods | Description
GetBorder | Specifies a RECT structure for toolbars and controls.
RequestBorderSpace | Determines if tools can be installed around object's window frame.
SetBorderSpace | Allocates space for the border.
SetActiveObject | Provides for direct communication between the object and each document and frame window.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleWindow


IOleInPlaceUIWindow::GetBorder

Returns a RECT structure in which the object can put toolbars and similar controls while active in place.

HRESULT GetBorder(
  LPRECT lprectBorder  //Pointer to structure
);

Parameter

lprectBorder
[out] Pointer to a RECT structure where the outer rectangle is to be returned. The RECT structure's coordinates are relative to the window being represented by the interface.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The rectangle was successfully returned.
E_NOTOOLSPACE
The object cannot install toolbars in this window object.

Notes to Callers

The IOleInPlaceUIWindow::GetBorder function, when called on a document or frame window object, returns the outer rectangle (relative to the window) where the object can put toolbars or similar controls.

If the object is to install these tools, it should negotiate space for the tools within this rectangle using IOleInPlaceUIWindow::RequestBorderSpace and then call IOleInPlaceUIWindow::SetBorderSpace to get this space allocated.

Note While executing IOleInPlaceUIWindow::GetBorder, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within GetBorder.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceUIWindow::RequestBorderSpace, IOleInPlaceUIWindow::SetBorderSpace, PeekMessage, GetMessage


IOleInPlaceUIWindow::RequestBorderSpace

Determines if there is available space for tools to be installed around the object's window frame while the object is active in place.

HRESULT RequestBorderSpace(
  LPCBORDERWIDTHS pborderwidths  //Pointer to a structure
);

Parameter

pborderwidths
[in] Pointer to a BORDERWIDTHS structure containing the requested widths (in pixels) needed on each side of the window for the tools.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The requested space could be allocated to the object.
E_NOTOOLSPACE
The object cannot install toolbars in this window object because the implementation does not support toolbars, or there is insufficient space to install the toolbars.

Notes to Callers

The active in-place object calls IOleInPlaceUIWindow::RequestBorderSpace to ask if tools can be installed inside the window frame. These tools would be allocated between the rectangle returned by IOleInPlaceUIWindow::GetBorder and the BORDERWIDTHS structure specified in the argument to this call.

The space for the tools is not actually allocated to the object until it calls IOleInPlaceUIWindow::SetBorderSpace, allowing the object to negotiate for space (such as while dragging toolbars around), but deferring the moving of tools until the action is completed.

The object can install these tools by passing the width in pixels that is to be used on each side. For example, if the object required 10 pixels on the top, 0 pixels on the bottom, and 5 pixels on the left and right sides, it would pass the following BORDERWIDTHS structure to IOleInPlaceUIWindow::RequestBorderSpace:

lpbw->top    = 10
lpbw->bottom =  0
lpbw->lLeft  =  5
lpbw->right  =  5

Note While executing IOleInPlaceUIWindow::RequestBorderSpace, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within RequestBorderSpace.

Notes to Implementers

If the amount of space an active object uses for its toolbars is irrelevant to the container, it can simply return NOERROR as shown in the following IOleInPlaceUIWindow::RequestBorderSpace example. Containers should not unduly restrict the display of tools by an active in-place object.

HRESULT InPlaceUIWindow_RequestBorderSpace(
    IOleInPlaceFrame *  lpThis,
    LPCBORDERWIDTHS     pborderwidths)
{
    /* Container allows the object to have as much border space as it
    ** wants.
    */
    return NOERROR;

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceUIWindow::GetBorder, IOleInPlaceUIWindow::SetBorderSpace, PeekMessage, GetMessage


IOleInPlaceUIWindow::SetActiveObject

Provides a direct channel of communication between the object and each of the frame and document windows.

HRESULT SetActiveObject(
  IOleInPlaceActiveObject *pActiveObject,
                        //Pointer to active in-place object
  LPCOLESTR pszObjName  //Pointer tostring containing a name
                        // describing the object
);

Parameters

pActiveObject
[in] Pointer to the IOleInPlaceActiveObject interface on the active in-place object.
pszObjName
[in] Pointer to a string containing a name that describes the object an embedding container can use in composing its window title. It can be NULL if the object does not require the container to change its window titles. The Microsoft Windows User Interface Design Guide recommends that containers ignore this parameter and always use their own name in the title bar.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The method completed successfully.

Remarks

Generally, an embedded object should pass NULL for the pszObjName parameter (see "Notes to Implementers" below). However, if you are working in conjunction with a container that does display the name of the in-place active object in its title bar, then you should compose a string in the following form:

<application name> – <object short-type name>

Notes to Callers

IOleInPlaceUIWindow::SetActiveObject is called by the object to establish a direct communication link between itself and the document and frame windows.

When deactivating, the object calls IOleInPlaceUIWindow::SetActiveObject, passing NULL for the pActiveObject and pszObjName parameters.

An object must call IOleInPlaceUIWindow::SetActiveObject before calling IOleInPlaceFrame::SetMenu to give the container the pointer to the active object. The container then uses this pointer in processing IOleInPlaceFrame::SetMenu and to pass to OleSetMenuDescriptor.

Notes to Implementers

The Microsoft Windows User Interface Design Guide recommends that an in-place container ignore the pszObjName parameter passed in this method. The guide says "The title bar is not affected by in-place activation. It always displays the top-level container's name."

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceFrame::SetMenu, OleSetMenuDescriptor


IOleInPlaceUIWindow::SetBorderSpace

Allocates space for the border requested in the call to IOleInPlaceUIWindow::RequestBorderSpace.

HRESULT SetBorderSpace(
  LPCBORDERWIDTHS pborderwidths  //Pointer to a structure
);

Parameter

pborderwidths
[in] Pointer to a BORDERWIDTHS structure containing the requested width (in pixels) of the tools. It can be NULL, indicating the object does not need any space.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The requested space has been allocated to the object.
OLE_E_INVALIDRECT
The rectangle does not lie within the specifications returned by IOleInPlaceUIWindow::GetBorder.

Remarks

The object must call IOleInPlaceUIWindow::SetBorderSpace. It can do any one of the following:

  • Use its own toolbars, requesting border space of a specific size, or,
  • Use no toolbars, but force the container to remove its toolbars by passing a valid BORDERWIDTHS structure containing nothing but zeros in the pborderwidths parameter, or,
  • Use no toolbars but allow the in-place container to leave its toolbars up by passing NULL as the pborderwidths parameter.

The BORDERWIDTHS structure used in this call would generally have been passed in a previous call to IOleInPlaceUIWindow::RequestBorderSpace, which must have returned S_OK.

If an object must renegotiate space on the border, it can call SetBorderSpace again with the new widths. If the call to SetBorderSpace fails, the object can do a full negotiation for border space with calls to GetBorder, RequestBorderSpace, and SetBorderSpace.

Note While executing IOleInPlaceUIWindow::SetBorderSpace, do not make calls to the Windows PeekMessage or GetMessage functions, or a dialog box. Doing so may cause the system to deadlock. There are further restrictions on which OLE interface methods and functions can be called from within SetBorderSpace.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceUIWindow::GetBorder, IOleInPlaceUIWindow::RequestBorderSpace, PeekMessage, GetMessage


IOleLink

The IOleLink interface is the means by which a linked object provides its container with functions pertaining to linking. The most important of these functions is binding to the link source, that is, activating the connection to the document that stores the linked object's native data. IOleLink also defines functions for managing information about the linked object, such as the location of the link source and the cached presentation data for the linked object.

A container application can distinguish between embedded objects and linked objects by querying for IOleLink; only linked objects implement IOleLink.

When to Implement

You do not have to implement this interface yourself; the system supplies an implementation of IOleLink that is suitable for all situations. This implementation is used automatically whenever you create or load a linked object.

When to Use

You must use IOleLink if you are writing a container application that allows its documents to contain linked objects. You primarily call IOleLink methods in order to implement the Links dialog box. If you use the OleUIEditLinks function to display the Links dialog box, your calls to IOleLink methods take place in your implementation of the IOleUILinkContainer interface.

Some IOleLink methods don't have to be called directly. Instead, you call methods of IOleObject; the default linked object provides an implementation of IOleObject that often calls methods of IOleLink. For example, a container application typically activates a linked object by calling IOleObject::DoVerb, which in turn calls IOleLink::BindToSource.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleLink Methods | Description
SetUpdateOptions | Sets the update options.
GetUpdateOptions | Returns the update options.
SetSourceMoniker | Sets the moniker for the link source.
GetSourceMoniker | Returns the moniker for the link source.
SetSourceDisplayName | Sets the display name for the link source.
GetSourceDisplayName | Returns the display name for the link source.
BindToSource | Binds the moniker to the link source.
BindIfRunning | Binds the moniker if the source is running.
GetBoundSource | Returns a pointer to the link source if it's running.
UnbindSource | Break connection to the link source.
Update | Update the cached views of the link source.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject, IOleUILinkContainer, OleUIEditLinks


IOleLink::BindIfRunning

Activates the connection between the linked object and the link source if the link source is already running.

HRESULT BindIfRunning(void);

Return Values

S_OK
The link source was bound.
S_FALSE
The link source is not running.
CreateBindCtx, IMoniker::IsRunning, or IOleLink::BindToSource errors
Binding the moniker might require calling these functions, therefore, errors generated by these functions may be returned.

Remarks

You typically do not need to call IOleLink::BindIfRunning. This method is primarily called by the linked object.

Notes on Provided Implementation

The linked object's implementation of IOleLink::BindIfRunning checks the Running Object Table (ROT) to determine whether the link source is already running. It checks both the relative and absolute monikers. If the link source is running, IOleLink::BindIfRunning calls IOleLink::BindToSource to connect the linked object to the link source.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::BindToSource


IOleLink::BindToSource

Activates the connection to the link source by binding the moniker stored within the linked object.

HRESULT BindToSource(
  DWORD bindflags,
                 //Flag in case CLSID of link source is different
  IBindCtx *pbc  //Pointer to bind context to be used
);

Parameters

bindflags
[in] Specifies how to proceed if the link source has a different CLSID from the last time it was bound. If this parameter is zero and the CLSIDs are different, the method fails and returns OLE_E_CLASSDIFF. If the OLELINKBIND_EVENIFCLASSDIFF value from the OLELINKBIND enumeration is specified and the CLSIDs are different, the method binds successfully and updates the CLSID stored in the linked object.
pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this binding operation. This parameter can be NULL. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the binding implementation should retrieve information about its environment. For more information, see IBindCtx.

Return Values

S_OK
The link source is bound.
OLE_E_CLASSDIFF
The link source was not bound because its CLSID has changed. This error is returned only if the OLELINKBIND_EVENIFCLASSDIFF flag is not specified in the bindflags parameter.
MK_E_NOOBJECT
The link source could not be found or (if the link source's moniker is a composite) some intermediate object identified in the composite could not be found.
E_UNSPEC
The link's moniker is NULL.
CreateBindCtx errors
Binding the moniker might require calling this function; therefore, this method may return errors generated by this function.

Notes to Callers

Typically, your container application does not need to call the IOleLink::BindToSource method directly. When it's necessary to activate the connection to the link source, your container typically calls IOleObject::DoVerb, IOleObject::Update, or IOleLink::Update. The linked object's implementation of these methods calls IOleLink::BindToSource. Your container can also call the OleRun function, which — when called on a linked object — calls IOleLink::BindToSource.

In each of the examples listed previously, in which IOleLink::BindToSource is called indirectly, the bindflags parameter is set to zero. Consequently, these calls can fail with the OLE_E_CLASSDIFF error if the class of the link source is different from what it was the last time the linked object was bound. This could happen, for example, if the original link source was an embedded Lotus spreadsheet that an end user had subsequently converted (using the Change Type dialog box) to an Excel spreadsheet.

If you want your container to bind even though the link source now has a different CLSID, you can call IOleLink::BindToSource directly and specify OLELINKBIND_EVENIFCLASSDIFF for the bindflags parameter. This call binds to the link source and updates the link object's CLSID. Alternatively, your container can delete the existing link and use the OleCreateLink function to create a new linked object.

Notes on Provided Implementation

The linked object caches the interface pointer to the link source acquired during binding.

The linked object's IOleLink::BindToSource implementation first tries to bind using a moniker consisting of the compound document's moniker composed with the link source's relative moniker. If successful, it updates the link's absolute moniker. Otherwise, it tries to bind using the absolute moniker, updating the relative moniker if successful.

If IOleLink::BindToSource binds to the link source, it calls the compound document's IOleContainer::LockContainer implementation to keep the containing compound document alive while the link source is running. IOleLink::BindToSource also calls the IOleObject::Advise and IDataObject::DAdvise implementations of the link source to set up advisory connections. The IOleLink::UnbindSource implementation unlocks the container and deletes the advisory connections.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IDataObject::DAdvise, IOleContainer::LockContainer, IOleLink::Update, IOleLink::UnbindSource, IOleObject::Advise, IOleObject::DoVerb, IOleObject::Update, OleRun


IOleLink::GetBoundSource

Returns an IUnknown pointer to the link source if the connection is currently active.

HRESULT GetBoundSource(
  IUnknown **ppunk  //Address of output variable that receives the
                    // IUnknown interface pointer
);

Parameter

ppunk
[out] Address of IDataObject* pointer variable that receives the interface pointer to the link source. When successful, the implementation must call IUnknown::AddRef on *ppunk; it is the caller's responsibility to call IUnknown::Release. If an error occurs, the implementation sets *ppunk to NULL.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
A pointer was returned successfully.

Remarks

You typically do not need to call IOleLink::GetBoundSource.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleLink::GetSourceDisplayName

Retrieves the display name of the link source of the linked object.

HRESULT GetSourceDisplayName(
  LPOLESTR *ppszDisplayName  //Address of output variable that
                             // receives a pointer to the display name
                             // string for the link source
);

Parameter

ppszDisplayName
[out] Address of LPOLESTR pointer variable that receives a pointer to the zero-terminated wide character string (two bytes per character) containing the display name of the link source. If an error occurs, *ppszDisplayName is set to NULL; otherwise, the implementation must use IMalloc::Alloc to allocate the string returned in *ppszDisplayName, and the caller is responsible for calling IMalloc::Free to free it. Both caller and called use the allocator returned by CoGetMalloc.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The display name was successfully retrieved.
CreateBindCtx and IMoniker::GetDisplayName errors
Retrieving the display name requires calling these functions; therefore, this method may return errors generated by these functions.

Notes to Callers

Your container application can call IOleLink::GetSourceDisplayName in order to display the current source of a link.

The current source of a link is displayed in the Links dialog box. If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementations of IOleUILinkContainer::GetLinkSource to get the string it should display. Your implementation of that method can call IOleLink::GetSourceDisplayName.

Notes on Provided Implementation

The linked object's implementation of IOleLink::GetSourceDisplayName calls IOleLink::GetSourceMoniker to get the link source moniker, and then calls IMoniker::GetDisplayName to get that moniker's display name. This operation is potentially expensive because it might require binding the moniker. All of the system-supplied monikers can return a display name without binding, but there is no guarantee that other moniker implementations can. Instead of making repeated calls to IOleLink::GetSourceDisplayName, your container application can cache the name and update it whenever the link source is bound.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::SetSourceDisplayName, IOleUILinkContainer, IMoniker::GetDisplayName, OleUIEditLinks


IOleLink::GetSourceMoniker

Retrieves the moniker identifying the link source of a linked object.

HRESULT GetSourceMoniker(
  IMoniker **ppmk  //Address of output variable that receives the
                   // IMoniker interface pointer
);

Parameter

ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to an absolute moniker that identifies the link source. When successful, the implementation must call IUnknown::AddRef on *ppmk; it is the caller's responsibility to call IUnknown::Release. If an error occurs the implementation must set *ppmk to NULL.

Return Values

S_OK
The moniker was returned successfully.
MK_E_UNAVAILABLE
No moniker is available.

Notes to Callers

Your container application can call IOleLink::GetSourceMoniker to display the current source of a link in the Links dialog box. Note that this requires your container to use the IMoniker::GetDisplayName method to get the display name of the moniker. If you'd rather get the display name directly, your container can call IOleLink::GetSourceDisplayName instead of IOleLink::GetSourceMoniker.

If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementations of IOleUILinkContainer::GetLinkSource to get the string it should display. Your implementation of that method can call IOleLink::GetSourceMoniker.

Notes on Provided Implementation

The linked object stores both an absolute and a relative moniker for the link source. If the relative moniker is non-NULL and a moniker is available for the compound document, IOleLink::GetSourceMoniker returns the moniker created by composing the relative moniker onto the end of the compound document's moniker. Otherwise, it returns the absolute moniker or, if an error occurs, NULL.

The container specifies the absolute moniker when it calls one of the OleCreateLink functions to create a link. The application can call IOleLink::SetSourceMoniker or IOleLink::SetSourceDisplayName to change the absolute moniker. In addition, the linked object automatically updates the monikers whenever it successfully binds to the link source, or when it is bound to the link source and it receives a rename notification through the IAdviseSink::OnRename method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::SetSourceDisplayName, IOleLink::SetSourceMoniker


IOleLink::GetUpdateOptions

Retrieves a value indicating how often the linked object updates its cached data.

HRESULT GetUpdateOptions(
  DWORD *pdwUpdateOpt  //Pointer to update option
);

Parameter

pdwUpdateOpt
[out] Pointer to a DWORD that specifies the current value for the linked object's update option, indicating how often the linked object updates the cached data for the linked object. The legal values for pdwUpdateOpt are taken from the enumeration OLEUPDATE.

Return Value

S_OK
The update option was retrieved successfully.

Notes to Callers

Your container application should call IOleLink::GetUpdateOptions to display the current update option for a linked object.

A linked object's current update option is displayed in the Links dialog box. If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementation of IOleUILinkContainer::GetLinkUpdateOptions to determine which update option it should display. Your implementation of that method should call IOleLink::GetUpdateOptions to retrieve the current update option.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::SetUpdateOptions, IOleUILinkContainer, OleUIEditLinks


IOleLink::SetSourceDisplayName

Specifies the new link source of a linked object using a display name.

HRESULT SetSourceDisplayName(
  LPCOLESTR pszStatusText  //Pointer to display name of new link
                           // source
);

Parameter

pszStatusText
[in] Pointer to the display name of the new link source. It may not be NULL.

Return Values

S_OK
The display name was set successfully.
MkParseDisplayName errors
Setting the display name requires calling this function; therefore, this method may return errors generated by this function.

Notes to Callers

Your container application can call IOleLink::SetSourceDisplayName when the end user changes the source of a link or breaks a link. Note that this requires the linked object to create a moniker out of the display name. If you'd rather parse the display name into a moniker yourself, your container can call IOleLink::SetSourceMoniker instead of IOleLink::SetSourceDisplayName.

If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementations of IOleUILinkContainer::SetLinkSource and IOleUILinkContainer::CancelLink. Your implementation of these methods can call IOleLink::SetSourceDisplayName.

If your container application is immediately going to bind to a newly specified link source, you should call MkParseDisplayName and IOleLink::SetSourceMoniker instead, and then call IOleLink::BindToSource using the bind context from the parsing operation. By reusing the bind context, you can avoid redundant loading of objects that might otherwise occur.

Notes on Provided Implementation

The contract for IOleLink::SetSourceDisplayName does not specify when the linked object will parse the display name into a moniker. The parsing can occur before IOleLink::SetSourceDisplayName returns, or the linked object can store the display name and parse it only when it needs to bind to the link source. Note that parsing the display name is potentially an expensive operation because it might require binding to the link source. The provided implementation of IOleLink::SetSourceDisplayName parses the display name and then releases the bind context used in the parse operation. This can result in running and then stopping the link source server.

If the linked object is bound to the current link source, the implementation of IOleLink::SetSourceDisplayName breaks the connection.

For more information on how the linked object stores and uses the moniker to the link source, see IOleLink::SetSourceMoniker.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::SetSourceMoniker, IOleUILinkContainer, MkParseDisplayName, OleUIEditLinks


IOleLink::SetSourceMoniker

Specifies the new link source of a linked object using a moniker.

HRESULT SetSourceMoniker(
  IMoniker *pmk,  //Pointer to a moniker identifying new link source
  REFCLSID rclsid //CLSID of link source
);

Parameters

pmk
[in] Pointer to the IMoniker interface on a moniker that identifies the new link source of the linked object. A value of NULL breaks the link.
rclsid
[in] Specifies the CLSID of the link source that the linked object should use to access information about the linked object when it is not bound.

Return Value

S_OK
The moniker was set successfully.

Notes to Callers

Your container application can call IOleLink::SetSourceMoniker when the end user changes the source of a link or breaks a link. Note that this requires your container to use the MkParseDisplayName function to create a moniker out of the display name that the end user enters. If you'd rather have the linked object perform the parsing, your container can call IOleLink::SetSourceDisplayName instead of IOleLink::SetSourceMoniker.

The end user changes the source of a link or breaks a link using the Links dialog box. If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementations of IOleUILinkContainer::SetLinkSource and IOleUILinkContainer::CancelLink; your implementation of these methods can call IOleLink::SetSourceMoniker.

If the linked object is currently bound to its link source, the linked object's implementation of IOleLink::SetSourceMoniker closes the link before changing the moniker.

Notes on Provided Implementation

The IOleLink contract does not specify how the linked object stores or uses the link source moniker. The provided implementation stores the absolute moniker specified when the link is created or when the moniker is changed; it then computes and stores a relative moniker. Future implementations might manage monikers differently to provide better moniker tracking. The absolute moniker provides the complete path to the link source. The linked object uses this absolute moniker and the moniker of the compound document to compute a relative moniker that identifies the link source relative to the compound document that contains the link.

pmkCompoundDoc->RelativePathTo(pmkAbsolute, ppmkRelative)

When binding to the link source, the linked object first tries to bind using the relative moniker. If that fails, it tries to bind the absolute moniker.

When the linked object successfully binds using either the relative or the absolute moniker, it automatically updates the other moniker. The linked object also updates both monikers when it is bound to the link source and it receives a rename notification through the IAdviseSink::OnRename method. A container application can also use the IOleLink::SetSourceDisplayName method to change a link's moniker.

The linked object's implementation of IPersistStorage::Save saves both the relative and the absolute moniker.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::GetSourceMoniker, IOleLink::SetSourceDisplayName, IOleUILinkContainer, OleUIEditLinks


IOleLink::SetUpdateOptions

Specifies how often a linked object should update its cached data.

HRESULT SetUpdateOptions(
  DWORD dwUpdateOpt  //Update option
);

Parameter

dwUpdateOpt
[in] Specifies how often a linked object should update its cached data. The legal values for dwUpdateOpt are taken from the enumeration OLEUPDATE.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The update option was successfully set.

Notes to Callers

Your container application should call IOleLink::SetUpdateOptions when the end user changes the update option for a linked object.

The end user selects the update option for a linked object using the Links dialog box. If you use the OleUIEditLinks function to display this dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your IOleUILinkContainer::SetLinkUpdateOptions method to specify the update option chosen by the end user. Your implementation of this method should call the IOleLink::SetUpdateOptions method to pass the selected option to the linked object.

Notes on Provided Implementation

The default update option is OLEUDPATE_ALWAYS. The linked object's implementation of IPersistStorage::Save saves the current update option.

If OLEUDPATE_ALWAYS is specified as the update option, the linked object updates the link's caches in the following situations:

  • When the update option is changed from manual to automatic, if the link source is running.
  • Whenever the linked object binds to the link source.
  • Whenever the link source is running and the linked object's IOleObject::Close, IPersistStorage::Save, or IAdviseSink::OnSave implementations are called.

For both manual and automatic links, the linked object updates the cache whenever the container application calls IOleObject::Update or IOleLink::Update.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::Update, IOleLink::GetUpdateOptions, IOleLink::Update, IOleUILinkContainer, OleUIEditLinks


IOleLink::UnbindSource

Deactivates the connection between a linked object and its link source.

HRESULT UnbindSource(void);

Return Value

S_OK
The connection was deactivated.

Remarks

You typically do not call IOleLink::UnbindSource directly. When it's necessary to deactivate the connection to the link source, your container typically calls IOleObject::Close or IUnknown::Release; the linked object's implementation of these methods calls IOleLink::UnbindSource. The linked object's IAdviseSink::OnClose implementation also calls IOleLink::UnbindSource.

Notes on Provided Implementation

The linked object's implementation of IOleLink::UnbindSource does nothing if the link source is not currently bound. If the link source is bound, IOleLink::UnbindSource calls the link source's IOleObject::Unadvise and IDataObject::DUnadvise implementations to delete the advisory connections to the link source. The IOleLink::UnbindSource method also calls the compound document's IOleClientSite::LockContainer implementation to unlock the containing compound document. This undoes the lock on the container and the advisory connections that were established in IOleLink::BindToSource. IOleLink::UnbindSource releases all the linked object's interface pointers to the link source.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IAdviseSink::OnClose, IDataObject::DUnadvise, IOleObject::Close, IOleObject::Unadvise, IOleLink::BindToSource


IOleLink::Update

Updates the compound document's cached data for a linked object. This involves binding to the link source, if it is not already bound.

HRESULT Update(
  IBindCtx *pbc  //Pointer to bind context to be used
);

Parameter

pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in binding the link source. This parameter can be NULL. The bind context caches objects bound during the binding process, contains parameters that apply to all operations using the bind context, and provides the means by which the binding implementation should retrieve information about its environment. For more information, see IBindCtx.

Return Values

S_OK
All caches were updated successfully.
CACHE_E_NOCACHE_UPDATED
The bind operation worked but no caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED
The bind operation worked but not all caches were updated.
OLE_E_CANT_BINDTOSOURCE
Unable to bind to the link source.

Notes to Callers

Your container application should call IOleLink::Update if the end user updates the cached data for a linked object.

The end user can update the cached data for a linked object by choosing the Update Now button in the Links dialog box. If you use the OleUIEditLinks function to display the Links dialog box, you must implement the IOleUILinkContainer interface. The dialog box calls your implementations of IOleUILinkContainer::UpdateLink when the end user chooses the Update Now button. Your implementation of that method can call IOleLink::Update.

Your container application can also call IOleObject::Update to update a linked object, because that method — when called on a linked object — calls IOleLink::Update.

This method updates both automatic links and manual links. For manual links, calling IOleLink::Update or IOleObject::Update is the only way to update the caches. For more information on automatic and manual links, see IOleLink::SetUpdateOptions.

Notes on Provided Implementation

If pbc is non-NULL, the linked object's implementation of IOleLink::Update calls IBindCtx::RegisterObjectBound to register the bound link source. This ensures that the link source remains running until the bind context is released.

The current caches are left intact if the link source cannot be bound.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IBindCtx::RegisterObjectBound, IOleLink::SetUpdateOptions, IOleObject::Update, IOleUILinkContainer, OleUIEditLinks


IOleObject

The IOleObject interface is the principal means by which an embedded object provides basic functionality to, and communicates with, its container.

When to Implement

An object application must implement this interface, along with at least IDataObject and IPersistStorage, for each type of embedded object that it supports. Although this interface contains 21 methods, only three are nontrivial to implement and must be fully implemented: DoVerb, SetHostNames, and Close. Six of the methods provide optional functionality, which, if not desired, can be implemented to return E_NOTIMPL: SetExtent, InitFromData, GetClipboardData, SetColorScheme, SetMoniker, and GetMoniker. The latter two methods are useful mainly for enabling links to embedded objects.

When to Use

Call the methods of this interface to enable a container to communicate with an embedded object. A container must call DoVerb to activate an embedded object, SetHostNames to communicate the names of the container application and container document, and Close to move an object from a running to a loaded state. Calls to all other methods are optional.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleObject Methods | Description
SetClientSite | Informs object of its client site in container.
GetClientSite | Retrieves object's client site.
SetHostNames | Communicates names of container application and container document.
Close | Moves object from running to loaded state.
SetMoniker | Informs object of its moniker.
GetMoniker | Retrieves object's moniker.
InitFromData | Initializes embedded object from selected data.
GetClipboardData | Retrieves a data transfer object from the Clipboard.
DoVerb | Invokes object to perform one of its enumerated actions ("verbs").
EnumVerbs | Enumerates actions ("verbs") for an object.
Update | Updates an object.
IsUpToDate | Checks if object is up to date.
GetUserClassID | Returns an object's class identifier.
GetUserType | Retrieves object's user-type name.
SetExtent | Sets extent of object's display area.
GetExtent | Retrieves extent of object's display area.
Advise | Establishes advisory connection with object.
Unadvise | Destroys advisory connection with object.
EnumAdvise | Enumerates object's advisory connections.
GetMiscStatus | Retrieves status of object.
SetColorScheme | Recommends color scheme to object application.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleObject::Advise

Establishes an advisory connection between a compound document object and the calling object's advise sink, through which the calling object receives notification when the compound document object is renamed, saved, or closed.

HRESULT Advise(
  IAdviseSink *pAdvSink,  //Pointer to advisory sink
  DWORD *pdwConnection    //Pointer to a token
);

Parameters

pAdvSink
[in] Pointer to the IAdviseSink interface on the advise sink of the calling object.
pdwConnection
[out] Pointer to a DWORD token that can be passed to IOleObject::Unadvise to delete the advisory connection.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
Advisory connection is successfully established.

Remarks

The Advise method sets up an advisory connection between an object and its container, through which the object informs the container's advise sink of close, save, rename, and link-source change events in the object. A container calls this method, normally as part of initializing an object, to register its advisory sink with the object. In return, the object sends the container compound-document notifications by calling IAdviseSink or IAdviseSink2.

If container and object successfully establish an advisory connection, the object receiving the call returns a nonzero value through pdwConnection to the container. If the attempt to establish an advisory connection fails, the object returns zero. To delete an advisory connection, the container calls IOleObject::Unadvise and passes this nonzero token back to the object.

An object can delegate the job of managing and tracking advisory events to an OLE advise holder, to which you obtain a pointer by calling CreateOleAdviseHolder. The returned IOleAdviseHolder interface has three methods for sending advisory notifications, as well as Advise, Unadvise, and EnumAdvise methods that are identical to those for IOleObject. Calls to IOleObject:Advise, Unadvise, or EnumAdvise are delegated to corresponding methods in the advise holder.

To destroy the advise holder, simply call Release on the IOleAdviseHolder interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CreateOleAdviseHolder, IOleObject::UnAdvise, IOleObject::EnumAdvise, IOleAdviseHolder::Advise


IOleObject::Close

Changes an embedded object from the running to the loaded state. Disconnects a linked object from its link source.

HRESULT Close(
  DWORD dwSaveOption  //Indicates whether to save object before
                      // closing
);

Parameter

dwSaveOption
[in] DWORD that indicates whether the object is to be saved as part of the transition to the loaded state. Valid values are taken from the enumeration OLECLOSE.

Note The OLE 2 user model recommends that object applications do not prompt users before saving linked or embedded objects, including those activated in place. This policy represents a change from the OLE 1 user model, in which object applications always prompt the user to decide whether to save changes.

Return Values

S_OK
The object closed successfully.
OLE_E_PROMPTSAVECANCELLED
The user was prompted to save but chose the Cancel button from the prompt message box.

Notes to Callers

A container application calls IOleObject::Close when it wants to move the object from a running to a loaded state. Following such a call, the object still appears in its container but is not open for editing. Calling IOleObject::Close on an object that is loaded but not running has no effect. Closing a linked object simply means disconnecting it.

Notes to Implementers

Upon receiving a call to IOleObject::Close, a running object should do the following:

  • If the object has been changed since it was last opened for editing, it should request to be saved, or not, according to instructions specified in dwSaveOption. If the option is to save the object, then it should call its container's IOleClientSite::SaveObject interface.
  • If the object has IDataObject::DAdvise connections with ADVF_DATAONSTOP flags, then it should send an OnDataChange notification. See IDataObject::DAdvise for details.
  • If the object currently owns the Clipboard, it should empty it by calling OleFlushClipboard.
  • If the object is currently visible, notify its container by calling IOleClientSite::OnShowWindow with the fshow argument set to FALSE.
  • Send IAdvise::OnClose notifications to appropriate advise sinks.
  • Finally, forcibly cut off all remoting clients by calling CoDisconnectObject.

If the object application is a local server (an EXE rather than a DLL), closing the object should also shut down the object application unless the latter is supporting other running objects or has another reason to remain in the running state. Such reasons might include the presence of IClassFactory::LockServer locks, end-user control of the application, or the existence of other open documents requiring access to the application.

Calling IOleObject::Close on a linked object disconnects it from, but does not shut down, its source application. A source application that is visible to the user when the object is closed remains visible and running after the disconnection and does not send an OnClose notification to the link container.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CoDisconnectObject, IAdviseSink::OnClose, IClassFactory::LockServer, IDataObject::DAdvise, IOleClientSite::OnShowWindow, IOleClientSite::SaveObject, OLECLOSE, OleFlushClipboard


IOleObject::DoVerb

Requests an object to perform an action in response to an end-user's action. The possible actions are enumerated for the object in IOleObject::EnumVerbs.

HRESULT DoVerb(
  LONG iVerb,          //Value representing verb to be performed
  LPMSG lpmsg,         //Pointer to structure that describes Windows
                       // message
  IOleClientSite *pActiveSite,
                       //Pointer to active client site
  LONG lindex,         //Reserved
  HWND hwndParent,     //Handle of window containing the object
  LPCRECT lprcPosRect  //Pointer to object's display rectangle
);

Parameters

iVerb
[in] Number assigned to the verb in the OLEVERB structure returned by IOleObject::EnumVerbs.
lpmsg
[in] Pointer to the MSG structure describing the event (such as a double-click) that invoked the verb. The caller should pass the MSG structure unmodified, without attempting to interpret or alter the values of any of the fields of lpmsg.
pActiveSite
[in] Pointer to the IOleClientSite interface on the object's active client site, where the event occurred that invoked the verb.
lindex
[in] Reserved for future use; must be zero.
hwndParent
[in] Handle of the document window containing the object. This and lprcPosRect together make it possible to open a temporary window for an object, where hwndParent is the parent window in which the object's window is to be displayed, and lprcPosRect defines the area available for displaying the object window within that parent. A temporary window is useful, for example, to a multimedia object that opens itself for playback but not for editing.
lprcPosRect
[in] Pointer to the RECT structure containing the coordinates, in pixels, that define an object's bounding rectangle in hwndParent. This and hwndParent together enable opening multimedia objects for playback but not for editing.

Return Values

S_OK
Object successfully invoked specified verb.
OLE_E_NOT_INPLACEACTIVE
iVerb set to OLEIVERB_UIACTIVATE or OLEIVERB_INPLACEACTIVATE and object is not already visible.
OLE_E_CANT_BINDTOSOURCE
The object handler or link object cannot connect to the link source.
DV_E_LINDEX
Invalid lindex.
OLEOBJ_S_CANNOT_DOVERB_NOW
The verb is valid, but in the object's current state it cannot carry out the corresponding action.
OLEOBJ_S_INVALIDHWND
DoVerb was successful but hwndParent is invalid.
OLEOBJ_E_NOVERBS
The object does not support any verbs.
OLEOBJ_S_INVALIDVERB
Object does not recognize a positive verb number. Verb is treated as OLEIVERB_PRIMARY.
MK_E_CONNECT
Link source is across a network that is not connected to a drive on this machine.
OLE_E_CLASSDIFF
Class for source of link has undergone a conversion.
E_NOTIMPL
Object does not support in-place activation or does not recognize a negative verb number.

Remarks

A "verb" is an action that an OLE object takes in response to a message from its container. An object's container, or a client linked to the object, normally calls IOleObject::DoVerb in response to some end-user action, such as double-clicking on the object. The various actions that are available for a given object are enumerated in an OLEVERB structure, which the container obtains by calling IOleObject::EnumVerbs. IOleObject::DoVerb matches the value of iVerb against the iVerb member of the structure to determine which verb to invoke.

Through IOleObject::EnumVerbs, an object, rather than its container, determines which verbs (i.e., actions) it supports. OLE 2 defines seven verbs that are available, but not necessarily useful, to all objects. In addition, each object can define additional verbs that are unique to it. The following table describes the verbs defined by OLE:

Verb | Description
OLEIVERB_PRIMARY (0L) | Specifies the action that occurs when an end user double-clicks the object in its container. The object, not the container, determines this action. If the object supports in-place activation, the primary verb usually activates the object in place.
OLEIVERB_SHOW (-1) | Instructs an object to show itself for editing or viewing. Called to display newly inserted objects for initial editing and to show link sources. Usually an alias for some other object-defined verb.
OLEIVERB_OPEN (-2) | Instructs an object, including one that otherwise supports in-place activation, to open itself for editing in a window separate from that of its container. If the object does not support in-place activation, this verb has the same semantics as OLEIVERB_SHOW.
OLEIVERB_HIDE (-3) | Causes an object to remove its user interface from the view. Applies only to objects that are activated in-place.
OLEIVERB_UIACTIVATE (-4) | Activates an object in place, along with its full set of user-interface tools, including menus, toolbars, and its name in the title bar of the container window. If the object does not support in-place activation, it should return E_NOTIMPL.
OLEIVERB_INPLACEACTIVATE (-5) | Activates an object in place without displaying tools, such as menus and toolbars, that end users need to change the behavior or appearance of the object. Single-clicking such an object causes it to negotiate the display of its user-interface tools with its container. If the container refuses, the object remains active but without its tools displayed.
OLEIVERB_DISCARDUNDOSTATE (-6) | Used to tell objects to discard any undo state that they may be maintaining without deactivating the object.

Note to Callers

Containers call IOleObject::DoVerb as part of initializing a newly created object. Before making the call, containers should first call IOleObject::SetClientSite to inform the object of its display location and IOleObject::SetHostNames to alert the object that it is an embedded object and to trigger appropriate changes to the user interface of the object application in preparation for opening an editing window.

Like the OleActivate function in OLE 1, IOleObject::DoVerb automatically runs the OLE server application. If an error occurs during verb execution, the object application is shut down.

If an end user invokes a verb by some means other than selecting a command from a menu (say, by double-clicking or, more rarely, single-clicking an object), the object's container should pass a pointer (lpmsg) to a Windows MSG structure containing the appropriate message. For example, if the end user invokes a verb by double-clicking the object, the container should pass a MSG structure containing WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, or WM_RBUTTONDBLCLK. If the container passes no message, lpmsg should be set to NULL. The object should ignore the hwnd member of the passed MSG structure, but can use all the other MSG members.

If the object's embedding container calls IOleObject::DoVerb, the client-site pointer (pClientSite) passed to DoVerb is the same as that of the embedding site. If the embedded object is a link source, the pointer passed to DoVerb is that of the linking client's client site.

When IOleObject::DoVerb is invoked on an OLE link, it may return OLE_E_CLASSDIFF or MK_CONNECTMANUALLY. The link object returns the former error when the link source has been subjected to some sort of conversion while the link was passive. The link object returns the latter error when the link source is located on a network drive that is not currently connected to the caller's computer. The only way to connect a link under these conditions is to first call QueryInterface, ask for IOleLink, allocate a bind context, and run the link source by calling IOleLink::BindToSource.

Container applications that do not support general in-place activation can still use the hwndParent and lprcPosRect parameters to support in-place playback of multimedia files. Containers must pass valid hwndParent and lprcPosRect parameters to IOleObject::DoVerb.

Some code samples pass a lindex value of -1 instead of zero. The value -1 works but should be avoided in favor of zero. The lindex parameter is a reserved parameter, and for reasons of consistency Microsoft recommends assigning a zero value to all reserved parameters.

Notes to Implementers

In addition to the above verbs, an object can define in its OLEVERB structure additional verbs that are specific to itself. Positive numbers designate these object-specific verbs. An object should treat any unknown positive verb number as if it were the primary verb and return OLEOBJ_S_INVALIDVERB to the calling function. The object should ignore verbs with negative numbers that it does not recognize and return E_NOTIMPL.

If the verb being executed places the object in the running state, you should register the object in the Running Object Table (ROT) even if its server application doesn't support linking. Registration is important because the object at some point may serve as the source of a link in a container that supports links to embeddings. Registering the object with the ROT enables the link client to get a pointer to the object directly, instead of having to go through the object's container. To perform the registration, call IOleClientSite::GetMoniker to get the full moniker of the object, call the GetRunningObjectTable function to get a pointer to the ROT, and then call IRunningObjectTable::Register.

Note When the object leaves the running state, remember to revoke the object's registration with the ROT by calling IOleObject::Close. If the object's container document is renamed while the object is running, you should revoke the object's registration and re-register it with the ROT, using its new name. The container should inform the object of its new moniker either by calling IOleObject::SetMoniker or by responding to the object's calling IOleClientSite::GetMoniker.

When showing a window as a result of DoVerb, it is very important for the object to explicitly call SetForegroundWindow on its editing window. This ensures that the object's window will be visible to the user even if another process originally obscured it. For more information see SetForegroundWindow and SetActiveWindow.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

GetRunningObjectTable, IOleClientSite::GetMoniker, IOleLink::BindToSource, IOleObject::Close, IOleObject::EnumVerbs, IOleObject::GetMoniker, IOleObject::SetMoniker, IRunningObjectTable::Register, OleRun, SetForegroundWindow, SetActiveWindow in Win32


IOleObject::EnumAdvise

Retrieves a pointer to an enumerator that can be used to enumerate the advisory connections registered for an object, so a container can know what to release prior to closing down.

HRESULT EnumAdvise(
  IEnumSTATDATA **ppenumAdvise  //Address of output variable that
                                // receives the IEnumSTATDATA interface
                                // pointer
);

Parameter

ppenumAdvise
[out] Address of IEnumSTATDATA* pointer variable that receives the interface pointer to the enumerator object. If the object does not have any advisory connectionsor if an error occurs, the implementation must set *ppenumAdvise to NULL. Each time an object receives a successful call to EnumAdvise, it must increase the reference count on *ppenumAdvise. It is the caller's responsibility to call Release when it is done with the *ppenumAdvise.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Enumerator returned successfully.
E_NOTIMPL
EnumAdvise is not implemented.

Remarks

The EnumAdvise method supplies an enumerator that provides a way for containers to keep track of advisory connections registered for their objects. A container normally would call this function so that it can instruct an object to release each of its advisory connections prior to closing down.

The enumerator to which you get access through IOleObject::EnumAdvise enumerates items of type STATDATA. Upon receiving the pointer, the container can then loop through STATDATA and call IOleObject::Unadvise for each enumerated connection.

The usual way to implement this function is to delegate the call to the IOleAdviseHolder interface. Only the pAdvise and dwConnection members of STATDATA are relevant for IOleObject::EnumAdvise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::Advise, IOleObject::UnAdvise


IOleObject::EnumVerbs

Exposes a pull-down menu listing the verbs available for an object in ascending order by verb number.

HRESULT EnumVerbs(
  IEnumOleVerb **ppEnumOleVerb  //Address of output variable that
                                // receives the IEnumOleVerb interface
                                // pointer
);

Parameter

ppEnumOleVerb
[out] Address of IEnumOleVerb* pointer variable that receives the interface pointer to the new enumerator object. Each time an object receives a call to EnumVerbs, it must increase the reference count on *ppEnumOleVerb. It is the caller's responsibility to call Release when it is done with *ppEnumOleVerb. If an error occurs, *ppEnumOleVerb must be set to NULL.

Return Values

S_OK
Verb(s) enumerated successfully.
OLE_S_USEREG
Delegate to the default handler to use the entries in the registry to provide the enumeration.
OLEOBJ_E_NOVERBS
Object does not support any verbs.

Notes to Callers

Containers call this method to expose a pull-down menu of the verbs available for their embedded objects. You may want your container to call IOleObject::EnumVerbs each and every time such a menu is selected in order to enable such objects as media clips, whose verbs may change while they are running, to update their menus. The default verb for a media clip, for example, changes from "Play" before it is activated to "Stop" once it is running.

Notes to Implementers

The default handler's implementation of IOleObject::EnumVerbs uses the registry to enumerate an object's verbs. If an object application is to use the default handler's implementation, it should return OLE_S_USEREG.

The enumeration returned is of type IEnumOLEVERB:

typedef Enum < OLEVERB > IEnumOLEVERB;

where OLEVERB is defined as:

typedef struct tagOLEVERB
{
    LONG    iVerb;
    LPOLESTR   lpszVerbName;
    DWORD   fuFlags;
    DWORD   grfAttribs;
} OLEVERB;

The following table describes the members of the OLEVERB structure:

OLEVERB Member | Description
iVerb | Verb number being enumerated. If the object supports OLEIVERB_OPEN, OLEIVERB_SHOW and/or OLEIVERB_HIDE (or another predefined verb), these will be the first verbs enumerated, since they have the lowest verb numbers.
lpszVerbName | Name of the verb. In Windows, this value, along with optional embedded ampersand characters to indicate accelerator keys, can be passed to the AppendMenu function. On the Macintosh, the following metacharacters may be passed along with this value: ! marks the menu item with the subsequent character. < sets the character style of the item. ( disables the item. The metacharacters / and ^ are not permitted.
fuFlags | In Windows, a group of flags taken from the flag constants beginning with MF_ defined in AppendMenu. Containers should use these flags in building an object's verb menu. All Flags defined in AppendMenu are supported except for: MF_BITMAP MF_OWNERDRAW MF_POPUP
grfAttribs | In Windows, a group of flag bits taken from the enumeration OLEVERBATTRIB. The flag OLEVERBATTRIB_NEVERDIRTIES indicates that executing this verb will not cause the object to become dirty and is therefore in need of saving to persistent storage. OLEVERBATTRIB_ONCONTAINERMENU indicates that this verb should be placed on the container's menu of object verbs when the object is selected. OLEIVERB_HIDE, OLEIVERB_SHOW, and OLEIVERB_OPEN never have this value set.

For more information see the Windows AppendMenu.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::DoVerb, OleRegEnumVerbs


IOleObject::GetClientSite

Obtains a pointer to an embedded object's client site.

HRESULT GetClientSite(
  IOleClientSite **ppClientSite  //Address of output variable that
                                 // receives the IOleClientSite
                                 // interface pointer
);

Parameter

ppClientSite
[out] Address of IOleClientSite* pointer variable that receives the interface pointer to the object's client site. If an object does not yet know its client site, or if an error has occurred, *ppClientSite must be set to NULL. Each time an object receives a call to GetClientSite, it must increase the reference count on *ppClientSite. It is the caller's responsibility to call Release when it is done with *ppClientSite.

Return Value

S_OK
Client site pointer returned successfully.

Remarks

Link clients most commonly call the IOleObject::GetClientSite method in conjunction with the IOleClientSite::GetContainer method to traverse a hierarchy of nested objects. A link client calls IOleObject::GetClientSite to get a pointer to the link source's client site. The client then calls IOleClientSite::GetContainer to get a pointer to the link source's container. Finally, the client calls IOleContainer::QueryInterface to get IOleObject and IOleObject::GetClientSite to get the container's client site within its container. By repeating this sequence of calls, the caller can eventually retrieve a pointer to the master container in which all the other objects are nested.

Notes to Callers

The returned client-site pointer will be NULL if an embedded object has not yet been informed of its client site. This will be the case with a newly loaded or created object when a container has passed a NULL client-site pointer to one of the object-creation helper functions but has not yet called IOleObject::SetClientSite as part of initializing the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::SetClientSite


IOleObject::GetClipboardData

Retrieves a data object containing the current contents of the embedded object on which this method is called. Using the pointer to this data object, it is possible to create a new embedded object with the same data as the original.

HRESULT GetClipboardData(
  DWORD dwReserved,  //Reserved
  IDataObject **ppDataObject
                     //Address of output variable that receives the
                     // IDataObject interface pointer
);

Parameters

dwReserved
[in] Reserved for future use; must be zero.
ppDataObject
[out] Address of IDataObject* pointer variable that receives the interface pointer to the data object. If an error occurs, *ppDataObject must be set to NULL. Each time an object receives a call to GetClipboardData, it must increase the reference count on *ppDataObject. It is the caller's responsibility to call Release when it is done with *ppDataObject.

Return Values

S_OK
The data transfer object is successfully returned.
E_NOTIMPL
GetClipboardData is not supported.
OLE_E_NOTRUNNING
The object is not running.

Remarks

You can use the GetClipboardData method to convert a linked object to an embedded object, in which case the container application would call IOleObject::GetClipboardData and then pass the data received to OleCreateFromData. This method returns a pointer to a data object that is identical to what would have been passed to the Clipboard by a standard copy operation.

Notes to Callers

If you want a stable snapshot of the current contents of an embedded object, call IOleObject::GetClipboardData. Should the data change, you will need to call the function again for an updated snapshot. If you want the caller to be informed of changes that occur to the data, call IDataObject::QueryInterface, then call IDataObject::DAdvise.

Notes to Implementers

If you implement this function, you must return an IDataObject pointer for an object whose data will not change.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IDataObject, IOleObject::InitFromData, IUnknown::QueryInterface, OleCreateFromData


IOleObject::GetExtent

Retrieves a running object's current display size.

HRESULT GetExtent(
  DWORD dwDrawAspect,  //Value indicating object aspect
  SIZEL *psizel        //Pointer to storage of object size limit
);

Parameters

dwDrawAspect
[in] Value indicating the aspect of the object whose limit is to be retrieved; the value is obtained from the enumerations DVASPECT and from DVASPECT2. Note that newer objects and containers that support optimized drawing interfaces support the DVASPECT2 enumeration values. Older objects and containers that do not support optimized drawing interfaces may not support DVASPECT2. The most common value for this method is DVASPECT_CONTENT, which specifies a full rendering of the object within its container.
psizel
[out] Pointer to where the object's size is to be returned.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Extent information successfully returned.

Remarks

A container calls IOleObject::GetExtent on a running object to retrieve its current display size. If the container can accommodate that size, it will normally do so because the object, after all, knows what size it should be better than the container does. A container normally makes this call as part of initializing an object.

The display size returned by IOleObject::GetExtent may differ from the size last set by IOleObject::SetExtent because the latter method dictates the object's display space at the time the method is called but does not necessarily change the object's native size, as determined by its application.

Note This method must return the same size as DVASPECT_CONTENT for all the new aspects in DVASPECT2. IViewObject2::GetExtent must do the same thing.

If one of the new aspects is requested in dwAspect, this method can either fail or return the same rectangle as for the DVASPECT_CONTENT aspect.

Notes to Callers

Because a container can make this call only to a running object, the container must instead call IViewObject2::GetExtent if it wants to get the display size of a loaded object from its cache.

Notes to Implementers

Implementation consists of filling the sizel structure with an object's height and width.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT, DVASPECT2, IOleObject::SetExtent, IViewObject2::GetExtent


IOleObject::GetMiscStatus

Returns a value indicating the status of an object at creation and loading.

HRESULT GetMiscStatus(
  DWORD dwAspect,  //Value indicating object aspect
  DWORD *pdwStatus //Pointer to storage of status information
);

Parameters

dwAspect
[in] Value indicating the aspect of an object about which status information is being requested. The value is obtained from the enumeration DVASPECT (see "FORMATETC Data Structure").
pdwStatus
[out] Pointer to where the status information is returned. May not be NULL.

Return Values

S_OK
Status information returned successfully.
OLE_S_USEREG
Delegate the retrieval of miscellaneous status information to the default handler's implementation of this method.
CO_E_CLASSNOTREG
There is no CLSID registered for the object.
CO_E_READREGDB
Error accessing the registry.

Remarks

A container normally calls IOleObject::GetMiscStatus when it creates or loads an object in order to determine how to display the object and what types of behaviors it supports.

Objects store status information in the registry. If the object is not running, the default handler's implementation of IOleObject::GetMiscStatus retrieves this information from the registry. If the object is running, the default handler invokes IOleObject::GetMiscStatus on the object itself.

The information that is actually stored in the registry varies with individual objects. The status values to be returned are defined in the enumeration OLEMISC.

The default value of MiscStatus is used if a subkey corresponding to the specified DVASPECT is not found. To set an OLE control, specify DVASPECT==1. This will cause the following to occur in the Registry:

HKEY_CLASSES_ROOT\CLSID\ . . .\MiscStatus = 1

Notes to Implementers

Implementation normally consists of delegating the call to the default handler.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT, FORMATETC, OLEMISC


IOleObject::GetMoniker

Retrieves an embedded object's moniker, which the caller can use to link to the object.

HRESULT GetMoniker(
  DWORD dwAssign,  //Specifies how moniker is assigned to object
  DWORD dwWhichMoniker,
                   //Specifies which moniker is assigned
  IMoniker **ppmk  //Address of output variable that receives the
                   // IMoniker interface pointer
);

Parameters

dwAssign
[in] Determines how the moniker is assigned to the object. Depending on the value of dwAssign, IOleObject::GetMoniker does one of the following:
  • Obtains a moniker only if one has already been assigned,
  • Forces assignment of a moniker, if necessary, in order to satisfy the call, or
  • Obtains a temporary moniker.

Values for dwAssign are specified in the enumeration OLEGETMONIKER.

Note You cannot pass OLEGETMONIKER_UNASSIGN when calling IOleObject::GetMoniker. This value is valid only when calling IOleClientSite::GetMoniker.

dwWhichMoniker
[in] Specifies the form of the moniker being requested. Valid values are taken from the enumeration OLEWHICHMK.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer to the object's moniker. If an error occurs, *ppmk must be set to NULL. Each time an object receives a call to GetMoniker, it must increase the reference count on *ppmk. It is the caller's responsibility to call Release when it is done with *ppmk.

Remarks

The IOleObject::GetMoniker method returns an object's moniker. Like IOleObject::SetMoniker, this method is important only in the context of managing links to embedded objects and even in that case is optional. A potential link client that requires an object's moniker to bind to the object can call this method to obtain that moniker. The default implementation of IOleObject::GetMoniker calls the IOleClientSite::GetMoniker, returning E_UNEXPECTED if the object is not running or does not have a valid pointer to a client site.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CreateItemMoniker, IOleClientSite::GetMoniker, IOleObject::SetMoniker, OLEGETMONIKER, OLEWHICHMK


IOleObject::GetUserClassID

Returns an object's class identifier, the CLSID corresponding to the string identifying the object to an end user.

HRESULT GetUserClassID(
  CLSID *pClsid   //Pointer to the class identifier
);

Parameter

pClsid
[out] Pointer to the class identifier (CLSID) to be returned. An object's CLSID is the binary equivalent of the user-type name returned by IOleObject::GetUserType.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
CLSID returned successfully.

Remarks

GetUserClassID returns the CLSID associated with the object in the registration database. Normally, this value is identical to the CLSID stored with the object, which is returned by IPersist::GetClassID. For linked objects, this is the CLSID of the last bound link source. If the object is running in an application different from the one in which it was created and for the purpose of being edited is emulating a class that the container application recognizes, the CLSID returned will be that of the class being emulated rather than that of the object's own class.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetUserType, IPersist::GetClassID, OleDoAutoConvert, OleGetAutoConvert, OleSetAutoConvert, GetConvertStg, SetConvertStg


IOleObject::GetUserType

Retrieves the user-type name of an object for display in user-interface elements such as menus, list boxes, and dialog boxes.

HRESULT GetUserType(
  DWORD dwFormOfType,  //Specifies form of type name
  LPOLESTR *pszUserType
                       //Address of output variable that receives a
                       // pointer to the requested user type string
);

Parameters

dwFormOfType
[in] Value specifying the form of the user-type name to be presented to users. Valid values are obtained from the USERCLASSTYPE enumeration.
pszUserType
[out] Address of LPOLESTR pointer variable that receives a pointer to the user type string. The caller must free *pszUserType using the current IMalloc instance. If an error occurs, the implementation must set *pszUserType to NULL.

Return Values

S_OK
The object's user-type name is successfully returned.
OLE_S_USEREG
Delegate to the default handler's implementation using the registry to provide the requested information.

Remarks

Containers call IOleObject::GetUserType in order to represent embedded objects in list boxes, menus, and dialog boxes by their normal, user-recognizable names. Examples include "Word Document," "Excel Chart," and "Paintbrush Object." The information returned by IOleObject::GetUserType is the user-readable equivalent of the binary class identifier returned by IOleObject::GetUserClassID.

Notes to Callers

The default handler's implementation of IOleObject::GetUserType uses the object's class identifier (the pClsid parameter returned by IOleObject::GetUserClassID) and the dwFormOfType parameter together as a key into the registry. If an entry is found that matches the key exactly, then the user type specified by that entry is returned. If only the CLSID part of the key matches, then the lowest-numbered entry available (usually the full name) is used. If the CLSID is not found, or there are no user types registered for the class, the user type currently found in the object's storage is used.

You should not cache the string returned from GetUserType. Instead, call this method each and every time the string is needed. This guarantees correct results when the embedded object is being converted from one type into another without the caller's knowledge. Calling this method is inexpensive because the default handler implements it using the registry.

Notes to Implementers

You can use the implementation provided by the default handler by returning OLE_S_USEREG as your application's implementation of this method. If the user type name is an empty string, the message "Unknown Object" is returned.

You can call the OLE helper function OleRegGetUserType to return the appropriate user type.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::SetHostNames, IOleObject::GetUserClassID, OleRegGetUserType, ReadFmtUserTypeStg, USERCLASSTYPE


IOleObject::InitFromData

Initializes a newly created object with data from a specified data object, which can reside either in the same container or on the Clipboard.

HRESULT InitFromData(
  IDataObject *pDataObject,  //Pointer to data object
  BOOL fCreation,            //Specifies how object is created
  DWORD dwReserved           //Reserved
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on the data object from which the initialization data is to be obtained. This parameter can be NULL, which indicates that the caller wants to know if it is worthwhile trying to send data; that is, whether the container is capable of initializing an object from data passed to it. The data object to be passed can be based on either the current selection within the container document or on data transferred to the container from an external source.
fCreation
[in] TRUE indicates the container is inserting a new object inside itself and initializing that object with data from the current selection; FALSE indicates a more general programmatic data transfer, most likely from a source other than the current selection.
dwReserved
[in] Reserved for future use; must be zero.

Return Values

S_OK
If pDataObject is not NULL, the object successfully attempted to initialize itself from the provided data; if pDataObject is NULL, the object is able to attempt a successful initialization.
S_FALSE
If pDataObject is not NULL, the object made no attempt to initialize itself; if pDataObject is NULL, the object cannot attempt to initialize itself from the data provided.
E_NOTIMPL
The object does not support InitFromData.
OLE_E_NOTRUNNING
The object is not running and therefore cannot perform the operation.

Remarks

This method enables a container document to insert within itself a new object whose content is based on a current data selection within the container. For example, a spreadsheet document may want to create a graph object based on data in a selected range of cells.

Using this method, a container can also replace the contents of an embedded object with data transferred from another source. This provides a convenient way of updating an embedded object.

Notes to Callers

Following initialization, the container should call IOleObject::GetMiscStatus to check the value of the OLEMISC_INSERTNOTREPLACE bit. If the bit is on, the new object inserts itself following the selected data. If the bit is off, the new object replaces the selected data.

Notes to Implementers

A container specifies whether to base a new object on the current selection by passing either TRUE or FALSE to the fCreation parameter.

If fCreation is TRUE, the container is attempting to create a new instance of an object, initializing it with the selected data specified by the data object.

If fCreation is FALSE, the caller is attempting to replace the object's current contents with that pointed to by pDataObject. The usual constraints that apply to an object during a paste operation should be applied here. For example, if the type of the data provided is unacceptable, the object should fail to initialize and return S_FALSE.

If the object returns S_FALSE, it cannot initialize itself from the provided data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetMiscStatus, IDataObject::SetData


IOleObject::IsUpToDate

Checks recursively whether or not an object is up to date.

HRESULT IsUpToDate();

Return Values

S_OK
Object is up to date.
S_FALSE
Object is not up to date.
OLE_E_UNAVAILABLE
Status of object cannot be determined in a timely manner.

Remarks

The IsUpToDate method provides a way for containers to check recursively whether or not all objects are up to date. That is, when the container calls this method on the first object, the object in turn calls it for all its own objects, and they in turn for all of theirs, until all objects have been checked.

Notes to Implementers

Because of the recursive nature of IOleObject:IsUpToDate, determining whether an object is out-of-date, particularly one containing one or more other objects, can be as time-consuming as simply updating the object in the first place. If you would rather avoid lengthy queries of this type, make sure that IOleObject::IsUpToDate returns OLE_E_UNAVAILABLE. In cases where the object to be queried is small and contains no objects itself, thereby making an efficient query possible, this method can return either S_OK or S_FALSE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::UpDate


IOleObject::SetClientSite

Informs an embedded object of its display location, called a "client site," within its container.

HRESULT SetClientSite(
  IOleClientSite *pClientSite  //Pointer to an embedded object's
                               // client site
);

Parameter

pClientSite
[in] Pointer to the IOleClientSite interface on the container application's client-site.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
Client site successfully set.

Remarks

Within a compound document, each embedded object has its own client site — the place where it is displayed and through which it receives information about its storage, user interface, and other resources. IOleObject::SetClientSite is the only method enabling an embedded object to obtain a pointer to its client site.

Notes to Callers

A container can notify an object of its client site either at the time the object is created or, subsequently, when the object is initialized.

When creating or loading an object, a container may pass a client-site pointer (along with other arguments) to one of the following helper functions: OleCreate, OleCreateFromFile, OleCreateFromData or OleLoad. These helper functions load an object handler for the new object and call IOleObject::SetClientSite on the container's behalf before returning a pointer to the new object.

Passing a client-site pointer informs the object handler that the client site is ready to process requests. If the client site is unlikely to be ready immediately after the handler is loaded, you may want your container to pass a NULL client-site pointer to the helper function. The NULL pointer says that that no client site is available and thereby defers notifying the object handler of the client site until the object is initialized. In response, the helper function returns a pointer to the object, but upon receiving that pointer the container must call SetClientSite as part of initializing the new object.

Notes to Implementers

Implementation consists simply of incrementing the reference count on, and storing, the pointer to the client site.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleClientSite, IOleObject::GetClientSite, OleCreate, OleCreateFromFile, OleCreateFromData, OleLoad


IOleObject::SetColorScheme

Specifies the color palette that the object application should use when it edits the specified object.

HRESULT SetColorScheme(
  LOGPALETTE *pLogpal   //Pointer to a structure
);

Parameter

pLogpal
[in] Pointer to a LOGPALETTE structure that specifies the recommended palette.

Return Values

S_OK
Color palette received successfully.
E_NOTIMPL
Object does not support setting palettes.
OLE_E_PALETTE
Invalid LOGPALETTE structure pointed to by lpLogPal.
OLE_E_NOTRUNNING
Object must be running to perform this operation.

Remarks

The IOleObject::SetColorScheme method sends the container application's recommended color palette to the object application, which is not obliged to use it.

Notes to Implementers

Upon receiving the palette, the object application should:

  1. Allocate and fill in its own LOGPALETTE structure with the colors specified in the container application's LOGPALETTE structure.
  2. Call CreatePalette to create a palette from the resulting LOGPALETTE structure. This palette can be used to render objects and color menus as the user edits objects in the document.

The first palette entry in the LOGPALETTE structure specifies the foreground color recommended by the container. The second palette entry specifies the recommended background color. The first half of the remaining palette entries are fill colors and the second half are colors for the lines and text.

Container applications typically specify an even number of palette entries. If a container specifies an odd number of entries, the object application should assume that the first half of the total entries plus one designate fill colors, while the remainder designate line and text colors. For example, if there are five entries, the first three should be interpreted as fill colors and the last two as line and text colors.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IOleObject::SetExtent

Informs an object of how much display space its container has assigned it.

HRESULT SetExtent(
  DWORD dwDrawAspect,  //DVASPECT value
  SIZEL  *psizel       //Pointer to size limit for object
);

Parameters

dwDrawAspect
[in] DWORD that describes which form, or "aspect," of an object is to be displayed. The object's container obtains this value from the enumeration DVASPECT (refer to the FORMATETC enumeration). The most common aspect is DVASPECT_CONTENT, which specifies a full rendering of the object within its container. An object can also be rendered as an icon, a thumbnail version for display in a browsing tool, or a print version, which displays the object as it would be rendered using the File Print command.
psizel
[in] Pointer to the size limit for the object.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The object has resized successfully.
OLE_E_NOTRUNNING
The object is not running.

Remarks

A container calls IOleObject::SetExtent when it needs to dictate to an embedded object the size at which it will be displayed. Often, this call occurs in response to an end user resizing the object window. Upon receiving the call, the object, if possible, should recompose itself gracefully to fit the new window.

Whenever possible, a container seeks to display an object at its finest resolution, sometimes called the object's native size. All objects, however, have a default display size specified by their applications, and in the absence of other constraints, this is the size they will use to display themselves. Since an object knows its optimum display size better than does its container, the latter normally requests that size from a running object by calling IOleObject::GetExtent. Only in cases where the container cannot accommodate the value returned by the object does it override the object's preference by calling IOleObject::SetExtent.

Notes to Callers

You can call SetExtent on an object only when the object is running. If a container resizes an object while an object is not running, the container should keep track of the object's new size but defer calling IOleObject::SetExtent until a user activates the object. If the OLEMISC_RECOMPOSEONRESIZE bit is set on an object, its container should force the object to run before calling OleObject::SetExtent.

As noted above, a container may want to delegate responsibility for setting the size of an object's display site to the object itself, by calling IOleObject::GetExtent.

Notes to Implementers

You may want to implement this method so that your object rescales itself to match as closely as possible the maximum space available to it in its container.

If an object's size is fixed, that is, if it cannot be set by its container, OleObject::SetExtent should return E_FAIL. This is always the case with linked objects, whose sizes are set by their link sources, not by their containers.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IAdviseSink::OnViewChange, IOleObject::GetExtent, IViewObject2::GetExtent


IOleObject::SetHostNames

Provides an object with the name of its container application and the compound document in which it is embedded.

HRESULT SetHostNames(
  LPCOLESTR szContainerApp,  //Pointer to name of container
                             // application
  LPCOLESTR szContainerObj  //Pointer to name of container document
);

Parameters

szContainerApp
[in] Pointer to the name of the container application in which the object is running.
szContainerObj
[in] Pointer to the name of the compound document that contains the object. If you do not wish to display the name of the compound document, you can set this parameter to NULL.

Return Value

S_OK
Window title information set successfully.

Remarks

When a container initializes an embedded object, it calls this function to inform the object of the names of both the container application and container document. When the object is opened for editing, it displays these names in the title bar of its window.

Notes to Callers

Call SetHostNames only for embedded objects, because for linked objects, the link source supplies its own separate editing window and title bar information.

Notes to Implementers

An object's application of SetHostNames should include whatever modifications to its user interface may be appropriate to an object's embedded state. Such modifications typically will include adding and removing menu commands and altering the text displayed in the title bar of the editing window.

The complete window title for an embedded object in an SDI container application or an MDI application with a maximized child window should appear as follows:

<object application name> – <object short type> in <container document>

Otherwise, the title should be:

<object application name> – <container document>

The "object short type" refers to a form of an object's name short enough to be displayed in full in a list box.

Since these identifying strings are not stored as part of the persistent state of the object, IOleObject::SetHostNames must be called each time the object loads or runs.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetUserType


IOleObject::SetMoniker

Notifies an object of its container's moniker, the object's own moniker relative to the container, or the object's full moniker.

HRESULT SetMoniker(
  DWORD dwWhichMoniker,  //Value specifying moniker being set
  IMoniker *pmk          //Pointer to moniker
);

Parameters

dwWhichMoniker
[in] Value specifying which moniker is passed in pmk. Values are from the enumeration OLEWHICHMK.
pmk
[in] Pointer to where to return the moniker.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Moniker successfully set.

Remarks

A container that supports links to embedded objects must be able to inform an embedded object when its moniker has changed. Otherwise, subsequent attempts by link clients to bind to the object will fail. The IOleObject::SetMoniker method provides one way for a container to communicate this information.

The container can pass either its own moniker, an object's moniker relative to the container, or an object's full moniker. In practice, if a container passes anything other than an object's full moniker, each object calls the container back to request assignment of the full moniker, which the object requires to register itself in the running object table.

The moniker of an object relative to its container is stored by the object handler as part of the object's persistent state. The moniker of the object's container, however, must not be persistently stored inside the object because the container can be renamed at any time.

Notes to Callers

A container calls IOleObject::SetMoniker when the container has been renamed, and the container's embedded objects currently or can potentially serve as link sources. Containers call SetMoniker mainly in the context of linking because an embedded object is already aware of its moniker. Even in the context of linking, calling this method is optional because objects can call IOleClientSite::GetMoniker to force assignment of a new moniker.

Note to Implementers

Upon receiving a call to SetMoniker, an object should register its full moniker in the running object table and send OnRename notification to all advise sinks that exist for the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

CreateItemMoniker, IAdviseSink::OnRename, IOleClientSite::GetMoniker, IOleObject::GetMoniker


IOleObject::Unadvise

Deletes a previously established advisory connection.

HRESULT Unadvise(
  DWORD dwConnection  //Token
);

Parameter

dwConnection
[in] Contains a token of nonzero value, which was previously returned from IOleObject::Advise through its pdwConnection parameter.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
Advisory connection deleted successfully.
OLE_E_NOCONNECTION
dwConnection does not represent a valid advisory connection.

Remarks

Normally, containers call IOleObject::Unadvise at shutdown or when an object is deleted. In certain cases, containers can call this method on objects that are running but not currently visible as a way of reducing the overhead of maintaining multiple advisory connections. The easiest way to implement this method is to delegate the call to IOleAdviseHolder::Unadvise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::Advise, IOleObject::EnumAdvise, IOleAdviseHolder::Unadvise


IOleObject::Update

Updates an object handler's or link object's data or view caches.

HRESULT Update();

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
All caches are up to date.
OLE_E_CANT_BINDTOSOURCE
Cannot run object to get updated data. The object is for some reason unavailable to the caller.
CACHE_E_NOCACHE_UPDATED
No caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED
Some caches were not updated.

Remarks

The Update method provides a way for containers to keep data updated in their linked and embedded objects. A link object can become out-of-date if the link source has been updated. An embedded object that contains links to other objects can also become out of date. An embedded object that does not contain links cannot become out of date because its data is not linked to another source.

Notes to Implementers

When a container calls a link object's IOleObject::Update method, the link object finds the link source and gets a new presentation from it. This process may also involve running one or more object applications, which could be time-consuming.

When a container calls an embedded object's IOleObject::Update method, it is requesting the object to update all link objects it may contain. In response, the object handler recursively calls IOleObject::Update for each of its own linked objects, running each one as needed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::IsUpToDate


IOleParentUndoUnit

The IOleParentUndoUnit interface enables undo units to contain child undo units. For example, a complex action can be presented to the end user as a single undo action even though a number of separate actions are involved. All the subordinate undo actions are contained within the top-level, parent undo unit.

A parent undo unit is initially created using the IOleUndoManager::Open method. The addition of undo units should always be done through the undo manager. The IOleParentUndoUnit::Open and IOleParentUndoUnit::Close methods on parent units will end up being called by the undo manager. Having parent units call back into the undo manager will cause infinite recursion.

While a parent unit is open, the undo manager adds undo units to it by calling IOleParentUndoUnit::Add. When the undo manager closes a top-level parent, the entire undo unit with its nested subordinates is placed on top of the undo stack.

An enabling parent is required to be open on the stack before any other undo units can be added. If one isn't open, the stack should be cleared instead. This is to ensure that undo units only get added as a result of user actions and not programmatic actions. For example, if your application wants to make clicking a certain button undoable, but that same action is also exposed through the object model. That action should be undoable through the user interface but not the object model because you cannot restore the state of the user's script code. Since the same code implements the change in both cases, the UI code that handles the button click should open an enabling parent on the stack, call the appropriate code, and then close the parent unit. The object model code would not open a parent unit, causing the undo stack to be cleared.

A blocking parent is used when you don’t want your code to call other code that you know may try to add undo units to the stack. For example, you should use a blocking parent if you call code that creates undo units, that your outer code has already created that will fully undo all the desired behavior.

A non-enabling parent is used when you fire an event in response to a user action. The stack would be cleared only if the event handler did something that tried to create an undo unit, but if no handler exists then the undo stack would be preserved.

This interface is derived from IOleUndoUnit and supports all the methods on that interface.

If an object needs to create a parent unit, there are several cases to consider:

  • To create an enabling parent unit, the object calls IOleUndoManager::GetOpenParentState on the undo manager and checks the return value. If the value is S_FALSE, then the object creates the enabling parent and opens it. If the return value is S_OK, then there is a parent already open. If the open parent is blocked (UAS_BLOCKED bit set), or an enabling parent (UAS_BLOCKED and UAS_NOPARENTENABLE bits not set), then there is no need to create the enabling parent. If the currently open parent is a disabling parent (UAS_NOPARENTENABLE bit set), then the enabling parent should be created and opened to re-enable adding undo units. Note that UAS_NORMAL has a value of zero, which means it is the absence of all other bits and is not a bit flag that can be set. If comparing *pdwState against UAS_NORMAL, mask out unused bits from *pdwState with UAS_MASK to allow for future expansion.
  • To create a blocked parent, the object calls IOleUndoManager::GetOpenParentState and checks for an open parent that is already blocked. If one exists, then there is no need to create the new blocking parent. Otherwise, the object creates it and opens it on the stack.
  • To create a disabling parent, the object calls IOleUndoManager::GetOpenParentState and checks for an open parent that is blocked or disabling. If either one exists it is unnecessary to create the new parent. Otherwise, the object creates the parent and opens it on the stack.

In the event that both the UAS_NOPARENTENABLE and UAS_BLOCKED flags are set, the flag that is most relevant to the caller should be used with UAS_NOPARENTENABLE taking precedence. For example, if an object is creating a simple undo unit, it should pay attention to the UAS_NOPARENTENABLE flag and clear the undo stack. If it is creating an enabling parent unit, then it should pay attention to the UAS_BLOCKED flag and skip the creation.

When the parent undo unit is marked blocked, it discards any undo units it receives.

When to Implement

An undo unit that is capable of containing child undo units implements this interface in addition to IOleUndoUnit.

When to Use

The undo manager calls the methods in this interface to add child undo units.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUndoUnit Methods | Description
Do | Instructs the undo unit to carry out its action.
GetDescription | Returns a string that describes the undo unit and can be used in the undo or redo user interface.
GetUnitType | Returns the CLSID and a type identifier for the undo unit.
OnNextAdd | Notifies the last undo unit in the collection that a new unit has been added.
IOleParentUndoUnit Methods | Description
Open | Opens a new parent undo unit, which becomes part of the containing unit's undo stack.
Close | Closes the most recently opened parent undo unit.
Add | Adds a simple undo unit to the collection.
FindUnit | Indicates if the specified unit is a child of this undo unit or one of its children, that is if the specified unit is part of the hierarchy in this parent unit.
GetParentState | Returns state information about the innermost open parent undo unit.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager, IOleUndoUnit


IOleParentUndoUnit::Add

Adds a simple undo unit to the collection.

HRESULT Add(
  IOleUndoUnit* pUU   //Pointer to undo unit to be added
);

Parameters

pUU
[in] Pointer to undo unit to be added.

Return Values

S_OK
The specified unit was successfully added or the parent unit was blocked.

Remarks

The parent undo unit or undo manager must accept any undo unit given to it, unless it is blocked. If it is blocked, it should do nothing but return S_OK.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::Add


IOleParentUndoUnit::Close

Closes the specified parent undo unit.

HRESULT Close(
  IOleParentUndoUnit* pPUU,
                 //Pointer to the currently open parent unit
  BOOL fCommit   //Indicates whether to keep or discard the unit
);

Parameters

pPUU
[in] Pointer to the currently open parent unit to be closed.
fCommit
[in] Indicates whether to keep or discard the unit. If TRUE, the unit is kept in the collection. If FALSE, the unit is discarded. This parameter is used to allow the client to discard an undo unit under construction if an error or a cancellation occurs.

Return Values

S_OK
The parent unit had open child units and it was successfully closed.
S_FALSE
The parent undo unit did not have an open child and it was successfully closed.
E_INVALIDARG
If pPUU does not match the currently open parent undo unit, then implementations of this method should return E_INVALIDARG without changing any internal state unless the parent unit is blocked.

Remarks

A parent undo unit knows it is being closed when it returns S_FALSE from this method. At that time, it should terminate any communication with other objects which may be giving data to it through private interfaces.

Note to Implementers

To process a Close, a parent undo unit first checks to see if it has an open child unit. If it does not, it returns S_FALSE.

If it does have a child unit open, it calls the Close method on the child. If the child returns S_FALSE, then the parent undo unit verifies that pPUU points to the child unit, and closes that child undo unit. If the child returns S_OK then it handled the Close internally and its parent should do nothing.

If the parent unit is blocked, it should check the pPUU parameter to determine the appropriate return code. If pPUU is pointing to itself, then it should return S_FALSE.

Otherwise, it should return S_OK; the fCommit parameter is ignored; and no action is taken.

If pPUU does not match the currently open parent undo unit, then implementations of this method should return E_INVALIDARG without changing any internal state. The only exception to this is if the unit is blocked.

Note to Callers

An error return indicates a fatal error condition.

The parent unit or undo manager must accept the undo unit if fCommit is TRUE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::Close


IOleParentUndoUnit::FindUnit

Indicates if the specified unit is a child of this undo unit or one of its children, that is if the specified unit is part of the hierarchy in this parent unit.

HRESULT FindUnit(
  IOleUndoUnit* pUU   //Pointer to the undo unit to be found
);

Parameters

pUU
[in] Pointer to the undo unit to be found.

Return Values

S_OK
The specified undo unit is in the hierarchy subordinate to this parent.
S_FALSE
The specified undo unit is not part of the hierarchy under this parent. An error indicates an RPC failure condition.

Remarks

This is typically called by the undo manager in its implementation of its DiscardFrom method in the rare event that the unit being discarded is not a top-level unit. The parent unit should look in its own list first, then delegate to each child that is also a parent unit, as determined by doing a QueryInterface for IOleParentUndoUnit.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::DiscardFrom


IOleParentUndoUnit::GetParentState

Returns state information about the innermost open parent undo unit.

HRESULT GetParentState(
  DWORD* pdwState   //Pointer to state information
);

Parameters

pdwState
[out] Pointer to state information. This information is a value taken from the UASFLAGS enumeration.

Return Values

S_OK
The parent's state was successfully returned.

Note to Implementers

If the unit has an open child, it should delegate this method to that child. If not, it should fill in *pdwState values appropriately and return. Note that a parent unit must never be blocked while it has an open child. If this happened it could prevent the child unit from being closed, which would cause serious problems.

Note to Callers

When checking for a normal state, use the UAS_MASK value to mask unused bits in the pdwState parameter to this method for future compatibility. For example:

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

UASFLAGS


IOleParentUndoUnit::Open

Opens a new parent undo unit, which becomes part of the containing unit's undo stack.

HRESULT Open(
  IOleParentUndoUnit* pPUU   //Pointer to the parent undo unit to
                             // open
);

Parameters

pPUU
[in] Pointer to the parent undo unit to be opened.

Return Values

S_OK
The parent undo unit was successfully opened or it is currently blocked.

Remarks

The specified parent unit is created and remains open. The undo manager then calls the Add or Open methods on this parent unit to add new units to it. This parent unit receives any additional undo units until its Close method is called.

The parent unit specified by pPUU is not added to the undo stack until its Close method is called with the fCommit parameter set to TRUE.

The parent undo unit or undo manager must contain any undo unit given to it unless it is blocked. If it is blocked, it must return S_OK but should do nothing else.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::Open


IOleUILinkContainer

The IOleUILinkContainer interface is implemented by containers and used by OLE common dialog boxes. It supports these dialog boxes by providing the methods needed to manage a container's links.

The IOleUILinkContainer methods enumerate the links associated with a container, and specify how they should be updated, automatically or manually. They change the source of a link and obtain information associated with a link. They also open a link's source document, update links, and break a link to the source.

When to Implement

You must implement this interface if you are creating a container application that will use the Links, Change Source, or Update Links dialog boxes, as well as the Object Properties dialog box, which uses this interface indirectly. The Links dialog box calls back to the container application to perform OLE functions that manipulate the links within the container.

When to Use

OLE common dialog boxes use only this interface to manage the properties of a container's links. They can also use it to manage non-OLE (DDE and other container-specific) links.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUILinkContainer Methods | Description
GetNextLink | Enumerates the links in the container.
SetLinkUpdateOptions | Sets update options.
GetLinkUpdateOptions | Determines current update options for the link.
SetLinkSource | Changes the source of the link.
GetLinkSource | Returns Links dialog box information about the link.
OpenLinkSource | Opens a link's source.
UpdateLink | Forces a link to connect to its source and update.
CancelLink | Disconnects selected links.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

OleUIEditLinks, OleUIChangeSource, OleUIUpdateLinks, OleUIObjectProperties, OLEUIEDITLINKS


IOleUILinkContainer::CancelLink

Disconnects the selected links.

HRESULT CancelLink(
  DWORD dwLink  //Unique 32-bit link identifier
);

Parameter

dwLink
[in] Container-defined unique 32-bit identifier for a single link. Containers can use the pointer to the link's container site for this value.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully disconnected the selected links.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Callers

Call CancelLink when the user selects the Break Link button from the Links dialog box. The link should be converted to a picture. The Links dialog box will not be dismissed for OLE links.

Notes To Implementers

For OLE links, OleCreateStaticFromData can be used to create a static picture object using the IDataObject interface of the link as the source.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IDataObject, OleCreateStaticFromData


IOleUILinkContainer::GetLinkSource

Returns information about a link that can be displayed in the UI.

HRESULT GetLinkSource(
  DWORD dwLink,                     //Unique 32-bit link identifier
  LPTSTR FAR* lplpszDisplayName,    //Address of output variable that
                                    // receives a pointer to the display
                                    // name string
  ULONG FAR* lplenFileName,         //Pointer to length of file name
                                    // portion
  LPTSTR FAR* lplpszFullLinkType,   //Address of output variable that
                                    // receives a pointer to the full
                                    // link type string
  LPTSTR FAR* lplpszShortLinkType,  //Address of output variable
                                    // that receives a pointer to the
                                    // short link type string
  BOOL FAR* lpfSourceAvailable,     //Pointer to availability of link
  BOOL FAR* lpfIsSelected           //Pointer to indicate that link
                                    // entry should be selected in listbox
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. See IOleUILinkContainer::GetNextLink.
lplpszDisplayName
[out] Address of LPTSTR pointer variable that receives a pointer to the full display name string for the link source. The Links dialog box will free this string.
lplenFileName
[out] Pointer to the length of the leading file name portion of the lplpszDisplayName string. If the link source is not stored in a file, then *lplenFileName should be 0. For OLE links, call IOleLink::GetSourceDisplayName.
lplpszFullLinkType
[out] Address of LPTSTR pointer variable that receives a pointer to the full link type string that is displayed at the bottom of the Links dialog box. The caller allocates this string. The Links dialog box will free this string. For OLE links, this should be the full User Type name. Use IOleObject::GetUserType, specifying USERCLASSTYPE_FULL for dwFormOfType.
lplpszShortLinkType
[out] Address of LPTSTR pointer variable that receives a pointer to the short link type string that is displayed in the listbox of the Links dialog box. The caller allocates this string. The Links dialog box will free this string. For OLE links, this should be the short User Type name. Use IOleObject::GetUserType, specifying USERCLASSTYPE_SHORT for dwFormOfType.
lpfSourceAvailable
[out] Pointer that returns FALSE if it is known that a link is unavailable since the link is to some known but unavailable document. Certain options, such as Update Now, are disabled (grayed in the UI) for such cases.
lpfIsSelected
[out] Pointer to a BOOL variable that tells the Edit Links dialog box that this link's entry should be selected in the dialog's multi-selection listbox. OleUIEditLinks calls this method at least once for each item to be placed in the links list. If none of them return TRUE, then none of them will be selected when the dialog box is first displayed. If all of them return TRUE, then all will be displayed. That is, it returns TRUE if this link is currently part of the selection in the underlying document, FALSE if not. Any links that are selected in the underlying document are selected in the dialog box; this way, the user can select a set of links and use the dialog box to update them or change their source(s) simultaneously.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Callers

Call this method during dialog box initialization, after returning from the Change Source dialog box.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleLink::GetSourceDisplayName, IOleObject::GetUserType, USERCLASSTYPE, OleUIChangeSource, OLEUICHANGESOURCE


IOleUILinkContainer::GetLinkUpdateOptions

Determine the current update options for a link.

HRESULT GetNextLink(
  DWORD dwLink,  //Unique 32-bit link identifier
  DWORD FAR * lpdwUpdateOpt
                 //Pointer to address to return update option
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. See IOleUILinkContainer::GetNextLink.
lpdwUpdateOpt
[out] Pointer to the location that the current update options will be written.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully determined update options.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

Containers can implement this method for OLE links simply by calling IOleLink::SetUpdateOptions on the link object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleUILinkContainer::GetNextLink, IOleUILinkContainer::SetLinkUpdateOptions, IOleLink::SetUpdateOptions


IOleUILinkContainer::GetNextLink

Enumerates the links in a container.

DWORD GetNextLink(
  DWORD dwLink  //Unique 32-bit linkidentifier
);

Parameter

dwLink
[in] Container-defined unique 32-bit identifier for a single link. This value is only passed to other methods on this interface, so it can be any value that uniquely identifies a link to the container. Containers frequently use the pointer to the link's container site object for this value.

Return Values

Returns a container's link identifiers in sequence; NULL if it has returned the last link.

Notes to Callers

Call this method to enumerate the links in a container. If the value passed in dwLink is NULL, then the container should return the first link's 32-bit identifier. If dwLink identifies the last link in the container, then the container should return NULL.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleUILinkContainer::SetLinkUpdateOptions, IOleUILinkContainer::GetLinkUpdateOptions


IOleUILinkContainer::OpenLinkSource

Opens the link's source.

HRESULT OpenLinkSource(
  DWORD dwLink  //Unique 32-bit link identifier
);

Parameter

dwLink
[in] Container-defined unique 32-bit identifier for a single link. Containers can use the pointer to the link's container site for this value.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully opened the link's source.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Callers

The OpenLinkSource method is called when the Open Source button is selected from the Links dialog box. For OLE links, call IOleObject::DoVerb, specifying OLEIVERB_SHOW for iVerb.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleObject::DoVerb, OLEVERB


IOleUILinkContainer::SetLinkSource

Changes the source of a link.

HRESULT SetLinkSource(
  DWORD dwLink,         //Unique 32-bit link identifier
  LPTSTR lpszDisplayName,  //Pointer to source string to parse
  ULONG FAR* lenFileName,  //Length of the file name portion
  ULONG FAR* pchEaten,  //Pointer to number of characters
                        // successfully parsed
  BOOL fValidateSource  //Specifies whether moniker should be
                        // validated
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. See IOleUILinkContainer::GetNextLink.
lpszDisplayName
[in] Pointer to new source string to be parsed.
lenFileName
Length of the leading file name portion of the lpszDisplayName string. If the link source is not stored in a file, then lenFileName should be 0. For OLE links, call IOleLink::GetSourceDisplayName.
pchEaten
[out] Pointer to the number of characters successfully parsed in lpszDisplayName.
fValidateSource
[in] TRUE if the moniker should be validated; for OLE links, MkParseDisplayName should be called. FALSE if the moniker should not be validated. If possible, the link should accept the unvalidated source, and mark itself as unavailable.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully changed the links source.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Callers

Call this method from the Change Source dialog box, with fValidateSource initially set to TRUE. Change Source can be called directly or from the Links dialog box. If this call to SetLinkSource returns an error (e.g., MkParseDisplayName failed because the source was unavailable), then you should display an Invalid Link Source message, and the user should be allowed to decide whether or not to fix the source. If the user chooses to fix the source, then the user should be returned to the Change Source dialog box with the invalid portion of the input string highlighted. If the user chooses not to fix the source, then SetLinkSource should be called a second time with fValidateSource set to FALSE, and the user should be returned to the Links dialog box with the link marked Unavailable.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

MkParseDisplayName


IOleUILinkContainer::SetLinkUpdateOptions

Sets a link's update options to Automatic (OLEUPDATE_ALWAYS) or Manual (OLEUPDATE_ONCALL).

HRESULT SetLinkUpdate(
  DWORD dwLink,      //Unique 32-bit link identifier
  DWORD dwUpdateOpt  //Update options
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. See IOleUILinkContainer::GetNextLink.
dwUpdateOpt
[in] Update options, which can be Automatic (OLEUPDATE_ALWAYS) or Manual (OLEUPDATE_ONCALL).

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully set the links update options.
E_ACCESSDENIED
Insufficient access permissions.

Remarks

The user selects update options from the Links dialog box.

Notes To Implementers

Containers can implement this method for OLE links by simply calling IOleLink::SetUpdateOptions on the link object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleUILinkContainer::GetNextLink, IOleUILinkContainer::GetLinkUpdateOptions, IOleLink::SetUpdateOptions


IOleUILinkContainer::UpdateLink

Forces selected links to connect to their source and retrieve current information.

HRESULT UpdateLink(
  DWORD dwLink,         //Unique 32-bit link identifier
  DWORD fErrorMessage,  //Determines whether or not caller should
                        // display error message
  DWORD fReserved       //Reserved
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. Containers can use the pointer to the link's container site for this value.
fErrorMessage
[in] Determines whether or not the caller (implementer of IOleUILinkContainer) should show an error message upon failure to update a link. The Update Links dialog box sets this to FALSE. The Object Properties and Links dialog boxes set it to TRUE.
fReserved
[in] Reserved for future use; must be set to FALSE.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully updated linked objects.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Callers

Call this method with fErrorMessage set to TRUE in cases where the user expressly presses a button to have a link updated, that is, presses the Links' Update Now button. Call it with FALSE in cases where the container should never display an error message, that is, where a large set of operations are being performed and the error should be propagated back to the user later, as might occur with the Update links progress meter. Rather than providing one message for each failure, assuming there are failures, provide a single message for all failures at the end of the operation.

Notes To Implementers

For OLE links, call IOleObject::Update.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

IOleObject::Update


IOleUILinkInfo

The IOleUILinkInfo interface is an extension of the IOleUILinkContainer interface. It returns the time that an object was last updated, which is link information that IOleUILinkContainer does not provide.

When To Implement

You must implement this interface so your container can support the "Link" page of the Object Properties dialog box. If you are writing a container that does not implement links, you do not need to implement this interface.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUILinkContainer Methods | Description
GetNextLink | Enumerates the links in the container.
SetLinkUpdateOptions | Sets update options.
GetLinkUpdateOptions | Determines current update options for the link.
SetLinkSource | Changes the source of the link.
GetLinkSource | Returns Links dialog box information about link.
OpenLinkSource | Opens a link's source.
UpdateLink | Forces a link to connect to its source and update.
CancelLink | Breaks the link.
IOleUILinkInfo Methods | Description
GetLastUpdate | Determines the last time the object was updated, whether automatically or manually.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.


IOleUILinkInfo::GetLastUpdate

Indicates when the object was last updated.

HRESULT GetLastUpdate(
  DWORD dwLink,  //Unique 32-bit link identifier
  FILETIME FAR * lpLastUpdate
                 //Pointer to the time object was last updated
);

Parameters

dwLink
[in] Container-defined unique 32-bit identifier for a single link. Containers can use the pointer to the link's container site for this value.
lpLastUpdate
[out] Pointer to the time that the object was last updated.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

If the time that the object was last updated is known, copy it to lpLastUpdate. If it is not known, then leave lpLastUpdate unchanged and Unknown will be displayed in the link page.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.


IOleUIObjInfo

The IOleUIObjInfo interface is implemented by containers and used by the container's Object Properties dialog box and by the Convert dialog box. It provides information used by the General and View pages of the Object Properties dialog box , which display information about the object's size, location, type, and name. It also allows the object to be converted via the Convert dialog box. The View page allows the object's icon to be modified from its original form, and its display aspect to be changed (iconic versus content). Optionally, you can have your implementation of this interface allow the scale of the object to be changed.

When To Implement

You must implement this interface so your container application can support the OleUIObjectProperties function and the dialog box that it implements.

When To Use

Use this interface when you need to get and set information required by the Object Properties dialog box , and to support the Convert dialog box.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUILinkContainer Methods | Description
GetNextLink | Enumerates the links in the container.
SetLinkUpdateOptions | Sets update options.
GetLinkUpdateOptions | Determines current update options for the link.
SetLinkSource | Changes the source of the link.
GetLinkSource | Returns Links dialog box information about link.
OpenLinkSource | Opens a link's source.
UpdateLink | Forces a link to connect to its source and update.
CancelLink | Breaks the link.
IOleUIObjInfo Methods | Description
GetObjectInfo | Gets general information about the object.
GetConvertInfo | Gets information that is used for the Convert dialog box.
ConvertObject | Converts the object once the user selects a destination type.
GetViewInfo | Gets the current icon, aspect, and scale of the object.
SetViewInfo | Sets the current icon, aspect, and scale of the object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

OleUIObjectProperties


IOleUIObjInfo::ConvertObject

Converts the object to the type of the new CLSID.

HRESULT ConvertObject(
  DWORD dwObject,    //Unique 32-bit object identifier
  REFCLSID clsidNew  //CLSID to convert the object to
);

Parameters

dwObject
[in] Unique 32-bit identifier for the object.
clsidNew
[in] CLSID to convert the object to.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

Your implementation of ConvertObject needs to convert the object to the CLSID specified. The actions taken by the convert operation are similar to the actions taken after calling OleUIConvert.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

OleUIConvert


IOleUIObjInfo::GetConvertInfo

Gets the conversion information associated with the specified object.

HRESULT GetConvertInfo(
  DWORD dwObject,  //Unique 32-bit object identifier
  CLSID FAR * lpClassID,
                   //Pointer to location of CLSID of the object
  WORD FAR *lpwFormat,
                   //Pointer to clipboard format of the object
  CLSID FAR * lpConvertDefaultClassID,
                   //Pointer to default class to convert object to
  LPCLSID FAR * lplpClsidExclude,
                   //Address of output variable that receives a
                   // pointer to an array of excluded CLSIDs
  UINT FAR * lpcClsidExclude
                   //Pointer to output variable that receives the
                   // number of CLSIDs in the array of excluded CLSIDs
);

Parameters

dwObject
[in] Unique 32-bit identifier for the object.
lpClassID
[out] Pointer to the location to return the object's CLSID.
lpwFormat
[out] Pointer to the clipboard format of the object.
lpConvertDefaultClassID
[out] Pointer to the default class, selected from the UI, to convert the object to.
lplpClsidExclude
[out] Address of LPCLSID pointer variable that receives a pointer to an array of CLSIDs that should be excluded from the UI for this object. If lpcClsidExclude is zero, then *lplpClsidExclude is set to NULL.
lpcClsidExclude
[out] Address of an output variable that receives the number of CLSIDs at *lplpClsidExclude. May be zero.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

You must fill in the CLSID of the object at a minimum. lpwFormat may be left at zero if the format of the storage is unknown.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.


IOleUIObjInfo::GetObjectInfo

Gets size, type, name and location information about an object.

HRESULT GetObjectInfo(
  DWORD dwObject,              //Unique 32-bit object identifier
  DWORD FAR *lpdwObjSize,      //Pointer to object's size
  LPTSTR FAR *lplpszLabel,     //Address of output variable that
                               // receives a pointer to the object's
                               // label string
  LPTSTR FAR * lplpszType,     //Address of output variable that
                               // receives a pointer to the object's
                               // long type string
  LPTSTR FAR * lplpszShortType,  //Address of output variable that
                                 // receives a pointer to the object's
                                 // short type string
  LPTSTR FAR * lplpszLocation  //Address of output variable that
                               // receives a pointer to the object's
                               // location string
);

Parameters

dwObject
[in] Unique 32-bit identifier for the object.
lpdwObjSize
[out] Pointer to the object's size, in bytes, on disk. This may be an approximate value.
lplpszLabel
[out] Address of LPTSTR pointer variable that receives a pointer to the object's label string. The parameter lplpszLabel may be NULL to indicate that the implementation should not return the label string.
lplpszType
[out] Address of LPTSTR pointer variable that receives a pointer to the object's long type string. The parameter lplpszType may be NULL to indicate that the implementation should not return the long type string.
lplpszShortType
[out] Address of LPTSTR pointer variable that receives a pointer to the object's short type string. The parameter lplpszShortType may be NULL to indicate that the implementation should not return the short type string.
lplpszLocation
[out] Address of LPTSTR pointer variable that receives a pointer to the object's source location string. The parameter lplpszLocation may be NULL to indicate that the implementation should not return the location string.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned object information.

Remarks

The strings and the object's size are displayed in the object properties "General" page.

Notes To Implementers

Your implementation of GetObjectInfo should place each of the object's attributes in the out parameters provided. Set lpdwObjSize to (DWORD)-1 when the size of the object is unknown. Allocate all strings (the rest of the params) with the OLE task allocator obtained via CoGetMalloc, as is standard for all OLE interfaces with [out] string parameters, or you can simply use CoTaskMemAlloc.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

CoGetMalloc, CoTaskMemAlloc


IOleUIObjInfo::GetViewInfo

Gets the view information associated with the object.

HRESULT GetViewInfo(
  DWORD dwObject,            //Unique 32-bit object identifier
  HGLOBAL FAR * phMetaPict,  //Pointer to object's current icon
  DWORD * pdvAspect,         //Pointer to object's current aspect
  int * pnCurrentScale       //Pointer to object's current scale
);

Parameters

dwObject
[in] Unique 32-bit identifier for the object.
phMetaPict
[in] Pointer to the object's current icon. Could be NULL, indicating that the caller is not interested in the object's current presentation.
pdvAspect
[in] Pointer to the object's current aspect. Could be NULL, indicating that the caller is not interested in the object's current aspect, i.e., DVASPECT_ICONIC or DVASPECT_CONTENT.
pnCurrentScale
[in] Pointer to the object's current scale. Could be NULL, indicating that the caller is not interested in the current scaling factor applied to the object in the container's view.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

You must fill in the object's current icon, aspect, and scale.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

OLEUIVIEWPROPS


IOleUIObjInfo::SetViewInfo

Sets the view information associated with the object.

HRESULT SetViewInfo(
  DWORD dwObject,       //Unique 32-bit object identifier
  HGLOBAL hMetaPict,    //New icon for the object
  DWORD dvAspect,       //New display aspect for the objec
  int nCurrentScale,    //New scale for the objec
  BOOL bRelativeToOrig  //Scale of the object relative to origin
);

Parameters

dwObject
[in] Unique 32-bit identifier for the object.
hMetaPict
[in] New icon for the object.
dvAspect
[in] Object's new display aspect or view.
nCurrentScale
[in] Object's new scale.
bRelativeToOrig
[in] Scale of the object, relative to the origin. This value is TRUE if the new scale should be relative to the original scale of the object. If FALSE, nCurrentScale applies to the object's current size.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
Successfully returned link information.
E_ACCESSDENIED
Insufficient access permissions.

Notes To Implementers

You should apply the new attributes (icon, aspect, and scale) to the object. If bRelativeToOrig is set to TRUE, nCurrentScale (in percentage units) applies to the original size of the object before it was scaled. If bRelativeToOrig is FALSE, nCurrentScale applies to the object's current size.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.

See Also

DVASPECT


IOleUndoManager

The IOleUndoManager interface enables containers to implement multi-level undo and redo operations for actions that occur within contained controls.

The control must create an undo unit with the IOleUndoUnit interface or a parent undo unit with the IOleParentUndoUnit interface derived from IOleUndoUnit. Both of these interfaces perform the undo action and the parent undo unit additionally can contain nested undo units.

The undo manager provides a centralized undo and redo service. It manages parent undo units and simple undo units on the undo and redo stacks. Whether an object is UI-active or not, it can deposit undo units on these stacks by calling methods in the undo manager.

The centralized undo manager then has the data necessary to support the undo and redo user interface for the host application and can discard undo information gradually as the stack becomes full.

The undo manager is implemented as a service and objects obtain a pointer to IOleUndoManger from the IServiceProvider interface. It is usually implemented by the container. The service manages two stacks, the undo stack and the redo stack, each of which contains undo units generated by embedded objects or by the container application itself.

Undo units are typically generated in response to actions taken by the end user. An object does not generate undo actions for programmatic events. In fact, programmatic events should clear the undo stack since the programmatic event can possibly invalidate assumptions made by the undo units on the stack.

When the object's state changes, it creates an undo unit encapsulating all the information needed to undo that change. The object calls methods in the undo manager to place its undo units on the stack. Then, when the end user selects an Undo operation, the undo manager takes the top undo unit off the stack, invokes its action by calling its IOleUndoUnit::Do method, and then releases it. When an end user selects a Redo operation, the undo manager takes the top redo unit off the stack, invokes its action by calling its IOleUndoUnit::Do method, and then releases it.

The undo manager has three states: the base state, the undo state, and the redo state. It begins in the base state. To perform an action from the undo stack, it puts itself into the undo state, calls IOleUndoUnit::Do on the undo unit, and goes back to the base state. To perform an action from the redo stack, it puts itself into the redo state, calls IOleUndoUnit::Do on the undo unit, and goes back to the base state.

If the undo manager receives a new undo unit while in the base state, it places the unit on the undo stack and discards the entire redo stack. While it is in the undo state, it puts incoming units on the redo stack. While it is in the redo state, it places them on the undo stack without flushing the redo stack.

The undo manager also allows objects to discard the undo or redo stack starting from any object in either stack.

The host application determines the scope of an undo manager. For example, in one application, the scope might be at the document level; a separate undo manager is maintained for each document; and undo is managed independently for each document. However, another application maintain one undo manager, and therefore one undo scope, for the entire application.

Handling Errors

Having an undo operation fail and leaving the document in an unstable state is something the undo manager, undo units, and the application itself all have to work together to avoid. As a result, there are certain requirements that undo units, the undo manager, and the application or component using undo must conform to.

To perform an undo, the undo manager calls IOleUndoUnit::Do on one or more undo units which can, in turn, contain more units. If a unit somewhere in the hierarchy fails, the error will eventually reach the undo manager, which is responsible for making an attempt to roll back the state of the document to what it was before the call to the last top-level unit. The undo manager performs the rollback by calling IOleUndoUnit::Do on the unit that was added to the redo stack during the undo attempt. If the rollback also fails, then the undo manager is forced to abandon everything and return to the application. The undo manager indicates whether or not the rollback succeeded, and the application can take different actions based on this, such as reinitializing components so they're in a known state.

All the steps in adding an undo unit to the stack should be performed atomically. That is, all steps must succeed or none of them should succeed.

The host application that provides the undo manager decides what action to take when undo fails. At the very least, it should inform the user of the failure. The host application will be told by the undo manager whether or not the undo succeeded and whether or not the attempted rollback succeeded. In case both the undo and rollback failed, the host application can present the user with several options, including immediately shutting down the application.

Simple undo units must not change the state of any object if they return failure. This includes the state of the redo stack or undo stack if performing a redo. They are also required to put a corresponding unit on the redo or undo stack if they succeed. The application should be stable before and after the unit is called.

Parent undo units have the same requirements as simple units, with one exception. If one or more children succeeded prior to another child's failure, the parent unit must commit its corresponding unit on the redo stack and return the failure to its parent. If no children succeeded, the parent unit should commit its redo unit only if it has made a state change that needs to be rolled back. For example, suppose a parent unit contains three simple units. The first two succeed and added units to the redo stack, but the third one failed. At this point, the parent unit commits its redo unit and returns the failure.

As a side effect, the parent unit should never make state changes that depend on the success of their children. Doing this will cause the rollback behavior to break. If a parent unit makes state changes, it should do them before calling any children. Then, if the state change fails, it should not commit its redo unit, it should not call any children, and it should return the failure to its parent.

The undo manager has one primary requirement for error handling: to attempt rollback when an undo or redo fails.

Non-compliant Objects

Objects that do not support multi-level undo can cause serious problems for a global undo service. Since the object cannot be relied on to properly update the undo manager, any units submitted by other objects are also suspect, because their units may rely on the state of the non-compliant object. Attempting to undo a compliant object's units may not be successful, because the state in the non-compliant object will not match.

To detect objects that do not support multi-level undo, check for the OLEMISC_SUPPORTSMULTILEVELUNDO value. An object that can participate in the global undo service sets this value.

When an object without this value is added to a user-visible undo context, the safest thing to do is disable the undo user interface for this context. Alternatively, a dialog could be presented to the user, asking them whether to attempt to provide partial undo support, working around the non-compliance of the new object.

In addition, non-compliant objects may be added to nested containers. In this case, the nested container needs to notify the undo manager that undo can no longer be safely supported by calling IOleUndoManager::Enable(FALSE).

When to Implement

Implement this interface to provide centralized undo services to the objects in a container.

When to Use

Call the methods in this interface to participate in global undo services.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUndoManager Methods | Description
Open | Opens a new parent undo unit, which becomes part of its containing unit's undo stack.
Close | Closes the specified parent undo unit.
Add | Adds a simple undo unit to the collection.
GetOpenParentState | Returns state information about the innermost open parent undo unit.
DiscardFrom | Instructs the undo manager to discard the specified undo unit and all undo units below it on the undo or redo stack.
UndoTo | Instructs the undo manager to perform actions back through the undo stack, down to and including the specified undo unit.
RedoTo | Instructs the undo manager to invoke undo actions back through the redo stack, down to and including the specified undo unit.
EnumUndoable | Creates an enumerator object that the caller can use to iterate through a series of top-level undo units from the undo stack.
EnumRedoable | Creates an enumerator object that the caller can use to iterate through a series of top-level undo units from the redo stack.
GetLastUndoDescription | Returns the description for the top-level undo unit that is on top of the undo stack.
GetLastRedoDescription | Returns the description for the top-level undo unit that is on top of the redo stack.
Enable | Enables or disables the undo manager.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit, IOleUndoUnit


IOleUndoManager::Add

Adds a simple undo unit to the collection. While a parent undo unit is open, the undo manager adds undo units to it by calling IOleParentUndoUnit::Add.

HRESULT Add(
  IOleUndoUnit* pUU   //Pointer to undo unit to be added
);

Parameters

pUU
[in] Pointer to undo unit to be added.

Return Values

S_OK
The specified unit was successfully added, the parent unit was blocked, or the undo manager is disabled.

Remarks

This method is implemented the same as IOleParentUndoUnit::Add. The parent undo unit or undo manager must accept any undo unit given to it, unless it is blocked. If it is blocked, it should do nothing but return S_OK.

Note to Implementers

If the undo manager is in the base state, it should put the new unit on the undo stack and discard the entire redo stack. If the undo manager is in the undo state, it should put new units on the redo stack. If the undo manager is in the redo state, it should put units on the undo stack without affecting the redo stack.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::Add


IOleUndoManager::Close

Closes the specified parent undo unit.

HRESULT Close(
  IOleParentUndoUnit* pPUU,
                 //Pointer to the currently open parent unit
  BOOL fCommit   //Indicates whether to keep or discard the unit
);

Parameters

pPUU
[in] Pointer to the currently open parent unit to be closed.
fCommit
[in] Indicates whether to keep or discard the unit. If TRUE, the unit is kept in the collection. If FALSE, the unit is discarded. This parameter is used to allow the client to discard an undo unit under construction if an error or a cancellation occurs.

Return Values

S_OK
The undo manager had an open parent undo unit and it was successfully closed. If the undo manager is disabled, it should immediately return S_OK and do nothing else.
S_FALSE
The parent undo unit did not have an open child and it was successfully closed.
E_INVALIDARG
If pPUU does not match the currently open parent undo unit, then implementations of this method should return E_INVALIDARG without changing any internal state unless the parent unit is blocked.

Remarks

This method is implemented the same as IOleParentUndoUnit::Close. A parent undo unit knows it is being closed when it returns S_FALSE from this method. At that time, it should terminate any communication with other objects which may be giving data to it through private interfaces.

Note to Implementers

To process a Close, a parent undo unit first checks to see if it has an open child unit. If it does not, it returns S_FALSE.

If it does have a child unit open, it calls the Close method on the child. If the child returns S_FALSE, then the parent undo unit verifies that pPUU points to the child unit, and closes that child undo unit. If the child returns S_OK then it handled the Close internally and its parent should do nothing.

If the parent unit is blocked, it should check the pPUU parameter to determine the appropriate return code. If pPUU is pointing to itself, then it should return S_FALSE.

Otherwise, it should return S_OK; the fCommit parameter is ignored; and no action is taken.

If pPUU does not match the currently open parent undo unit, then implementations of this method should return E_INVALIDARG without changing any internal state. The only exception to this is if the unit is blocked.

Note to Callers

An error return indicates a fatal error condition.

The parent unit or undo manager must accept the undo unit if fCommit is TRUE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::Close


IOleUndoManager::DiscardFrom

Instructs the undo manager to discard the specified undo unit and all undo units below it on the undo or redo stack.

HRESULT DiscardFrom(
  IOleUndoUnit* pUU   //Pointer to undo unit to be discarded
);

Parameters

pUU
[in] Pointer to undo unit to be discarded. This parameter can be NULL to discard the entire undo or redo stack. If the parameter is not NULL then the stack will not be discarded.

Return Values

S_OK
The specified undo unit was successfully discarded.
E_INVALIDARG
The specified undo unit was not found in the stacks.
E_UNEXPECTED
The undo manager is disabled.

Remarks

The undo manager first searches the undo stack for the given unit, and if not found there searches the redo stack. Once found, the given unit and all below it on the same stack are discarded. The undo unit may be a child of a parent unit contained by the undo manager, as determined by calling IOleParentUndoUnit::FindUnit. If it is a child unit, then the root unit containing the given unit and all units below it on the appropriate stack are discarded.

If there is an open parent unit and the DiscardFrom method is called and the puu parameter is NULL, the undo manager should immediately release and discard the open parent unit without calling the IOleUnderManager::Close method first. When the object that opened the parent unit attempts to close it, IOleUndoManager::Close will return S_FALSE. If a parent unit is open, throw it away and discard the stack. If the parent unit is not open, just throw the stack away. If the pUU parameter is not NULL, then any open parent units should be left open.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::FindUnit, IOleUndoManager::Close


IOleUndoManager::Enable

Enables or disables the undo manager.

HRESULT Enable(
  BOOL fEnable   //Indicates whether to enable or disable the undo
                 // manager
);

Parameters

fEnable
[in] Indicates whether to enable or disable the undo manager. If TRUE, the undo manager should be enabled. If FALSE, the undo manager should be disabled.

Return Values

S_OK
The undo manager was successfully enabled or disabled.
E_UNEXPECTED
There is an open undo unit on the stack or the undo manager is currently performing an undo or a redo.

Remarks

The undo manager should clear both stacks when making the transition from enabled to disabled.

If the undo manager is disabled, each method in IOleUndoManager must behave as specified. See each method for details.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager


IOleUndoManager::EnumRedoable

Creates an enumerator object that the caller can use to iterate through a series of top-level undo units from the redo stack.

HRESULT EnumRedoable(
  IEnumOleUndoUnits** ppEnum   //Address of output variable that
                               // receives the IEnumOleUndoUnits
                               // interface pointer
);

Parameters

ppEnum
[out] Address of IEnumOleUndoUnits* pointer variable that receives the interface pointer to the enumerator object.

Return Values

S_OK
The enumerator object was successfully created and the interface pointer was returned.
E_UNEXPECTED
The undo manager is disabled.

Remarks

A new enumerator object is created each time this method is called. If the series of enumerated items changes over time, the results of enumeration operations can vary from one call to the next.

This method calls AddRef on the new enumerator object to increment its reference count. The caller is responsible for calling Release on the enumerator object when it is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IEnumOleUndoUnits


IOleUndoManager::EnumUndoable

Creates an enumerator object that the caller can use to iterate through a series of top-level undo units from the undo stack.

HRESULT EnumUndoable(
  IEnumOleUndoUnits** ppEnum   //Address of output variable that
                               // receives the IEnumOleUndoUnits
                               // interface pointer
);

Parameters

ppEnum
[out] Address of IEnumOleUndoUnits* pointer variable that receives the interface pointer to the enumerator object.

Return Values

S_OK
The enumerator object was successfully created and the interface pointer was returned.
E_UNEXPECTED
The undo manager is disabled.

Remarks

A new enumerator object is created each time this method is called. If the series of enumerated items changes over time, the results of enumeration operations can vary from one call to the next.

This method calls AddRef on the new enumerator object to increment its reference count. The caller is responsible for calling Release on the enumerator object when it is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IEnumOleUndoUnits


IOleUndoManager::GetLastRedoDescription

Returns the description for the top-level undo unit that is on top of the redo stack.

HRESULT GetLastRedoDescription(
  BSTR* pBstr   //Pointer to string
);

Parameters

pBstr
[out] Pointer to a string that contains a description of the top-level undo unit on the redo stack.

Return Values

S_OK
The string was successfully returned and it contains a valid description.
E_FAIL
The undo stack is empty.
E_UNEXPECTED
The undo manager is disabled.

Remarks

This method provides a convenient shortcut for the host application to add a description to its Edit Redo menu item. The *pbstr parameter is a string allocated with the standard string allocator. The caller is responsible for freeing this string.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::GetLastUndoDescription


IOleUndoManager::GetLastUndoDescription

Returns the description for the top-level undo unit that is on top of the undo stack.

HRESULT GetLastUndoDescription(
  BSTR* pBstr   //Pointer to string
);

Parameters

pBstr
[out] Pointer to a string that contains a description of the top-level undo unit on the undo stack.

Return Values

S_OK
The string was successfully returned and it contains a valid description.
E_FAIL
The undo stack is empty.
E_UNEXPECTED
The undo manager is disabled.

Remarks

This method provides a convenient shortcut for the host application to add a description to its Edit Undo menu item. The *pbstr parameter is a string allocated with the standard string allocator. The caller is responsible for freeing this string.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::GetLastRedoDescription


IOleUndoManager::GetOpenParentState

Returns state information about the innermost open parent undo unit.

HRESULT GetOpenParentState(
  DWORD* pdwState   //Pointer to state information
);

Parameters

pdwState
[out] Pointer to state information. This information is a value taken from the UASFLAGS enumeration.

Return Values

S_OK
There was an open parent unit and its state was successfully returned or the undo manager is disabled.
S_FALSE
There is no open parent unit.

Note to Implementers

If there is an open parent unit, this method calls IOleParentUnit::GetParentState.

If the undo manager is disabled, it should fill the pdwState parameter with UAS_BLOCKED and return S_OK.

Note to Callers

When checking for a normal state, use the UAS_MASK value to mask unused bits in the pdwState parameter to this method for future compatibility.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

UASFLAGS


IOleUndoManager::Open

Opens a new parent undo unit, which becomes part of its containing unit's undo stack.

HRESULT Open(
  IOleParentUndoUnit* pPUU   //Pointer to the parent undo unit to
                             // open
);

Parameters

pPUU
[in] Pointer to the parent undo unit to be opened.

Return Values

S_OK
The parent undo unit was successfully opened or if a currently open unit is blocked. If the undo manager is currently disabled, it should return S_OK and do nothing else.

Remarks

This method is implemented the same as IOleParentUndoUnit::Open. The specified parent unit is created and remains open. The undo manager then calls the Add or Open methods on this parent unit to add new units to it. This parent unit receives any additional undo units until its Close method is called.

The parent unit specified by pPUU is not added to the undo stack until its Close method is called with the fCommit parameter set to TRUE.

The parent undo unit or undo manager must contain any undo unit given to it unless it is blocked. If it is blocked, it must return S_OK but should do nothing else.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::Open


IOleUndoManager::RedoTo

Instructs the undo manager to invoke undo actions back through the redo stack, down to and including the specified undo unit.

HRESULT RedoTo(
  IOleUndoUnit* pUU   //Pointer to the top level unit to redo
);

Parameters

pUU
[in] Pointer to the top level unit to redo. If this parameter is NULL, the most recently added top level unit is used.

Return Values

S_OK
The specified undo unit was successfully invoked to perform its undo action.
E_INVALIDARG
The specified undo unit is not on the redo stack.
E_ABORT
Both the undo attempt and the rollback attempt failed. The undo manager should never propagate the E_ABORT obtained from a contained undo unit. Instead, it should map any E_ABORT values returned from other undo units to E_FAIL.
E_UNEXPECTED
The undo manager is disabled.

Remarks

This method calls the IOleUndoUnit::Do method on each top-level undo unit. Then, it releases that undo unit.

Note that the specified undo unit must be a top-level unit, typically retrieved through IOleUndoManager::EnumRedoable.

In case an error is returned from the undo unit, the undo manager needs to attempt to rollback the state of the document to recover from the error by performing actions on the undo stack.

No matter what the success of the rollback, the undo manager should always clear both stacks before returning the error.

If the undo manager has called the Do method on more than one top-level unit, it should only rollback the unit that returned the error. The top-level units that succeeded should not be rolled back.

The undo manager must also keep track of whether or not units were added to the opposite stack so it won't attempt rollback if nothing was added. See the IOleUndoManager interface for detailed description of error handling.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::EnumRedoable, IOleUndoManager::UndoTo, IOleUndoUnit::Do


IOleUndoManager::UndoTo

Instructs the undo manager to invoke undo actions back through the undo stack, down to and including the specified undo unit.

HRESULT UndoTo(
  IOleUndoUnit* pUU   //Pointer to the top level unit to undo
);

Parameters

pUU
[in] Pointer to the top level unit to undo. If this parameter is NULL, the most recently added top level unit is used.

Return Values

S_OK
The specified undo unit was successfully invoked to perform its undo action.
E_INVALIDARG
The specified undo unit is not on the undo stack.
E_ABORT
Both the undo attempt and the rollback attempt failed. The undo manager should never propagate the E_ABORT obtained from a contained undo unit. Instead, it should map any E_ABORT values returned from other undo units to E_FAIL. The undo manager should map any E_ABORT value returned from other undo units to E_FAIL because the caller of IOleUndoManager::UndoTo knows that the undo attempt and the rollback attempt failed and this is the only reason for the return value of E_ABORT.
E_UNEXPECTED
The undo manager is disabled.

Note to Callers

It is possible for an undo unit to return E_ABORT as a failure, but that has no specific meaning on IOleUndoUnit. Since the undo manager will typically return the error code given by the failed undo unit, and E_ABORT does have a specific meaning on IOleUndoManager::UndoTo, the undo manager should never pass on E_ABORT because the caller will interpret that as the rollback failing when in fact it may have succeeded.

Remarks

This method calls the IOleUndoUnit::Do method on each top-level undo unit. Then, it releases that undo unit.

Note that the specified undo unit must be a top-level unit, typically retrieved through IOleUndoManager::EnumUndoable.

In case an error is returned from the undo unit, the undo manager needs to attempt to rollback the state of the document to recover from the error by performing actions on the redo stack.

No matter what the success of the rollback, the undo manager should always clear both stacks before returning the error.

If the undo manager has called the Do method on more than one top-level unit, it should only rollback the unit that returned the error. The top-level units that succeeded should not be rolled back.

The undo manager must also keep track of whether or not units were added to the opposite stack so it won't attempt rollback if nothing was added. See the IOleUndoManager interface for detailed description of error handling.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::EnumUndoable, IOleUndoManager::RedoTo, IOleUndoUnit::Do


IOleUndoUnit

The IOleUndoUnit interface is the main interface on an undo unit. An undo unit encapsulates the information necessary to undo or redo a single action.

When an object's state changes and it needs to create an undo unit, it first needs to know what parent units are open. It calls the IOleUndoManager::GetOpenParentState method to determine this. If the call returns S_FALSE, then there is no enabling parent. If the call returns S_OK but the UAS_NOPARENTENABLE flag is set, then the open parent is a disabling parent. In either of these cases, the object calls IOleUndoManager::DiscardFrom(NULL) on the undo manager and skips creating the undo unit.

If the method returns S_OK, but the UAS_BLOCKED flag is set, then the open parent is a blocking parent. The object does not need to create an undo unit, since it would be immediately discarded. If the return value is S_OK and neither of the bit flags are set, then the object creates the undo unit and calls IOleUndoManager::Add on the undo manager.

The object should retain a pointer to the undo manager.

When to Implement

An object creates an undo unit that implements this interface when the end user has performed an action that can be undone. The object calls the IOleUndoManager::Add method to add the undo unit to the undo stack. Most controls can implement this interface to support the centralized undo operations. They do not have to implement IOleParentUndoUnit or IOleUndoManager. The undo manager with the IOleUndoManager interface is usually provided by the object container.

When to Use

The undo manager calls the Do and GetDescription methods in this interface to perform undo actions and to get strings that can be displayed in the user interface to describe the undo action. A parent undo unit can call the GetUnitType and OnNextAdd methods.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleUndoUnit Methods | Description
Do | Instructs the undo unit to carry out its action.
GetDescription | Returns a string that describes the undo unit and can be used in the undo or redo user interface.
GetUnitType | Returns the CLSID and a type identifier for the undo unit.
OnNextAdd | Notifies the last undo unit in the collection that a new unit has been added.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit, IOleUndoManager


IOleUndoUnit::Do

Instructs the undo unit to carry out its action. Note that if it contains child undo units, it must call their Do methods as well.

HRESULT Do(
  IOleUndoManager* pUndoManager   //Pointer to the undo manager
);

Parameters

pUndoManager
[in] Pointer to the undo manager.

Return Values

S_OK
The undo unit successfully carried out its action.

Remarks

The undo unit is responsible for carrying out its action. Performing its own undo action results in another action that can potentially be reversed. However, if pUndoManager is NULL, the undo unit should perform its undo action but should not attempt to put anything on the redo or undo stack.

If pUndoManager is not NULL, then the unit is required to put a corresponding unit on the redo or undo stack. As a result, this method either moves itself to the redo or undo stack, or it creates a new undo unit and adds it to the appropriate stack. After creating a new undo unit, this undo unit calls IOleUndoManager::Open or IOleUndoManager::Add. The undo manager will put the new undo unit on the undo or redo stack depending on its current state.

A parent unit must pass to its children the same undo manager, possibly NULL, that was given to the parent. It is permissible, but not necessary, when pUndoManager is NULL to open a parent unit on the redo or undo stack as long as it is not committed. A blocked parent unit ensures that nothing is added to the stack by child units.

If this undo unit is a parent unit, it should put itself on the redo or undo stack before calling the Do on its children.

After calling this method, the undo manager must release the undo unit.

Note to Implementers

See the IOleUndoManager interface for error handling strategies for undo units. The error handling strategy affects the implementation of this method, particularly for parent units.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleUndoManager::Add, IOleUndoManager::Open


IOleUndoUnit::GetDescription

Returns a string that describes the undo unit and can be used in the undo or redo user interface.

HRESULT GetDescription(
  BSTR* pbstr   //Pointer to string
);

Parameters

pbstr
[out] Pointer to string describing this undo unit.

Return Values

S_OK
The string was successfully returned.

Remarks

All units are required to provide a user-readable description of themselves.

Note to Callers

The *pbstr parameter is allocated with the standard string allocator. The caller is responsible for freeing this string.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleUndoUnit::GetUnitType

Returns the CLSID and a type identifier for the undo unit.

HRESULT GetUnitType(
  CLSID* pClsid ,   //Pointer to CLSID for undo unit
  LONG* plID        //Pointer to type identifier for undo unit
);

Parameters

pclsid
[out] Pointer to CLSID for the undo unit.
plID
[out] Pointer to the type identifier for the undo unit.

Return Values

S_OK
Both the CLSID and type identifier were successfully returned.

Remarks

A parent undo unit can call this method on its child units to determine whether it can apply special handling to them. The CLSID returned can be the CLSID of the undo unit itself, of its creating object, or an arbitrary GUID. The undo unit has the option of returning CLSID_NULL, in which case the caller can make no assumptions about the type of this unit. The only requirement is that the CLSID and type identifier together uniquely identify this type of undo unit.

Note that the undo manager and parent undo units do not have the option of accepting or rejecting child units based on their type.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IOleUndoUnit::OnNextAdd

Notifies the last undo unit in the collection that a new unit has been added.

HRESULT OnNextAdd(void);

Return Values

S_OK
Implementations of this method always return S_OK. The HRESULT return type is provided only for remotability.

Remarks

An object can create an undo unit for an action and add it to the undo manager but can continue inserting data into it through private interfaces. When the undo unit receives a call to this method, it communicates back to the creating object that the context has changed. Then, the creating object stops inserting data into the undo unit.

The parent undo unit calls this method on its most recently added child undo unit to notify the child unit that the context has changed and a new undo unit has been added.

For example, this method is used for supporting fuzzy actions, like typing, which do not have a clear point of termination but instead are terminated only when something else happens.

This method may not always be called if the undo manager or an open parent unit chooses to discard the unit by calling Release instead. Any connection which feeds data to the undo unit behind the scenes through private interfaces should not AddRef the undo unit.

Note to Implementers

Note that parent units merely delegate this method to their most recently added child unit. A parent unit should terminate communication through any private interfaces when it is closed. A parent unit knows it is being closed when it receives S_FALSE from calling IOleParentUndoUnit::Close.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::Close


IOleWindow

The IOleWindow interface provides methods that allow an application to obtain the handle to the various windows that participate in in-place activation, and also to enter and exit context-sensitive help mode.

Several other in-place activation interfaces are derived from the IOleWindow interface. Containers and objects must implement and use these interfaces in order to support in-place activation. The following table briefly summarizes each of these interfaces:

IOleWindow | The base interface. Implemented and used by containers and objects to obtain window handles and manage context-sensitive help.
IOleInPlaceObject | Implemented by objects and used by an object's immediate container to activate and deactivate the object.
IOleInPlaceActiveObject | Implemented by objects and used by the top-level container to manipulate the object while it is active. Provides a direct channel of communication between an active object and its frame and document windows.
IOleInPlaceUIWindow | Implemented by containers and used by objects to manipulate the container's document window.
IOleInPlaceFrame | Implemented by containers and used by objects to control the container's frame window.
IOleInPlaceSite | Implemented by containers and used by objects to interact with the in-place client site.
IOleInPlaceSiteEx | Implemented by containers and called by objects to optimize activation and deactivation.
IOleInPlaceSiteWIndowless | Implemented by containers and called by windowless object to obtain services from its container.
IOleInPlaceObjectWindowless | Implemented by windowless objects called by containers to support window message processing and drag and drop operations for windowless objects.

These interfaces can be arranged in three hierarchical levels with various interfaces implemented at each level. Calls that install user-interface menus commands and frame adornments, activate and switch between windows, and dispatch menu and keystrokes take place between the top-level container and the active object. Calls that support activating, deactivating, scrolling, or clipping span the containment hierarchy, with each level performing the correct actions.

When to Implement

The inherited methods of this interface are implemented by all in-place objects and containers.

When to Use

Use this interface to obtain the window handle to the windows associated with in-place activation (frame, document, parent, and in-place object). It is also used to enter and exit context-sensitive help.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IOleWindow Methods | Description
GetWindow | Gets a window handle.
ContextSensitiveHelp | Controls enabling of context-sensitive help.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceObject, IOleInPlaceActiveObject, IOleInPlaceUIWindow, IOleInPlaceFrame, IOleInPlaceSite, OleCreateMenuDescriptor, OleDestroyMenuDescriptor, OleTranslateAccelerator, IOleInPlaceSiteEx, IOleInPlaceSiteWIndowless, IOleInPlaceObjectWindowless


IOleWindow::ContextSensitiveHelp

Determines whether context-sensitive help mode should be entered during an in-place activation session.

HRESULT ContextSensitiveHelp(
  BOOL fEnterMode  //Specifies whether or not to enter help mode
);

Parameter

fEnterMode
[in] TRUE if help mode should be entered; FALSE if it should be exited.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The help mode was entered or exited successfully, depending on the value passed in fEnterMode.

Remarks

Applications can invoke context-sensitive help when the user

  • Presses SHIFT+F1, then clicks a topic.
  • Presses F1 when a menu item is selected.

When SHIFT+F1 is pressed, either the frame or active object can receive the keystrokes. If the container's frame receives the keystrokes, it calls its containing document's IOleWindow::ContextSensitiveHelp method with fEnterMode set to TRUE. This propagates the help state to all of its in-place objects so they can correctly handle the mouse click or WM_COMMAND.

If an active object receives the SHIFT+F1 keystrokes, it calls the container's IOleInPlaceSite::ContextSensitiveHelp method with fEnterMode TRUE, which then recursively calls each of its in-place sites until there are no more to be notified. The container then calls its document's or frame's ContextSensitiveHelp method with fEnterMode TRUE.

When in context-sensitive help mode, an object that receives the mouse click can either:

  1. Ignore the click if it does not support context-sensitive help, or
  2. Tell all the other objects to exit context-sensitive help mode with ContextSensitiveHelp set to FALSE and then provide help for that context.

An object in context-sensitive help mode that receives a WM_COMMAND should tell all the other in-place objects to exit context-sensitive help mode and then provide help for the command.

If a container application is to support context-sensitive help on menu items, it must either provide its own message filter so that it can intercept the F1 key or ask the OLE library to add a message filter by calling OleSetMenuDescriptor, passing valid, non-NULL values for the lpFrame and lpActiveObj parameters.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OleSetMenuDescriptor


IOleWindow::GetWindow

Returns the window handle to one of the windows participating in in-place activation (frame, document, parent, or in-place object window).

HRESULT GetWindow(
  HWND * phwnd  //Pointer to where to return window handle
);

Parameter

phwnd
[out] Pointer to where to return the window handle.

Return Values

This method supports the standard return values E_FAIL, E_OUTOFMEMORY, E_INVALIDARG, and E_UNEXPECTED, as well as the following:

S_OK
The window handle was successfully returned.

Note For windowless objects, this method should always fail and return E_FAIL.

Remarks

Five types of windows comprise the windows hierarchy. When a object is active in place, it has access to some or all of these windows:

Window | Description
Frame | The outermost main window where the container application's main menu resides.
Document | The window that displays the compound document containing the embedded object to the user.
Pane | The subwindow of the document window that contains the object's view. Applicable only for applications with split-pane windows.
Parent | The container window that contains that object's view. The object application installs its window as a child of this window.
In-place | The window containing the active in-place object. The object application creates this window and installs it as a child of its hatch window, which is a child of the container's parent window.

Each type of window has a different role in the in-place activation architecture. However, it is not necessary to employ a separate physical window for each type. Many container applications use the same window for their frame, document, pane, and parent windows.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IViewObject

The IViewObject interface enables an object to display itself directly without passing a data object to the caller. In addition, this interface can create and manage a connection with an advise sink so the caller can be notified of changes in the view object.

The caller can request specific representations and specific target devices. For example, a caller can ask for either an object's content or an iconic representation. Also, the caller can ask the object to compose a picture for a target device that is independent of the drawing device context. As a result, the picture can be composed for one target device and drawn on another device context. For example, to provide a print preview operation, you can compose the drawing for a printer target device but actually draw the representation on the display.

The IViewObject interface is similar to IDataObject; except that IViewObject places a representation of the data onto a device context while IDataObject places the representation onto a transfer medium.

Unlike most other interfaces, IViewObject cannot be marshaled to another process. This is because device contexts are only effective in the context of one process.

When to Implement

Object handlers and in-process servers that manage their own presentations implement IViewObject for use by compound document containers. OLE provides an IViewObject implementation for its default object handler's cache.

When to Use

You call IViewObject from a container application if you need to draw a contained object on a specific device context. For example, if you want to print the object to a printer, you call the Draw method in the IViewObject interface.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IViewObject Methods | Description
Draw | Draws a representation of the object onto a device context.
GetColorSet | Returns the logical palette the object uses for drawing.
Freeze | Freezes the drawn representation of an object so it will not change until a subsequent Unfreeze.
Unfreeze | Unfreezes the drawn representation of an object.
SetAdvise | Sets up a connection between the view object and an advise sink so that the advise sink can receive notifications of changes in the view object.
GetAdvise | Returns the information on the most recent SetAdvise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IViewObject::Draw

Draws a representation of an object onto the specified device context.

HRESULT Draw(
  DWORD dwAspect,   //Aspect to be drawn
  LONG lindex,      //Part of the object of interest in the draw
                    //operation
  void * pvAspect,  //Pointer to DVASPECTINFO structure or NULL
  DVTARGETDEVICE * ptd,
                    //Pointer to target device in a structure
  HDC hicTargetDev, //Information context for the target device
  HDC hdcDraw,      //Device context on which to draw
  const LPRECTL lprcBounds,
                    //Pointer to the rectangle in which the object
                    //is drawn
  const LPRECTL lprcWBounds,
                    //Pointer to the window extent and window origin
                    //when drawing a metafile
  BOOL (*) (DWORD) pfnContinue,
                    //Pointer to the callback function for canceling
                    //or continuing the drawing
  DWORD dwContinue  //Value to pass to the callback function
);

Parameters

dwAspect
[in] Specifies the aspect to be drawn, that is, how the object is to be represented. Representations include content, an icon, a thumbnail, or a printed document. Valid values are taken from the enumerations DVASPECT and DVASPECT2. Note that newer objects and containers that support optimized drawing interfaces support the DVASPECT2 enumeration values. Older objects and containers that do not support optimized drawing interfaces may not support DVASPECT2. Windowless objects allow only DVASPECT_CONTENT, DVASPECT_OPAQUE, and DVASPECT_TRANSPARENT.
lindex
[in] Portion of the object that is of interest for the draw operation. Its interpretation varies depending on the value in the dwAspect parameter. See the DVASPECT enumeration for more information.
pvAspect
[in] Pointer to additional information in a DVASPECTINFO structure that enables drawing optimizations depending on the aspect specified. Note that newer objects and containers that support optimized drawing interfaces support this parameter as well. Older objects and containers that do not support optimized drawing interfaces always specify NULL for this parameter.
ptd
[in] Pointer to the DVTARGETDEVICE structure that describes the device for which the object is to be rendered. If NULL, the view should be rendered for the default target device (typically the display). A value other than NULL is interpreted in conjunction with hicTargetDev and hdcDraw. For example, if hdcDraw specifies a printer as the device context, the ptd parameter points to a structure describing that printer device. The data may actually be printed if hicTargetDev is a valid value or it may be displayed in print preview mode if hicTargetDev is NULL.
hicTargetDev
[in] Information context for the target device indicated by the ptd parameter from which the object can extract device metrics and test the device's capabilities. If ptd is NULL; the object should ignore the value in the hicTargetDev parameter.
hdcDraw
[in] Device context on which to draw. For a windowless object, the hdcDraw parameter should be in MM_TEXT mapping mode with its logical coordinates matching the client coordinates of the containing window. For a windowless object, the device context should be in the same state as the one normally passed by a WM_PAINT message.
lprcBounds
[in] Pointer to a RECTL structure specifying the rectangle on hdcDraw and in which the object should be drawn. This parameter controls the positioning and stretching of the object. This parameter should be NULL to draw a windowless in-place active object. In every other situation, NULL is not a legal value and should result in an E_INVALIDARG error code. If the container passes a non-NULL value to a windowless object, the object should render the requested aspect into the specified device context and rectangle. A container can request this from a windowless object to render a second, non-active view of the object or to print the object.
lprcWBounds
[in] If hdcDraw is a metafile device context, pointer to a RECTL structure specifying the bounding rectangle in the underlying metafile. The rectangle structure contains the window extent and window origin. These values are useful for drawing metafiles. The rectangle indicated by lprcBounds is nested inside this lprcWBounds rectangle; they are in the same coordinate space.

If hdcDraw is not a metafile device context; lprcWBounds will be NULL.

pfnContinue
[in] Pointer to a callback function that the view object should call periodically during a lengthy drawing operation to determine whether the operation should continue or be canceled. This function returns TRUE to continue drawing. It returns FALSE to stop the drawing in which case IViewObject::Draw returns DRAW_E_ABORT.
dwContinue
[in] Value to pass as a parameter to the function pointed to by the pfnContinue parameter. Typically, dwContinue is a pointer to an application-defined structure needed inside the callback function.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The object was drawn successfully.
OLE_E_BLANK
No data to draw from.
DRAW_E_ABORT
Draw operation aborted.
VIEW_E_DRAW
Error in drawing.
DV_E_LINDEX
Invalid value for lindex; currently only -1 is supported.
DV_E_DVASPECT
Invalid value for dwAspect.
OLE_E_INVALIDRECT
Invalid rectangle.

Remarks

A container application issues a call to IViewObject::Draw to create a representation of a contained object. This method draws the specified piece (lindex) of the specified view (dwAspect and pvAspect) on the specified device context (hdcDraw). Formatting, fonts, and other rendering decisions are made on the basis of the target device specified by the ptd parameter.

There is a relationship between the dwDrawAspect value and the lprcbounds value. The lprcbounds value specifies the rectangle on hdcDraw into which the drawing is to be mapped. For DVASPECT_THUMBNAIL, DVASPECT_ICON, and DVASPECT_SMALLICON, the object draws whatever it wants to draw, and it maps it into the space given in the best way. Some objects might scale to fit while some might scale to fit but preserve the aspect ratio. In addition, some might scale so the drawing appears at full width, but the bottom is cropped. The container can suggest a size via IOleObject::SetExtent, but it has no control over the rendering size. In the case of DVASPECT_CONTENT, the Draw implementation should either use the extents given by IOleObject::SetExtent or use the bounding rectangle given in the lprcBounds parameter.

For newer objects that support optimized drawing techniques and for windowless objects, this method should be used as follows:

  • New drawing aspects are supported in dwAspect as defined in DVASPECT2.
  • The pvAspect parameter can be used to pass additional information allowing drawing optimizations through the DVASPECTINFO structure.
  • The Draw method can be called to redraw a windowless in-place active object by setting the lrpcBounds parameter to NULL. In every other situation, NULL is an illegal value and should result in an E_INVALIDARG error code. A windowless object uses the rectangle passed by the activation verb or calls IOleInPlaceSite::SetObjectRects instead of using this parameter. If the container passes a non-NULL value to a windowless object, the object should render the requested aspect into the specified device context and rectangle. A container can request this from a windowless object to render a second, non-active view of the object or to print the object. See the IOleInPlaceSiteWindowless interface for more information on drawing windowless objects.
  • For windowless objects, the dwAspect parameter only allows the DVASPECT_CONTENT, DVASPECT_OPAQUE, and DVASPECT_TRANSPARENT aspects.
  • For a windowless object, the hdcDraw parameter should be in MM_TEXT mapping mode with its logical coordinates matching the client coordinates of the containing window. For a windowless object, the device context should be in the same state as the one normally passed by a WM_PAINT message.

To maintain compatibility with older objects and containers that do not support drawing optimizations, all objects, rectangular or not, are required to maintain an origin and a rectangular extent. This allows the container to still consider all its embedded objects as rectangles and to pass them appropriate rendering rectangles in IViewObjectEx::Draw.

An object's extent depends on the drawing aspect. For non-rectangular objects, the extent should be the size of a rectangle covering the entire aspect. By convention, the origin of an object is the top-left corner of the rectangle of the DVASPECT_CONTENT aspect. In other words, the origin always coincides with the top-left corner of the rectangle maintained by the object's site, even for a non-rectangular object.

Note to Callers

The value of hicTargetDevice is typically an information context for the target device. However, it may be a full device context instead.

Note to Implementers

If you are writing an object handler (such as the default handler) that implements IViewObject::Draw by playing a metafile, you have to treat SetPaletteEntries metafile records in a special way because of Windows' behavior. The Windows function PlayMetaFile sets these palette entries to the foreground. You must override this default by setting them to the background palette. Use EnumMetaFile to do this. Enhanced metafiles are always recorded with the background palette so there is no need to do it manually.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT, DVASPECT2, DVASPECTINFO, IOleInPlaceSiteWindowless, OleDraw


IViewObject::Freeze

Freezes a certain aspect of the object's presentation so that it does not change until the IViewObject::Unfreeze method is called. The most common use of this method is for banded printing.

HRESULT Freeze(
  DWORD dwAspect,   //How the object is to be represented
  LONG lindex,      //Part of the object of interest in the draw
                    //operation
  void * pvAspect,  //Always NULL
  DWORD * pdwFreeze //Points to location containing an identifying
                    //key
);

Parameters

dwAspect
[in] Specifies how the object is to be represented. Representations include content, an icon, a thumbnail, or a printed document. Valid values are taken from the enumeration DVASPECT. See the DVASPECT enumeration for more information.
lindex
[in] Portion of the object that is of interest for the draw operation. Its interpretation varies with dwAspect. See the DVASPECT enumeration for more information.
pvAspect
[in] Pointer to additional information about the view of the object specified in dwAspect. Since none of the current aspects support additional information, pvAspect must always be NULL.
pdwFreeze
[out] Pointer to where an identifying DWORD key is returned. This unique key is later used to cancel the freeze by calling IViewObject::Unfreeze. This key is an index that the default cache uses to keep track of which object is frozen.

Return Values

S_OK
The presentation was successfully frozen.
VIEW_S_ALREADYFROZEN
Presentation has already been frozen. The value of pdwFreeze is the identifying key of the already frozen object.
OLE_E_BLANK
Presentation not in cache.
DV_E_LINDEX
Invalid value for lindex; currently; only -1 is supported.
DV_E_DVASPECT
Invalid value for dwAspect.

Remarks

The IViewObject::Freeze method causes the view object to freeze its drawn representation until a subsequent call to IViewObject::Unfreeze releases it. After calling IViewObject::Freeze, successive calls to IViewObject::Draw with the same parameters produce the same picture until IViewObject::Unfreeze is called.

IViewObject::Freeze is not part of the persistent state of the object and does not continue across unloads and reloads of the object.

The most common use of this method is for banded printing.

While in a frozen state, view notifications are not sent. Pending view notifications are deferred to the subsequent call to IViewObject::Unfreeze.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT, IViewObject::Unfreeze


IViewObject::GetAdvise

Retrieves the existing advisory connection on the object if there is one. This method simply returns the parameters used in the most recent call to the IViewObject::SetAdvise method.

HRESULT GetAdvise(
  DWORD * pdwAspect,  //Pointer to where dwAspect parameter from
                      //previous SetAdvise call is returned
  DWORD * padvf,      //Pointer to where advf parameter from
                      //previous SetAdvise call is returned
  IAdviseSink ** ppAdvSink
                      //Address of output variable that receives the
                      //IAdviseSink interface pointer
);

Parameters

pdwAspect
[out] Pointer to where the dwAspect parameter from the previous SetAdvise call is returned. If this pointer is NULL, the caller does not permit this value to be returned.
padvf
[out] Pointer to where the advf parameter from the previous SetAdvise call is returned. If this pointer is NULL, the caller does not permit this value to be returned.
ppAdvSink
[out] Address of IAdviseSink* pointer variable that receives the interface pointer to the advise sink. The connection to this advise sink must have been established with a previous SetAdvise call, which provides the pAdvSink parameter. If *ppvAdvSink is NULL, there is no established advisory connection.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The existing advisory connection was retrieved.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

ADVF, IAdviseSink, IViewObject::SetAdvise


IViewObject::GetColorSet

Returns the logical palette that the object will use for drawing in its IViewObject::Draw method with the corresponding parameters.

HRESULT GetColorSet(
  DWORD dwAspect,   //How the object is to be represented
  LONG lindex,      //Part of the object of interest in the draw
                    //operation
  void * pvAspect,  //Always NULL
  DVTARGETDEVICE * ptd,
                    //Pointer to target device in a structure
  HDC hicTargetDev, //Information context for the target device
  LOGPALETTE ** ppColorSet
                    //Address of output variable that receives a
                    //pointer to the requested LOGPALETTE structure
);

Parameters

dwAspect
[in] Specifies how the object is to be represented. Representations include content, an icon, a thumbnail, or a printed document. Valid values are taken from the enumeration DVASPECT. See the DVASPECT enumeration for more information.
lindex
[in] Portion of the object that is of interest for the draw operation. Its interpretation varies with dwAspect. See the DVASPECT enumeration for more information.
pvAspect
[in] Pointer to additional information about the view of the object specified in dwAspect. Since none of the current aspects support additional information, pvAspect must always be NULL.
ptd
[in] Pointer to the DVTARGETDEVICE structure that describes the device for which the object is to be rendered. If NULL, the view should be rendered for the default target device (typically the display). A value other than NULL is interpreted in conjunction with hicTargetDev and hdcDraw. For example, if hdcDraw specifies a printer as the device context, ptd points to a structure describing that printer device. The data may actually be printed if hicTargetDev is a valid value or it may be displayed in print preview mode if hicTargetDev is NULL.
hicTargetDev
[in] Information context for the target device indicated by the ptd parameter from which the object can extract device metrics and test the device's capabilities. If ptd is NULL, the object should ignore the hicTargetDev parameter.
ppColorSet
[out] Address of LOGPALETTE pointer variable that receives a pointer to the LOGPALETTE structure. The LOGPALETTE structure contains the set of colors that would be used if IViewObject::Draw were called with the same parameters for dwAspect, lindex, pvAspect, ptd, and hicTargetDev. If *ppColorSet is NULL, the object does not use a palette.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The set of colors was returned successfully.
S_FALSE
Set of colors is empty or the object will not give out the information.
OLE_E_BLANK
No presentation data for object.
DV_E_LINDEX
Invalid value for lindex; currently only -1 is supported.
DV_E_DVASPECT
Invalid value for dwAspect.

Remarks

The IViewObject::GetColorSet method recursively queries any nested objects and returns a color set that represents the union of all colors requested. The color set eventually percolates to the top-level container that owns the window frame. This container can call IViewObject::GetColorSet on each of its embedded objects to obtain all the colors needed to draw the embedded objects. The container can use the color sets obtained in conjunction with other colors it needs for itself to set the overall color palette.

The OLE-provided implementation of IViewObject::GetColorSet looks at the data it has on hand to draw the picture. If CF_DIB is the drawing format, the palette found in the bitmap is used. For a regular bitmap, no color information is returned. If the drawing format is a metafile, the object handler enumerates the metafile looking for a CreatePalette metafile record. If one is found, the handler uses it as the color set.

Note to Implementers

Object applications that rely on the default handler for drawing and that use metafiles for doing so should provide a SetPaletteEntries record when they generate their metafiles. If a SetPaletteEntries record is not found, the default object handler returns S_FALSE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT


IViewObject::SetAdvise

Sets up a connection between the view object and an advise sink so that the advise sink can be notified about changes in the object's view.

HRESULT SetAdvise(
  DWORD dwAspect,  //View for which notification is being requested
  DWORD advf,      //Information about the advise sink
  IAdviseSink * pAdvSink
                   //Pointer to the advise sink that is to receive
                   //change notifications
);

Parameters

dwAspect
[in] View for which the advisory connection is being set up. Valid values are taken from the enumeration DVASPECT. See the DVASPECT enumeration for more information.
advf
[in] Contains a group of flags for controlling the advisory connection. Valid values are from the enumeration ADVF. However, only some of the possible ADVF values are relevant for this method. The following table briefly describes the relevant values. See the ADVF enumeration for a more detailed description.
ADVF Value | Description
ADVF_ONLYONCE | Causes the advisory connection to be destroyed after the first notification is sent.
ADVF_PRIMEFIRST | Causes an initial notification to be sent regardless of whether data has changed from its current state.

Note that the ADVF_ONLYONCE and ADVF_PRIMEFIRST can be combined to provide an asynchronous call to IDataObject::GetData.

pAdvSink
[out] Pointer to the IAdviseSink interface on the advisory sink that is to be informed of changes. A NULL value deletes any existing advisory connection.

Return Values

This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The advisory connection was successfully established.
OLE_E_ADVISENOTSUPPORTED
Advisory notifications are not supported.
DV_E_DVASPECT
Invalid value for dwAspect.

Remarks

A container application that is requesting a draw operation on a view object can also register with the IViewObject::SetAdvise method to be notified when the presentation of the view object changes. To find out about when an object's underlying data changes, you must call IDataObject::DAdvise separately.

To remove an existing advisory connection, call the IViewObject::SetAdvise method with pAdvSink set to NULL.

If the view object changes, a call is made to the appropriate advise sink through its IAdviseSink::OnViewChange method.

At any time, a given view object can support only one advisory connection. Therefore, when IViewObject::SetAdvise is called and the view object is already holding on to an advise sink pointer, OLE releases the existing pointer before the new one is registered.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

ADVF, IAdviseSink, IViewObject::GetAdvise


IViewObject::Unfreeze

Releases a previously frozen drawing. The most common use of this method is for banded printing.

HRESULT Unfreeze(
  DWORD dwFreeze  //Contains key that determines view object to
                  //unfreeze
);

Parameter

dwFreeze
[in] Contains a key previously returned from IViewObject::Freeze that determines which view object to unfreeze.

Return Values

S_OK
The drawing was unfrozen successfully.
OLE_E_NOCONNECTION
Error in the unfreezing process or the object is currently not frozen.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IViewObject::Freeze


IViewObject2

The IViewObject2 interface is an extension to the IViewObject interface which returns the size of the drawing for a given view of an object. You can prevent the object from being run if it isn't already running by calling this method instead of IOleObject::GetExtent.

Like the IViewObject interface, IViewObject2 cannot be marshaled to another process. This is because device contexts are only effective in the context of one process.

The OLE-provided default implementation provides the size of the object in the cache.

When to Implement

Object handlers and in-process servers that manage their own presentations implement IViewObject2 for use by compound document containers.

When to Use

A container application or object handler calls the GetExtent method in the IViewObject2 interface to get the object's size from its cache.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IViewObject Methods | Description
Draw | Draws a representation of the object onto a device context.
GetColorSet | Returns the logical palette the object uses for drawing.
Freeze | Freezes the drawn representation of an object so it will not change until a subsequent Unfreeze.
Unfreeze | Unfreezes the drawn representation of an object.
SetAdvise | Sets up a connection between the view object and an advise sink so that the advise sink can receive notifications of changes in the view object.
GetAdvise | Returns the information on the most recent SetAdvise.
IViewObject2 Method | Description
GetExtent | Returns the size of the view object from the cache.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.


IViewObject2::GetExtent

Returns the size that the specified view object will be drawn on the specified target device.

HRESULT GetExtent(
  DWORD dwAspect,  //View object for which the size is being
                   //requested
  LONG lindex,    //Part of the object to draw
  DVTARGETDEVICE ptd,
                   //Pointer to the target device in a structure
  LPSIZEL lpsizel  //Pointer to size of object
);

Parameters

dwAspect
[in] Requested view of the object whose size is of interest. Valid values are taken from the enumerations DVASPECT and from DVASPECT2. Note that newer objects and containers that support optimized drawing interfaces support the DVASPECT2 enumeration values. Older objects and containers that do not support optimized drawing interfaces may not support DVASPECT2.
lindex
[in] Portion of the object that is of interest. Currently only -1 is valid.
ptd
[in] Pointer to the DVTARGETDEVICE structure defining the target device for which the object's size should be returned.
lpsizel
[out] Pointer to where the object's size is returned.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The object's extent was successfully returned.
OLE_E_BLANK
An appropriate cache is not available.

Remarks

The OLE-provided implementation of IViewObject2::GetExtent searches the cache for the size of the view object.

The GetExtent method in the IOleObject interface provides some of the same information as IViewObject2::GetExtent.

Note This method must return the same size as DVASPECT_CONTENT for all the new aspects in DVASPECT2. IOleObject::GetExtent must do the same thing.

If one of the new aspects is requested in dwAspect, this method can either fail or return the same rectangle as for the DVASPECT_CONTENT aspect.

Note to Callers

To prevent the object from being run if it isn't already running, you can call IViewObject2::GetExtent rather than IOleObject::GetExtent to determine the size of the presentation to be drawn.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DVASPECT, DVASPECT2, IOleObject::GetExtent, IViewObject


IViewObjectEx

The IViewObjectEx interface is an extension derived from IViewObject2 to provide support for:

  • enhanced, flicker-free drawing for non-rectangular objects and transparent objects
  • hit testing for non-rectangular objects
  • control sizing

Flicker free drawing

Containers can now choose between a variety of drawing algorithms, depending on their sophistication and the situation.

Flicker is created by redrawing the background before letting an object redraw its foreground as in the back to front drawing algorithm known as the Painter's Algorithm. There are essentially two ways to avoid flickering:

  • Draw into an offscreen bitmap and then copy the resulting image to the screen in one chunk. This technique might require significant additional resources to store the offscreen image, depending on the size of the region to drawn, the resolution, and the number of colors.
  • Draw front to back, instead of back to front, excluding each rectangular area from the clipping region as soon as its has been painted. One benefit of this technique is that each pixel is painted only once. Speed depends essentially on the performance of the clipping support.

Each technique has advantages and disadvantages, depending on the specific situation. There is no single algorithm that is most efficient in all situations. Depending on the situation and their sophistication, containers may choose to use one or another, or a mix of both. The IViewObjectEx interface provides methods to support both techniques or a mixture of the two. Simple containers can implement a simplistic back to front painting algorithm directly to the screen. The speed is likely to be high but so will flicker. If flicker is to be reduced to a minimum, painting to an off-screen device context is the solution of choice. If memory consumption is a problem, containers can use clipping to reduce the use of off screen bitmaps.

To draw as flicker-free as possible without using an offscreen bitmap, the container will have to paint in two passes. The first pass is done front to back. During that pass, each object draws regions of itself that are cheap enough to clip out efficiently and that it can entirely obscure. These regions are known as opaque. After each object is done, the container clips out the regions just painted to ensure that subsequent objects will not modify the bits on the screen.

During the second pass, which occurs back to front, each object draws its remaining parts - irregular, oblique or in general difficult to clip out, such as text on transparent background. Such parts are known as transparent. At this point, the container is responsible for clipping out any opaque, already painted regions in front of the object currently drawing. The less painting during this second pass, the less flicker on the screen.

Clipping during the second pass may be very inefficient, since the clipping region needs to be recreated for every object that has something to draw. This might be acceptable if not too many overlapping objects have irregular or transparent parts. An object can tell its container ahead of time whether it wants to be called during this second pass or not.

If the container provides an offscreen bitmap to paint into, then it can skip the first pass and ask every object to render itself entirely during the second pass. In certain cases, the container may also decide than flicker is not a problem and use that same technique while painting directly on screen. For example, flicker might be acceptable when painting a form for the first time, but not when repainting.

Note Although documented here two pass drawing is not currently utilized by any containers.

Hit testing for non-rectangular objects

The IViewObjectEx interface supports hit detection for non-rectangular objects. Using the QueryHitPoint and QueryHitRect methods, the object can participate in the hit-test logic with the container.

Control Sizing

The IViewObjectEx interface allows controls to provide sizing hints as the user resizes the control. The control can specify a minimum and maximum size and can specify the nearest good size to a size requested by the user.

When to Implement

Implement this interface on objects that need to support efficient flicker-free drawing, non-rectangular hit testing, or control sizing. This interface is derived from the IViewObject2 interface which, in turn, is derived from IViewObject.

All IViewObjectEx methods described in this document that take or return a position assume that the location is expressed in HIMETRIC units relative to the origin of the object.

When to Use

Containers call the methods of this interface to draw objects in an efficient, flicker free manner, test whether points or rectangles are within the object, or to resize controls.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IViewObject Methods | Description
Draw | Draws a representation of the object onto a device context.
GetColorSet | Returns the logical palette the object uses for drawing.
Freeze | Freezes the drawn representation of an object so it will not change until a subsequent Unfreeze.
Unfreeze | Unfreezes the drawn representation of an object.
SetAdvise | Sets up a connection between the view object and an advise sink so that the advise sink can receive notifications of changes in the view object.
GetAdvise | Returns the information on the most recent SetAdvise.
IViewObject2 Method | Description
GetExtent | Returns the size that the specified view object will be drawn on the specified target device.
IViewObjectEx Methods | Description
GetRect | Returns a rectangle describing a requested drawing aspect.
GetViewStatus | Returns information about the opacity of the object, and what drawing aspects are supported.
QueryHitPoint | Indicates whether a point is within a given aspect of an object.
QueryHitRect | Indicates whether any point in a rectangle is within a given drawing aspect of an object.
GetNaturalExtent | Provides sizing hints from the container for the object to use as the user resizes it.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IViewObject2


IViewObjectEx::GetNaturalExtent

Provides sizing hints from the container for the object to use as the user resizes it.

HRESULT GetNaturalExtent(
  DWORD dwAspect,   //Requested drawing aspect
  LONG lindex,      //Portion of object for draw operation
  DVTARGETDEVICE* ptd,
                    //Pointer to structure describing target device
  HDC hicTargetDev,
                    //Information context for ptd
  DVEXTENTINFO* pExtentInfo,
                    //Structure specifying sizing data
  LPSIZEL* pSizel  //Pointer to sizing data returned by object
);

Parameters

dwAspect
[in] Requested drawing aspect. It can be any of the values from the DVASPECT enumeration.
lindex
[in] Indicates the portion of the object that is of interest for the draw operation. Its interpretation varies depending on the value in the dwAspect parameter. See the DVASPECT enumeration for more information.
ptd
[in] Pointer to the target device structure that describes the device for which the object is to be rendered. If NULL, the view should be rendered for the default target device (typically the display). A value other than NULL is interpreted in conjunction with hicTargetDev and hdcDraw. For example, if hdcDraw specifies a printer as the device context, the ptd parameter points to a structure describing that printer device. The data may actually be printed if hicTargetDev is a valid value or it may be displayed in print preview mode if hicTargetDev is NULL.
hicTargetDev
[in] Specifies the information context for the target device indicated by the ptd parameter from which the object can extract device metrics and test the device's capabilities. If ptd is NULL; the object should ignore the value in the hicTargetDev parameter.
pExtentInfo
[in] Pointer to DVEXTENTINFO structure that specifies the sizing data.
pSizel
[out] Pointer to sizing data returned by the object. The returned sizing data is set to -1 for any dimension that was not adjusted. That is if cx is -1 then the width was not adjusted, if cy is -1 then the height was not adjusted. If E_FAIL is returned indicating no size was adjusted then psizel may be NULL.

Return Values

S_OK
The sizing hints were successfully returned.
E_FAIL
This method is not implemented for the specified dwAspect, or the size was not adjusted.
E_NOTIMPL
This method was not implemented.

Remarks

There are two general approaches to sizing a control. The first approach gives the control responsibility for sizing itself; the second approach gives the container responsibility for sizing the control. The first approach is called autosizing. There are two alternatives involved in the second approach: content sizing and integral sizing.

The IViewObjectEx::GetNaturalExtent method supports both content and integral sizing. In content sizing, the container passes the DVEXTENTINFO structure to the object into which the object returns a suggested size. In integral sizing, the container passes a preferred size to the object in DVEXTENTINFO, and the object actually adjusts its height. Integral sizing is used when the user rubberbands a new size in design mode.

Autosizing typically occurs with objects such as the Label control which resizes if the autosize property was enabled and the associated text changed. Autosizing is handled differently depending on the state of the object.

If the object is inactive, the following occurs:

  1. The object calls IOleClientSite::RequestNewObjectLayout.
  2. The container calls IOleObject::GetExtent and retrieves the new extents
  3. The container calls IOleObject::SetExtent and adjusts the new extents.

If the object is active, the following occurs:

  1. The object calls IOleInPlaceSite::OnPosRectChange to specify that it requires resizing.
  2. The container calls IOleInPlaceObject::SetObjectRects and specifies the new size.

The values of the dwAspect parameter can be one of the following DVASPECT enumeration values:

DVASPECT_CONTENT
Provide a representation of the control so it can be displayed as an embedded object inside of a container. This value is typically specified for compound document objects. The presentation can be provided for the screen or printer.
DVASPECT_DOCPRINT
Provide a representation of the control on the screen as though it were printed to a printer using the Print command from the File menu. The described data may represent a sequence of pages..
DVASPECT_ICON
Provide an iconic representation of the control.
DVASPECT_THUMBNAIL
Provide a thumbnail representation of an object so it can be displayed in a browsing tool. The thumbnail is approximately a 120 by 120 pixel, 16-color (recommended) device-independent bitmap potentially wrapped in a metafile.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

DVASPECT, DVEXTENTINFO, IOleClientSite::RequestNewObjectLayout, IOleInPlaceObject::SetObjectRects, IOleInPlaceSite::OnPosRectChange, IOleObject::GetExtent, IOleObject::SetExtent


IViewObjectEx::GetRect

Returns a rectangle describing a requested drawing aspect.

HRESULT GetRect(
  DWORD dwAspect,  //Requested drawing aspect
  LPRECTL pRect    //Pointer to the rectangle
);

Parameters

dwAspect
[in] Drawing aspect requested.
pRect
[out] Pointer to the rectangle describing the requested drawing aspect.

Return Values

S_OK
The rectangle was successfully returned.
DV_E_DVASPECT
The method does not support the specified aspect. Either the object does not support the aspect requested or the aspect is not rectangular.

Remarks

This method returns a rectangle describing the specified drawing aspect. The returned rectangle is in HIMETRIC units, relative to the object's origin. The rectangle returned depends on the drawing aspect as follows:

DVASPECT_CONTENT
Objects should return the bounding rectangle of the whole object. The top-left corner is at the object's origin and the size is equal to the extent returned by IViewObject2::GetExtent.
DVASPECT_OPAQUE
Objects with a rectangular opaque region should return that rectangle. Others should fail and return error code DV_E_DVASPECT.

If a rectangle is returned, it is guaranteed to be completely obscured by calling IViewObject::Draw for that aspect. The container should use that rectangle to clip out the object's opaque parts before drawing any object behind it during the back to front pass. If this method fails on an object with a non-rectangular opaque region, the container should draw the entire object in the back to front part using the DVASPECT_CONTENT aspect.

DVASPECT_TRANSPARENT
Objects should return the rectangle covering all transparent or irregular parts. If the object does not have any transparent or irregular parts, it may return DV_E_ASPECT. A container may use this rectangle to determine whether there are other objects overlapping the transparent parts of a given object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IViewObjectEx::GetViewStatus

Returns information about the opacity of the object, and what drawing aspects are supported.

HRESULT GetViewStatus(
  DWORD* pdwStatus  //Pointer to the view status
);

Parameters

pdwStatus
[out] Pointer to the view status. This information is returned as a combination of the VIEWSTATUS enumeration values.

Return Values

S_OK
The view status was successfully returned. This method should never fail or return an error code.

Remarks

In order to optimize the drawing process, the container needs to be able to determine whether an object is opaque and whether it has a solid background. It is not necessary to redraw objects that are entirely covered by a completely opaque object. Other operations, such as scrolling for example, can also be highly optimized if an object is opaque and has a solid background.

The IViewObjectEx::GetViewStatus method returns whether the object is entirely opaque or not (VIEWSTATUS_OPAQUE bit) and whether its background is solid (VIEWSTATUS_SOLIDBKGND bit). This information may change in time. An object may be opaque at a given time and become totally or partially transparent later on, for example, because of a change of the BackStyle property. An object should notify its sites when it changes using IAdviseSinkEx::OnViewStatusChange so the sites can cache this information for high speed access.

Objects not supporting IViewObjectEx are considered to be always transparent.

The IViewObjectEx::GetViewStatus method also returns a combination of bits indicating which aspects are supported.

If a given drawing aspect is not supported, all IViewObjectEx methods taking a drawing aspect as an input parameter should fail and return E_INVALIDARG. The IViewObjectEx::GetViewStatus method allows the container to get back information about all drawing aspects in one quick call. Normally the set of supported drawing aspects should not change with time. However, if this was not the case, an object should notify its container using IAdviseSinkEx::OnViewStatusChange.

Which drawing aspects are supported is independent of whether the object is opaque, partially transparent, or totally transparent. In particular, a transparent object that does not support DVASPECT_TRANSPARENT should be drawn correctly during the back to front pass using DVASPECT_CONTENT. However, this is likely to result in more flicker.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IAdviseSinkEx::OnViewStatusChange, VIEWSTATUS


IViewObjectEx::QueryHitPoint

Indicates whether a point is within a given aspect of an object.

HRESULT QueryHitPoint(
  DWORD dwAspect,       //Requested drawing aspect
  LPRECT pRectBounds,   //Object bounding rectangle
  POINTL ptlLoc,        //Hit location
  LONG lCloseHint,      //Suggested distance considered close
  DWORD* pHitResult     //Pointer to returned hit information
);

Parameters

dwAspect
[in] Requested drawing aspect.
pRectBounds
[in] Object bounding rectangle in client coordinates of the containing window. This rectangle is computed and passed by the container so that the object can meaningfully interpret the hit location.
ptlLoc
[in] Hit location in client coordinates of the containing window.
lCloseHint
[in] Suggested distance in HIMETRIC units that the container considers close. This value is a hint, and objects can interpret it in their own way. Objects can also use this hint to roughly infer output resolution to choose expansiveness of hit test implementation.
pHitResult
[out] Pointer to returned information about the hit expressed as the HITRESULT enumeration values.

Return Values

S_OK
The hit information was successfully returned in pHitResult.
E_FAIL
This method is not implemented for the requested aspect. Use DVASPECT_CONTENT instead.

Remarks

To support hit detection on non-rectangular objects, the container needs a reliable way to ask an object whether or not a given location is inside one of its drawing aspects. This function is provided by IViewObjectEx::QueryHitPoint.

Note that since this method is part of the IViewObjectEx interface, the container can figure whether an mouse hit is over an object without having to necessarily launch the server. If the hit happens to be inside the object, then it is likely that the object will be in-place activated and the server started.

Typically, the container first quickly determines whether a given location is within the rectangular extent of an object. If the location is within the rectangular extent of an object, the container calls IViewObjectEx::QueryHitPoint to get confirmation that the location is actually inside the object. The hit location is passed in client coordinates of the container window. Since the object may be inactive when this method is called, the bounding rectangle of the object in the same coordinate system is also passed to this method, similarly to what happens in IPointerInactive::OnInactiveSetCursor.

Possible returned values include:

  • outside, on a transparent region
  • close enough to be considered a hit (may be used by small or thin objects)
  • hit

QueryHitPoint is not concerned by the sub-objects of the object it is called for. It merely indicates whether the mouse hit was within the object or not.

QueryHitPoint can be called for any of the drawing aspects an object supports. It should fail if the it is not supported for the requested drawing aspect.

Transparent objects may wish to implement a complex hit-detection mechanism where the user can select either the transparent object or an object behind it, depending on where exactly the click happens inside the object. For example, a transparent TextBox showing big enough text may let the user select the object behind, for example, a bitmap, when the user clicks between the characters. For this reason, the information returned by QueryHitPoint includes indication about whether the hit happens on an opaque or transparent region.

An example of non-rectangular and transparent hit detection is a transparent circle control with an object behind it (a line in the example below):

The values shown are for hit tests against the circle; gray regions are not part of the control, but are shown here to indicate an area around the image considered close. Each object implements its own definition of close but is assisted by a hint provided by the container so that closeness can be adjusted as images zoom larger or smaller.

In the picture above, the points marked "Hit", "Close" and "Transparent" would all be hits of varying strength on the circle, with the exception of the one marked "Transparent, (but for the line, close)." This illustrates the effect of the different strength of hits. Since the circle responds "transparent" while the line claims "close," and transparent is weaker than close, the line takes the hit.

Note to Implementers

An object supporting IViewObjectEx is required to implement this method at least for the DVASPECT_CONTENT aspect. The object should not take any other action in response to this method other than to return the information; there should be no side-effects.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IPointerInactive::OnInactiveSetCursor, HITRESULT


IViewObjectEx::QueryHitRect

Indicates whether any point in a rectangle is within a given drawing aspect of an object.

HRESULT QueryHitRect(
  DWORD dwAspect,       //Requested drawing aspect
  LPRECT pRectBounds,   //Object bounding rectangle
  LPRECT pRectLoc,      //Hit location
  LONG lCloseHint,      //Suggested distance considered close
  DWORD* pHitResult     //Pointer to returned hit information
);

Parameters

dwAspect
[in] Requested drawing aspect.
pRectBounds
[in] Object bounding rectangle in client coordinates of the containing window. This rectangle is computed and passed by the container so that the object can meaningfully interpret the hit location.
pRectLoc
[in] Hit test rectangle in specified in HIMETRIC units, relative to the top-left corner of the object.
lCloseHint
[in] Suggested distance in HIMETRIC units that the container considers close. This value is a hint, and objects can interpret it in their own way. Objects can also use this hint to roughly infer output resolution to choose expansiveness of hit test implementation.
pHitResult
[out] Pointer to returned information about the hit expressed as the HITRESULT enumeration values.

Return Values

S_OK
The hit information was successfully returned in pHitInfo.
E_FAIL
This method is not implemented for the requested aspect. Use DVASPECT_CONTENT instead.

Remarks

Containers may need to test whether an object overlaps a given drawing aspect of another object. They can determine whether the objects overlap by requesting a region or at least a bounding rectangle of the aspect in question. However, a quicker way to do this is to call IViewObjectEx::QueryHitRect to ask the object whether a given rectangle intersects one of its drawing aspects.

Note Unlike IViewObjectEx::QueryHitPoint, this method does not return HITRESULT_TRANSPARENT or HITRESULT_CLOSE. It is strictly hit or miss, returning HITRESULT_OUTSIDE if no point in the rectangle is hit and HITRESULT_HIT if at least one point in the rectangle is a hit.

Note to Implementers

An object supporting IViewObjectEx is required to implement this method at least for the DVASPECT_CONTENT aspect. The object should not take any other action in response to this method other than to return the information; there should be no side-effects. If there is any ambiguity about whether a point is a hit, for instance due to coordinates not converting exactly, the object should return HITRESULT_HIT whenever any point in the rectangle might be a hit on the object. That is, it is permissible to claim a hit for a point that is not actually rendered, but never correct to claim a miss for any point that is in the rendered image of the object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

HITRESULT


CreateDataAdviseHolder

Supplies a pointer to the OLE implementation of IDataAdviseHolder on the data advise holder object.

WINOLEAPI CreateDataAdviseHolder(
  IDataAdviseHolder **ppDAHolder  //Address of output variable that
                                  // receives the IDataAdviseHolder
                                  // interface pointer
);

Parameter

ppDAHolder
[out] Address of IDataAdviseHolder* pointer variable that receives the interface pointer to the new advise holder object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The advise holder object has been instantiated and the pointer supplied.

Remarks

Call CreateDataAdviseHolder in your implementation of IDataObject::DAdvise to get a pointer to the OLE implementation of IDataAdviseHolder interface. With this pointer, you can then complete the implementation of IDataObject::DAdvise by calling the IDataAdviseHolder::Advise method, which creates an advisory connection between the calling object and the data object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Import Library: Use ole32.lib.

See Also

IDataAdviseHolder


CreateDataCache

Supplies a pointer to a new instance of an OLE-provided implementation of a data cache.

WINOLEAPI CreateDataCache(
  LPUNKNOWN pUnkOuter,
                    //Pointer to whether cache is to be aggregated
  REFCLSID rclsid,  //CLSID used to generate icon labels
  REFIID riid,      //Reference to the identifier of the interface
  LPVOID FAR *ppvObj
                    //Address of output variable that receives the
                    // interface pointer to the supplied cache object
);

Parameters

pUnkOuter
[in] If the cache is to be created as part of an aggregate, pointer to the controlling IUnknown of the aggregate. If not, the parameter should be NULL.
rclsid
[in] CLSID used to generate icon labels. This value is typically CLSID_NULL.
riid
[in] Reference to the identifier of the interface the caller wants to use to communicate with the cache. This value is typically IID_IOleCache (defined in the OLE headers to equal the interface identifier for IOleCache).
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer to the supplied cache object.

Return Values

This function supports the standard return values E_INVALIDARG and

E_OUTOFMEMORY, as well as the following:

S_OK
The OLE-provided cache was instantiated and the pointer supplied.
E_NOINTERFACE
The interface represented by riid is not supported by the object. The parameter ppvObj is set to NULL.

Remarks

The cache object created by CreateDataCache supports the IOleCache, IOleCache2, and IOleCacheControl interfaces for controlling the cache. It also supports the IPersistStorage, IDataObject (without advise sinks), IViewObject, and IViewObject2 interfaces.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objbase.h.
Import Library: Use ole32.lib.

See Also

IOleCache, IOleCache2, IOleCacheControl


CreateFormatEnumerator

Creates an object that implements IEnumFORMATETC over a static array of FORMATETC structures.

HRESULT CreateFormatEnumerator(
  UINT cfmtetc,  //Number of FORMATETC structures in rgfmtetc
  FORMATETC *rgfmtetc,
                 //Static array of formats.
  IpenumFORMATETC **ppenumfmtetc
                 //Address of output variable that receives the
                 // IEnumFORMATETC interface pointer
);

Parameters

cfmtetc
[in] Number of FORMATETC structures in the static array specified by the rgfmtetc parameter. The cfmtetc parameter cannot be zero.
rgfmtetc
[in] Pointer to a static array of FORMATETC structures.
ppenumfmtetc
[out] Address of IEnumFORMATETC* pointer variable that receives the interface pointer to the enumerator object.

Return Values

S_OK
The operation was successful.
E_INVALIDARG
One or more parameters are invalid.

Remarks

The CreateFormatEnumerator function creates an enumerator object that implements IEnumFORMATETC over a static array of FORMATETC structures. The cfmtetc parameter specifies the number of these structures. With the pointer, you can call the standard enumeration methods to enumerate the structures, as described in the IEnumXXX reference.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in objbase.h.
Import Library: Use ole32.lib.

See Also

RegisterFormatEnumerator Function, RevokeFormatEnumerator Function


CreateOleAdviseHolder

Creates an advise holder object for managing compound document notifications. It returns a pointer to the object's OLE implementation of the IOleAdviseHolder interface.

WINOLEAPI CreateOleAdviseHolder(
  LPOLEADVISEHOLDER FAR* ppOAHolder  //Address of output variable
                                     // that receives the
                                     // IOleAdviseHolder interface
                                     // pointer
);

Parameter

ppOAHolder
[out] Address of IOleAdviseHolder* pointer variable that receives the interface pointer to the new advise holder object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The new OLE advise holder returned successfully.

Remarks

The function CreateOleAdviseHolder creates an instance of an advise holder, which supports the OLE implementation of the IOleAdviseHolder interface. The methods of this interface are intended to be used to implement the advisory methods of IOleObject, and, when advisory connections have been set up with objects supporting an advisory sink, to send notifications of changes in the object to the advisory sink. The advise holder returned by CreateOleAdviseHolder will suffice for the great majority of applications. The OLE-provided implementation does not, however, support IOleAdviseHolder::EnumAdvise, so if you need to use this method, you will need to implement your own advise holder.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IOleAdviseHolder, IOleObject


DoDragDrop

Carries out an OLE drag and drop operation.

WINOLEAPI DoDragDrop(
  IDataObject * pDataObject,  //Pointer to the data object
  IDropSource * pDropSource,  //Pointer to the source
  DWORD dwOKEffect,           //Effects allowed by the source
  DWORD * pdwEffect           //Pointer to effects on the source
);

Parameters

pDataObject
[in] Pointer to the IDataObject interface on a data object that contains the data being dragged.
pDropSource
[in] Pointer to an implementation of the IDropSource interface, which is used to communicate with the source during the drag operation.
dwOKEffect
[in] Effects the source allows in the OLE drag-and-drop operation. Most significant is whether it permits a move. The dwOKEffect and pdwEffect parameters obtain values from the DROPEFFECT enumeration. For a list of values, see DROPEFFECT.
pdwEffect
[out] Pointer to a value that indicates how the OLE drag-and-drop operation affected the source data. The pdwEffect parameter is set only if the operation is not canceled.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

DRAGDROP_S_DROP
The OLE drag-and-drop operation was successful.
DRAGDROP_S_CANCEL
The OLE drag-and-drop operation was canceled.
E_UNSPEC
Unexpected error occurred.

Remarks

If you are developing an application that can act as a data source for an OLE drag-and-drop operation, you must call DoDragDrop when you detect that the user has started an OLE drag-and-drop operation.

The DoDragDrop function enters a loop in which it calls various methods in the IDropSource and IDropTarget interfaces. (For a successful drag-and-drop operation, the application acting as the data source must also implement IDropSource, while the target application must implement IDropTarget.)

  1. The DoDragDrop function determines the window under the current cursor location. It then checks to see if this window is a valid drop target.
  2. If the window is a valid drop target, DoDragDrop calls IDropTarget::DragEnter. This method supplies an effect code indicating what would happen if the drop actually occurred. For a list of valid drop effects, see the DROPEFFECT enumeration.
  3. DoDragDrop calls IDropSource::GiveFeedback with the effect code so that the drop source interface can provide appropriate visual feedback to the user. The pDropSource pointer passed into DoDragDrop specifies the appropriate IDropSource interface.
  4. DoDragDrop tracks mouse cursor movements and changes in the keyboard or mouse button state.
    • If the user moves out of a window, DoDragDrop calls IDropTarget::DragLeave.
    • If the mouse enters another window, DoDragDrop determines if that window is a valid drop target and then calls IDropTarget::DragEnter for that window.
    • If the mouse moves but stays within the same window, DoDragDrop calls IDropTarget::DragOver.
  5. If there is a change in the keyboard or mouse button state, DoDragDrop calls IDropSource::QueryContinueDrag and determines whether to continue the drag, to drop the data, or to cancel the operation based on the return value.
    • If the return value is S_OK, DoDragDrop first calls IDropTarget::DragOver to continue the operation. This method returns a new effect value and DoDragDrop then calls IDropSource::GiveFeedback with the new effect so appropriate visual feedback can be set. For a list of valid drop effects, see the DROPEFFECT enumeration. IDropTarget::DragOver and IDropSource::GiveFeedback are paired so that as the mouse moves across the drop target, the user is given the most up-to-date feedback on the mouse's position.
    • If the return value is DRAGDROP_S_DROP, DoDragDrop calls IDropTarget::Drop. The DoDragDrop function returns the last effect code to the source, so the source application can perform the appropriate operation on the source data, for example, cut the data if the operation was a move.
    • If the return value is DRAGDROP_S_CANCEL, the DoDragDrop function calls IDropTarget::DragLeave.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IDropSource


OleCreate

Creates an embedded object identified by a CLSID. You use it typically to implement the menu item that allows the end user to insert a new object.

WINOLEAPI OleCreate(
  REFCLSID rclsid,  //CLSID of embedded object to be created
  REFIID riid,      //Reference to the identifier of the interface
                    // used to communicate with new object
  DWORD renderopt,  //RENDEROPT value indicating cached capabilities
  FORMATETC * pFormatEtc,
                    //Pointer to a FORMATETC structure
  IOleClientSite * pClientSite,
                    //Pointer to request services from the container
  IStorage * pStg,  //Pointer to storage for the object
  void ** ppvObject //Address of output variable that receives the
                    // interface pointer requested in riid
);

Parameters

rclsid
[in] CLSID of the embedded object that is to be created.
riid
[in] Reference to the identifier of the interface, usually IID_IOleObject (defined in the OLE headers as the interface identifier for IOleObject), through which the caller will communicate with the new object.
renderopt
[in] A value from the enumeration OLERENDER, indicating the locally cached drawing capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pFormatEtc
[in] Depending on which of the OLERENDER flags is used as the value of renderopt, pointer to one of the FORMATETC enumeration values. Refer to the OLERENDER enumeration for restrictions. This parameter, along with the renderopt parameter, specifies what the new object can cache initially.
pClientSite
[in] If you want OleCreate to call IOleObject::SetClientSite, pointer to the IOleClientSite interface on the container. The value may be NULL, in which case you must specifically call IOleClientSite::SetClientSite before attempting operations.
pStg
[in] Pointer to an instance of the IStorage interface on the storage object. This parameter may not be NULL.
ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
Embedded object created successfully.

Remarks

The OleCreate function creates a new embedded object, and is typically called to implement the menu item Insert New Object. When OleCreate returns, the object it has created is blank (contains no data), unless renderopt is OLERENDER_DRAW or OLERENDER_FORMAT, and is loaded. Containers typically then call the OleRun function or IOleObject::DoVerb to show the object for initial editing.

The rclsid parameter specifies the CLSID of the requested object. CLSIDs of registered objects are stored in the system registry. When an application user selects Insert Object, a selection box allows the user to select the type of object desired from those in the registry. When OleCreate is used to implement the Insert Object menu item, the CLSID associated with the selected item is assigned to the rclsid parameter of OleCreate.

The riid parameter specifies the interface the client will use to communicate with the new object. Upon successful return, the ppvObject parameter holds a pointer to the requested interface.

The created object's cache contains information that allows a presentation of a contained object when the container is opened. Information about what should be cached is passed in the renderopt and pFormatetc values. When OleCreate returns, the created object's cache is not necessarily filled. Instead, the cache is filled the first time the object enters the running state. The caller can add additional cache control with a call to IOleCache::Cache after the return of OleCreate and before the object is run. If renderopt is OLERENDER_DRAW or OLERENDER_FORMAT, OleCreate requires that the object support the IOleCache interface. There is no such requirement for any other value of renderopt.

If pClientSite is non-NULL, OleCreate calls IOleObject::SetClientSite through the pClientSite pointer. IOleClientSite is the primary interface by which an object requests services from its container. If pClientSite is NULL, you must make a specific call to IOleObject::SetClientSite before attempting any operations.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OLERENDER, FORMATETC, IOleClientSite, IOleObject


OleCreateDefaultHandler

Creates a new instance of the default embedding handler. This instance is initialized so it creates a local server when the embedded object enters the running state.

WINOLEAPI OleCreateDefaultHandler(
  REFCLSID clsid,      //OLE server to be loaded
  LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown if
                       // aggregated; else NULL
  REFIID riid,         //Reference to the identifier of the
                       // interface for communicating with handler
  LPVOID FAR * ppvObj  //Address of output variable that receives
                       // the interface pointer  requested in riid
);

Parameters

clsid
[in] CLSID identifying the OLE server to be loaded when the embedded object enters the running state.
pUnkOuter
[in] Pointer to the controlling IUnknown interface if the handler is to be aggregated; NULL if it is not to be aggregated.
riid
[in] Reference to the identifier of the interface, usually IID_IOleObject, through which the caller will communicate with the handler.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created handler.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

NOERROR
The creation operation was successful.

Remarks

OleCreateDefaultHandler creates a new instance of the default embedding handler, initialized so it creates a local server identified by the clsid parameter when the embedded object enters the running state. If you are writing a handler and want to use the services of the default handler, call OleCreateDefaultHandler. OLE also calls it internally when the CLSID specified in an object creation call is not registered.

If the given class does not have a special handler, a call to OleCreateDefaultHandler produces the same results as a call to the CoCreateInstance function with the class context parameter assigned the value CLSCTX_INPROC_HANDLER.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

CoCreateInstance, CLSCTX


OleCreateEmbeddingHelper

Creates an OLE embedding helper object using application-supplied code aggregated with pieces of the OLE default object handler. This helper object can be created and used in a specific context and role, as determined by the caller.

WINOLEAPI OleCreateEmbeddingHelper(
  REFCLSID clsid,      //Identifier of the class to be helped
  LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown if
                       // aggregated; else NULL
  DWORD flags,         //Purpose for the helper
  LPCLASSFACTORY pCF,  //Pointer on the class object for the
                       // secondary object
  REFIID riid,         //Reference to the identifier of the
                       // interface desired by the caller
  LPVOID * ppvObj      //Address of output variable that receives
                       // the interface pointer requested in riid
);

Parameters

clsid
[in] CLSID of the class to be helped.
pUnkOuter
[in] If the embedding helper is to be aggregated, pointer to the outer object's controlling IUnknown interface. If it is not to be aggregated, although this is rare, the value should be NULL.
flags
[in] DWORD containing flags that specify the role and creation context for the embedding helper. For legal values, see the following Remarks section.
pCF
[in] Pointer to the IClassFactory interface on the class object the function uses to create the secondary object. In some situations, this value may be NULL. For more information, see the following Remarks section.
riid
[in] Reference to the identifier of the interface desired by the caller.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created embedding helper.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The OLE embedding helper was created successfully.
E_NOINTERFACE
The interface is not supported by the object.

Remarks

The OleCreateEmbeddingHelper function creates an object that supports the same interface implementations found in the default handler, but which has additional hooks that allow it to be used more generally than just as a handler object. The following two calls produce the same result:

OleCreateEmbeddingHelper(clsid, pUnkOuter, EMBDHLP_INPROC_HANDLER |
    EMBDHLP_CREATENOW, NULL, iid, ppvObj)

OleCreateDefaultHandler(clsid, pUnkOuter, iid, ppvObj)

The embedding helper is aggregatable; pUnkOuter is the controlling IUnknown of the aggregate of which the embedding helper is to be a part. It is used to create a new instance of the OLE default handler, which can be used to support objects in various roles. The caller passes a pointer to its IClassFactory implementation to OleCreateEmbeddingHelper. This object and the default handler are then aggregated to create the new embedding helper object.

The OleCreateEmbeddingHelper function is usually used to support one of the following implementations:

  • An EXE object application that is being used as both a container and a server, and which supports inserting objects into itself. For this case, CreateEmbeddingHelper allows the object to support the interfaces usually supported only in the handler. To accomplish this, the application must first register its CLSID for different contexts, making two registration calls to the CoRegisterClassObject function, rather than one, as follows:
        CoRegisterClassObject(clsidMe, pUnkCfLocal, CLSCTX_LOCAL_SERVER,
        REGCLS_MULTI_SEPARATE...)

    CoRegisterClassObject(clsidMe, pUnkCfInProc, CLSCTX_INPROC_SERVER,

        REGCLS_MULTI_SEPARATE...)

    In these calls, you would pass along different class factory implementations to each of pUnkCfLocal and pUnkCfInProc. The class factory pointed to by pUnkCfLocal would be used to create objects that are to be embedded in a remote process, which is the normal case which uses a handler object associated with the client. However, when a server both creates an object and embeds it within itself, pUnkCfInProc points to a class object that can create an object that supports the handler interfaces. The local class is used to create the object and the in-process class creates the embedding helper, passing in the pointer to the first object's class factory in pCF.

  • A custom in-process object handler, in which case, the DLL creates the embedding helper by passing in a pointer to a private implementation of IClassFactory in pCF.

The flags parameter indicates how the embedding helper is to be used and how and when the embedding helper is initialized. The values for flags are obtained by OR-ing together values from the following table:

Values for flags Parameter | Purpose
EMBDHLP_INPROC_HANDLER | Creates an embedding helper that can be used with DLL object applications; specifically, the helper exposes the caching features of the default object handler.
EMBDHLP_INPROC_SERVER | Creates an embedding helper that is to be used as part of an in-process server. pCF cannot be NULL.
EMBDHLP_CREATENOW | Creates the secondary object using pCF immediately; if pCF is null, the standard proxy manager is used.
EMBDHLP_DELAYCREATE | Delays creation of the secondary object until it is needed (when the helper is put into the running state) to enhance speed and memory use. pCF must not be NULL. The EMBDHLP_INPROC_HANDLER flag cannot be used with this flag.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateDefaultHandler


OleCreateEx

Extends OleCreate functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple presentation formats or data, instead of the single format supported by OleCreate.

HRESULT OleCreateEx(
  REFCLSID rclsid,        //Class of object to create
  REFIID riid,            //Reference to the identifier of the
                          // interface of object to return
  DWORD dwFlags,          //Value from OLECREATE
                          // enumeration
  DWORD renderopt,        //Value from OLERENDER
                          // enumeration
  ULONG cFormats,         //Number of FORMATETC
                          // structures in rgFormatEtc
  DWORD rgAdvf,           //Points to an array of cFormats DWORD
                          // elements
  LPFORMATETC rgFormatEtc,  //Points to an array of cFormats
                            // FORMATETC structures; NULL otherwise
  LPADVISESINK pAdviseSink,  //IAdviseSink pointer
                             // (custom caching); NULL (default
                             // caching); NULL otherwise
  DWORD* rgdwConnection,  //Location to return array of dwConnection
                          // values
  LPOLECLIENTSITE pClientSite,
                          //Pointer to the primary interface the
                          // object will use to request services
  LPSTORAGE pStg,         //Pointer to storage to use for object
  LPVOID FAR* ppvObj      //Address of output variable that receives
                          // the interface pointer requested in riid
);

Parameters

rclsid
Identifies the class of the object to create.
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the RENDEROPT enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the objects in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object. In all other cases, this parameter must be NULL.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats. In all other cases, this parameter must be NULL.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it. This parameter may not be NULL.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

This function supports the standard return value E_INVALIDARG, as well as the following:

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.

Remarks

The following call to OleCreate:

    OleCreate(rclsid, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateEx(rclsid, riid, renderopt, 1, &advf, pFormatEtc, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions, (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData) create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage), during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreate, IOleCache::Cache, IDataObject::DAdvise, IOleObject::SetClientSite, IAdviseSink::OnDataChange, IStorage, OLERENDER, FORMATETC, ADVF


OleCreateFontIndirect

Creates and initializes a standard font object using an initial description of the font's properties in a FONTDESC structure. The function returns an interface pointer to the new font object specified by caller in the riid parameter. A QueryInterface is built into this call. The caller is responsible for calling Release through the interface pointer returned.

STDAPI OleCreateFontIndirect(
  FONTDESC* pFontDesc,
                //Pointer to the structure of parameters for font
  REFIID riid,  //Reference to the identifier of the interface
  VOID** ppvObj //Address of output variable that receives the
                // interface pointer requested in riid
);

Parameters

pFontDesc
[in] Address of a caller-allocated, FONTDESC structure containing the initial state of the font. This value must not be NULL.
riid
[in] Reference to the identifier of the interface describing the type of interface pointer to return in ppvObj.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created font object. If successful, the caller is responsible to call Release through this interface pointer when the new object is no longer needed. If unsuccessful, the value of *ppvObj is set to NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The new font was created successfully.
E_NOINTERFACE
The object does not support the interface specified in riid.
E_POINTER
The address in pFontDesc or ppvObj is not valid. Note that if pFontDesc is set to NULL, the function returns NO_ERROR.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Import Library: Included as a resource in olepro32.dll.

See Also

FONTDESC


OleCreateFromData

Creates an embedded object from a data transfer object retrieved either from the clipboard or as part of an OLE drag-and-drop operation. It is intended to be used to implement a paste from an OLE drag-and-drop operation.

WINOLEAPI OleCreateFromData(
  LPDATAOBJECT pSrcDataObj,  //Pointer to the data transfer object
  REFIID riid,               //Reference to the identifier of the
                             // interface to be used to communicate
                             // with the new object
  DWORD renderopt,           //Value from OLERENDER
  LPFORMATETC pFormatEtc,    //Pointer to value from FORMATETC,
                             // depending on renderopt
  LPOLECLIENTSITE pClientSite,  //Pointer to interface
  LPSTORAGE pStg,            //Pointer to storage of object
  LPVOID FAR* ppvObj         //Address of output variable that
                             // receives the interface pointer
                             // requested in riid
);

Parameters

pSrcDataObj
[in] Pointer to the IDataObject interface on the data transfer object that holds the data from which the object is created.
riid
[in] Reference to the identifier of the interface the caller later uses to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface identifier for IOleObject).
renderopt
[in] Value from the enumeration OLERENDER that indicates the locally cached drawing or data-etrieval capabilities the newly created object is to have. Additional considerations are described in the following Remarks section.
pFormatEtc
[in] Pointer to a value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface on the storage object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
The embedded object was created successfully.
OLE_E_STATIC
Indicates OLE can create only a static object.
DV_E_FORMATETC
No acceptable formats are available for object creation.

Remarks

The OleCreateFromData function creates an embedded object from a data transfer object supporting the IDataObject interface. The data object in this case is either the type retrieved from the clipboard with a call to the OleGetClipboard function or is part of an OLE drag-and-drop operation (the data object is passed to a call to IDropTarget::Drop).

If either the FileName or FileNameW clipboard format (CF_FILENAME) is present in the data transfer object, and CF_EMBEDDEDOBJECT or CF_EMBEDSOURCE do not exist, OleCreateFromData first attempts to create a package containing the indicated file. Generally, it takes the first available format. The File Manager places these formats on the clipboard when the user selects the File/Copy To Clipboard menu command.

If OleCreateFromData cannot create a package, it tries to create an object using the CF_EMBEDDEDOBJECT format. If that format is not available, OleCreateFromData tries to create it with the CF_EMBEDSOURCE format. If neither of these formats is available and the data transfer object supports the IPersistStorage interface, OleCreateFromData calls the object's IPersistStorage::Save to have the object save itself.

If an existing linked object is selected, then copied, it appears on the clipboard as just another embeddable object. Consequently, a paste operation that invokes OleCreateFromData may create a linked object. After the paste operation, the container should call the QueryInterface function, requesting IID_IOleLink (defined in the OLE headers as the interface identifier for IOleLink), to determine if a linked object was created.

Use the renderopt and pFormatetc parameters to control the caching capability of the newly created object. For general information about using the interaction of these parameters to determine what is to be cached, refer to the OLERENDER enumeration. There are, however, some additional specific effects of these parameters on the way OleCreateFromData initializes the cache.

When OleCreateFromData uses either the CF_EMBEDDEDOBJECT or the CF_EMBEDSOURCE clipboard format to create the embedded object, the main difference between the two is where the cache-initialization data is stored:

  • CF_EMBEDDEDOBJECT indicates that the source is an existing embedded object. It already has in its cache the appropriate data, and OLE uses this data to initialize the cache of the new object.
  • CF_EMBEDSOURCE indicates that the source data object contains the cache-initialization information in formats other than CF_EMBEDSOURCE. OleCreateFromData uses these to initialize the cache of the newly embedded object.

The renderopt values affect cache initialization as follows:

Value | Description
OLERENDER_DRAW & OLERENDER_FORMAT | If the presentation information to be cached is currently present in the appropriate cache-initialization pool, it is used. (Appropriate locations are in the source data object cache for CF_EMBEDDEDOBJECT, and in the other formats in the source data object for CF_EMBEDSOURCE.) If the information is not present, the cache is initially empty, but will be filled the first time the object is run. No other formats are cached in the newly created object.
OLERENDER_NONE | Nothing is to be cached in the newly created object. If the source has the CF_EMBEDDEDOBJECT format, any existing cached data that has been copied is removed.
OLERENDER_ASIS | If the source has the CF_EMBEDDEDOBJECT format, the cache of the new object is to contain the same cache data as the source object. For CF_EMBEDSOURCE, nothing is to be cached in the newly created object. This option should be used by more sophisticated containers. After this call, such containers would call IOleCache::Cache and IOleCache::Uncache to set up exactly what is to be cached. For CF_EMBEDSOURCE, they would then also call IOleCache::InitCache.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreate, IDataObject


OleCreateFromDataEx

Extends OleCreateFromData functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple formats of presentation or data, instead of the single format supported by OleCreateFromData.

HRESULT OleCreateFromDataEx(
  LPDATAOBJECT pSrcDataObj,  //Pointer to data transfer object
  REFIID riid,              //Reference to the identifier of the
                            // interface of object to return
  DWORD dwFlags,            //Value from
                            // OLECREATE enumeration
  DWORD renderopt,          //Value from OLERENDER
                            // enumeration
  ULONG cFormats,           //Number of FORMATETCs in
                            // rgFormatEtc
  DWORD rgAdvf,             //Points to an array of cFormats DWORD
                            // elements
  LPFORMATETC rgFormatEtc,  //Points to an array of cFormats
                            // FORMATETC structures
  LPADVISESINK pAdviseSink,  //IAdviseSink pointer,
                             // or NULL, indicating default data format
                             // caching
  DWORD FAR* rgdwConnection,  //Location to return array of
                              // dwConnection values
  LPCLIENTSITE pClientSite,  //Pointer to primary interface the
                             // object will use to request services
  LPSTORAGE pStg,           //Pointer to storage to use for object
  LPVOID FAR* ppvObj        //Address of output variable that
                            // receives the interface pointer requested
                            // in riid
);

Parameters

pSrcDataObj
Pointer to the data transfer object holding the new data used to create the new object. (see OleCreateFromData).
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the RENDEROPT enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the object's in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The following call to OleCreateFromData:

    OleCreateFromData(lpszFileName, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateFromDataEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateFromFileEx(rclsid, lpszFileName, riid, renderopt, 1, &advf, pFormatEtc, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData) create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage) during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx, contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically, such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateFromData, IOleCache::Cache, IDataObject::DAdvise, IStorage, IAdviseSink::OnDataChange, IOleObject::SetClientSite, OLECREATE, OLERENDER, FORMATETC, ADVF


OleCreateFromFile

Creates an embedded object from the contents of a named file.

WINOLEAPI OleCreateFromFile(
  REFCLSID rclsid,         //Reserved. Must be CLSID_NULL
  LPCOLESTR lpszFileName,  //Pointer to full path of file used to
                           // create object
  REFIID riid,             //Reference to the identifier of the
                           // interface to be used to communicate with
                           // new object
  DWORD renderopt,         //Value from OLERENDER
  LPFORMATETC pFormatEtc,  //Pointer to the FORMATETC structure
  LPOLECLIENTSITE pClientSite,
                           //Pointer to an interface
  LPSTORAGE pStg,          //Pointer tothe interface to be used as
                           // object storage
  LPVOID FAR* ppvObj       //Address of output variable that
                           // receives the interface pointer requested
                           // in riid
);

Parameters

rclsid
[in] Reserved. Must be CLSID_NULL.
lpszFileName
[in] Pointer to a string specifying the full path of the file from which the object should be initialized.
riid
[in] Reference to the identifier of the interface the caller later uses to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface ID of IOleObject).
renderopt
[in] Value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pFormatEtc
[in] Depending on which of the OLERENDER flags is used as the value of renderopt, pointer to one of the FORMATETC enumeration values. Refer also to the OLERENDER enumeration for restrictions.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface on the storage object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
Embedded object successfully created.
STG_E_FILENOTFOUND
File not bound.
OLE_E_CANT_BINDTOSOURCE
Not able to bind to source.
STG_E_MEDIUMFULL
The medium is full.
DV_E_TYMED
Invalid TYMED.
DV_E_LINDEX
Invalid LINDEX.
DV_E_FORMATETC
Invalid FORMATETC structure.

Remarks

The OleCreateFromFile function creates a new embedded object from the contents of a named file. If the ProgID in the registration database contains the PackageOnFileDrop key, it creates a package. If not, the function calls the GetClassFile function to get the CLSID associated with the lpszFileName parameter, and then creates an OLE 2-embedded object associated with that CLSID. The rclsid parameter of OleCreateFromFile will always be ignored, and should be set to CLSID_NULL.

As for other OleCreate Xxx functions, the newly created object is not shown to the user for editing, which requires a DoVerb operation. It is used to implement insert file operations, such as the Create from File command in Word for Windows.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

GetClassFile


OleCreateFromFileEx

Extends OLeCreateFromFile functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple presentation formats or data, instead of the single format supported by OleCreateFromFile.

HRESULT OleCreateFromFileEx(
  REFCLSID rclsid,         //Reserved; must be CLSID_NULL
  LPCOLESTR lpszFileName,  //Pointer to name of file to initialize
                           // new object from
  REFIID riid,             //Reference to the identifier of the
                           // interface of object to return
  DWORD dwFlags,           //Value from OLECREATE
                           // enumeration
  DWORD renderopt,         //Value from OLERENDER
                           // enumeration
  ULONG cFormats,          //Number of FORMATETC
                           // structures in the rgFormatEtc array
  DWORD rgAdvf,            //Points to an array of cFormats DWORD
                           // elements
  LPFORMATETC rgFormatEtc,  //Points to an array of cFormats
                            // FORMATETC structures
  LPADVISESINK pAdviseSink,  //IAdviseSink pointer
                             // (custom caching), or NULL (default
                             // caching)
  DWORD FAR* rgdwConnection,  //Location to return the array of
                              // dwConnection values
  LPCLIENTSITE pClientSite,  //Pointer to primary interface the
                             // object will use to request services
  LPSTORAGE pStg,          //Pointer to storage to use for object
  LPVOID FAR* ppvObj       //Address of output variable that
                           // receives the interface pointer requested
                           // in riid
);

Parameters

rclsid
Reserved for future use; must be CLSID_NULL.
lpszFileName
Pointer to the name of the file from which the new object should be initialized.
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the OLERENDER enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the objects in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The following call to OleCreateFromFile:

    OleCreateFromFile(rclsid, lpszFileName, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateFromFileEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateFromFileEx(rclsid, lpszFileName, riid, renderopt, 1, &advf, pFormatEtc, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData) create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage), during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx, contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically, such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateFromFile, IOleCache::Cache, IDataObject::DAdvise, IAdviseSink::OnDataChange, IOleObject::SetClientSite, IStorage, OLERENDER, FORMATETC, ADVF


OleCreateLink

Creates an OLE compound-document linked object.

WINOLEAPI OleCreateLink(
  LPMONIKER pmkLinkSrc,         //Pointer to moniker indicating
                                // source of linked object
  REFIID riid,                  //Reference to the identifier of the
                                // interfacer to be used to communicate
                                // with the new object
  DWORD renderopt,              //Value from OLERENDER
  LPFORMATETC pFormatEtc,       //Pointer to a FORMATETC structure
  LPOLECLIENTSITE pClientSite,  //Pointer to an interface
  LPSTORAGE pStg,               //Pointer to the object's storage
  LPVOID FAR* ppvObj            //Address of output variable that
                                // receives the interface pointer
                                // requested in riid
);

Parameters

pmkLinkSrc
[in] Pointer to the IMoniker interface on the moniker that can be used to locate the the source of the linked object.
riid
[in] Reference to the identifier of the interface the caller later uses to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface identifier for IOleObject).
renderopt
[in] Specifies a value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. Additional considerations are described in the Remarks section below.
pFormatEtc
[in] Pointer to a value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface on the storage object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
The compound-document linked object was created successfully.
OLE_E_CANT_BINDTOSOURCE
Not able to bind to source. Binding is necessary to get the cache's initialization data.

Remarks

Call OleCreateLink to allow a container to create a link to an object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IOleObject::SetMoniker, IOleClientSite::GetMoniker


OleCreateLinkEx

Extends OleCreateLink functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple formats of presentations or data, instead of the single format supported by OleCreateLink.

HRESULT OleCreateFromLinkEx(
  LPMONIKER pmkLinkSrc,
                 //Pointer to a moniker to object to create link to
  REFIID riid,   //Reference to the identifier of the interface of
                 // the link object to return
  DWORD dwFlags, //Value from OLECREATE
                 // enumeration
  DWORD renderopt,
                 //Value from OLERENDER enumeration
  ULONG cFormats,
                 //Number of FORMATETCs in rgFormatEt
  DWORD rgAdvf,  //Points to an array of cFormats DWORD elements
  LPFORMATETC rgFormatEtc,
                 //Points to an array of cFormats FORMATETC
                 // structures
  LPADVISESINK pAdviseSink,
                 //IAdviseSink pointer, or NULL,
                 // indicating default data format caching
  DWORD FAR* rgdwConnection,
                 //Location to return array of dwConnection values
  LPCLIENTSITE pClientSite,
                 //Pointer to the primary interface the object will
                 // to use to request services
  LPSTORAGE pStg,
                 //Pointer to storage to use for the object
  LPVOID FAR* ppvObj
                 //Address of output variable that receives the
                 // interface pointer requested in riid
);

Parameters

pmkLinkSrc
Pointer to a moniker to the object to create a link to.
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the OLERENDER enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the objects in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The following call to OleCreateLink:

    OleCreateLink(pmkLinkSrc, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateLinkEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateFromFileEx(pmkLinkSrc, riid, renderopt, 1, &advf, pFormatEtc, NULL, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData) create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage) during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching in at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx, contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically, such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateLink, IOleCache::Cache, IDataObject::DAdvise, IAdviseSink::OnDataChange, IOleObject::SetClientSite, OLECREATE, OLERENDER, FORMATETC, ADVF


OleCreateLinkFromData

Creates a linked object from a data transfer object retrieved either from the clipboard or as part of an OLE drag-and-drop operation.

WINOLEAPI OleCreateLinkFromData(
  LPDATAOBJECT pSrcDataObj,  //Pointer to data transfer objectt
  REFIID riid,               //Reference to the identifier of the
                             // interface to be used to communicate
                             // with the new object
  DWORD renderopt,           //OLERENDER value
  LPFORMATETC pFormatEtc,    //Pointer to a FORMATETC structure
  LPOLECLIENTSITE pClientSite,  //Pointer to an interface
  LPSTORAGE pStg,            //Pointer to the object storage
  LPVOID FAR* ppvObj         //Address of output variable that
                             // receives the interface pointer
                             // requested in riid
);

Parameters

pSrcDataObj
[in] Pointer to the IDataObject interface on the data transfer object from which the linked object is to be created.
riid
[in] Reference to the identifier of interface the caller later uses to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface identifier for IOleObject).
renderopt
[in] Value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. Additional considerations are described in the following Remarks section.
pFormatEtc
[in] Pointer to a value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface on the storage object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
The linked object was created successfully.
CLIPBRD_E_CANT_OPEN
Not able to open the clipboard.
OLE_E_CANT_GETMONIKER
Not able to extract the object's moniker.
OLE_E_CANT_BINDTOSOURCE
Not able to bind to source. Binding is necessary to get the cache's initialization data.

Remarks

The OleCreateLinkFromData function is used to implement either a paste-link or a drag-link operation. Its operation is similar to that of the OleCreateFromData function, except that it creates a link, and looks for different data formats. If the CF_LINKSOURCE format is not present, and either the FileName or FileNameW clipboard format is present in the data transfer object, OleCreateLinkFromData creates a package containing the link to the indicated file.

You use the renderopt and pFormatetc parameters to control the caching capability of the newly created object. For general information on how to determine what is to be cached, refer to the OLERENDER enumeration for a description of the interaction between renderopt and pFormatetc. There are, however, some additional specific effects of these parameters on the way OleCreateLinkFromData initializes the cache, as follows:

Value | Description
OLERENDER_DRAW, OLERENDER_FORMAT | If the presentation information is in the other formats in the source data object, this information is used. If the information is not present, the cache is initially empty, but will be filled the first time the object is run. No other formats are cached in the newly created object.
OLERENDER_NONE, OLERENDER_ASIS | Nothing is to be cached in the newly created object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateLink


OleCreateLinkFromDataEx

Extends OleCreateLinkFromData functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple formats of presentations or data, instead of the single format supported by OleCreateLinkFromData.

HRESULT OleCreateLinkFromDataEx(
  LPDATAOBJECT pSrcDataObj,
                  //Pointer to the data object to create a link
                  // object from
  REFIID riid,    //Reference to the identifier of the interface of
                  // the link object to return
  DWORD dwFlags,  //Value from OLECREATE
                  // enumeration
  DWORD renderopt,
                  //Value from OLERENDER enumeration
  ULONG cFormats, //Number of FORMATETCs in rgFormatEtc
  DWORD rgAdvf,   //Points to an array of cFormats DWORD elements
  LPFORMATETC rgFormatEtc,
                  //Points to an array of cFormats FORMATETC
                  // structures
  LPADVISESINK pAdviseSink,
                  //IAdviseSink pointer (custom
                  // caching); NULL (default caching); NULL otherwise
  DWORD FAR* rgdwConnection,
                  //Location to return array of dwConnection values
  LPCLIENTSITE pClientSite,
                  //Pointer to the primary interface the object will
                  // use to request services
  LPSTORAGE pStg, //Pointer to storage to use for object
  LPVOID FAR* ppvObj
                  //Address of output variable that receives the
                  // interface pointer requested in riid
);

Parameters

pSrcDataObj
Pointer to the data object to create a link object from.
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the RENDEROPT enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the objects in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The following call to OleCreateLinkFromData:

    OleCreateLinkFromData(pSrcDataObj, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateLinkFromDataEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateLinkFromDataEx(pSrcDataObj, riid, renderopt, 1, &advf, pFormatEtc, NULL, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData), create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage) during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx, contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateLinkFromData, IOleCache::Cache, IDataObject::DAdvise, IAdviseSink::OnDataChange, IOleObject::SetClientSite, OLECREATE, OLERENDER, FORMATETC, ADVF


OleCreateLinkToFile

Creates an object that is linked to a file.

WINOLEAPI OleCreateLinkToFile(
  LPWSTR lpszFileName,  //Pointer to source of linked object
  REFIID riid,          //Reference to the identifier of the
                        // interface to be used to communicate with the
                        // new object
  DWORD renderopt,      //Value from OLERENDER
  LPFORMATETC pFormatEtc,  //Pointer to a FORMATETC structure
  IOleClientSite * pClientSite,
                        //Pointer to an interface
  IStorage * pStg,      //Pointer to the object's storage
  void ** ppvObj        //Address of output variable that receives
                        // the interface pointer requested in riid
);

Parameters

lpszFileName
[in] Pointer to a string naming the source file to be linked to.
riid
[in] Reference to the identifier of the interface the caller later uses to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface identifier for IOleObject).
renderopt
[in] Value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. Additional considerations are described in the following Remarks section.
pFormatEtc
[in] Pointer to a value from the enumeration OLERENDER that indicates the locally cached drawing or data-retrieval capabilities the newly created object is to have. The OLERENDER value chosen affects the possible values for the pFormatEtc parameter.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface on the storage object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
The object was created successfully.
STG_E_FILENOTFOUND
The file name is invalid.
OLE_E_CANT_BINDTOSOURCE
Not able to bind to source.

Remarks

The OleCreateLinkToFile function differs from the OleCreateLink function because it can create links both to files that are not aware of OLE, as well as to those that are using the Windows Packager.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateLink


OleCreateLinkToFileEx

Extends OleCreateLinkToFile functionality by supporting more efficient instantiation of objects in containers requiring caching of multiple formats of presentations or data, instead of the single format supported by OleCreateLinkToFile.

HRESULT OleCreateLinkToFileEx(
  LPCOLESTR lpszFileName,
                  //Pointer to the name of the file to create a link
                  // to.
  REFIID riid,    //Reference to the identifier of the interface of
                  // the link object to return
  DWORD dwFlags,  //Value from OLECREATE
                  // enumeration
  DWORD renderopt,
                  //Value from OLERENDER enumeration
  ULONG cFormats, //Number of FORMATETCs in rgFormatEtc
  DWORD rgAdvf,   //Points to an array of cFormats DWORD elements
  LPFORMATETC rgFormatEtc,
                  //Points to an array of cFormats FORMATETC
                  // structures
  LPADVISESINK pAdviseSink,
                  ///IAdviseSink pointer (custom
                  // caching); NULL (default caching); NULL otherwise
  DWORD FAR* rgdwConnection,
                  //Location to return array of dwConnection values
  LPCLIENTSITE pClientSite,
                  //Pointer to the primary interface the object will
                  // use to request services.
  LPSTORAGE pStg, //Pointer to storage to use for object
  LPVOID FAR* ppvObj
                  //Address of output variable that receives the
                  // interface pointer requested in riid
);

Parameters

lpszFileName
Pointer to the name of the file to create a link to.
riid
Reference to the identifier of the interface of the object to return.
dwFlags
Value taken from the OLECREATE enumeration.
renderopt
Value taken from the RENDEROPT enumeration.
cFormats
When renderopt is OLERENDER_FORMAT, indicates the number of FORMATETC structures in the rgFormatEtc array, which must be at least one. In all other cases, this parameter must be zero.
rgAdvf
When renderopt is OLERENDER_FORMAT, points to an array of cFormats DWORD elements, each of which is a combination of values from the ADVF enumeration. Each element of this array is passed in as the advf parameter to a call to either IOleCache::Cache or IDataObject::DAdvise, depending on whether pAdviseSink is NULL or non-NULL (see below). In all other cases, this parameter must be NULL.
rgFormatEtc
When renderopt is OLERENDER_FORMAT, points to an array of cFormats FORMATETC structures. When pAdviseSink is NULL, each element of this array is passed as the pFormatEtc parameter to a call to the object's IOleCache::Cache. This populates the data and presentation cache managed by the objects in-process handler (typically the default handler) with presentation or other cacheable data. When pAdviseSink is non-NULL, each element of this array is passed as the pFormatEtc parameter to a call to IDataObject::DAdvise. This allows the caller (typically an OLE Container) to do its own caching or processing of data received from the object.
pAdviseSink
When renderopt is OLERENDER_FORMAT, may be either a valid IAdviseSink pointer, indicating custom caching or processing of data advises, or NULL, indicating default caching of data formats.
rgdwConnection
Location to return the array of dwConnection values returned when the pAdviseSink interface is registered for each advisory connection using IDataObject::DAdvise, or NULL if the returned advisory connections are not needed. Must be NULL, if pAdviseSink is NULL.
pClientSite
Pointer to the primary interface through which the object will request services from its container. This parameter may be NULL, in which case it is the caller's responsibility to establish the client site as soon as possible using IOleObject::SetClientSite.
pStg
Pointer to the storage to use for the object and any default data or presentation caching established for it.
ppvObj
Address of output pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Values

S_OK
Success.
E_NOINTERFACE
The object does not support the riid interface.
E_INVALIDARG
One or more arguments are invalid.

Remarks

The following call to OleCreateLinkToFile:

    OleCreateLinkToFile(lpszFileName, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj);

is equivalent to the following call to OleCreateLinkToFileEx:

    DWORD    advf = ADVF_PRIMEFIRST;
    OleCreateLinkToFileEx(lpszFileName, riid, renderopt, 1, &advf, pFormatEtc, NULL, NULL, pClientSite, pStg, ppvObj);

Existing instantiation functions (OleCreate, OleCreateFromFile, OleCreateFromData, OleCreateLink, OleCreateLinkToFile, and OleCreateLinkFromData) create only a single presentation or data format cache in the default cache location (within the '\001OlePresXXX' streams of the passed-in IStorage) during instantiation. Plus, these caches must be created when the object next enters the running state. Since most applications require caching at least two presentations (screen and printer) and may require caching data in a different format or location from the handler, applications must typically launch and shut down the object server multiple times in order to prime their data caches during object creation, i.e., Insert Object, Insert Object from File, and Paste Object.

Extended versions of these creation functions solve this problem. OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, and OleCreateLinkFromDataEx, contain the following new parameters: dwFlags to indicate additional options, cFormats to indicate how many formats to cache, rgAdvf, from the ADVF enumeration, to specify the advise flags for each format to be cached, pAdviseSink to indicate whether presentation (default-handler) or data (non-default-handler) caching is required, rgdwConnection to return IDataObject::DAdvise cookies, and pFormatEtc, an array of formats rather than a single format.

Containers requiring that multiple presentations be cached on their behalf by the object's handler can simply call these functions and specify the number of formats in cFormats, the ADVF flags for each format in rgAdvf, and the set of formats in pFormatEtc. These containers pass NULL for pAdviseSink.

Containers performing all their own data- or presentation-caching perform these same steps, but pass a non-NULL pAdviseSink. They perform their own caching or manipulation of the object or data during IAdviseSink::OnDataChange. Typically, such containers never establish the advisory connections with ADVF_NODATA, although they are not prevented from doing so.

These new functions are for OLE Compound Documents. Using these functions, applications can avoid the repeated launching and initialization steps required by the current functions. They are targeted at OLE Compound Document container applications that use default data- and presentation-caching, and also at applications that provide their own caching and data transfer from the underlying IDataObject::DAdvise support.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateLinkToFile, IOleCache::Cache, IDataObject::DAdvise, IStorage, IAdviseSink::OnDataChange, IOleObject::SetClientSite, OLECREATE, OLERENDER, FORMATETC, ADVF


OleCreateMenuDescriptor

Creates and returns an OLE menu descriptor (that is, an OLE-provided data structure that describes the menus) for OLE to use when dispatching menu messages and commands.

HOLEMENU OleCreateMenuDescriptor(
  HMENU hmenuCombined,               //Handle to the combined menu
  LPOLEMENUGROUPWIDTHS lpMenuWidths  //Pointer to the number of
                                     // menus in each group
);

Parameters

hmenuCombined
[in] Handle to the combined menu created by the object.
lpMenuWidths
[in] Pointer to an array of six LONG values giving the number of menus in each group.

Return Value

Returns the handle to the descriptor, or NULL if insufficient memory is available.

Remarks

The OleCreateMenuDescriptor function can be called by the object to create a descriptor for the composite menu. OLE then uses this descriptor to dispatch menu messages and commands. To free the shared menu descriptor when it is no longer needed, the container should call the companion helper function, OleDestroyMenuDescriptor.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleDestroyMenuDescriptor


OleCreatePictureIndirect

Creates a new picture object initialized according to a PICTDESC structure, which can be NULL to create an uninitialized object if the caller wishes to have the picture initialize itself through IPersistStream::Load. The fOwn parameter indicates whether the picture is to own the GDI picture handle for the picture it contains, so that the picture object will destroy its picture when the object itself is destroyed. The function returns an interface pointer to the new picture object specified by the caller in the riid parameter. A QueryInterface is built into this call. The caller is responsible for calling Release through the interface pointer returned.

STDAPI OleCreatePictureIndirect(
  PICTDESC* pPictDesc,
                 //Pointer to the structure of parameters for picture
  REFIID  riid,  //Reference to the identifier of the interface
  BOOL fOwn,     //Whether the picture is to be destroyed
  VOID** ppvObj  //Address of output variable that receives the
                 // interface pointer requested in riid
);

Parameters

pPictDesc
[in] Pointer to a caller-allocated structure containing the initial state of the picture.
riid
[in] Reference to the identifier of the interface describing the type of interface pointer to return in ppvObj.
fOwn
[in] If TRUE, the picture object is to destroy its picture when the object is destroyed. If FALSE, the caller is responsible for destroying the picture.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object. If the call is successful, the caller is responsible for calling Release through this interface pointer when the new object is no longer needed. If the call fails, the value of *ppvObj is set to NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The new picture object was created successfully.
E_NOINTERFACE
The object does not support the interface specified in riid.
E_POINTER
The address in pPictDesc or ppvObj is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Import Library: Included as a resource in olepro32.dll.

See Also

OleLoadPicture, PICTDESC


OleCreateStaticFromData

Creates a static object (containing only a representation, with no native data) from a data transfer object.

WINOLEAPI OleCreateStaticFromData(
  LPDATAOBJECT pSrcDataObj,  //Pointer to the data transfer object
  REFIID riid,               //Reference to the identifier of the
                             // interface to be used to communicate
                             // with the new object
  DWORD renderopt,           //Value from OLERENDER
  LPFORMATETC pFormatEtc,    //Depending on renderopt, pointer to
                             // value from FORMATETC
  LPOLECLIENTSITE pClientSite,  //Pointer to the interface
  LPSTORAGE pStg,            //Pointer to store object
  LPVOID FAR* ppvObj         //Address of output variable that
                             // receives the interface pointer
                             // requested in riid
);

Parameters

pSrcDataObj
[in] Pointer to the IDataObject interface on the data transfer object that holds the data from which the object will be created.
riid
[in] Reference to the identifier of the interface with which the caller is to communicate with the new object (usually IID_IOleObject, defined in the OLE headers as the interface identifier for IOleObject).
renderopt
[in] Value from the enumeration OLERENDER indicating the locally cached drawing or data-retrieval capabilities that the container wants in the newly created component. It is an error to pass the render options OLERENDER_NONE or OLERENDER_ASIS to this function.
pFormatEtc
[in] Depending on which of the OLERENDER flags is used as the value of renderopt, may be a pointer to one of the FORMATETC enumeration values. Refer to the OLERENDER enumeration for restrictions.
pClientSite
[in] Pointer to an instance of IOleClientSite, the primary interface through which the object will request services from its container. May be NULL.
pStg
[in] Pointer to the IStorage interface for storage for the object. This parameter may not be NULL.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly created object.

Return Value

S_OK
The object was successfully created.

Remarks

The OleCreateStaticFromData function can convert any object, as long as it provides an IDataObject interface, to a static object. It is useful in implementing the Convert To Picture option for OLE linking or embedding.

Static objects can be created only if the source supports one of the OLE-rendered clipboard formats: CF_METAFILEPICT, CF_DIB, or CF_ BITMAP, and CF_ENHMF.

You can also call OleCreateStaticFromData to paste a static object from the clipboard. To determine whether an object is static, call the OleQueryCreateFromData function, which returns OLE_S_STATIC if one of CF_METAFILEPICT, CF_DIB, or CF_BITMAP is present and an OLE format is not present. This indicates that you should call OleCreateStaticFromData rather than the OleCreateFromData function to create the object.

The new static object is of class CLSID_StaticMetafile (in the case of CF_METAFILEPICT) and CLSID_StaticDib (in the case of CF_DIB or CF_BITMAP). The static object sets the OLEMISC_STATIC and OLE_CANTLINKINSIDE bits returned from IOleObject::GetMiscStatus. The static object will have the aspect DVASPECT_CONTENT and a LINDEX of -1.

The pDataObject is still valid after OleCreateStaticFromData returns. It is the caller's responsibility to free pDataObject — OLE does not release it.

There cannot be more than one presentation stream in a static object.

Note The OLESTREAM<->IStorage conversion functions also convert static objects.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateFromData


OleDestroyMenuDescriptor

Called by the container to free the shared menu descriptor allocated by the OleCreateMenuDescriptor function.

void OleDestroyMenuDescriptor(
  HOLEMENU holemenu  //Handle to the shared menu descriptor
);

Parameter

holemenu
[in] Handle to the shared menu descriptor that was returned by the OleCreateMenuDescriptor function.

Return Value

None. (This function does not indicate failure.)

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateMenuDescriptor


OleDraw

The OleDraw helper function can be used to draw objects more easily. You can use it instead of calling IViewObject::Draw directly.

WINOLEAPI OleDraw(
  IUnknown * pUnk,    //Pointer to the view object to be drawn
  DWORD dwAspect,     //How the object is to be represented
  HDC hdcDraw,        //Device context on which to draw
  LPCRECT lprcBounds  //Pointer to the rectangle in which the object
                      // is drawn
);

Parameters

pUnk
[in] Pointer to the IUnknown interface on the view object that is to be drawn.
dwAspect
[in] How the object is to be represented. Representations include content, an icon, a thumbnail, or a printed document. Valid values are taken from the enumeration DVASPECT. See DVASPECT for more information.
hdcDraw
[in] Device context on which to draw. Cannot be a metafile device context.
lprcBounds
[in] Pointer to a RECT structure specifying the rectangle in which the object should be drawn. This parameter is converted to a RECTL structure and passed to IViewObject::Draw.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
Object was successfully drawn.
OLE_E_BLANK
No data to draw from.
E_ABORT
The draw operation was aborted.
VIEW_E_DRAW
An error occurred in drawing.
OLE_E_INVALIDRECT
The rectangle is invalid.
DV_E_NOIVIEWOBJECT
The object doesn't support the IViewObject interface.

Remarks

The OleDraw helper function calls the QueryInterface method for the object specified (pUnk), asking for an IViewObject interface on that object. Then, OleDraw converts the RECT structure to a RECTL structure, and calls IViewObject::Draw as follows:

lpViewObj->Draw(dwAspect,-1,0,0,0,hdcDraw,&rectl,0,0,0);

Do not use OleDraw to draw into a metafile because it does not specify the lprcWBounds parameter required for drawing into metafiles.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IViewObject::Draw


OleDuplicateData

Duplicates the data found in the specified handle and returns a handle to the duplicated data. The source data is in a clipboard format. Use this function to help implement some of the data transfer interfaces such as IDataObject.

HANDLE OleDuplicateData(
  HANDLE hSrc,          //Handle of the source data
  CLIPFORMAT cfFormat,  //Clipboard format of the source data
  UINT uiFlags          //Flags used in global memory allocation
);

Parameters

hSrc
[in] Handle of the source data.
cfFormat
[in] Clipboard format of the source data.
uiFlags
[in] Flags to be used to allocate global memory for the copied data. These flags are passed to GlobalAlloc. If the value of uiFlags is NULL, GMEM_MOVEABLE is used as a default flag.

Return Values

handle
When the function is successful, contains the handle to the new data because data was successfully duplicated.
NULL
A NULL return value indicates that there was an error duplicating data.

Remarks

The CF_METAFILEPICT, CF_PALETTE, or CF_BITMAP formats receive special handling. They are GDI handles and a new GDI object must be created instead of just copying the bytes. All other formats are duplicated byte-wise.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.


OleFlushClipboard

Carries out the clipboard shutdown sequence. It also releases the IDataObject pointer that was placed on the clipboard by the OleSetClipboard function.

WINOLEAPI OleFlushClipboard( );

Return Values

S_OK
The clipboard has been flushed.
CLIPBRD_E_CANT_OPEN
The Windows OpenClipboard function used within OleFlushClipboard failed.
CLIPBRD_E_CANT_CLOSE
The Windows CloseClipboard function used within OleFlushClipboard failed.

Remarks

OleFlushClipboard renders the data from a data object onto the clipboard and releases the IDataObject pointer to the data object. While the application that put the data object on the clipboard is running, the clipboard holds only a pointer to the data object, thus saving memory. If you are writing an application that acts as the source of a clipboard operation, you can call the OleFlushClipboard function when your application is closed, such as when the user exits from your application. Calling OleFlushClipboard enables pasting and paste-linking of OLE objects after application shutdown.

Before calling OleFlushClipboard, you can easily determine if your data is still on the clipboard with a call to the OleIsCurrentClipboard function.

OleFlushClipboard leaves all formats offered by the data transfer object, including the OLE 1 compatibility formats, on the clipboard so they are available after application shutdown. In addition to OLE 1 compatibility formats, these include all formats offered on a global handle medium (all except for TYMED_FILE) and formatted with a NULL target device. For example, if a data-source application offers a particular clipboard format (say cfFOO) on an IStorage object, and calls the OleFlushClipboard function, the storage object is copied into memory and the hglobal memory handle is put on the Clipboard.

To retrieve the information on the clipboard, you can call the OleGetClipboard function from another application, which creates a default data object, and the hglobal from the clipboard again becomes a storage object. Furthermore, the FORMATETC enumerator and the IDataObject::QueryGetData method would all correctly indicate that the original clipboard format (cfFOO) is again available on a TYMED_ISTORAGE.

To empty the clipboard, call the OleSetClipboard function specifying a NULL value for its parameter. The application should call this when it closes if there is no need to leave data on the clipboard after shutdown, or if data will be placed on the clipboard using the standard Windows clipboard functions.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleGetClipboard, OleSetClipboard, OleIsCurrentClipboard, IDataObject


OleGetClipboard

Retrieves a data object that you can use to access the contents of the clipboard.

WINOLEAPI OleGetClipboard(
  IDataObject ** ppDataObj  //Address of output variable that
                            // receives the IDataObject interface
                            // pointer
);

Parameter

ppDataObj
[out] Address of IDataObject* pointer variable that receives the interface pointer to the clipboard data object.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The data object was successfully retrieved.
CLIPBRD_E_CANT_CLOSE
The Windows CloseClipboard function used within OleGetClipboard failed.
CLIPBRD_E_CANT_OPEN
The Windows OpenClipboard function used with OleGetClipboard failed.

Remarks

If you are writing an application that can accept data from the clipboard, call the OleGetClipboard function to get a pointer to the IDataObject interface that you can use to retrieve the contents of the clipboard.

OleGetClipboard handles three cases:

  1. The application that placed data on the clipboard with the OleSetClipboard function is still running.
  2. The application that placed data on the clipboard with the OleSetClipboard function has subsequently called the OleFlushClipboard function.
  3. There is data from a non-OLE application on the clipboard.

In the first case, the clipboard data object returned by OleGetClipboard may forward calls as necessary to the original data object placed on the clipboard and, thus, can potentially make RPC calls.

In the second case, OLE creates a default data object and returns it to the user. Because the data on the Clipboard originated from an OleSetClipboard call, the OLE-provided data object contains more accurate information about the type of data on the Clipboard. In particular, the original medium (TYMED) on which the data was offered is known. Thus, if a data-source application offers a particular clipboard format, for example cfFOO, on a storage object and calls the OleFlushClipboard function, the storage object is copied into memory and the hglobal memory handle is put on the Clipboard. Then, when the OleGetClipboard function creates its default data object, the hglobal from the clipboard again becomes an IStorage object. Furthermore, the FORMATETC enumerator and the IDataObject::QueryGetData method would all correctly indicate that the original clipboard format (cfFOO) is again available on a TYMED_ISTORAGE.

In the third case, OLE still creates a default data object, but there is no special information about the data in the Clipboard formats (particularly for application-defined Clipboard formats). Thus, if an hGlobal-based storage medium were put on the Clipboard directly by a call to the SetClipboardData function, the FORMATETC enumerator and the IDataObject::QueryGetData method would not indicate that the data was available on a storage medium. A call to the IDataObject::GetData method for TYMED_ISTORAGE would succeed, however. Because of these limitations, it is strongly recommended that OLE-aware applications interact with the Clipboard using the OLE Clipboard functions.

The clipboard data object created by the OleGetClipboard function has a fairly extensive IDataObject implementation. The OLE-provided data object can convert OLE 1 clipboard format data into the representation expected by an OLE 2 caller. Also, any structured data is available on any structured or flat medium, and any flat data is available on any flat medium. However, GDI objects (such as metafiles and bitmaps) are only available on their respective mediums.

Note that the tymed member of the FORMATETC structure used in the FORMATETC enumerator contains the union of supported mediums. Applications looking for specific information (such as whether CF_TEXT is available on TYMED_HGLOBAL) should do the appropriate bit masking when checking this value.

If you call the OleGetClipboard function, you should only hold on to the returned IDataObject for a very short time. It consumes resources in the application that offered it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleSetClipboard


OleInitialize

Initializes the COM library on the current apartment, identifies the concurrency model as single-thread apartment (STA), and enables additional functionality described in the Remarks section below. Applications must initialize the COM library before they can call COM library functions other than CoGetMalloc and memory allocation functions.

Applications that do not require the additional functionality enabled by OleInitialize should call CoInitializeEx instead of OleInitialize.

WINOLEAPI OleInitialize(
  LPVOID pvReserved  //Reserved; must be NULL.
);

Parameter

pvReserved
[in] Reserved; must be NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The COM library and additional functionality were initialized successfully on this apartment.
S_FALSE
The COM library is already initialized on this apartment.
OLE_E_WRONGCOMPOBJ
The versions of COMPOBJ.DLL and OLE2.DLL on your machine are incompatible with each other.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified the concurrency model for this apartment as multithread apartment (MTA). If running Windows 2000, this could also mean that a change from neutral threaded apartment to single threaded apartment occurred.

Remarks

Applications that use the following functionality must call OleInitialize before calling any other function in the COM library:

  • Clipboard
  • Drag and drop
  • Object linking and embedding (OLE)
  • In-place activation

OleInitialize calls CoInitializeEx internally to initialize the COM library on the current apartment. Because OLE operations are not thread-safe, OleInitialize specifies the concurrency model as single-thread apartment.

Once the concurrency model for an apartment is set, it cannot be changed. A call to OleInitialize on an apartment that was previously initialized as multithreaded will fail and return RPC_E_CHANGED_MODE.

You need to initialize the COM library on an apartment before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions.

Typically, the COM library is initialized on an apartment only once. Subsequent calls will succeed, as long as they do not attempt to change the concurrency model of the apartment, but will return S_FALSE. To close the COM library gracefully, each successful call to OleInitialize, including those that return S_FALSE, must be balanced by a corresponding call to OleUninitialize.

If OleInitialize returns OLE_E_WRONGCOMPOBJ, either COMPOBJ.DLL or OLE2.DLL has been updated to a version that is incompatible with the other file. To correct this problem, replace both of these files with versions for your operating system from the same release of the Platform SDK.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleUninitialize, CoInitialize, CoInitializeEx


OleIsCurrentClipboard

Determines whether the data object pointer previously placed on the clipboard by the OleSetClipboard function is still on the clipboard.

WINOLEAPI OleIsCurrentClipboard(
  IDataObject * pDataObject  //Pointer to the data object previously
                             // copied or cut
);

Parameter

pDataObject
[in] Pointer to the IDataObject interface on the data object containing clipboard data of interest, which the caller previously placed on the clipboard.

Return Values

S_OK
The IDataObject pointer is currently on the clipboard and the caller is the owner of the clipboard.
S_FALSE
The indicated pointer is not on the clipboard.

Remarks

OleIsCurrentClipboard only works for the data object used in the OleSetClipboard function. It cannot be called by the consumer of the data object to determine if the object that was on the clipboard at the previous OleGetClipboard call is still on the clipboard.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleFlushClipboard, OleSetClipboard


OleIsRunning

Determines whether a compound document object is currently in the running state.

BOOL OleIsRunning(
  LPOLEOBJECT pObject  //Pointer to the interface
);

Parameter

pObject
[in] Pointer to the IOleObject interface on the object of interest.

Return Value

The return value is TRUE if the object is running; otherwise, it is FALSE.

Remarks

You can use OleIsRunning and IRunnableObject::IsRunning interchangeably. OleIsRunning queries the object for a pointer to the IRunnableObject interface and calls its IsRunning method. If successful, the function returns the results of the call to IRunnableObject::IsRunning.

Note The implementation of OleIsRunning in earlier versions of OLE differs from that described here.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IRunnableObject::IsRunning


OleLoad

Loads into memory an object nested within a specified storage object.

WINOLEAPI OleLoad(
  IStorage * pStg,   //Pointer to the storage object from which to
                     // load
  REFIID riid,       //Reference to the identifier of the interface
  IOleClientSite * pClientSite,
                     //Pointer to the client site for the object
  LPVOID * ppvObj    //Address of output variable that receives the
                     // interface pointer requested in riid
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object from which to load the specified object.
riid
[in] Reference to the identifier of the interface that the caller wants to use to communicate with the object once it is loaded.
pClientSite
[in] Pointer to the IOleClientSite interface on the client site object being loaded.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly loaded object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The object was loaded successfully.
E_NOINTERFACE
The object does not support the specified interface.

This function can return any of the error values returned by the IPersistStorage::Load method.

Remarks

OLE containers load objects into memory by calling this function. When calling the OleLoad function, the container application passes in a pointer to the open storage object in which the nested object is stored. Typically, the nested object to be loaded is a child storage object to the container's root storage object. Using the OLE information stored with the object, the object handler (usually, the default handler) attempts to load the object. On completion of the OleLoad function, the object is said to be in the loaded state with its object application not running.

Some applications load all of the object's native data. Containers often defer loading the contained objects until required to do so. For example, until an object is scrolled into view and needs to be drawn, it does not need to be loaded.

The OleLoad function performs the following steps:

  1. If necessary, performs an automatic conversion of the object (see the OleDoAutoConvert function).
  2. Gets the CLSID from the open storage object by calling the IStorage::Stat method.
  3. Calls the CoCreateInstance function to create an instance of the handler. If the handler code is not available, the default handler is used (see the OleCreateDefaultHandler function).
  4. Calls the IOleObject::SetClientSite method with the pClientSite parameter to inform the object of its client site.
  5. Calls the QueryInterface method for the IPersistStorage interface. If successful, the IPersistStorage::Load method is invoked for the object.
  6. Queries and returns the interface identified by the riid parameter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

ReadClassStg, IClassFactory::CreateInstance, IPersistStorage::Load


OleLoadFromStream

Loads an object from the stream.

WINOLEAPI OleLoadFromStream(
  IStream * pStm,       //Pointer to stream from which object is to
                        // be loaded
  REFIID iidInterface,  //Interface identifier
  void ** ppvObj        //Address of output variable that receives
                        // the interface pointer requested in riid
);

Parameters

pStm
[in] Pointer to the IStream interface on the stream from which the object is to be loaded.
iidInterface
[in] Interface identifier (IID) the caller wants to use to communicate with the object once it is loaded.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the newly loaded object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The object was successfully loaded.
E_NOINTERFACE
The specified interface is not supported.

This function can also return any of the error values returned by the ReadClassStm and CoCreateInstance functions, and the IPersistStorage::Load method.

Remarks

This function can be used to load an object that supports the IPersistStream interface. The CLSID of the object must immediately precede the object's data in the stream, which is accomplished by the companion function OleSaveToStream (or the operations it wraps, which are described under that topic).

If the CLSID for the stream is CLSID_NULL, the ppvObj parameter is set to NULL.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleSaveToStream


OleLoadPicture

Creates a new picture object and initializes it from the contents of a stream. This is equivalent to calling OleCreatePictureIndirect(NULL, ...) followed by IPersistStream::Load.

STDAPI OleLoadPicture(
  IStream * pStream,
               //Pointer to the stream that contains picture's data
  LONG lSize,  //Number of bytes read from the stream
  BOOL fRunmode,
               //The opposite of the initial value of the picture's
               // property
  REFIID riid, //Reference to the identifier of the interface
               // describing the type of interface pointer to return
  VOID ppvObj  //Address of output variable that receives interface
               // pointer requested in riid
);

Parameters

pStream
[in] Pointer to the stream that contains the picture's data.
lSize
[in] Number of bytes that should be read from the stream, or zero if the entire stream should be read.
fRunmode
[in] The opposite of the initial value of the KeepOriginalFormat property. If TRUE, KeepOriginalFormat is set to FALSE and vice-versa.
riid
[in] Reference to the identifier of the interface describing the type of interface pointer to return in ppvObj.
ppvObj
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObj contains the requested interface pointer on the storage of the object identified by the moniker. If *ppvObj is non-NULL, this function calls IUnknown::AddRef on the interface; it is the caller's responsibility to call IUnknown::Release. If an error occurs, *ppvObj is set to NULL.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The picture was created successfully.
E_POINTER
The address in pStream or ppvObj is not valid. For example, either may be NULL.
E_NOINTERFACE
The object does not support the interface specified in riid.

Remarks

The stream must be in BMP (bitmap), WMF (metafile), or ICO (icon) format. A picture object created using OleLoadPicture always has ownership of its internal resources (fOwn==TRUE is implied).

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Import Library: Included as a resource in olepro32.dll.

See Also

OleCreatePictureIndirect, PICTDESC


OleLockRunning

Locks an already running object into its running state or unlocks it from its running state.

WINOLEAPI OleLockRunning(
  LPUNKNOWN pUnknown,   //Pointer to interface
  BOOL fLock,           //Flag indicating whether object is locked
  BOOL fLastUnlockCloses
                        //Flag indicating whether to close object
);

Parameters

pUnknown
[in] Pointer to the IUnknown interface on the object, which the function uses to query for a pointer to IRunnableObject.
fLock
[in] TRUE locks the object into its running state. FALSE unlocks the object from its running state.
fLastUnlockCloses
[in] TRUE specifies that if the connection being released is the last external lock on the object, the object should close. FALSE specifies that the object should remain open until closed by the user or another process.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was successfully locked or unlocked.

Remarks

The OleLockRunning function saves you the trouble of calling the IRunnableObject::LockRunning method. You can use OleLockRunning and IRunnableObject::LockRunning interchangeably. With the IUnknown pointer passed in with the pUnknown parameter, OleLockRunning queries for an IRunnableObject pointer. If successful, it calls IRunnableObject::LockRunning and returns the results of the call.

Note The implementation of OleLockRunning in earlier versions of OLE differs from that described here.

For more information on using this function, see IRunnableObject::LockRunning.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

CoLockObjectExternal, IRunnableObject::LockRunning, OleNoteObjectVisible


OleMetafilePictFromIconAndLabel

Creates a METAFILEPICT structure that contains a metafile in which the icon and label are drawn.

HGLOBAL OleMetafilePictFromIconAndLabel(
  HICON hIcon,              //Icon to be drawn in metafile
  LPOLESTR lpszLabel,       //Icon label
  LPOLESTR lpszSourceFile,  //Path name of icon file
  UINT iIconIndex           //Index of icon in lpszSourceFile
);

Parameters

hIcon
[in] Handle to the icon that is to be drawn into the metafile. Can be NULL. If hIcon is NULL, this function returns NULL without creating a metafile.
lpszLabel
[in] String to be used as the icon label. Can be NULL. If lpszLabel is NULL, the resulting metafile will not include a label.
lpszSourceFile
[in] String that contains the path and file name of the icon file. This string can be obtained through the user interface or from the registration database. Can be NULL.
iIconIndex
[in] Location of the icon within the file named by lpszSourceFile, expressed as an offset in bytes from the beginning of file.

Return Value

A global handle to a METAFILEPICT structure containing the icon and label. The metafile uses the MM_ANISOTROPIC mapping mode.

If an error occurs, the returned handle is NULL. In this case, the caller can call GetLastError to obtain further information.

Remarks

This function is called by OleGetIconOfFile and OleGetIconOfClass.

If lpszSourceFile is not NULL and iIconIndex is not 0, the name of the source file passed in lpszSourceFile and the index passed by iIconIndex are added to the created metafile as a comment record.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleGetIconOfClass, OleGetIconOfFile


OleNoteObjectVisible

Increments or decrements an external reference that keeps an object in the running state.

WINOLEAPI OleNoteObjectVisible(
  LPUNKNOWN pUnknown,  //Pointer to the interface on the object in
                       // question
  BOOL fVisible        //Whether object is visible
);

Parameters

pUnknown
[in] Pointer to the IUnknown interface on the object that is to be locked or unlocked.
fVisible
[in] Whether the object is visible. If TRUE, OLE increments the reference count to hold the object visible and alive regardless of external or internal IUnknown::AddRef and IUnknown::Release operations, registrations, or revocation. If FALSE, OLE releases its hold (decrements the reference count) and the object can be closed.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
Indicates the object was successfully locked or unlocked.

Remarks

The OleNoteObjectVisible function calls the CoLockObjectExternal function. It is provided as a separate function to reinforce the need to lock an object when it becomes visible to the user and to release the object when it becomes invisible. This creates a strong lock on behalf of the user to ensure that the object cannot be closed by its container while it is visible.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

CoLockObjectExternal


OleQueryCreateFromData

Checks whether a data object has one of the formats that would allow it to become an embedded object through a call to either the OleCreateFromData or OleCreateStaticFromData function.

WINOLEAPI OleQueryCreateFromData(
  IDataObject * pSrcDataObject  //Pointer to the data transfer
                                // object to be queried
);

Parameter

pSrcDataObject
[in] Pointer to the IDataObject interface on the data transfer object to be queried.

Return Values

S_OK
Formats that support embedded-object creation are present.
S_FALSE
No formats are present that support either embedded- or static-object creation.
OLE_S_STATIC
Formats that support static-object creation are present.

Remarks

When an application retrieves a data transfer object through a call to the OleGetClipboard function, the application should call OleQueryCreateFromData as part of the process of deciding to enable or disable the Edit/Paste or Edit/Paste Special... commands. It tests for the presence of the following formats in the data object:

CF_EMBEDDEDOBJECT
CF_EMBEDSOURCE
cfFileName
CF_METAFILEPICT
CF_DIB
CF_BITMAP

Determining that the data object has one of these formats does not absolutely guarantee that the object creation will succeed, but is intended to help the process.

If OleQueryCreateFromData finds one of the CF_METAFILEPICT, CF_BITMAP, or CF_DIB formats and none of the other formats, it returns OLE_S_STATIC, indicating that you should call the OleCreateStaticFromData function to create the embedded object.

If OleQueryCreateFromData finds one of the other formats (CF_EMBEDDEDOBJECT, CF_EMBEDSOURCE, or cfFileName), even in combination with the static formats, it returns S_OK, indicating that you should call the OleCreateFromData function to create the embedded object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateFromData, OleCreateStaticFromData, OleQueryLinkFromData


OleQueryLinkFromData

The OleQueryLinkFromData function determines whether an OLE linked object (rather than an OLE embedded object) can be created from a clipboard data object.

WINOLEAPI OleQueryLinkFromData(
  IDataObject * pSrcDataObject  //Pointer to the clipboard data
                                // object to be used to create the new
                                // object
);

Parameter

pSrcDataObject
[in] Pointer to the IDataObject interface on the clipboard data object from which the object is to be created.

Return Value

Returns S_OK if the OleCreateLinkFromData function can be used to create the linked object; otherwise S_FALSE.

Remarks

The OleQueryLinkFromData function is similar to the OleQueryCreateFromData function, but determines whether an OLE linked object (rather than an OLE embedded object) can be created from the clipboard data object. If the return value is S_OK, the application can then attempt to create the object with a call to OleCreateLinkFromData. A successful return from OleQueryLinkFromData does not, however, guarantee the successful creation of a link.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.


OleRegEnumFormatEtc

Supplies a pointer to an enumeration object that can be used to enumerate data formats that an OLE object server has registered in the system registry. An object application or object handler calls this function when it must enumerate those formats. Developers of custom DLL object applications use this function to emulate the behavior of the default object handler.

WINOLEAPI OleRegEnumFormatEtc(
  REFCLSID clsid,     //Class identifier
  DWORD dwDirection,  //Value specifying data formats
  LPENUMFORMATETC * ppenumFormatetc
                      //Address of output variable that receives the
                      // IEnumFORMATETC interface pointer
);

Parameters

clsid
[in] CLSID of the class whose formats are being requested.
dwDirection
[in] Whether to enumerate formats that can be passed to IDataObject::GetData or formats that can be passed to IDataObject::SetData. Valid values are taken from the enumeration DATADIR.
ppenumFormatetc
[out] Address of IEnumFORMATETC* pointer variable that receives the interface pointer to the enumeration object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The enumerator was returned successfully.
REGDB_E_CLASSNOTREG
There is no CLSID registered for the class object.
REGDB_E_READREGDB
There was an error reading the registry.
OLE_E_REGDB_KEY
The DataFormats/GetSet key is missing from the registry.

Remarks

Object applications can ask OLE to create an enumeration object for FORMATETC structures to enumerate supported data formats in one of two ways. One way is to call OleRegEnumFormatEtc. The other is to return OLE_S_USEREG in response to calls by the default object handler to IDataObject::EnumFormatEtc. OLE_S_USEREG instructs the default handler to call OleRegEnumFormatEtc. Because DLL object applications cannot return OLE_S_USEREG, they must call OleRegEnumFormatEtc rather than delegating the job to the object handler. With the supplied IEnumFORMATETC pointer to the object, you can call the standard enumeration object methods to do the enumeration.

The OleRegEnumFormatEtc function and its sibling functions, OleRegGetUserType, OleRegGetMiscStatus, and OleRegEnumVerbs, provide a way for developers of custom DLL object applications to emulate the behavior of OLE's default object handler in getting information about objects from the registry. By using these functions, you avoid the considerable work of writing your own, and the pitfalls inherent in working directly in the registry. In addition, you get future enhancements and optimizations of these functions without having to code them yourself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IDataObject::EnumFormatEtc, IEnumFORMATETC


OleRegEnumVerbs

Supplies an enumeration of the registered verbs for the specified class. Developers of custom DLL object applications use this function to emulate the behavior of the default object handler.

WINOLEAPI OleRegEnumVerbs(
  REFCLSID clsid,  //Class identifier
  LPENUMOLEVERB * ppenumOleVerb
                   //Address of output variable that receives the
                   // IEnumOLEVERB interface pointer
);

Parameters

clsid
[in] Class identifier whose verbs are being requested.
ppenumOleVerb
[out] Address of IEnumOLEVERB* pointer variable that receives the interface pointer to the new enumeration object.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The enumerator was created successfully.
OLEOBJ_E_NOVERBS
No verbs are registered for the class.
REGDB_E_CLASSNOTREG
No CLSID is registered for the class object.
REGDB_E_READREGDB
An error occurred reading the registry.
OLE_E_REGDB_KEY
The DataFormats/GetSet key is missing from the registry.

Remarks

Object applications can ask OLE to create an enumeration object for OLEVERB structures to enumerate supported verbs in one of two ways. One way is to call OleRegEnumVerbs. The other way is to return OLE_S_USEREG in response to calls by the default object handler to IOleObject::EnumVerbs. OLE_S_USEREG instructs the default handler to call OleRegEnumVerbs. Because DLL object applications cannot return OLE_S_USEREG, they must call OleRegEnumVerbs rather than delegating the job to the object handler. With the supplied IEnumOLEVERB pointer to the object, you can call the standard enumeration object methods to do the enumeration.

The OleRegEnumVerbs function and its sibling functions, OleRegGetUserType, OleRegGetMiscStatus, and OleRegEnumFormatEtc, provide a way for developers of custom DLL object applications to emulate the behavior of OLE's default object handler in getting information about objects from the registry. By using these functions, you avoid the considerable work of writing your own, and the pitfalls inherent in working directly in the registry. In addition, you get future enhancements and optimizations of these functions without having to code them yourself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IOleObject::EnumVerbs, IEnumOLEVERB


OleRun

Puts an OLE compound document object into the running state.

WINOLEAPI OleRun(
  LPUNKNOWN pUnknown  //Pointer to interface on the object
);

Parameter

pUnknown
[in] Pointer to the IUnknown interface on the object, with which it will query for a pointer to the IRunnableObject interface, and then call its Run method.

Return Values

This function supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The object was successfully placed in the running state.
OLE_E_CLASSDIFF
The source of an OLE link has been converted to a different class.

Remarks

The OleRun function puts an object in the running state. The implementation of OleRun was changed in OLE 2.01 to coincide with the publication of the IRunnableObject interface. You can use OleRun and IRunnableObject::Run interchangeably. OleRun queries the object for a pointer to IRunnableObject. If successful, the function returns the results of calling the IRunnableObject::Run method.

Note The implementation of OleRun in earlier versions of OLE differs from that described here.

For more information on using this function, see IRunnableObject::Run.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IOleLink::BindToSource, IRunnableObject::Run


OleSave

Saves an object opened in transacted mode into the specified storage object.

WINOLEAPI OleSave(
  IPersistStorage * pPS,   //Pointer to the object to be saved
  IStorage * pStg,         //Pointer to the destination storage to
                           // which pPS is saved
  BOOL fSameAsLoad         //Whether the object was loaded from pstg
                           // or not
);

Parameters

pPS
[in] Pointer to the IPersistStorage interface on the object to be saved.
pStg
[in] Pointer to the IStorage interface on the destination storage object to which the object indicated in pPS is to be saved.
fSameAsLoad
[in] TRUE indicates that pStg is the same storage object from which the object was loaded or created; FALSE indicates that pstg was loaded or created from a different storage object.

Return Values

S_OK
The object was successfully saved.
STG_E_MEDIUMFULL
The object could not be saved due to lack of disk space.

This function can also return any of the error values returned by the IPersistStorage::Save method.

Remarks

The OleSave helper function handles the common situation in which an object is open in transacted mode and is then to be saved into the specified storage object which uses the OLE-provided compound file implementation. Transacted mode means that changes to the object are buffered until either of the IStorage methods Commit or Revert is called. Callers can handle other situations by calling the IPersistStorage and IStorage interfaces directly.

OleSave does the following:

  1. Calls the IPersistStorage::GetClassID method to get the CLSID of the object.
  2. Writes the CLSID to the storage object using the WriteClassStg function.
  3. Calls the IPersistStorage::Save method to save the object.
  4. If there were no errors on the save; calls the IStorage::Commit method to commit the changes.

Note Static objects are saved into a stream called CONTENTS. Static metafile objects get saved in "placeable metafile format" and static DIB data gets saved in "DIB file format." These formats are defined to be the OLE standards for metafile and DIB. All data transferred using an IStream interface or a file (that is, via IDataObject::GetDataHere) must be in these formats. Also, all objects whose default file format is a metafile or DIB must write their data into a CONTENTS stream using these standard formats.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IStorage, IPersistStorage


OleSaveToStream

Saves an object with the IPersistStream interface on it to the specified stream.

WINOLEAPI OleSaveToStream(
  IPersistStream * pPStm,  //Pointer to the interface on the object
                           // to be saved
  IStream * pStm           //Pointer to the destination stream to
                           // which the object is saved
);

Parameters

pPStm
[in] Pointer to the IPersistStream interface on the object to be saved to the stream. The pPStm parameter cannot be NULL.
pStm
[in] Pointer to the IStream interface on the stream in which the object is to be saved.

Return Values

S_OK
The object was successfully saved.
OLE_E_BLANK
The pPStm parameter is NULL.
STG_E_MEDIUMFULL
There is no space left on device to save the object.

This function can also return any of the error values returned by the WriteClassStm function or the IPersistStream::Save method.

Remarks

This function simplifies saving an object that implements the IPersistStream interface to a stream. In this stream, the object's CLSID precedes its data. When the stream is retrieved, the CLSID permits the proper code to be associated with the data. The OleSaveToStream function does the following:

  1. Calls the IPersistStream::GetClassID method to get the object's CLSID.
  2. Writes the CLSID to the stream with the WriteClassStm function.
  3. Calls the IPersistStream::Save method with fClearDirty set to TRUE, which clears the dirty bit in the object.

The companion helper, OleLoadFromStream, loads objects saved in this way.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleLoadFromStream, IPersistStream, IStream


OleSetClipboard

Places a pointer to a specific data object onto the clipboard. This makes the data object accessible to the OleGetClipboard function.

WINOLEAPI OleSetClipboard(
  IDataObject * pDataObj  //Pointer to the data object being copied
                          // or cut
);

Parameter

pDataObj
[in] Pointer to the IDataObject interface on the data object from which the data to be placed on the clipboard can be obtained. This parameter can be NULL; in which case the clipboard is emptied.

Return Values

S_OK
The IDataObject pointer was placed on the clipboard.
CLIPBRD_E_CANT_OPEN
The Windows OpenClipboard function used within OleSetClipboard failed.
CLIPBRD_E_CANT_EMPTY
The Windows EmptyClipboard function used within OleSetClipboard failed.
CLIPBRD_E_CANT_CLOSE
The Windows CloseClipboard function used within OleSetClipboard failed.
CLIPBRD_E_CANT_SET
The Windows SetClipboard function used within OleSetClipboard failed.

Remarks

If you are writing an application that can act as the source of a clipboard operation, you must do the following:

  1. Create a data object (on which is the IDataObject interface) for the data being copied or cut to the clipboard. This object should be the same object used in OLE drag-and-drop operations.
  2. Call OleSetClipboard to place the IDataObject pointer onto the clipboard, so it is accessible to the OleGetClipboard function. OleSetClipboard also calls the IUnknown::AddRef method on your data object.
  3. If you wish, release the data object once you have placed it on the clipboard to free the IUnknown::AddRef counter in your application.
  4. If the user is cutting data (deleting it from the document and putting it on to the clipboard), remove the data from the document.

All formats are offered on the clipboard using delayed rendering (the clipboard contains only a pointer to the data object unless a call to OleFlushClipboard renders the data onto the clipboard). The formats necessary for OLE 1 compatibility are synthesized from the OLE 2 formats that are present and are also put on the clipboard.

The OleSetClipboard function assigns ownership of the clipboard to an internal OLE window handle. The reference count of the data object is increased by 1, to enable delayed rendering. The reference count is decreased by a call to the OleFlushClipboard function or by a subsequent call to OleSetClipboard specifying NULL as the parameter value (which clears the clipboard).

When an application opens the clipboard (either directly or indirectly by calling the Win32 OpenClipboard function), the clipboard cannot be used by any other application until it is closed. If the clipboard is currently open by another application, OleSetClipboard fails. The internal OLE window handle satisfies WM_RENDERFORMAT messages by delegating them to the IDataObject implementation on the data object that is on the clipboard.

Specifying NULL as the parameter value for OleSetClipboard empties the current clipboard. If the contents of the clipboard are the result of a previous OleSetClipboard call and the clipboard has been released, the IDataObject pointer that was passed to the previous call is released. The clipboard owner should use this as a signal that the data it previously offered is no longer on the clipboard.

If you need to leave the data on the clipboard after your application is closed, you should call OleFlushClipboard instead of OleSetClipboard.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleFlushClipboard, OleGetClipboard, OleIsCurrentClipboard


OleSetContainedObject

Notifies an object that it is embedded in an OLE container, which ensures that reference counting is done correctly for containers that support links to embedded objects.

WINOLEAPI OleSetContainedObject(
  LPUNKNOWN pUnk,  //Pointer to the interface on the embedded object
  BOOL fContained  //Indicates if the object is embedded
);

Parameters

pUnk
[in] Pointer to the IUnknown interface of the object.
fContained
[in] TRUE if the object is an embedded object; FALSE otherwise.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The object was notified successfully.

Remarks

The OleSetContainedObject function notifies an object that it is embedded in an OLE container. The implementation of OleSetContainedObject was changed in OLE 2.01 to coincide with the publication of the IRunnableObject interface. You can use OleSetContainedObject and the IRunnableObject::SetContainedObject method interchangeably. The OleSetContainedObject function queries the object for a pointer to the IRunnableObject interface. If successful, the function returns the results of calling IRunnableObject::SetContainedObject.

Note The implementation of OleSetContainedObject in earlier versions of OLE differs from that described here.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IRunnableObject::SetContainedObject


OleSetMenuDescriptor

Installs or removes OLE dispatching code from the container's frame window.

WINOLEAPI OleSetMenuDescriptor(
  HOLEMENU holemenu,      //Handle to the composite menu descriptor
  HWND hwndFrame,         //Handle to the container's frame window
  HWND hwndActiveObject,  //Handle to the object's in-place
                          // activation window
  LPOLEINPLACEFRAME lpFrame,  //Pointer to the container's frame
                              // window
  LPOLEINPLACEACTIVEOBJECT lpActiveObj
                          //Active in-place object
);

Parameters

holemenu
[in] Handle to the composite menu descriptor returned by the OleCreateMenuDescriptor function. If NULL, the dispatching code is unhooked.
hwndFrame
[in] Handle to the container's frame window where the in-place composite menu is to be installed.
hwndActiveObject
[in] Handle to the object's in-place activation window. OLE dispatches menu messages and commands to this window.
lpFrame
[in] Pointer to the IOleInPlaceFrame interface on the container's frame window.
lpActiveObj
[in] Pointer to the IOleInPlaceActiveObject interface on the active in-place object.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The menu was installed correctly.

Remarks

The container should call OleSetMenuDescriptor to install the dispatching code on hwndFrame when the object calls the IOleInPlaceFrame::SetMenu method, or to remove the dispatching code by passing NULL as the value for holemenu to OleSetMenuDescriptor.

If both the lpFrame and lpActiveObj parameters are non-NULL, OLE installs the context-sensitive help F1 message filter for the application. Otherwise, the application must supply its own message filter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleCreateMenuDescriptor, IOleInPlaceFrame::SetMenu, IOleInPlaceActiveObject


OleTranslateAccelerator

Called by the object application, allows an object's container to translate accelerators according to the container's accelerator table.

WINOLEAPI OleTranslateAccelerator(
  LPOLEINPLACEFRAME lpFrame,
               //Pointer to send keystrokes
  LPOLEINPLACEFRAMEINFO lpFrameInfo,
               //Pointer to accelerator table obtained from container
  LPMSG lpmsg  //Pointer to structure containing the keystroke
);

Parameters

lpFrame
[in] Pointer to the IOleInPlaceFrame interface to which the keystroke might be sent.
lpFrameInfo
[in] Pointer to an OLEINPLACEFRAMEINFO structure containing the accelerator table obtained from the container.
lpmsg
[in] Pointer to an MSG structure containing the keystroke.

Return Values

This function supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The keystroke was processed.
S_FALSE
The object should continue processing this message.

Remarks

Object servers call OleTranslateAccelerator to allow the object's container to translate accelerator keystrokes according to the container's accelerator table, pointed to by lpFrameInfo. While a contained object is the active object, the object's server always has first chance at translating any messages received. If this is not desired, the server calls OleTranslateAccelerator to give the object's container a chance. If the keyboard input matches an accelerator found in the container-provided accelerator table, OleTranslateAccelerator passes the message and its command identifier on to the container through the IOleInPlaceFrame::TranslateAccelerator method. This method returns S_OK if the keystroke is consumed; otherwise it returns S_FALSE.

Note Accelerator tables for containers should be defined so they will work properly with object applications that do their own accelerator keystroke translations. These tables should take the form:

"char", wID, VIRTKEY, CONTROL

This is the most common way to describe keyboard accelerators. Failure to do so can result in keystrokes being lost or sent to the wrong object during an in-place session.

Objects can call the IsAccelerator function to see whether the accelerator keystroke belongs to the object or the container.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

IsAccelerator, IOleInPlaceFrame::TranslateAccelerator


OleUIAddVerbMenu

Adds the Verb menu for the specified object to the given menu.

BOOL OleUIAddVerbMenu(
  LPOLEOBJECT *lpOleObj,  //Pointer to the object
  LPCTSTR lpszShortType,  //Pointer to the short name corresponding
                          // to the object
  HMENU hMenu,            //Handle to the menu to modify
  UINT uPos,              //Position of the menu item.
  UINT uIDVerbMin,        //Value at which to start the verbs
  UINT uIDVerbMax,        //Maximum identifier value for object verbs
  BOOL bAddConvert,       //Whether to add convert item
  UINT idConvert,         //Value to use for the convert item
  HMENU FAR * lphMenu     //Pointer to the cascading verb menu, if
                          // created
);

Parameters

lpOleObj
[in] Pointer to the IOleObject interface on the selected object. If this is NULL, then a default disabled menu item is created.
lpszShortType
[in] Pointer to the short name defined in the registry (AuxName==2) for the object identified with lpOleObj. If the string is NOT known, then NULL may be passed. If NULL is passed, IOleObject::GetUserType is called to retrieve it. If the caller has easy access to the string, it is faster to pass it in.
hMenu
[in] Handle to the menu in which to make modifications.
uPos
[in] Position of the menu item.
uIDVerbMin
[in] The UINT identifier value at which to start the verbs.
uIDVerbMax
[in] The UINT Maximum identifier value to be used for object verbs. If uIDVerbMax is 0, then no maximum identifier value is used.
bAddConvert
[in] The BOOL specifying whether or not to add a Convert item to the bottom of the menu (preceded by a separator).
idConvert
[in] The UINT identifier value to use for the Convert menu item, if bAddConvert is TRUE.
lphMenu
[out] An HMENU pointer to the cascading verb menu if it's created. If there is only one verb, this will be filled with NULL.

Return Values

TRUE
Indicates lpOleObj was valid and at least one verb was added to the menu.
FALSE
Indicates lpOleObj was NULL and a disabled default menu item was created.

Remarks

If the object has one verb, the verb is added directly to the given menu.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.


OleUIBusy

Invokes the standard Busy dialog box, allowing the user to manage concurrency.

UINT OleUIBusy(
  LPOLEUIBUSY  lpBZ  //Pointer to the initialization structure
);

Parameter

lpBZ
[in] Pointer to an OLEUIBUSY structure that contains information used to initialize the dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user has pressed the Cancel button and that the caller should cancel the operation.
OLEUI_BZ_SWITCHTOSELECTED
The user has pressed Switch To and OleUIBusy was unable to determine how to switch to the blocking application. In this case, the caller should either take measures to attempt to resolve the conflict itself, if possible, or retry the operation. OleUIBusy will only return OLEUI_BZ_SWITCHTOSELECTED if the user has pressed the Switch To button, hTask is NULL and the BZ_NOTRESPONDING flag is set.
OLEUI_BZ_RETRYSELECTED
The user has either pressed the Retry button or attempted to resolve the conflict (probably by switching to the blocking application). In this case, the caller should retry the operation.
OLEUI_BZ_CALLUNBLOCKED
The dialog box has been informed that the operation is no longer blocked.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_BZERR_HTASKINVALID
The hTask specified in the hTask member of the OLEUIBUSY structure is invalid.

Remarks

The standard OLE Server Busy dialog box notifies the user that the server application is not receiving messages. The dialog box then asks the user to cancel the operation, switch to the task that is blocked, or continue waiting.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUIBUSY

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


OleUICanConvertOrActivateAs

Determines if there are any OLE object classes in the registry that can be used to convert or activate the specified CLSID from.

BOOL OleUICanConvertOrActivateAs(
  REFCLSID rClsid,       //CLSID of the specified class
  BOOL fIsLinkedObject,  //Whether the original object was a linked
                         // object
  WORD wFormat           //Format of the original class
);

Parameters

rClsid
[in] The CLSID of the class for which the information is required.
fIsLinkedObject
[in] TRUE if the original object is a linked object; FALSE otherwise.
wFormat
[in] Format of the original class.

Return Values

TRUE
The specified class can be converted to or activated as another class.
FALSE
The specified class cannot be converted to or activated as another class.

Remarks

OleUICanConvertOrActivateAs searches the registry for classes that include wFormat in their \Conversion\Readable\Main, \Conversion\ReadWriteable\Main, and \DataFormats\DefaultFile entries.

This function is useful for determining if a Convert... menu item should be disabled. If the CF_DISABLEDISPLAYASICON flag is specified in the call to OleUIConvert, then the Convert... menu item should be enabled only if OleUICanConvertOrActivateAs returns TRUE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.

See Also

OleUIConvert


OleUIChangeIcon

Invokes the standard Change Icon dialog box, which allows the user to select an icon from an icon file, executable, or DLL.

UINT OleUIChangeIcon(
  LPOLEUICHANGEICON lpCI  //Pointer to the in-out structure for this
                          // dialog box
);

Parameter

lpCI
[in] Pointer to the in-out OLEUICHANGEICON structure for this dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_CIERR_MUSTHAVECLSID
The clsid member was not the current CLSID.
OLEUI_CIERR_MUSTHAVECURRENTMETAFILE
The hMetaPict member was not the current metafile.
OLEUI_CIERR_SZICONEXEINVALID
The szIconExe value was invalid.

Remarks

OleUIChangeIcon uses information contained in the OLEUICHANGEICON structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUICHANGEICON

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


OleUIChangeSource

Invokes the Change Source dialog box, allowing the user to change the source of a link.

UINT OleUIChangeSource(
  LPOLEUICHANGESOURCE lpCS  //Pointer to the in-out structure
);

Parameter

lpCS
[in] Pointer to the in-out OLEUICHANGESOURCE structure for this dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_CSERR_LINKCNTRNULL
The lpOleUILinkContainer value is NULL.
OLEUI_CSERR_LINKCNTRINVALID
The lpOleUILinkContainer value is invalid.
OLEUI_CSERR_FROMNOTNULL
The lpszFrom value is not NULL.
OLEUI_CSERR_TONOTNULL
The lpszTo value is not NULL.
OLEUI_CSERR_SOURCEINVALID
The lpszDisplayName or nFileLength value is invalid, or cannot retrieve the link source.
OLEUI_CSERR_SOURCEPARSEERROR
The nFilename value is wrong.

Remarks

The link source is not changed by the Change Source dialog box itself. Instead, it is up to the caller to change the link source using the returned file and item strings. The Edit Links dialog box typically does this for the caller.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUICHANGESOURCE, OleUIEditLinks, IOleUILinkContainer


OleUIConvert

Invokes the standard Convert dialog box, allowing the user to change the type of a single specified object, or the type of all OLE objects of the specified object's class.

UINT OleUIConvert(
  LPOLEUICONVERT lpCV  //Pointer to initialization structure
);

Parameter

lpCV
[in] Pointer to an OLEUICONVERT structure that contains information used to initialize the dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_CTERR_CLASSIDINVALID
A clsid value was invalid.
OLEUI_CTERR_DVASPECTINVALID
The dvAspect value was invalid. This member specifies the aspect of the object.
OLEUI_CTERR_CBFORMATINVALID
The wFormat value was invalid. This member specifies the data format of the object.
OLEUI_CTERR_STRINGINVALID
A string value (for example, lpszUserType or lpszDefLabel) was invalid.

Remarks

OleUIConvert populates the Convert dialog box's list box with object classes by traversing the Registry and looking for entries in the Readable and ReadWritable keys. Every class that includes the original class' default file format in its Readable key is added to the Convert list, and every class that includes the original class' default file format in its ReadWritable key is added to the Activate As list. The Convert list is shown in the dialog box's list box when the Convert radio button is selected (the default selection), and the Activate As list is shown when Activate As is selected.

Note that you can change the type of all objects of a given class only when CF_CONVERTONLY is not specified.

The convert command, which invokes this function, should only be made available to the user if OleUIConvertOrActivateAs returns S_OK.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUICONVERT, OleUICanConvertOrActivateAs


OleUIEditLinks

Invokes the standard Links dialog box, allowing the user to make modifications to a container's linked objects.

UINT OleUIEditLinks(
  LPOLEUIEDITLINKS lpEL  //Pointer to the initialization structure
);

Parameter

lpEL
[in] Pointer to an OLEUIEDITLINKS structure that contains information used to initialize the dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUIEDITLINKS, IOleUILinkContainer


OleUIInsertObject

Invokes the standard Insert Object dialog box, which allows the user to select an object source and class name, as well as the option of displaying the object as itself or as an icon.

UINT OleUIInsertObject(
  LPOLEUIINSERTOBJECT lpIO  //Pointer to the in-out structure
);

Parameter

lpIO
[in] Pointer to the in-out OLEUIINSERTOBJECT structure for this dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_IOERR_LPSZFILEINVALID
The lpszFile value is invalid or user has insufficient write access permissions.. This lpszFile member points to the name of the file linked to or inserted.
OLEUI_IOERR_LPFORMATETCINVALID
The lpFormatEtc value is invalid. This member identifies the desired format.
OLEUI_IOERR_PPVOBJINVALID
The ppvOjb value is invalid. This member points to the location where the pointer for the object is returned.
OLEUI_IOERR_LPIOLECLIENTSITEINVALID
The lpIOleClientSite value is invalid. This member points to the client site for the object.
OLEUI_IOERR_LPISTORAGEINVALID
The lpIStorage value is invalid. This member points to the storage to be used for the object.
OLEUI_IOERR_SCODEHASERROR
The sc member of lpIO has additional error information.
OLEUI_IOERR_LPCLSIDEXCLUDEINVALID
The lpClsidExclude value is invalid. This member contains the list of CLSIDs to exclude.
OLEUI_IOERR_CCHFILEINVALID
The cchFile or lpszFile value is invalid. The cchFile member specifies the size of the lpszFile buffer. The lpszFile member points to the name of the file linked to or inserted.

Remarks

OleUIInsertObject allows the user to select the type of object to be inserted from a list box containing the object applications registered on the user's system. To populate that list box, OleUIInsertObject traverses the registry, adding every object server it finds that meets the following criteria:

  • The registry entry does not include the NotInsertable key.
  • The registry entry includes an OLE 1.0 style Protocol\\StdFileEditing\\Server key.
  • The registry entry includes the Insertable key.
  • The object's CLSID is not included in the list of objects to exclude (the lpClsidExclude member of OLEUIINSERTOBJECT).

By default, OleUIInsertObject does not validate object servers, however, if the IOF_VERIFYSERVEREXIST flag is included in the dwFlags member of the OLEUIINSERTOBJECT structure, OleUIInsertObject verifies that the server exists. If it does not exist, then the server's object is not added to the list of available objects. Server validation is a time-extensive operation and is a significant performance factor.

To free an HMETAFILEPICT returned from the Insert Object or Paste Special dialog box, delete the attached metafile on the handle, as follows:

void FreeHmetafilepict(HMETAFILEPICT hmfp)
{
    if (hmfp != NULL)
        {
        LPMETAFILEPICT pmfp = GlobalLock(hmfp);

        DeleteMetaFile(pmfp->hMF);
        GlobalUnlock(hmfp);
        GlobalFree(hmfp);
        }

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUIINSERTOBJECT, OpenFile, DeleteMetaFile, GlobalUnlock, GlobalFree


OleUIObjectProperties

Invokes the Object Properties dialog box, which displays General, View, and Link information about an object.

UINT OleUIObjectProperties(
  LPOLEUIOBJECTPROPS lpOP  //Pointer to the structure
);

Parameter

lpOP
[in] Pointer to the OLEUIOBJECTPROPS structure.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_OPERR_SUBPROPNULL
lpGP or lpVP is NULL, or dwFlags and OPF_OBJECTISLINK and lpLP are NULL.
OLEUI_OPERR_SUBPROPINVALID
Insufficient write-access permissions for the structures pointed to by lpGP, lpVP, or lpLP.
OLEUI_OPERR_PROPSHEETNULL
The lpLP value is NULL.
OLEUI_OPERR_PROPSHEETINVALID
Insufficient write access for one or more of the structures used by OLEUIOBJECTPROPS.
OLEUI_OPERR_SUPPROP
The sub-link property pointer, lpLP, is NULL.
OLEUI_OPERR_PROPSINVALID
Insufficient write access for the sub-link property pointer, lpLP.
OLEUI_OPERR_PAGESINCORRECT
Some sub-link properties of the lpPS member are incorrect.
OLEUI_OPERR_INVALIDPAGES
Some sub-link properties of the lpPS member are incorrect.
OLEUI_OPERR_NOTSUPPORTED
A sub-link property of the lpPS member is incorrect.
OLEUI_OPERR_DLGPROCNOTNULL
A sub-link property of the lpPS member is incorrect.
OLEUI_OPERR_LPARAMNOTZERO
A sub-link property of the lpPS member is incorrect.
OLEUI_GPERR_STRINGINVALID
A string value (for example, lplpszLabel or lplpszType) is invalid.
OLEUI_GPERR_CLASSIDINVALID
The clsid value is invalid.
OLEUI_GPERR_LPCLSIDEXCLUDEINVALID
The ClsidExcluded value is invalid.
OLEUI_GPERR_CBFORMATINVALID
The wFormat value is invalid.
OLEUI_VPERR_METAPICTINVALID
The hMetaPict value is invalid.
OLEUI_VPERR_DVASPECTINVALID
The dvAspect value is invalid.
OLEUI_OPERR_PROPERTYSHEET
The lpPS value is incorrect.
OLEUI_OPERR_OBJINFOINVALID
The lpObjInfo value is NULL or the calling process doesn't have read access.
OLEUI_OPERR_LINKINFOINVALID
The lpLinkInfo value is NULL or the calling process doesn't have read access.

Remarks

OleUIObjectProperties is passed an OLEUIOBJECTPROPS structure, which supplies the information needed to fill in the General, View, and Link tabs of the Object Properties dialog box.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

IOleUIObjInfo, IOleUILinkInfo, OLEUIOBJECTPROPS, OLEUIGNRLPROPS, OLEUIVIEWPROPS, OLEUILINKPROPS


OleUIPasteSpecial

Invokes the standard Paste Special dialog box, allowing the user to select the format of the clipboard object to be pasted or paste-linked.

UINT OleUIPasteSpecial(*
  LPOLEUIPASTESPECIAL lpPS  //Pointer to the in-out structure for
                            // this dialog box
);

Parameter

lpPS
[in] Pointer to the in-out OLEUIPASTESPECIAL structure for this dialog box.

Return Values

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_IOERR_SRCDATAOBJECTINVALID
The lpSrcDataObject field of OLEUIPASTESPECIAL is invalid.
OLEUI_IOERR_ARRPASTEENTRIESINVALID
The arrPasteEntries field of OLEUIPASTESPECIAL is invalid.
OLEUI_IOERR_ARRLINKTYPESINVALID
The arrLinkTypes field of OLEUIPASTESPECIAL is invalid.
OLEUI_PSERR_CLIPBOARDCHANGED
The clipboard contents changed while the dialog box was displayed.
OLEUI_PSERR_GETCLIPBOAARDFAILED
The lpSrcDataObj member is incorrect.

Remarks

The design of the Paste Special dialog box assumes that if you are willing to permit a user to link to an object, you are also willing to permit the user to embed that object. For this reason, if any of the OLEUIPASTE_LINKTYPE flags associated with the OLEUIPASTEFLAG enumeration are set, then the OLEUIPASTE_PASTE flag must also be set in order for the data formats to appear in the Paste Special dialog box.

The text displayed in the Source field of the standard Paste Special dialog box, which is implemented in OLEDLG32.DLL, is the null-terminated string whose offset in bytes is specified in the dwSrcofCopy field of the OBJECTDESCRIPTOR structure for the object to be pasted. If an OBJECTDESCRIPTOR structure is not available for this object, the dialog box displays whatever text may be associated with CF_LINKSOURCEDESCRIPTOR. If neither structure is available, the dialog box looks for CF_FILENAME. If CF_FILENAME is not found, the dialog box displays the string "Unknown Source".

To free an HMETAFILEPICT returned from the Insert Object or Paste Special dialog box, delete the attached metafile on the handle, as follows:

void FreeHmetafilepict(HMETAFILEPICT hmfp)
{
    if (hmfp != NULL)
        {
        LPMETAFILEPICT pmfp = GlobalLock(hmfp);

        DeleteMetaFile(pmfp->hMF);
        GlobalUnlock(hmfp);
        GlobalFree(hmfp);
        }
}  // FreeHmetafilepict

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

OLEUIPASTEFLAG, DeleteMetaFile, GlobalUnlock, GlobalFree


OleUIPromptUser

Displays a dialog box with the specified template and returns the response (button identifier) from the user. This function is used to display OLE warning messages, for example, Class Not Registered.

int OleUIPromtUser(
  int nTemplate,  //Resource number of dialog box
  HWND hwndParent //Handle to the parent window of the dialog box
);

Parameters

nTemplate
[in] Resource number of the dialog box to display. See Remarks.
hwndParent
[in] Handle to the parent window of the dialog box. Specifies zero or more optional arguments. These parameters are passed to wsprintf to format the message string.

Return Values

Returns the button identifier selected by the user (template dependent).

Standard Success/Error Definitions

OLEUI_FALSE
Unknown failure (unused).
OLEUI_SUCCESS
No error, same as OLEUI_OK.
OLEUI_OK
The user pressed the OK button.
OLEUI_CANCEL
The user pressed the Cancel button.

Standard Field Validation Errors

OLEUI_ERR_STANDARDMIN
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.
OLEUI_ERR_STRUCTURENULL
The pointer to an OLEUIXXX structure passed into the function was NULL.
OLEUI_ERR_STRUCTUREINVALID
Insufficient permissions for read or write access to an OLEUIXXX structure.
OLEUI_ERR_CBSTRUCTINCORRECT
The cbstruct value is incorrect.
OLEUI_ERR_HWNDOWNERINVALID
The hWndOwner value is invalid.
OLEUI_ERR_LPSZCAPTIONINVALID
The lpszCaption value is invalid.
OLEUI_ERR_LPFNHOOKINVALID
The lpfnHook value is invalid.
OLEUI_ERR_HINSTANCEINVALID
The hInstance value is invalid.
OLEUI_ERR_LPSZTEMPLATEINVALID
The lpszTemplate value is invalid.
OLEUI_ERR_HRESOURCEINVALID
The hResource value is invalid.

Initialization Errors

OLEUI_ERR_FINDTEMPLATEFAILURE
Unable to find the dialog box template.
OLEUI_ERR_LOADTEMPLATEFAILURE
Unable to load the dialog box template.
OLEUI_ERR_DIALOGFAILURE
Dialog box initialization failed.
OLEUI_ERR_LOCALMEMALLOC
A call to LocalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_GLOBALMEMALLOC
A call to GlobalAlloc or the standard IMalloc allocator failed.
OLEUI_ERR_LOADSTRING
Unable to LoadString localized resources from the library.
OLEUI_ERR_OLEMEMALLOC
A call to the standard IMalloc allocator failed.

Function Specific Errors

OLEUI_ERR_STANDARDMAX
Errors common to all dialog boxes lie in the range OLEUI_ERR_STANDARDMIN to OLEUI_ERR_STANDARDMAX. This value allows the application to test for standard messages in order to display error messages to the user.

Remarks

The following dialog box templates are defined in Windows Interface Guidelines--A Guide for Designing Software. The nTemplate parameter must be a currently defined resource, however, additional templates can be added to prompt.dlg.

IDD_LINKSOURCEUNAVAILABLE
IDD_CANNOTUPDATELINK
IDD_SERVERNOTREG
IDD_CANNOTRESPONDVERB
IDD_SERVERNOTFOUND

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

wsprintf


OleUIUpdateLinks

Updates all links in the link container and displays a dialog box that shows the progress of the updating process. The process is stopped if the user presses the Stop button or when all links are processed.

BOOL OleUIUpdateLink(
  LPOLEUILINKCONTAINER lpOleUILinkCntr,
                    //Pointer to the link container
  HWND hwndParent,  //Dialog box's parent window
  LPTSTR lpszTitle, //pointer to the dialog box title
  int cLinks        //Number of links
);

Parameters

lpOleUILinkCntr
[in] Pointer to the IOleUILinkContainer interface on the link container.
hwndParent
[in] Parent window of the dialog box.
lpszTitle
[in] Pointer to the title of the dialog box.
cLinks
[in] Total number of links.

Return Values

TRUE
The links were successfully updated.
FALSE
Unable to update the links.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Import Library: Included as a resource in oledlg.dll.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

IOleUILinkContainer::GetLinkUpdateOptions, IOleUILinkContainer::UpdateLink


OleUninitialize

Closes the COM library on the apartment, releases any class factories, other COM objects, or servers held by the apartment, disables RPC on the apartment, and frees any resources the apartment maintains.

void OleUninitialize( );

Remarks

Call OleUninitialize on application shutdown, as the last COM library call, if the apartment was initialized with a call to OleInitialize. OleUninitialize calls the CoUninitialize function internally to shut down the OLE Component Object(COM) Library.

If the COM library was initialized on the apartment with a call to CoInitialize or CoInitializeEx, it must be closed with a call to CoUninitialize.

The OleInitialize and OleUninitialize calls must be balanced — if there are multiple calls to the OleInitialize function, there must be the same number of calls to OleUninitialize: Only the OleUninitialize call corresponding to the OleInitialize call that actually initialized the library can close it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

OleInitialize, CoUninitialize


RegisterDragDrop

Registers the specified window as one that can be the target of an OLE drag-and-drop operation and specifies the IDropTarget instance to use for drop operations.

WINOLEAPI RegisterDragDrop(
  HWND hwnd,  //Handle to a window that can accept drops
  IDropTarget * pDropTarget
              //Pointer to object that is to be target of drop
);

Parameters

hwnd
[in] Handle to a window that can be a target for an OLE drag-and-drop operation.
pDropTarget
[in] Pointer to the IDropTarget interface on the object that is to be the target of a drag-and-drop operation in a specified window. This interface is used to communicate OLE drag-and-drop information for that window.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The application was registered successfully.
DRAGDROP_E_INVALIDHWND
Invalid handle returned in the hwnd parameter.
DRAGDROP_E_ALREADYREGISTERED
The specified window has already been registered as a drop target.

Note If you use CoInitialize or CoInitializeEx instead of OleInitialize to initialize COM, RegisterDragDrop will always return an E_OUTOFMEMORY error.

Remarks

If your application can accept dropped objects during OLE drag-and-drop operations, you must call the RegisterDragDrop function. Do this whenever one of your application windows is available as a potential drop target, i.e., when the window appears unobscured on the screen.

The RegisterDragDrop function only registers one window at a time, so you must call it for each application window capable of accepting dropped objects.

As the mouse passes over unobscured portions of the target window during an OLE drag-and-drop operation, the DoDragDrop function calls the specified IDropTarget::DragOver method for the current window. When a drop operation actually occurs in a given window, the DoDragDrop function calls IDropTarget::Drop.

The RegisterDragDrop function also calls the IUnknown::AddRef method on the IDropTarget pointer.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

RevokeDragDrop


ReleaseStgMedium

Frees the specified storage medium.

void ReleaseStgMedium(
  STGMEDIUM * pmedium  //Pointer to storage medium to be freed
);

Parameter

pmedium
[in] Pointer to the storage medium that is to be freed.

Return Value

None.

Remarks

The ReleaseStgMedium function calls the appropriate method or function to release the specified storage medium. Use this function during data transfer operations where storage medium structures are parameters, such as IDataObject::GetData or IDataObject::SetData. In addition to identifying the type of the storage medium, this structure specifies the appropriate IUnknown::Release method for releasing the storage medium when it is no longer needed.

It is common to pass a STGMEDIUM from one body of code to another, such as in IDataObject::GetData, in which the one called can allocate a medium and return it to the caller. ReleaseStgMedium permits flexibility in whether the receiving body of code owns the medium, or whether the original provider of the medium still owns it, in which case the receiving code needs to inform the provider that it can free the medium.

When the original provider of the medium is responsible for freeing the medium, the provider calls ReleaseStgMedium, specifying the medium and the appropriate IUnknown pointer as the punkForRelease structure member. Depending on the type of storage medium being freed, one of the following actions is taken, followed by a call to the Release method on the specified IUnknown pointer:

Medium | ReleaseStgMedium Action
TYMED_HGLOBAL | None.
TYMED_GDI | None.
TYMED_ENHMF | None.
TYMED_MFPICT | None.
TYMED_FILE | Frees the file name string using standard memory management mechanisms.
TYMED_ISTREAM | Calls IStream::Release.
TYMED_ISTORAGE | Calls IStorage::Release.

The provider indicates that the receiver of the medium is responsible for freeing the medium by specifying NULL for the punkForRelease structure member. Then the receiver calls ReleaseStgMedium, which makes a call as described in the following table depending on the type of storage medium being freed:

Medium | ReleaseStgMedium Action
TYMED_HGLOBAL | Calls the Win32 GlobalFree function on the handle.
TYMED_GDI | Calls the Win32 DeleteObject function on the handle.
TYMED_ENHMF | Deletes the enhanced metafile.
TYMED_MFPICT | The hMF that it contains is deleted with the Win32 DeleteMetaFile function; then the handle itself is passed to GlobalFree.
TYMED_FILE | Frees the disk file by deleting it. Frees the file name string by using the standard memory management paradigm.
TYMED_ISTREAM | Calls IStream::Release.
TYMED_ISTORAGE | Calls IStorage::Release.

In either case, after the call to ReleaseStgMedium, the specified storage medium is invalid and can no longer be used.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

STGMEDIUM structure


RevokeDragDrop

Revokes the registration of the specified application window as a potential target for OLE drag-and-drop operations.

WINOLEAPI RevokeDragDrop(
  HWND hwnd  //Handle to a window that can accept drops
);

Parameter

hwnd
[in] Handle to a window previously registered as a target for an OLE drag-and-drop operation.

Return Values

This function supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
Registration as a target window was revoked successfully.
DRAGDROP_E_INVALIDHWND
Invalid handle returned in the hwnd parameter.
DRAGDROP_E_NOTREGISTERED
An attempt was made to revoke a drop target that has not been registered.

Remarks

When your application window is no longer available as a potential target for an OLE drag-and-drop operation, you must call RevokeDragDrop.

This function calls the IUnknown::Release method for your drop target interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ole2.h.
Import Library: Use ole32.lib.

See Also

RegisterDragDrop


DVASPECTINFO

The DVASPECTINFO structure is used in the IViewObject::Draw method to optimize rendering of an inactive object by making more efficient use of the GDI. The pvAspect parameter in IViewObject::Draw points to this structure. It is defined as follows:

typedef struct STRUCT tagDVASPECTINFO
{
    UNIT  cb;
    DWORD  dwFlags;
} DVASPECTINFO;

Members

cb
Size of the structure in bytes. The size includes this member as well as the dwFlags member.
dwFlags
A value taken from the DVASPECTINFOFLAG enumeration.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

DVASPECTINFOFLAG


DVEXTENTINFO

The DVEXTENTINFO structure is used in IViewObjectEx::GetNaturalExtent.

typedef struct tagDVEXTENTINFO
{
    ULONG cb;
    DWORD dwExtentMode;
    SIZEL sizelProposed;
}DVEXTENTINFO;

Members

cb
Size of the structure in bytes. The size includes this member as well as the dwExtentMode and sizelProposed members.
dwExtentMode
Indicates whether the sizing mode is content or integral sizing. See the DVEXTENTMODE enumeration for these values.
sizelProposed
Specifies the proposed size in content sizing or the preferred size in integral sizing.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

DVEXTENTMODE


DVTARGETDEVICE

Use the DVTARGETDEVICE structure to specify information about the target device for which data is being composed. DVTARGETDEVICE contains enough information about a Windows target device so a handle to a device context (hDC) can be created using the Windows CreateDC function.

typedef struct tagDVTARGETDEVICE
{
    DWORD tdSize;
    WORD  tdDriverNameOffset;
    WORD  tdDeviceNameOffset;
    WORD  tdPortNameOffset;
    WORD  tdExtDevmodeOffset;
    BYTE  tdData[1];
}DVTARGETDEVICE;

Members

tdSize
Size, in bytes, of the DVTARGETDEVICE structure. The initial size is included so the structure can be copied more easily.
tdDriverNameOffset
Offset, in bytes, from the beginning of the structure to the device driver name, which is stored as a NULL-terminated string in the tdData buffer.
tdDeviceNameOffset
Offset, in bytes, from the beginning of the structure to the device name, which is stored as a NULL-terminated string in the tdData buffer. This value can be zero to indicate no device name.
tdPortNameOffset
Offset, in bytes, from the beginning of the structure to the port name, which is stored as a NULL-terminated string in the tdData buffer. This value can be zero to indicate no port name.
tdExtDevmodeOffset
Offset, in bytes, from the beginning of the structure to the DEVMODE structure retrieved by calling ExtDeviceMode.
tdData
Aray of bytes containing data for the target device. It is not necessary to include empty strings in tdData (for names where the offset value is zero).

Remarks

Some OLE 1 client applications incorrectly construct target devices by allocating too few bytes in the DEVMODE structure for the OLETARGETDEVICE. They typically only supply the number of bytes in the DEVMODE.dmSize member. The number of bytes to be allocated should be the sum of DEVMODE.dmSize + DEVMODE.dmDriverExtra. When a call is made to the CreateDC function with an incorrect target device, the printer driver tries to access the additional bytes and unpredictable results can occur. To protect against a crash and make the additional bytes available, OLE pads the size of OLE 2 target devices created from OLE 1 target devices.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in winbase.h.

See Also

FORMATETC, IEnumFORMATETC, IViewObject, OleConvertOLESTREAMToIStorage


FORMATETC

The FORMATETC structure is a generalized Clipboard format. It is enhanced to encompass a target device, the aspect or view of the data, and a storage medium indicator. Where one might expect to find a Clipboard format, OLE uses a FORMATETC data structure instead. This structure is used as a parameter in OLE functions and methods that require data format information.

typedef struct tagFORMATETC
{
    CLIPFORMAT      cfFormat;
    DVTARGETDEVICE  *ptd;
    DWORD           dwAspect;
    LONG            lindex;
    DWORD           tymed;
}FORMATETC, *LPFORMATETC;

Members

cfFormat
Particular clipboard format of interest. There are three types of formats recognized by OLE:
  • Standard interchange formats, such as CF_TEXT.
  • Private application formats understood only by the application offering the format, or by other applications offering similar features.
  • OLE formats, which are used to create linked or embedded objects.
ptd
Pointer to a DVTARGETDEVICE structure containing information about the target device for which the data is being composed. A NULL value is used whenever the specified data format is independent of the target device or when the caller doesn't care what device is used. In the latter case, if the data requires a target device, the object should pick an appropriate default device (often the display for visual components). Data obtained from an object with a NULL target device, such as most metafiles, is independent of the target device. The resulting data is usually the same as it would be if the user chose the Save As command from the File menu and selected an interchange format.
dwAspect
One of the DVASPECT enumeration constants that indicate how much detail should be contained in the rendering. A single clipboard format can support multiple aspects or views of the object. Most data and presentation transfer and caching methods pass aspect information. For example, a caller might request an object's iconic picture, using the metafile clipboard format to retrieve it. Note that only one DVASPECT value can be used in dwAspect. That is, dwAspect cannot be the result of a BOOLEAN OR operation on several DVASPECT values.
lindex
Part of the aspect when the data must be split across page boundaries. The most common value is -1, which identifies all of the data. For the aspects DVASPECT_THUMBNAIL and DVASPECT_ICON, lindex is ignored.
tymed
One of the TYMED enumeration constants which indicate the type of storage medium used to transfer the object's data. Data can be transferred using whatever medium makes sense for the object. For example, data can be passed using global memory, a disk file, or structured storage objects. For more information, see the TYMED enumeration.

Remarks

The FORMATETC structure is used by methods in the data transfer and presentation interfaces as a parameter specifying the data being transferred. For example, the IDataObject::GetData method uses the FORMATETC structure to indicate exactly what kind of data the caller is requesting.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

DVASPECT, IDataAdviseHolder, IDataObject, IEnumFORMATETC, IOleCache, OleCreate, OleCreateFromData, OleCreateLinkFromData, OleCreateStaticFromData, OleCreateLink, OleCreateLinkToFile, OleCreateFromFile, STATDATA, STGMEDIUM, TYMED


OBJECTDESCRIPTOR

The OBJECTDESCRIPTOR structure is the data structure used for the CF_OBJECTDESRIPTOR and CF_LINKSRCDESCRIPTOR file formats. These formats provide user interface information during data transfer operations, for example, the Paste Special dialog box or target feedback information during drag-and-drop operations.

typedef struct tagOBJECTDESCRIPTOR
{
    ULONG  cbSize;
    CLSID  clsid;
    DWORD  dwDrawAspect;
    SIZEL  sizel;
    POINTL pointl;
    DWORD  dwStatus;
    DWORD  dwFullUserTypeName;
    DWORD  dwSrcOfCopy;
    /* variable sized string data may appear here */
} OBJECTDESCRIPTOR;

Members

cbSize
Size of structure in bytes.
clsid
CLSID of the object being transferred. The clsid is used to obtain the icon for the Display As Icon option in the Paste Special dialog box and is applicable only if the Embed Source or Embedded Object formats are offered. If neither is offered, the value of clsid should be CLSID_NULL. The clsid can be retrieved by the source by loading the object and calling the IOleObject::GetUserClassID method. Note that for link objects, this value is not the same as the value returned by the IPersist::GetClassID method.
dwDrawAspect
Display aspect of the object. Typically, this value is DVASPECT_CONTENT or DVASPECT_ICON. If the source application did not draw the object originally, the dwDrawAspect field contains a zero value (which is not the same as DVASPECT_CONTENT).
sizel
True extent of the object (without cropping or scaling) in HIMETRIC units. Setting this field is optional. The value can be (0,0) for applications that do not draw the object being transferred. This field is used primarily by targets of drag-and-drop operations, so they can give appropriate feedback to the user.
pointl
Offset in HIMETRIC units from the upper-left corner of the object where a drag-and-drop operation was initiated. This field is only meaningful for a drag-and-drop transfer operation since it corresponds to the point where the mouse was clicked to initiate the drag-and-drop operation. The value is (0,0) for other transfer situations, such as a Clipboard copy and paste.
dwStatus
Copy of the status flags for the object. These flags are defined by the OLEMISC enumeration. If an embedded object is being transferred, they are returned by calling the IOleObject::GetMiscStatus method.
dwFullUserTypeName
Offset for finding the full user type name of the object being transferred. It specifies the offset, in bytes, from the beginning of the OBJECTDESCRIPTOR data structure to the null-terminated string that specifies the full user type name of the object being transferred. The value is zero if the string is not present. This string is used by the destination of a data transfer to create labels in the Paste Special dialog box. The destination application must be able to handle the cases when this string is omitted.
dwSrcOfCopy
Offset, in bytes, from the beginning of the data structure to the null-terminated string that specifies the source of the transfer. The dwSrcOfCopy field is typically implemented as the display name of the temporary moniker that identifies the data source. The value for dwSrcOfCopy is displayed in the Source line of the Paste Special dialog box. A zero value indicates that the string is not present. If dwSrcOfCopy is zero, the string "Unknown Source" is displayed in the Paste Special dialog box.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IDataObject, FORMATETC


OLECMD

The OLECMD structure associates command flags from the OLECMDF enumeration with a command identifer through a call to IOleCommandTarget::QueryStatus.

typedef struct _tagOLECMD
{
    ULONG cmdID;
    DWORD cmdf;
}OLECMDF;

Members

cmdID
A command identifier; taken from the OLECMDID enumeration.
cmdf
Flags associated with cmdID; taken from the OLECMDF enumeration.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

OLECMDF, IOleCommandTarget::QueryStatus


OLECMDTEXT

The OLECMDTEXT structure specifies a text name or status string for a single command identifier. This structure is used with IOleCommandTarget::QueryStatus.

typedef struct _tagOLECMDTEXT
{
    DWORD cmdtextf;
    ULONG cwActual;
    ULONG cwBuf;
    wchar_t rgwz[1];
}OLECMDTEXT;

Members

cmdtextf
A value from the OLECMDTEXTF enumeration describing whether the rgwz parameter contains a command name or status text.
cwActual
The number of characters actually written into the rgwz buffer before QueryStatus returns.
cwBuf
The number of elements in the rgwz array.
rgwz
A caller-allocated array of wide characters to receive the command name or status text.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleCommandTarget::QueryStatus, OLECMDTEXTF


OLEINPLACEFRAMEINFO

The OLEINPLACEFRAMEINFO structure contains information about the accelerators supported by a container during an in-place session. The structure is used in the IOleInPlaceSite::GetWindowContext method and the OleTranslateAccelerator function.

typedef struct tagOIFI
{
    UINT   cb;
    BOOL   fMDIApp;
    HWND   hwndFrame;
    HACCEL haccel;
    UINT   cAccelEntries;
} OLEINPLACEFRAMEINFO, *LPOLEINPLACEFRAMEINFO;

Members

cb
Size in bytes of this structure. The object server must specify sizeof(OLEINPLACEFRAMEINFO) in the structure it passes to IOleInPlaceSite::GetWindowContext. The container can then use this size to determine the structure's version.
fMDIApp
Whether the container is an MDI application.
hwndFrame
Handle to the container's top-level frame window.
haccel
Handle to the accelerator table that the container wants to use during an in-place editing session.
cAccelEntries
Number of accelerators in haccel.

Remarks

When an object is being in-place activated, its server calls the container's IOleInPlaceSite::GetWindowContext method, which fills in an OLEINPLACEFRAMEINFO structure. During an in-place session, the message loop of an EXE server passes a pointer to the OLEINPLACEFRAMEINFO structure to OleTranslateAccelerator. OLE uses the information in this structure to determine whether a message maps to one of the container's accelerators.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceSite::GetWindowContext, OleTranslateAccelerator


OLEMENUGROUPWIDTHS

The OLEMENUGROUPWIDTHS structure is the mechanism for building a shared menu. It indicates the number of menu items in each of the six menu groups of a menu shared between a container and an object server during an in-place editing session.

The structure is defined in the IOleInPlaceFrame interface (inplcf.idl). It is used in the IOleInPlaceFrame::InsertMenus and ICDStandardForm::SetMenu methods, and the OleCreateMenuDescriptor function.

typedef struct tagOleMenuGroupWidths
{
    LONG width[6];
} OLEMENUGROUPWIDTHS, * LPOLEMENUGROUPWIDTHS;

Member

width
An array whose elements contain the number of menu items in each of the six menu groups of a shared in-place editing menu. Each menu group can have any number of menu items. The container uses elements 0, 2, and 4 to indicate the number of menu items in its File, View, and Window menu groups. The object server uses elements 1, 3, and 5 to indicate the number of menu items in its Edit, Object, and Help menu groups.

Remarks

A container application and an object server use this structure to build a shared menu. The object server initializes to zeros the array elements in an OLEMENUGROUPWIDTHS structure and passes a pointer to it along with a menu handle to the container in a call to IOleInPlaceFrame::InsertMenus. The container adds its menu items to the menu, and fills in the structure with the number of items in each of its groups (indexes 0, 2, and 4). The server then uses the group width values returned by the container to insert its menu items in the appropriate position in the menu. The server fills in the structure with the number of items in each of its groups (indexes 1, 3, and 5), and then passes the structure to OLE in a call to the OleCreateMenuDescriptor function. This enables OLE to intercept the container's menu messages and redirect the messages generated by the server's menus.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleInPlaceFrame::InsertMenus, OleCreateMenuDescriptor


OLEUIBUSY

The OLEUIBUSY structure contains information that the OLE User Interface Library uses to initialize the Busy dialog box, and space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUIBUSY
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORD          dwFlags;
    HWND           hWndOwner;
    LPCSTR         lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCSTR         lpszTemplate;
    HRSRC          hResource;

// Specifics for OLEUIBUSY.
    HTASK  hTask;
    HWND FAR *  lphWndDialog;
} OLEUICHANGEICON, *POLEUICHANGEICON, FAR *LPOLEUICHANGEICON;

Members

cbStruct
Size of the structure in bytes. This field must be filled on input.
dwFlags
On input, specifies the initialization and creation flags. On exit, it specifies the user's choices. It may be a combination of the following flags:

BZ_DISABLECANCELBUTTON

Input only: This flag disables the Cancel button.

BZ_DISABLESWITCHTOBUTTON

Input only: This flag disables the Switch To... button.

BZ_DISABLERETRYBUTTON

Input only: This flag disables the Retry button.

BZ_NOTRESPONDINGDIALOG

Input only: This flag generates a Not Responding dialog box instead of a Busy dialog box. The text is slightly different, and the Cancel button is disabled.

hWndOwner
Window that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Busy.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUIBUSY structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpTemplateName member.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Busy dialog box template.
hResource
Customized template handle.
hTask Input only:
Handle to the task that is blocking.
lphWndDialog
Pointer to the dialog box's HWND.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIBusy

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


OLEUICHANGEICON

The OLEUICHANGEICON structure contains information that the OLE User Interface Library uses to initialize the Change Icon dialog box, and it contains space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUICHANGEICON
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORD          dwFlags;
    HWND           hWndOwner;
    LPCSTR         lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCSTR         lpszTemplate;
    HRSRC          hResource;

 // Specifics for OLEUICHANGEICON.
    HGLOBAL  hMetaPict;
    CLSID    clsid;
    char     szIconExe[OLEUI_CCHPATHMAX];
    int      cchIconExe;
} OLEUICHANGEICON, *POLEUICHANGEICON, FAR *LPOLEUICHANGEICON;

Members

cbStruct
Size of the structure in bytes. This field must be filled on input.
dwFlags
On input, specifies the initialization and creation flags. On exit, it specifies the user's choices. It can be a combination of the following flags:

CIF_SHOWHELP

Dialog box will display a Help button.

CIF_SELECTCURRENT

On input, selects the Current radio button on initialization. On exit, specifies that the user selected Current.

CIF_SELECTDEFAULT

On input, selects the Default radio button on initialization. On exit, specifies that the user selected Default.

CIF_SELECTFROMFILE

On input, selects the From File radio button on initialization. On exit, specifies that the user selected From File.

CIF_USEICONEXE

Input only. Extracts the icon from the executable specified in the szIconExe member, instead of retrieving it from the class. This is useful for OLE embedding or linking to non-OLE files.

hWndOwner
Window that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Change Icon.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUICHANGEICON structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpTemplateName member.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Change Icon dialog box template.
hResource
Customized template handle.
hMetaPict
Current and final image. The source of the icon is embedded in the metafile itself.
clsid
Input only. The class to use to get the Default icon.
szIconExe
Input only. Pointer to the executable to extract the default icon from. This member is ignored unless CIF_USEICONEXE is included in the dwFlags parameter and an attempt to retrieve the class icon from the specified CLSID fails.
cchIconExe
Input only. The number of characters in szIconExe. This member is ignored unless CIF_USEICONEXE is included in the dwFlags member.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIChangeIcon

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


OLEUICHANGESOURCE

This structure is used to initialize the standard Change Source dialog box. It allows the user to modify the destination or source of a link. This may simply entail selecting a different file name for the link, or possibly changing the item reference within the file, for example, changing the destination range of cells within the spreadsheet that the link is to.

typedef struct tagOLEUICHANGESOURCEW
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORDT         dwFlags;
    HWND           hWndOwner;
    LPCWSTR        lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCWSTR        lpszTemplate;
    HRSRC          hResource;

// INTERNAL ONLY: do not modify these members
    OPENFILENAMEW*     lpOFN;
    DWORD              dwReserved1[4];

// Specifics for OLEUICHANGESOURCE.
    LPOLEUILINKCONTAINERW  lpOleUILinkContainer;
    DWORD                  dwLink;
    LPTSTR                 lpszDisplayName;
    ULONG                  nFileLength;
    LPTSTR                 lpszFrom;
    LPTSTR                 lpszTo;
} OLEUICHANGESOURCEW, *POLEUICHANGESOURCEW, FAR *LPOLEUICHANGESOURCEW;

Members

cbStruct
Size of the structure in bytes.
dwFlags
On input, this field specifies the initialization and creation flags. On exit, it specifies the user's choices. It may be a combination of the following flags:

CSF_SHOWHELP

Enables or shows the Help button.

CSF_VALIDSOURCE

Indicates that the link was validated.

CSF_ONLYGETSOURCE

Disables automatic validation of the link source when the user presses OK. If you specify this flag, you should validate the source when the dialog box returns OK.

hWndOwner
Window that owns the dialog box.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Change Source.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUICHANGEICON structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpszTemplate member. This member is ignored if the lpszTemplate member is NULL or invalid.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Convert dialog box template.
hResource
Resource handle for a custom dialog box. If this member is NULL, then the library uses the standard Convert dialog box template, or if it is valid, the template named by the lpszTemplate member.
lpOFN
Pointer to the OPENFILENAME structure, which contains information used by the operating system to initialize the system-defined Open or Save As dialog boxes.
dwReserved1[4]
Reserved for future use.
lpOleUILinkContainer
Pointer to the container's implementation of the IOleUILinkContainer interface, used to validate the link source. The Edit Links dialog box uses this to allow the container to manipulate its links.
dwLink
Container-defined unique 32-bit link identifier used to validate link sources. Used by lpOleUILinkContainer.
lpszDisplayName
Pointer to the complete source display name.
nFileLength
File moniker portion of lpszDisplayName.
lpszFrom
Pointer to the prefix of the source that was changed from.
lpszTo
Pointer to the prefix of the source to be changed to.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

IOleUILinkContainer, OleUIChangeSource


OLEUICONVERT

The OLEUICONVERT structure contains information that the OLE User Interface Library uses to initialize the Convert dialog box, and space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUICONVERT
{
// These IN fields are standard across all OLEUI dialog functions.
    DWORD          cbStruct
    DWORD          dwFlags
    HWND           hWndOwner
    LPCSTR         lpszCaption
    LPFNOLEUIHOOK  lpfnHook
    LPARAM         lCustData
    HINSTANCE      hInstance
    LPCSTR         lpszTemplate
    HRSRC          hResource

// Specifics for OLEUICONVERT.
    CLSID    clsid;
    CLSID    clsidConvertDefault;
    CLSID    clsidActivateDefault;
    CLSID    clsidNew;
    DWORD    dvAspect;
    WORD     wFormat;
    BOOL     fIsLinkedObject;
    HGLOBAL  hMetaPict;
    LPTSTR   lpszUserType;
    BOOL     fObjectsIconChanged;
    LPTSTR   lpszDefLabel
    UINT     cClsidExclude
    LPCLSID  lpClsidExclude
} OLEUICONVERT, *POLEUICONVERT, FAR *LPOLEUICONVERT;

Members

cbStruct
Size of the structure, in bytes. This field must be filled on input.
dwFlags
On input, this field specifies the initialization and creation flags. On exit, it specifies the user's choices. It may be a combination of the following flags:

CF_SHOWHELPBUTTON

Dialog box will display a Help button. This flag is set on input.

CF_SETCONVERTDEFAULT

Class whose CLSID is specified by clsidConvertDefault will be used as the default selection. This selection appears in the class listbox when the Convert To radio button is selected. This flag is set on input.

CF_SETACTIVATEDEFAULT

Class whose CLSID is specified by clsidActivateDefault will be used as the default selection. This selection appears in the class listbox when the Activate As radio button is selected. This flag is set on input.

CF_SELECTCONVERTTO

On input, this flag specifies that Convert To will be initially selected (default behavior). This flag is set on output if Convert To was selected when the user dismissed the dialog box.

CF_SELECTACTIVATEAS

On input, this flag specifies that Activate As will be initially selected. This flag is set on output if Activate As was selected when the user dismissed the dialog box.

CF_DISABLEDISPLAYASICON

The Display As Icon button will be disabled on initialization.

CF_DISABLEACTIVATEAS

The Activate As radio button will be disabled on initialization.

CF_HIDECHANGEICON

The Change Icon button will be hidden in the Convert dialog box.

CF_CONVERTONLY

The Activate As radio button will be disabled in the Convert dialog box.

hWndOwner
Window that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Convert.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUICONVERT structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpszTemplate member. This member is ignored if the lpszTemplate member is NULL or invalid.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Convert dialog box template.
hResource
Resource handle for a custom dialog box. If this member is NULL, then the library uses the standard Convert dialog box template, or if it is valid, the template named by the lpszTemplate member.
clsid
The CLSID of the object to be converted or activated. This member is set on input.
clsidConvertDefault
The CLSID to use as the default class when Convert To is selected. This member is ignored if the dwFlags member does not include CF_SETCONVERTDEFAULT. This member is set on input.
clsidActivateDefault
The CLSID to use as the default class when Activate As is selected. This member is ignored if the dwFlags member does not include CF_SETACTIVATEDEFAULT. This member is set on input.
clsidNew
The CLSID of the selected class. This member is set on output.
dvAspect
Aspect of the object. This must be either DVASPECT_CONTENT or DVASPECT_ICON. If dvAspect is DVASPECT_ICON on input, then the Display As Icon box is checked and the object's icon is displayed. This member is set on input and output.
wFormat
Data format of the object to be converted or activated.
fIsLinkedObject
TRUE if the object is linked. This member is set on input.
hMetaPict
The METAFILEPICT containing the iconic aspect. This member is set on input and output.
lpszUserType
Pointer to the User Type name of the object to be converted or activated. If this value is NULL, then the dialog box will retrieve the User Type name from the registry. This string is freed on exit.
fObjectsIconChanged
TRUE if the object's icon changed. (that is, if OleUIChangeIcon was called and not canceled.). This member is set on output.
lpszDefLabel
Pointer to the default label to use for the icon. If NULL, the short user type name will be used. If the object is a link, the caller should pass the Display Name of the link source. This is freed on exit.
cClsidExclude
Number of CLSIDs in lpClsidExclude.
lpClsidExclude
Pointer to the list of CLSIDs to exclude from the list.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIConvert, OleUIChangeIcon


OLEUIEDITLINKS

The OLEUIEDITLINKS structure contains information that the OLE User Interface Library uses to initialize the Edit Links dialog box, and contains space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUIEDITLINKS
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORD          dwFlags;
    HWND           hWndOwner;
    LPCSTR         lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCSTR         lpszTemplate;
    HRSRC          hResource;

// Specifics for OLEUIEDITLINKS.
    LPOLEUILINKCONTAINER      lpOleUILinkContainer;
} OLEUIEDITLINKS, *POLEUIEDITLINKS, FAR *LPOLEUIEDITLINKS;

Members

cbStruct
Size of the structure in bytes. This field must be filled on input.
dwFlags
On input, dwFlags specifies the initialization and creation flags. It may be a combination of the following flags:

ELF_SHOWHELP

Specifies that the dialog box will display a Help button.

ELF_DISABLEUPDATENOW

Specifies that the Update Now button will be disabled on initialization.

ELF_DISABLEOPENSOURCE

Specifies that the Open Source button will be disabled on initialization.

ELF_DISABLECHANGESOURCE

Specifies that the Change Source button will be disabled on initialization.

ELF_DISABLECANCELLINK

Specifies that the Cancel Link button will be disabled on initialization.

hWndOwner
Window that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Links.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUIEDITLINKS structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpTemplateName member.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Edit Links dialog box template.
hResource
Customized template handle.
lpOleUILinkContainer
Pointer to the container's implementation of the IOleUILinkContainer Interface. The Edit Links dialog box uses this to allow the container to manipulate its links.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

IOleUILinkContainer, OleUIEditLinks


OLEUIGNRLPROPS

This structure is used to initialize the General tab of the Object Properties dialog box. A reference to it is passed in as part of the OLEUIOBJECTPROPS structure to the OleUIObjectProperties function. This tab shows the type and size of an OLE embedding and allows it the user to tunnel to the Convert dialog box. This tab also shows the link destination if the object is a link.

typedef struct tagOLEUIGNRLPROPS
{
// These IN fields are standard across all OLEUI property pages.
    DWORD          cbStruct;
    DWORD          dwFlags;
    DWORD          dwReserved1[2];
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    DWORD          dwReserved2[3];
    struct tagOLEUIOBJECTPROPSW* lpOP;
} OLEUIGNRLPROPSW, *POLEUIGNRLPROPSW, FAR* LPOLEUIGNRLPROPSW;

Members

cbStruct
Size of the structure in bytes. This field must be filled on input.
dwFlags
Currently no flags associated with this member. It should be set to 0 (zero).
dwReserved1[2]
Reserved for future use.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member during WM_INITDIALOG.
dwReserved2[3]
Reserved for future use.
lpOP
Used internally.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIObjectProperties, OLEUIOBJECTPROPS


OLEUIINSERTOBJECT

The OLEUIINSERTOBJECT structure contains information that the OLE User Interface Library uses to initialize the Insert Object dialog box, and space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUIINSERTOBJECT
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORD          dwFlags;
    HWND           hWndOwner;
    LPCSTR         lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCSTR         lpszTemplate;
    HRSRC          hResource;
    CLSID          clsid;

// Specifics for OLEUIINSERTOBJECT.
    LPTSTR         lpszFile;
    UINT           cchFile;
    UINT           cClsidExclude;
    LPCLSID        lpClsidExclude;
    IID            iid;

// Specific to create objects if flags say so
    DWORD            oleRender;
    LPFORMATETC      lpFormatEtc;
    LPOLECLIENTSITE  lpIOleClientSite;
    LPSTORAGE        lpIStorage;
    LPVOID FAR *     ppvObj;
    SCODE            sc;
    HGLOBAL          hMetaPict;
} OLEUIINSERTOBJECT, *POLEUIINSERTOBJECT, FAR *LPOLEUIINSERTOBJECT;

Members

cbStruct
Size of the structure in bytes. This field must be filled on input.
dwFlags
On input, specifies the initialization and creation flags. On exit, specifies the user's choices. It can be a combination of the following flags:

IOF_SHOWHELP

The dialog box will display a Help button.

IOF_SELECTCREATENEW

The Create New radio button will initially be checked. This cannot be used with IOF_SELECTCREATEFROMFILE.

IOF_SELECTCREATEFROMFILE

The Create From File radio button will initially be checked. This cannot be used with IOF_SELECTCREATENEW.

IOF_CHECKLINK

The Link check box will initially be checked.

IOF_CHECKDISPLAYASICON

The Display As Icon check box will initially be checked, the current icon will be displayed, and the Change Icon button will be enabled.

IOF_CREATENEWOBJECT

A new object should be created when the user selects OK to dismiss the dialog box and the Create New radio button was selected.

IOF_CREATEFILEOBJECT

A new object should be created from the specified file when the user selects OK to dismiss the dialog box and the Create From File radio button was selected.

IOF_CREATELINKOBJECT

A new linked object should be created when the user selects OK to dismiss the dialog box and the user checked the Link check box.

IOF_DISABLELINK

The Link check box will be disabled on initialization.

IOF_VERIFYSERVERSEXIST

The dialog box should validate the classes it adds to the listbox by ensuring that the server specified in the registration database exists. This is a significant performance factor.

IOF_DISABLEDISPLAYASICON

The Display As Icon check box will be disabled on initialization.

IOF_HIDECHANGEICON

The Change Icon button will be hidden in the Insert Object dialog box.

IOF_SHOWINSERTCONTROL

Displays the Insert Control radio button.

IOF_SELECTCREATECONTROL

Displays the Create Control radio button.

hWndOwner
Window that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Insert Object.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUIINSERTOBJECT structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpTemplateName member.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Insert Object dialog box template.
hResource
Customized template handle.
clsid
CLSID for class of the object to be inserted. Filled on output.
lpszFile
Pointer to the name of the file to be linked or embedded. Filled on output.
cchFile
Size of lpszFile buffer; will not exceed OLEUI_CCHPATHMAX.
cClsidExclude
Number of CLSIDs included in the lpClsidExclude list. Filled on input.
lpClsidExclude
Pointer to a list of CLSIDs to exclude from listing.
iid
Identifier of the requested interface. If OleUIInsertObject creates the object, then it will return a pointer to this interface. This parameter is ignored if OleUIInsertObject does not create the object.
oleRender
Rendering option. If OleUIInsertObject creates the object, then it selects the rendering option when it creates the object. This parameter is ignored if OleUIInsertObject does not create the object.
lpFormatEtc
Desired format. If OleUIInsertObject creates the object, then it selects the format when it creates the object. This parameter is ignored if OleUIInsertObject does not create the object.
lpIOleClientSite
Pointer to the client site to be used for the object. This parameter is ignored if OleUIInsertObject does not create the object.
lpIStorage
Pointer to the storage to be used for the object. This parameter is ignored if OleUIInsertObject does not create the object.
ppvObj
Address of output pointer variable that contains the interface pointer for the object being inserted. This parameter is ignored if OleUIInsertObject does not create the object.
sc
Result of creation calls. This parameter is ignored if OleUIInsertObject does not create the object.
hMetaPict
MetafilePict structure containing the iconic aspect, if it wasn't placed in the object's cache.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIInsertObject

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


OLEUILINKPROPS

This structure is used to initialize the Link tab of the Object Properties dialog box. A reference to it is passed in as part of the OLEUIOBJECTPROPS structure to the OleUIObjectProperties function. This tab shows the location, update status, and update time for a link. It allows the user to change the source of the link, toggle its update status between automatic and manual update, open the source, force an update of the link, or break the link (convert it to a static picture).

// These IN fields are standard across all OLEUI property pages.

typedef struct tagOLEUILINKPROPSW
{
// These IN fields are standard across all OLEUI property pages.
    DWORD          cbStruct;
    DWORD          dwFlags;
    DWORD          dwReserved1[2];
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    DWORD          dwReserved2[3];

    struct tagOLEUIOBJECTPROPSW* lpOP;
} OLEUILINKPROPSW, *POLEUILINKPROPSW, FAR* LPOLEUILINKPROPSW;

Members

cbStruct
Size of the structure in bytes.
dwFlags
Contains in/out flags specific to the Links page.
dwReserved1[2]
Reserved for future use.
lpfnHook
Pointer to the hook callback (not used in this dialog box).
lCustData
Custom data to pass to hook (not used in this dialog box).
dwReserved2[3]
Reserved for future use.
lpOP
Used internally.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIObjectProperties, OLEUIOBJECTPROPS


OLEUIOBJECTPROPS

This structure is used to initialize the standard Object Properties dialog box. It contains references to interfaces used to gather information about the embedding or link, references to three structures that are used to initialize the default tabs — General (OLEUIGNRLPROPS), View (OLEUIVIEWPROPS), and Link (OLEUILINKPROPS), if appropriate — and a standard property-sheet extensibility interface that allows the caller to add additional custom property sheets to the dialog box.

typedef struct tagOLEUIOBJECTPROPS
{

// These IN fields are standard across all OLEUI property sheets.

    DWORD    cbStruct;
    DWORD    dwFlags;

// Standard PROPSHEETHEADER used for extensibility
    LPPROPSHEETHEADER        lpPS;

// Data which allows manipulation of the object
    DWORD    dwObject;
    LPOLEUIOBJINFO        lpObjInfo;

// Data which allows manipulation of the link
    DWORD    dwLink;
    LPOLEUILINKINFO        lpLinkInfo;

// Data specfic to each page
    LPOLEUIGNRLPROPS        lpGP;
    LPOLEUIVIEWPROPS        lpVP;
    LPOLEUILINKPROPS        lpLP;

} OLEUIOBJECTPROPS, *POLEUIOBJECTPROPS, FAR* LPOLEUIOBJECTPROPS;

Members

cbStruct
Size of the structure in bytes.
dwFlags
Contains in/out global flags for the property sheet.

OPF_OBJECTISLINK

Object is a link object and therefore has a link property page.

OPF_NOFILLDEFAULT

Do not fill in default values for the object.

OPF_SHOWHELP

Dialog box will display a Help button.

OPF_DISABLECONVERT

The Convert button will be disabled on the general property page.

lpPS
[in] Pointer to the standard property sheet header (PROPSHEETHEADER), used for extensibility.
//Data which allows manipulation of the object
dwObject
[in] Identifier for the object.
lpObjInfo
[in] Pointer to the interface to manipulate object.
//Data which allows manipulation of the link
dwLink;
[in] Container-defined unique 32-bit identifier for a single link. Containers can use the pointer to the link's container site for this value.
lpLinkInfo
[in] Pointer to the interface to manipulate link.
// Data specific to each page
lpGP
[in] Pointer to the general page data.
lpVP
[in] Pointer to the view page data.
lpLP
[in] Pointer to the link page data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIObjectProperties, OLEUIGNRLPROPS, OLEUIVIEWPROPS, OLEUILINKPROPS


OLEUIPASTEENTRY

This structure is an array of OLEUIPASTEENTRY entries specified in the OLEUIPASTESPECIAL structure for the Paste Special dialog box. Each entry includes a FORMATETC which specifies the formats that are acceptable, a string that is to represent the format in the dialog box's listbox, a string to customize the result text of the dialog box, and a set of flags from the OLEUIPASTEFLAG enumeration. The flags indicate if the entry is valid for pasting only, linking only or both pasting and linking. If the entry is valid for linking, the flags indicate which link types are acceptable by OR'ing together the appropriate OLEUIPASTE_LINKTYPE<#> values. These values correspond to the array of link types as follows:

OLEUIPASTE_LINKTYPE1=arrLinkTypes[0]
OLEUIPASTE_LINKTYPE2=arrLinkTypes[1]
OLEUIPASTE_LINKTYPE3=arrLinkTypes[2]
OLEUIPASTE_LINKTYPE4=arrLinkTypes[3]
OLEUIPASTE_LINKTYPE5=arrLinkTypes[4]
OLEUIPASTE_LINKTYPE6=arrLinkTypes[5]
OLEUIPASTE_LINKTYPE7=arrLinkTypes[6]
OLEUIPASTE_LINKTYPE8=arrLinkTypes[7]

arrLinkTypes[] is an array of registered clipboard formats for linking. A maximum of eight link types are allowed.

typedef struct tagOLEUIPASTEENTRY
{
    FORMATETC   fmtetc;
    LPCSTR      lpstrFormatName;
    LPCSTR      lpstrResultText;
    DWORD       dwFlags;
    DWORD       dwScratchSpace;
} OLEUIPASTEENTRY, *POLEUIPASTEENTRY, FAR *LPOLEUIPASTEENTRY;

Members

fmtetc
Format that is acceptable. The Paste Special dialog box checks if this format is offered by the object on the clipboard and if so, offers it for selection to the user.
lpstrFormatName
Pointer to the string that represents the format to the user. Any %s in this string is replaced by the FullUserTypeName of the object on the clipboard and the resulting string is placed in the list box of the dialog box. Only one %s is allowed. The presence or absence of %s specifies whether the result-text is to indicate that data is being pasted or that an object that can be activated by an application is being pasted. If %s is present, the resulting text says that an object is being pasted. Otherwise, it says that data is being pasted.
lpstrResultText
Pointer to the string used to customize the resulting text of the dialog box when the user selects the format corresponding to this entry. Any %s in this string is replaced by the application name or FullUserTypeName of the object on the clipboard. Only one %s is allowed.
dwFlags
Values from OLEUIPASTEFLAG enumeration.
dwScratchSpace
Scratch space available to routines that loop through an IEnumFORMATETC to mark if the PasteEntry format is available. This field CAN be left uninitialized.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OLEUIPASTEFLAG, OleUIPasteSpecial, OLEUIPASTESPECIAL


OLEUIPASTESPECIAL

The OLEUIPASTESPECIAL structure contains information that the OLE User Interface Library uses to initialize the Paste Special dialog box, as well as space for the library to return information when the dialog box is dismissed.

typedef struct tagOLEUIPASTESPECIAL
{
// These IN fields are standard across all OLEUI dialog box functions.
    DWORD          cbStruct;
    DWORD          dwFlags;
    HWND           hWndOwner;
    LPCSTR         lpszCaption;
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    HINSTANCE      hInstance;
    LPCSTR         lpszTemplate;
    HRSRC          hResource;

// Specifics for       OLEUIPASTESPECIAL.
    LPDATAOBJECT       lpSrcDataObj;
    LPOLEUIPASTEENTRY  arrPasteEntries;
    int                cPasteEntries;
    UINT FAR *         arrLinkTypes;
    int                cLinkTypes;
    UINT               cClsidExclude;
    LPCLSID            lpClsidExclude;
    int                nSelectedIndex;
    BOOL               fLink;
    HGLOBAL            hMetaPict;
    SIZEL              sizel;
} OLEUIPASTESPECIAL, *POLEUIPASTESPECIAL, FAR *LPOLEUIPASTESPECIAL;

Members

cbStruct
Size of the structure, in bytes. This field must be filled on input.
dwFlags
On input, dwFlags specifies the initialization and creation flags. On exit, it specifies the user's choices. It may be a combination of the following flags:

PSF_SHOWHELP

Dialog box will display a Help button.

PSF_SELECTPASTE

The Paste radio button will be selected at dialog box startup. This is the default, if PSF_SELECTPASTE or PSF_SELECTPASTELINK are not specified. Also, it specifies the state of the button on dialog termination. IN/OUT flag.

PSF_SELECTPASTELINK

The PasteLink radio button will be selected at dialog box startup. Also, specifies the state of the button on dialog termination. IN/OUT flag.

PSF_CHECKDISPLAYASICON

Whether the Display As Icon radio button was checked on dialog box termination. OUT flag.

PSF_DISABLEDISPLAYASICON

The Display As Icon check box will be disabled on initialization.

HIDECHANGEICON

Used to disable the change-icon button in the dialog box, which is available to users when they're pasting an OLE object by default. See STAYONCLIPBOARDCHANGE otherwise.

STAYONCLIPBOARDCHANGE

Used to tell the dialog box to stay up if the clipboard changes while the dialog box is up. If the user switches to another application and copies or cuts something, the dialog box will, by default, perform a cancel operation, which will remove the dialog box since the options it's in the middle of presenting to the user are no longer up-to-date with respect to what's really on the clipboard.

NOREFRESHDATAOBJECT

Used in conjunction with STAYONCLIPBOARDCHANGE (it doesn't do anything otherwise). If the clipboard changes while the dialog box is up and STAYONCLIPBOARDCHANGE is specified, then NOREFRESHDATAOBJECT indicates that the dialog box should NOT refresh the contents of the dialog box to reflect the new contents of the clipboard. This is useful if the application is using the paste-special dialog box on an IDataObject besides the one on the clipboard, for example, as part of a right-click drag-and-drop operation.

hWndOwner
Wwindow that owns the dialog box. It should not be NULL.
lpszCaption
Pointer to a string to be used as the title of the dialog box. If NULL, then the library uses Paste Special.
lpfnHook
Pointer to a hook function that processes messages intended for the dialog box. The hook function must return zero to pass a message that it didn't process back to the dialog box procedure in the library. The hook function must return a non-zero value to prevent the library's dialog box procedure from processing a message it has already processed.
lCustData
Application-defined data that the library passes to the hook function pointed to by the lpfnHook member. The library passes a pointer to the OLEUIPASTESPECIAL structure in the lParam parameter of the WM_INITDIALOG message; this pointer can be used to retrieve the lCustData member.
hInstance
Instance that contains a dialog box template specified by the lpTemplateName member.
lpszTemplate
Pointer to a null-terminated string that specifies the name of the resource file for the dialog box template that is to be substituted for the library's Paste Special dialog box template.
hResource
Customized template handle.
lpSrcDataObj
Pointer to the IDataObject* interface of the data object to be pasted (from the clipboard). This field is filled on input. If lpSrcDataObj is NULL when OleUIPasteSpecial is called, then OleUIPasteSpecial will attempt to retrieve a pointer to an IDataObject from the clipboard. If OleUIPasteSpecial succeeds, it is the caller's responsibility to free the IDataObject returned in lpSrcDataObj.
arrPasteEntries
The OLEUIPASTEENTRY array which specifies acceptable formats. This field is filled on input.
cPasteEntries
Number of OLEUIPASTEENTRY array entries. This field is filled on input.
arrLinkTypes
List of link types that are acceptable. Link types are referred to using OLEUIPASTEFLAG in arrPasteEntries. This field is filled on input.
cLinkTypes
Number of link types. This field is filled on input.
cClsidExclude
Number of CLSIDs in lpClsidExclude. This field is filled on input.
lpClsidExclude
Pointer to an array of CLSIDs to exclude from the list of available server objects for a Paste operation. Note that this does not affect Paste Link. An application can prevent embedding into itself by listing its own CLSID in this list. This field is filled on input.
nSelectedIndex
Index of arrPasteEntries[] that the user selected. This field is filled on output.
fLink
Whether Paste or Paste Link was selected by the user. This field is filled on output.
hMetaPict
Handle to the Metafile containing the icon and icon title selected by the user. This field is filled on output.
sizel
Size of object as displayed in its source, if the display aspect chosen by the user matches the aspect displayed in the source. If the user chooses a different aspect, then sizel.cx and sizel.cy are both set to zero. The size of the object as it is displayed in the source is retrieved from the ObjectDescriptor if fLink is FALSE and from the LinkSrcDescriptor if fLink is TRUE. This field is filled on output.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIPasteSpecial, OLEUIPASTEENTRY, OLEUIPASTEFLAG


OLEUIVIEWPROPS

This structure is used to initialize the View tab of the Object properties dialog box. A reference to it is passed in as part of the OLEUIOBJECTPROPS structure to the OleUIObjectProperties function. This tab allows the user to toggle between "content" and "iconic" views of the object, and change its scaling within the container. It also allows the user to tunnel to the change icon dialog box when the object is being displayed iconically.

// These IN fields are standard across all OLEUI property pages.

typedef struct tagOLEUIVIEWPROPSA
{
// These IN fields are standard across all OLEUI property pages.
    DWORD          cbStruct;
    DWORD          dwFlags;
    DWORD          dwReserved1[2];
    LPFNOLEUIHOOK  lpfnHook;
    LPARAM         lCustData;
    DWORD          dwReserved2[3];

struct tagOLEUIOBJECTPROPS* lpOP;

    int       nScaleMin;
    int       nScaleMax;

} OLEUIVIEWPROPSA, *POLEUIVIEWPROPSA, FAR* LPOLEUIVIEWPROPSA;

Members

cbStruct
Size of the structure in bytes.
dwFlags
IN-OUT: flags specific to view page

VPF_SELECTRELATIVE

[in] Relative to origin.

VPF_DISABLERELATIVE

[in] Disable relative to origin.

VPF_DISABLESCALE

[in] Disable scale option.

dwReserved1[2]
Reserved for future use.
lpfnHook
Pointer to hook callback (not used in this dialog box).
lCustData
Custom data to pass to hook (not used in this dialog box).
dwReserved2[3]
Reserved for future use.
lpOP
Used internally.
nScaleMin
Minimum value for the scale range.
nScaleMax
Maximum value for the scale range.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.
Unicode: Defined as Unicode and ANSI structures.

See Also

OleUIObjectProperties, OLEUIOBJECTPROPS


OLEVERB

The OLEVERB structure defines a verb that an object supports. The IOleObject::EnumVerbs method creates an enumerator that can enumerate these structures for an object, and supplies a pointer to the enumerator's IEnumOLEVERB.

typedef struct tagOLEVERB
{
    LONG     lVerb;
    LPWSTR   lpszVerbName;
    DWORD    fuFlags;
    DWORD    grfAttribs;
} OLEVERB, * LPOLEVERB;

Members

lVerb
Integer identifier associated with this verb.
lpszVerbName
Pointer to a string that contains the verb's name.
fuFlags
In Windows, a group of flags taken from the flag constants beginning with MF_ defined in AppendMenu. Containers should use these flags in building an object's verb menu. All Flags defined in AppendMenu are supported except for MF_BITMAP, MF_OWNERDRAW, and MF_POPUP.
grfAttribs
Combination of the verb attributes in the OLEVERBATTRIB enumeration.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IEnumOLEVERB, IOleObject::EnumVerbs


POINTF

The POINTF structure is used in the IOleControlSite::TransformCoords method to convert between container units, expressed in floating point, and control units, expressed in HIMETRIC. The POINTF structure specifically holds the floating point container units. Controls do not attempt to interpret either value in the structure.

typedef struct tagPOINTF
{
    float x;
    float y;
} POINTF;

Members

x
The x coordinates of the point in units that the container finds convenient.
y
The y coordinates of the point in units that the container finds convenient.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IControlOleSite::TransformCoords


STATDATA

The STATDATA structure is the data structure used to specify each advisory connection. It is used for enumerating current advisory connections. It holds data returned by the IEnumSTATDATA enumerator. This enumerator interface is returned by IDataObject:DAdvise. Each advisory connection is specified by a unique STATDATA structure.

typedef struct tagSTATDATA
{
    FORMATETC     formatetc;
    DWORD         grfAdvf;
    IAdviseSink*  pAdvSink;
    DWORD         dwConnection;
} STATDATA;

Members

formatetc
The FORMATETC structure for the data of interest to the advise sink. The advise sink receives notification of changes to the data specified by this FORMATETC structure.
grfAdvf
The ADVF enumeration value that determines when the advisory sink is notified of changes in the data.
pAdvSink
The pointer for the IAdviseSink interface that will receive change notifications.
dwConnection
The token that uniquely identifies the advisory connection. This token is returned by the method that sets up the advisory connection.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IEnumSTATDATA


STGMEDIUM

The STGMEDIUM structure is a generalized global memory handle used for data transfer operations by the IAdviseSink, IDataObject, and IOleCache interfaces.

typedef struct tagSTGMEDIUM
{
    DWORD tymed;
    [switch_type(DWORD), switch_is((DWORD) tymed)]
    union {
        [case(TYMED_GDI)]      HBITMAP        hBitmap;
        [case(TYMED_MFPICT)]   HMETAFILEPICT  hMetaFilePict;
        [case(TYMED_ENHMF)]    HENHMETAFILE   hEnhMetaFile;
        [case(TYMED_HGLOBAL)]  HGLOBAL        hGlobal;
        [case(TYMED_FILE)]     LPWSTR         lpszFileName;
        [case(TYMED_ISTREAM)]  IStream        *pstm;
        [case(TYMED_ISTORAGE)] IStorage       *pstg;
        [default] ;
    };
    [unique] IUnknown *pUnkForRelease;
}STGMEDIUM;
typedef STGMEDIUM *LPSTGMEDIUM;

Members

tymed
Type of storage medium. The marshaling and unmarshaling routines use this value to determine which union member was used. This value must be one of the elements of the TYMED enumeration.
union member
Handle, string, or interface pointer that the receiving process can use to access the data being transferred. If tymed is TYMED_NULL, the union member is undefined; otherwise, it is one of the following:

hBitmap

Bitmap handle. The tymed member is TYMED_GDI.

hMetaFilePict

Metafile handle. The tymed member is TYMED_MFPICT.

hEnhMetaFile

Enhanced metafile handle. The tymed member is TYMED_ENHMF.

hGlobal

Global memory handle. The tymed member is TYMED_HGLOBAL.

lpszFileName

Pointer to the path of a disk file that contains the data. The tymed member is TYMED_FILE.

pstm

Pointer to an IStream interface. The tymed member is TYMED_ISTREAM.

pstg

Pointer to an IStorage interface. The tymed member is TYMED_ISTORAGE.

pUnkForRelease
Pointer to an interface instance that allows the sending process to control the way the storage is released when the receiving process calls the ReleaseStgMedium function. If pUnkForRelease is NULL, ReleaseStgMedium uses default procedures to release the storage; otherwise, ReleaseStgMedium uses the specified IUnknown interface.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

FORMATETC, IAdviseSink, IDataObject, IOleCache, ReleaseStgMedium


ACTIVATEFLAGS

The ACTIVATEFLAGS enumeration value indicates whether an object is activated as a windowless object. It is used in IOleInPlaceSiteEx::OnInPlaceActivateEx.

typedef enum tagACTIVATEFLAGS
{
    ACTIVATE_WINDOWLESS    = 1
} ACTIVATEFLAGS;

Elements

ACTIVATE_WINDOWLESS
If TRUE, indicates that the object is activated in place as a windowless object. In the IOleInPlaceSiteEx::OnInPlaceActivateEx method, the container uses this value returned in the dwFlags parameter instead of calling the GetWindow method in the IOleInPlaceObjectWindowless interface to determine if the object is windowless or not.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


ADVF

The ADVF enumeration values are flags used by a container object to specify the requested behavior when setting up an advise sink or a caching connection with an object. These values have different meanings, depending on the type of connection in which they are used, and each interface uses its own subset of the flags.

typedef enum tagADVF
{
    ADVF_NODATA            = 1,
    ADVF_ONLYONCE          = 2,
    ADVF_PRIMEFIRST        = 4,
    ADVFCACHE_NOHANDLER    = 8,
    ADVFCACHE_FORCEBUILTIN = 16,
    ADVFCACHE_ONSAVE       = 32,
    ADVF_DATAONSTOP        = 64
} ADVF;

Elements

ADVF_NODATA
For data advisory connections (IDataObject::DAdvise or IDataAdviseHolder::Advise), this flag requests the data object not to send data when it calls IAdviseSink::OnDataChange. The recipient of the change notification can later request the data by calling IDataObject::GetData. The data object can honor the request by passing TYMED_NULL in the STGMEDIUM parameter, or it can provide the data anyway. For example, the data object might have multiple advisory connections, not all of which specified ADVF_NODATA, in which case the object might send the same notification to all connections. Regardless of the container's request, its IAdviseSink implementation must check the STGMEDIUM parameter because it is responsible for releasing the medium if it is not TYMED_NULL.

For cache connections (IOleCache::Cache), this flag requests that the cache not be updated by changes made to the running object. Instead, the container will update the cache by explicitly calling IOleCache::SetData. This situation typically occurs when the iconic aspect of an object is being cached.

ADVF_NODATA is not a valid flag for view advisory connections (IViewObject::SetAdvise) and it returns E_INVALIDARG.

ADVF_PRIMEFIRST
Requests that the object not wait for the data or view to change before making an initial call to IAdviseSink::OnDataChange (for data or view advisory connections) or updating the cache (for cache connections). Used with ADVF_ONLYONCE, this parameter provides an asynchronous GetData call.
ADVF_ONLYONCE
Requests that the object make only one change notification or cache update before deleting the connection.

ADVF_ONLYONCE automatically deletes the advisory connection after sending one data or view notification. The advisory sink receives only one IAdviseSink call. A nonzero connection identifier is returned if the connection is established, so the caller can use it to delete the connection prior to the first change notification.

For data change notifications, the combination of ADVF_ONLYONCE and ADVF_PRIMEFIRST provides, in effect, an asynchronous IDataObject::GetData call.

When used with caching, ADVF_ONLYONCE updates the cache one time only, on receipt of the first OnDataChange notification. After the update is complete, the advisory connection between the object and the cache is disconnected. The source object for the advisory connection calls the IAdviseSink::Release method.

ADVF_DATAONSTOP
For data advisory connections, assures accessibility to data. This flag indicates that when the data object is closing, it should call IAdviseSink::OnDataChange, providing data with the call. Typically, this value is used in combination with ADVF_NODATA. Without this value, by the time an OnDataChange call without data reaches the sink, the source might have completed its shutdown and the data might not be accessible. Sinks that specify this value should accept data provided in OnDataChange if it is being passed, because they may not get another chance to retrieve it.

For cache connections, this flag indicates that the object should update the cache as part of object closure.

ADVF_DATAONSTOP is not a valid flag for view advisory connections.

ADVFCACHE_NOHANDLER
Synonym for ADVFCACHE_FORCEBUILTIN, which is used more often.
ADVFCACHE_FORCEBUILTIN
This value is used by DLL object applications and object handlers that perform the drawing of their objects. ADVFCACHE_FORCEBUILTIN instructs OLE to cache presentation data to ensure that there is a presentation in the cache. This value is not a valid flag for data or view advisory connections. For cache connections, this flag caches data that requires only code shipped with OLE (or the underlying operating system) to be present in order to produce it with IDataObject::GetData or IViewObject::Draw. By specifying this value, the container can ensure that the data can be retrieved even when the object or handler code is not available.
ADVFCACHE_ONSAVE
For cache connections, this flag updates the cached representation only when the object containing the cache is saved. The cache is also updated when the OLE object transitions from the running state back to the loaded state (because a subsequent save operation would require rerunning the object). This value is not a valid flag for data or view advisory connections.

Remarks

For a data or view advisory connection, the container uses the ADVF constants when setting up a connection between an IAdviseSink instance and and either an IDataObject or IViewObject instance. These connections are set up using the IDataObject::DAdvise, IDataAdviseHolder::Advise, or IViewObject::SetAdvise methods.

For a caching connection, the constants are specified in the IOleCache::Cache method to indicate the container's requests on how the object should update its cache.

These constants are also used in the advf member of the STATDATA structure. This structure is used by IEnumSTATDATA to describe the enumerated connections, and the advf member indicates the flags that were specified when the advisory or cache connection was established. When STATDATA is used for an IOleObject::EnumAdvise enumerator, the advf member is indeterminate.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataAdviseHolder, IDataObject, IEnumSTATDATA, IOleCache, IViewObject


DATADIR

The DATADIR enumeration values specify the direction of the data flow in the dwDirection parameter of the IDataObject::EnumFormatEtc method. This determines the formats that the resulting enumerator can enumerate.

typedef enum tagDATADIR
{
    DATADIR_GET            = 1,
    DATADIR_SET            = 2
} DATADIR;

Elements

DATADIR_GET
Requests that IDataObject::EnumFormatEtc supply an enumerator for the formats that can be specified in IDataObject::GetData.
DATADIR_SET
Requests that IDataObject::EnumFormatEtc supply an enumerator for the formats that can be specified in IDataObject::SetData.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

IDataObject


DISCARDCACHE

The DISCARDCACHE enumeration values are used in the IOleCache2::DiscardCache method to specify what to do with caches that are to be discarded from memory if their dirty bit has been set. The dwDiscardOptions parameter specifies whether or not to save these caches.

typedef enum tagDISCARDCACHE
{
    DISCARDCACHE_SAVEIFDIRTY    = 0,
    DISCARDCACHE_NOSAVE         = 1
} DISCARDCACHE;

Elements

DISCARDCACHE_SAVEIFDIRTY
The cache is to be saved to disk.
DISCARDCACHE_NOSAVE
The cache can be discarded without saving it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleCache2::DiscardCache


DOCMISC

The DOCMISC enumeration provides miscellaneous property information about a document object. A combination of values from DOCMISC is returned in at the location specified by the pdwStatus parameter in IOleDocument::GetDocMiscStatus.

typedef enum
{
    DOCMISC_CANCREATEMULTIPLEVIEWS    =  1,
    DOCMISC_SUPPORTCOMPLEXRECTANGLES  =  2,
    DOCMISC_CANTOPENEDIT,             =  4,
    DOCMISC_NOFILESUPPORT             =  8
} DOCMISC;

Elements

DOCMISC_CANCREATEMULTIPLEVIEWS
Object supports multiple views.
DOCMISC_SUPPORTCOMPLEXRECTANGLES
Object supports complex rectangles and, therefore implements IOleDocumentView::SetRectComplex.
DOCMISC_CANTOPENEDIT
Object supports activation in a separate window and, therefore implements IOleDocumentView::Open.
DOCMISC_NOFILESUPPORT
Object does not support file read/write.

Remarks

Objects that have a limited user interface for activation purposes should set DOCMISC_CANTOPENEDIT. Those that support IPersistStorage only as a persistence mechanism should specify DOCMISC_NOFILESUPPORT. Otherwise, an object must also implement IPersistFile.

If an object requires none of these flags, it must write a zero to the *pdwStatus parameter of IOleDocument::GetDocMiscStatus.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleDocument::GetDocMiscStatus, IOleDocumentView::Open, IOleDocumentView::SetRectComplex, IPersistFile, IPersistStorage


DROPEFFECT

The DoDragDrop function and many of the methods in the IDropSource and IDropTarget interfaces pass information about the effects of a drag-and-drop operation in a DROPEFFECT enumeration.

Valid drop-effect values are the result of applying the OR operation to the values contained in the DROPEFFECT enumeration:

typedef enum tagDROPEFFECT
{
    DROPEFFECT_NONE   = 0,
    DROPEFFECT_COPY   = 1,
    DROPEFFECT_MOVE   = 2,
    DROPEFFECT_LINK   = 4,
    DROPEFFECT_SCROLL = 0x80000000
}DROPEFFECT;

These values have the following meaning:

DROPEFFECT name | Value | Description
DROPEFFECT_NONE | 0 | Drop target cannot accept the data.
DROPEFFECT_COPY | 1 | Drop results in a copy. The original data is untouched by the drag source.
DROPEFFECT_MOVE | 2 | Drag source should remove the data.
DROPEFFECT_LINK | 4 | Drag source should create a link to the original data.
DROPEFFECT_SCROLL | 0x80000000 | Scrolling is about to start or is currently occurring in the target. This value is used in addition to the other values.

Note Your application should always mask values from the DROPEFFECT enumeration to ensure compatibility with future implementations. Presently, only four of the 32-bit positions in a DROPEFFECT have meaning. In the future, more interpretations for the bits will be added. Drag sources and drop targets should carefully mask these values appropriately before comparing. They should never compare a DROPEFFECT against, say, DROPEFFECT_COPY by:

if (dwDropEffect == DROPEFFECT_COPY)...

Instead, the application should always mask for the value or values being sought:

if (dwDropEffect & DROPEFFECT_COPY) == DROPEFFECT_COPY)...

or

if (dwDropEffect & DROPEFFECT_COPY)...

This allows for the definition of new drop effects, while preserving backwards compatibility with existing code.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

DoDragDrop, IDropSource, IDropTarget


DVASPECT

The DVASPECT enumeration values specify the desired data or view aspect of the object when drawing or getting data.

typedef enum tagDVASPECT
{
    DVASPECT_CONTENT    = 1,
    DVASPECT_THUMBNAIL  = 2,
    DVASPECT_ICON       = 4,
    DVASPECT_DOCPRINT   = 8
} DVASPECT;

Elements

DVASPECT_CONTENT
Provides a representation of an object so it can be displayed as an embedded object inside of a container. This value is typically specified for compound document objects. The presentation can be provided for the screen or printer.
DVASPECT_THUMBNAIL
Provides a thumbnail representation of an object so it can be displayed in a browsing tool. The thumbnail is approximately a 120 by 120 pixel, 16-color (recommended) device-independent bitmap potentially wrapped in a metafile.
DVASPECT_ICON
Provides an iconic representation of an object.
DVASPECT_DOCPRINT
Provides a representation of the object on the screen as though it were printed to a printer using the Print command from the File menu. The described data may represent a sequence of pages.

Remarks

Values of this enumeration are used to define the dwAspect field of the FORMATETC structure. Only one DVASPECT value can be used to specify a single presentation aspect in a FORMATETC structure. The FORMATETC structure is used in many OLE functions and interface methods that require information on data presentation.

The default value of MiscStatus is used if a subkey corresponding to the specified DVASPECT is not found. To set an OLE control, specify DVASPECT==1. This will cause the following to occur in the Registry:

HKEY_CLASSES_ROOT\CLSID\ . . .\MiscStatus = 1

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IAdviseSink, IDataObject, IOleObject, IViewObject, IViewObject2, OleDraw, FORMATETC, OBJECTDESCRIPTOR


DVASPECT2

The DVASPECT2 enumeration value is used in IViewObject::Draw to specify new drawing aspects used to optimize the drawing process.

typedef enum tagDVASPECT2
{
    DVASPECT_OPAQUE         = 16,
    DVASPECT_TRANSPARENT    = 32
} DVASPECT2;

Elements

DVASPECT_OPAQUE
Represents the opaque, easy to clip parts of an object. Objects may or may not support this aspect.
DVASPECT_TRANSPARENT
Represents the transparent or irregular parts of on object, typically parts that are expensive or impossible to clip out. Objects may or may not support this aspect.

Remarks

To support drawing optimizations to reduce flicker, an object needs to be able to draw and return information about three separate aspects of itself:

DVASPECT_CONTENT
Same as before. Specifies the entire content of an object. All objects should support this aspect.
DVASPECT_OPAQUE
Represents the opaque, easy to clip parts of an object. Objects may or may not support this aspect.
DVASPECT_TRANSPARENT
Represents the transparent or irregular parts of on object, typically parts that are expensive or impossible to clip out. Objects may or may not support this aspect.

The container can determine which of these drawing aspects an object supports by calling the new method IViewObjectEx::GetViewStatus. Individual bits return information about which aspects are supported. If an object does not support the IViewObjectEx interface, it is assumed to support only DVASPECT_CONTENT.

Depending on which aspects are supported, the container can ask the object to draw itself during the front to back pass only, the back to front pass only, or both. The various possible cases are:

  • Objects supporting only DVASPECT_CONTENT should be drawn during the back to front pass, with all opaque parts of any overlapping object clipped out. Since all objects should support this aspect, a container not concerned about flickering - maybe because it is drawing in an offscreen bitmap - can opt to draw all objects that way and skip the front to back pass.
  • Objects supporting DVASPECT_OPAQUE may be asked to draw this aspect during the front to back pass. The container is responsible for clipping out the object's opaque regions (returned by IViewObjectEx::GetRegion) before painting any further object behind it.
  • Objects supporting DVASPECT_TRANSPARENT may be asked to draw this aspect during the back to front pass. The container is responsible for clipping out opaque parts of overlapping objects before letting an object draw this aspect.

Even when DVASPECT_OPAQUE and DVASPECT_TRANSPARENT are supported, the container is free to use these aspects or not. In particular, if it is painting in an offscreen bitmap and consequently is unconcerned about flicker, the container may use DVASPECT_CONTENT and a one-pass drawing only. However, in a two-pass drawing, if the container uses DVASPECT_OPAQUE during the front to back pass, then it must use DVASPECT_TRANSPARENT during the back to front pass to complete the rendering of the object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IViewObject::Draw


DVASPECTINFOFLAG

The DVASPECTINFOFLAG enumeration value is used in the DVASPECTINFO structure to indicate whether an object can support optimized drawing of itself.

typedef enum tagDVASPECTINFOFLAG
{
    DVASPECTINFOFLAG_CANOPTIMIZE    = 1
} DVASPECTINFOFLAG;

Elements

DVASPECTINFOFLAG_CANOPTIMIZE
If TRUE, indicates that the object can support optimized rendering of itself. Since most objects on a form share the same font, background color, and border types, leaving these values in the device context allows the next object to use them without having to re-select them. Specifically, the object can leave the font, brush, and pen selected on return from the IViewObject::Draw method instead of deselecting these from the device context. The container then must deselect these values at the end of the overall drawing process. The object can also leave other drawing state changes in the device context, such as the background color, the text color, raster operation code, the current point, the line drawing, and the poly fill mode. The object cannot change state values unless other objects are capable of restoring them. For example, the object cannot leave a changed mode, transformation value, selected bitmap, clip region, or metafile.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

DVASPECTINFO


DVEXTENTMODE

The DVEXTENTMODE enumeration values are used in IViewObjectEx::GetNaturalExtent.

typedef enum tagDVEXTENTMODE
{
    DVEXTENT_CONTENT               = 0,
    DVEXTENT_INTEGRAL              = 1
} DVEXTENTMODE;

Elements

DVEXTENT_CONTENT
Indicates that the container will ask the object how big it wants to be to exactly fit its content, for example, in snap-to-size operations.
DVEXTENT_INTEGRAL
Indicates that the container will provide a proposed size to the object for its use in resizing.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IViewObjectEx::GetNaturalExtent


HITRESULT

The HITRESULT enumeration values are used in IViewObjectEx::QueryHitPoint and IViewObjectEx::QueryHitRect.

typedef enum tagHITRESULT
{
    HITRESULT_OUTSIDE               = 0,
    HITRESULT_TRANSPARENT           = 1,
    HITRESULT_CLOSE                 = 2,
    HITRESULT_HIT                   = 3
} HITRESULT;

Elements

HITRESULT_OUTSIDE
The specified location is outside the object and not close to the object.
HITRESULT_TRANSPARENT
The specified location is within the bounds of the object, but not close to the image. For example, a point in the middle of a transparent circle could be HITRESULT_TRANSPARENT.
HITRESULT_CLOSE
The specified location is inside the object or is outside the object but is close enough to the object to be considered inside. Small, thin or detailed objects may use this value. Even if a point is outside the bounding rectangle of an object it may still be close. This value is needed for hitting small objects.
HITRESULT_HIT
The specified location is within the image of the object

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IViewObjectEx::QueryHitPoint, IViewObjectEx::QueryHitRect


OLECLOSE

The OLECLOSE enumeration constants are used in the IOleObject::Close method to determine whether the object should be saved before closing.

typedef enum tagOLECLOSE
{
    OLECLOSE_SAVEIFDIRTY  = 0,
    OLECLOSE_NOSAVE       = 1,
    OLECLOSE_PROMPTSAVE   = 2
} OLECLOSE;

Elements

OLECLOSE_SAVEIFDIRTY
The object should be saved if it is dirty.
OLECLOSE_NOSAVE
The object should not be saved, even if it is dirty. This flag is typically used when an object is being deleted.
OLECLOSE_PROMPTSAVE
If the object is dirty, the IOleObject::Close implementation should display a dialog box to let the end user determine whether to save the object. However, if the object is in the running state but its user interface is invisible, the end user should not be prompted, and the close should be handled as if OLECLOSE_SAVEIFDIRTY had been specified.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::Close


OLECMDEXECOPT

The OLECMDEXECOPT enumeration specifies command execution options. One value from this enumeration is passed in the nCmdExecOpt argument of IOleCommandTarget::Exec.

typedef enum
{
    OLECMDEXECOPT_DODEFAULT        = 0,
    OLECMDEXECOPT_PROMPTUSER       = 1,
    LECMDEXECOPT_DONTPROMPTUSER    = 2,
    OLECMDEXECOPT_SHOWHELP         = 3
} OLECMDEXECOPT;

Elements

OLECMDEXECOPT_DODEFAULT
Prompt the user for input or not, whichever is the default behavior.
OLECMDEXECOPT_PROMPTUSER
Execute the command after obtaining user input.
OLECMDEXECOPT_DONTPROMPTUSER
Execute the command without prompting the user. For example, clicking the Print toolbar button causes a document to be immediately printed without user input.
OLECMDEXECOPT_SHOWHELP
Show help for the corresponding command, but do not execute.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleCommandTarget::Exec


OLECMDF

The OLECMDF enumeration designates the type of support provided by an object for the command specified in an OLECMD structure.

typedef enum
{
    OLECMDF_SUPPORTED    = 1,
    OLECMDF_ENABLED      = 2,
    OLECMDF_LATCHED      = 4,
    OLECMDF_NINCHED      = 8
} OLECMDF;

Elements

OLECMDF_SUPPORTED
The command is supported by this object.
OLECMDF_ENABLED
The command is available and enabled.
OLECMDF_LATCHED
The command is an on-off toggle and is currently on.
OLECMDF_NINCHED
Reserved for future use.

Remarks

Values from the OLECMDF enumeration are used to fill the value of the cmdf member of OLECMD structures passed to IOleCommandTarget::QueryStatus.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleCommandTarget::QueryStatus, OLECMD


OLECMDID

The OLECMDID enumeration specifies which command to execute. (The commands in this enumeration are the standard commands defined by Office 95.) A single value from this enumeration is passed in the nCmdID argument of IOleCommandTarget::Exec.

typedef enum
{
     OLECMDID_OPEN              = 1,
     OLECMDID_NEW               = 2,
     OLECMDID_SAVE              = 3,
     OLECMDID_SAVEAS            = 4,
     OLECMDID_SAVECOPYAS        = 5,
     OLECMDID_PRINT             = 6,
     OLECMDID_PRINTPREVIEW      = 7,
     OLECMDID_PAGESETUP         = 8,
     OLECMDID_SPELL             = 9,
     OLECMDID_PROPERTIES        = 10,
     OLECMDID_CUT               = 11,
     OLECMDID_COPY              = 12,
     OLECMDID_PASTE             = 13,
     OLECMDID_PASTESPECIAL      = 14,
     OLECMDID_UNDO              = 15,
     OLECMDID_REDO              = 16,
     OLECMDID_SELECTALL         = 17,
     OLECMDID_CLEARSELECTION    = 18,
     OLECMDID_ZOOM              = 19,
     OLECMDID_GETZOOMRANGE      = 20
     OLECMDID_UPDATECOMMANDS    = 21
     OLECMDID_REFRESH           = 22
     OLECMDID_STOP              = 23
     OLECMDID_HIDETOOLBARS      = 24
     OLECMDID_SETPROGRESSMAX    = 25
     OLECMDID_SETPROGRESSPOS    = 26
     OLECMDID_SETPROGRESSTEXT   = 27
     OLECMDID_SETTITLE          = 28
     OLECMDID_SETDOWNLOADSTATE  = 29
     OLECMDID_STOPDOWNLOAD      = 30
} OLECMDID;

Elements

OLECMDID_OPEN
File menu, Open command
OLECMDID_NEW
File menu, New command
OLECMDID_SAVE
File menu, Save command
OLECMDID_SAVEAS
File menu, Save As command
OLECMDID_SAVECOPYAS
File menu, Save Copy As command
OLECMDID_PRINT
File menu, Print command
OLECMDID_PRINTPREVIEW
File menu, Print Preview command
OLECMDID_PAGESETUP
File menu, Page Setup command
OLECMDID_SPELL
Tools menu, Spelling command
OLECMDID_PROPERTIES
File menu, Properties command
OLECMDID_CUT
Edit menu, Cut command
OLECMDID_COPY
Edit menu, Copy command
OLECMDID_PASTE
Edit menu, Paste command
OLECMDID_PASTESPECIAL
Edit menu, Paste Special command
OLECMDID_UNDO
Edit menu, Undo command
OLECMDID_REDO
Edit menu, Redo command
OLECMDID_SELECTALL
Edit menu, Select All command
OLECMDID_CLEARSELECTION
Edit menu, Clear command
OLECMDID_ZOOM
View menu, Zoom command (see below for details)
OLECMDID_GETZOOMRANGE
Retrieves zoom range applicable to View Zoom. (See below for details.)
OLECMDID_UPDATECOMMANDS
Informs the receiver, usually a frame, of state changes. The receiver can then query the status of the commands whenever convenient.
OLECMDID_REFRESH
Asks the receiver to refresh its display. Implemented by the document/object.
OLECMDID_STOP
Stops all current processing. Implemented by the document/object.
OLECMDID_HIDETOOLBARS
View Menu, Toolbars command. Implemented by the document/object to hide its toolbars.
OLECMDID_SETPROGRESSMAX
Sets the maximum value of a progress indicator if one is owned by the receiving object, usually a frame. The minimum value is always zero.
OLECMDID_SETPROGRESSPOS
Sets the current value of a progress indicator if one is owned by the receiving object, usually a frame.
OLECMDID_SETPROGRESSTEXT
Sets the text contained in a progress indicator if one is owned by the receiving object, usually a frame. If the receiver currently has no progress indicator, this text should be displayed in the status bar (if one exists) as with IOleInPlaceFrame::SetStatusText.
OLECMDID_SETTITLE
Sets the title bar text of the receiving object, usually a frame.
OLECMDID_SETDOWNLOADSTATE
Called by the object when downloading state changes. Takes a VT_BOOL parameter, which is TRUE if the object is downloading data and FALSE if it not. Primarily implemented by the frame.
OLECMDID_STOPDOWNLOAD
Stops the download when executed. Typically, this command is propagated to all contained objects. When queried, sets MSOCMDF_ENABLED. Implemented by the document/object.

Remarks

In OLE Compound Documents technology, an object that is being edited in-place disables the Zoom control on its toolbar and the Zoom command on its View menu, because, the Zoom command applies logically to the container document, not to the object. The OLECMDID_ZOOM and OLECMDID_GETZOOMRANGE commands notify the container's frame object of the zoom range it should use to display a document object in its user interface. The container frame is the client-side object that implements IOleInPlaceFrame and, optionally, IOleCommandTarget.

The OLECMDID_ZOOM command takes one LONG argument as input and writes one LONG argument on output. This command is used for three purposes:

  • To query the current zoom value. The caller of IOleCommandTarget::Exec passes OLECMDEXECOPT_DONTPROMPTUSER as the execute option in nCmdExecOpt and NULL for pvIn. The object returns the current zoom value in pvaOut. When the object goes UI active, it retrieves the current zoom value from the container's frame object using this same mechanism and updates its zoom control with the returned value.
  • To display the Zoom dialog box. The caller of IOleCommandTarget::Exec passes OLECMDEXECOPT_PROMPTUSER in nCmdExecOpt. The caller can optionally pass the initial value for the dialog box through pvaIn; otherwise pvaIn must be NULL. If the user clicks Cancel, the object returns OLECMDERR_E_CANCELED. If the user clicks OK, the object passes the user-selected value in pvaOut. When user chooses the Zoom command from the View menu, the object calls the container's frame object in the same manner. The container then zooms the document to the user selected value, and the object updates its Zoom control with that value.
  • To set a Zoom value. The caller of IOleCommandTarget::Exec passes OLECMDEXECOPT_DONTPROMPTUSER in nCmdExecOpt and passes the zoom value to apply through pvaIn. The object validates and normalizes the new value and returns the validated value in pvaOut. When the user selects a new zoom value (using the Zoom control on the toolbar, for instance), the object calls the container's frame object in this manner. The container zooms the document to 100 percent, and the object updates the Zoom control with that value.

The OLECMDID_GETZOOMRANGE command is used to determine the range of valid zoom values from an object that implements IOleCommandTarget. The caller passes MSOCMDEXECOPT_DONTPROMPTUSER in nCmdExecOpt and NULL for pvaIn. The object returns its zoom range as a DWORD in pvaOut where the HIWORD contains the maximum zoom value and the LOWORD contains the minimum zoom value. Typically this command is used when the user drops down the Zoom control on the toolbar of the UI-active object. The applications and objects that support this command are required to support all the integral zoom values that are within the (min,max) pair they return.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleCommandTarget, IOleCommandTarget::Exec, IOleInPlaceFrame


OLECMDTEXTF

The OLECMDTEXTF enumeration specifies the type of information that an object should store in the OLECMDTEXT structure passed in IOleCommandTarget::QueryStatus. One value from this enumeration is stored the cmdtextf member of the OLECMDTEXT structure to indicate the desired information.

typedef enum
{
    OLECMDTEXTF_NONE      = 0,
    OLECMDTEXTF_NAME      = 1,
    OLECMDTEXTF_STATUS    = 2
} OLECMDTEXTF;

Elements

OLECMDTEXTF_NONE
No extra information is requested.
OLECMDTEXTF_NAME
The object should provide the localized name of the command.
OLECMDTEXTF_STATUS
The object should provide a localized status string for the command.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

IOleCommandTarget::QueryStatus, OLECMDTEXT


OLECONTF

The OLECONTF enumeration indicates the kind of objects to be enumerated by the returned IEnumUnknown interface. OLECONTF contains a set of bitwise constants used in the IOleContainer::EnumObjects method.

typedef enum tagOLECONTF
{
    OLECONTF_EMBEDDINGS    = 1;
    OLECONTF_LINKS         = 2;
    OLECONTF_OTHERS        = 4;
    OLECONTF_ONLYUSER      = 8;
    OLECONTF_ONLYIFRUNNING = 16
} OLECONTF;

Elements

OLECONTF_EMBEDDINGS
Enumerates the embedded objects in the container.
OLECONTF_LINKS
Enumerates the linked objects in the container.
OLECONTF_OTHERS
Enumerates all objects in the container that are not OLE compound document objects (i.e., objects other than linked or embedded objects). Use this flag to enumerate the container's pseudo-objects.
OLECONTF_ONLYUSER
Enumerates only those objects the user is aware of. For example, hidden named-ranges in Microsoft Excel would not be enumerated using this value.
OLECONTF_ONLYIFRUNNING
Enumerates only those linked or embedded objects that are currently in the running state for this container.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IEnumUnknown, IOleContainer::EnumObjects


OLECREATE

Values from the OLECREATE enumeration are passed as the dwFlags parameter to the OleCreateXXX functions to indicate how the creation operation should proceed.

typedef enum tagOLECREATE
{
    OLECREATE_LEAVERUNNING    = 1,
} OLECREATE;

Elements

OLECREATE_LEAVERUNNING
Indicates that the newly created object should be left in the running state upon successful completion of the call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OleCreateEx, OleCreateFromFileEx, OleCreateFromDataEx, OleCreateLinkEx, OleCreateLinkToFileEx, OleCreateLinkFromDataEx


OLEDCFLAGS

The OLEDCFLAGS enumeration value supplies additional information to the container about the device context that the object has requested. It is used in IOleInPlaceSiteWindowless::GetDC.

typedef enum tagOLEDCFLAGS
{
    OLEDC_NODRAW        = 0x1,
    OLEDC_PAINTBKGND    = 0x2,
    OLEDC_OFFSCREEN     = 0x4
} OLEDCFLAGS;

Elements

OLEDC_NODRAW
Indicates that the object will not use the returned hDC for drawing but merely to get information about the display device. In this case, the container can simply pass the window's device context without further processing.
OLEDC_PAINTBKGND
Requests that the container paint the background behind the object before returning the device context. Objects should use this flag when requesting a device context to paint a transparent area.
OLEDC_OFFSCREEN
Indicates that the object prefers to draw into an offscreen device context that should then be copied to the screen. The container can honor this request or not. If this bit is cleared, the container must return an on-screen device context allowing the object to perform direct screen operations such as showing a selection via an XOR operation. An object can specify this value when the drawing operation generates a lot of screen flicker.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless::GetDC


OLEGETMONIKER

The OLEGETMONIKER enumeration constants indicate the requested behavior of the IOleObject::GetMoniker and IOleClientSite::GetMoniker methods.

typedef enum tagOLEGETMONIKER
{
    OLEGETMONIKER_ONLYIFTHERE    = 1,
    OLEGETMONIKER_FORCEASSIGN    = 2,
    OLEGETMONIKER_UNASSIGN       = 3,
    OLEGETMONIKER_TEMPFORUSER    = 4
} OLEGETMONIKER;

Elements

OLEGETMONIKER_ONLYIFTHERE
If a moniker for the object or container does not exist, GetMoniker should return E_FAIL and not assign a moniker.
OLEGETMONIKER_FORCEASSIGN
If a moniker for the object or container does not exist, GetMoniker should create one.
OLEGETMONIKER_UNASSIGN
IOleClientSite::GetMoniker can release the object's moniker (although it is not required to do so). This constant is not valid in IOleObject::GetMoniker.
OLEGETMONIKER_TEMPFORUSER
If a moniker for the object does not exist, IOleObject::GetMoniker can create a temporary moniker that can be used for display purposes (IMoniker::GetDisplayName) but not for binding. This enables the object server to return a descriptive name for the object without incurring the overhead of creating and maintaining a moniker until a link is actually created.

Remarks

If the OLEGETMONIKER_FORCEASSIGN flag causes a container to create a moniker for the object, the container should notify the object by calling the IOleObject::SetMoniker method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IMoniker, IOleClientSite::GetMoniker, IOleObject::GetMoniker


OLELINKBIND

The OLELINKBIND enumeration constants control binding operations to a link source. They are used in the IOleLink::BindToSource method.

typedef enum tagOLELINKBIND
{
    OLELINKBIND_EVENIFCLASSDIFF    = 1,
} OLELINKBIND;

Element

OLELINKBIND_EVENIFCLASSDIFF
The binding operation should proceed even if the current class of the link source is different from the last time the link was bound. For example, the link source could be a Lotus spreadsheet that was converted to an Excel spreadsheet.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::BindToSource


OLEMISC

The OLEMISC enumeration is a set of bitwise constants that can be combined to describe miscellaneous characteristics of an object or class of objects. A container can call the IOleObject::GetMiscStatus method to determine the OLEMISC bits set for an object. The values specified in an object server's CLSID\MiscStatus entry in the registration database are based on the OLEMISC enumeration. These constants are also used in the dwStatus member of the OBJECTDESCRIPTOR structure.

// NOTE: the definition of these ID's here is strictly temporary (until
// they have an official definition)...
    cpp_quote("#define OLEMISC_INVISIBLEATRUNTIME  1024")
    cpp_quote("#define OLEMISC_ALWAYSRUN           2048")
    cpp_quote("#define OLEMISC_ACTSLIKEBUTTON      4096")
    cpp_quote("#define OLEMISC_ACTSLIKELABEL       8192")
AND THE FOLLOWING ARE FROM OLECTL.H
#define OLEMISC_INVISIBLEATRUNTIME  0x00000400L
#define OLEMISC_ALWAYSRUN           0x00000800L
#define OLEMISC_ACTSLIKEBUTTON      0x00001000L
#define OLEMISC_ACTSLIKELABEL       0x00002000L
#define OLEMISC_NOUIACTIVATE        0x00004000L
#define OLEMISC_ALIGNABLE           0x00008000L
#define OLEMISC_SIMPLEFRAME         0x00010000L
#define OLEMISC_SETCLIENTSITEFIRST  0x00020000L
#define OLEMISC_IMEMODE             0x00040000L

typedef enum tagOLEMISC // bitwise
{
    OLEMISC_RECOMPOSEONRESIZE            = 1,
    OLEMISC_ONLYICONIC                   = 2,
    OLEMISC_INSERTNOTREPLACE             = 4,
    OLEMISC_STATIC                       = 8,
    OLEMISC_CANTLINKINSIDE               = 16,
    OLEMISC_CANLINKBYOLE1                = 32,
    OLEMISC_ISLINKOBJECT                 = 64,
    OLEMISC_INSIDEOUT                    = 128,
    OLEMISC_ACTIVATEWHENVISIBLE          = 256,
    OLEMISC_RENDERINGISDEVICEINDEPENDENT = 512,
    OLEMISC_INVISIBLEATRUNTIME           = 1024
    OLEMISC_ALWAYSRUN                    = 2048,
    OLEMISC_ACTSLIKEBUTTON               = 4096,
    OLEMISC_ACTSLIKELABEL                = 8192,
    OLEMISC_NOUIACTIVATE                 = 16384,
    OLEMISC_ALIGNABLE                    = 32768,
    OLEMISC_SIMPLEFRAME                  = 65536,
    OLEMISC_SETCLIENTSITEFIRST           = 131072,
    OLEMISC_IMEMODE                      = 262144,
    OLEMISC_IGNOREACTIVATEWHENVISIBLE    = 524288,
    OLEMISC_WANTSTOMENUMERGE             = 1048576,
    OLEMISC_SUPPORTSMULTILEVELUNDO       = 2097152
} OLEMISC;

Elements

OLEMISC_RECOMPOSEONRESIZE
When the container resizes the space allocated to displaying one of the object's presentations, the object wants to recompose the presentation. This means that on resize, the object wants to do more than scale its picture. If this bit is set, the container should force the object to the running state and call IOleObject::SetExtent with the new size.
OLEMISC_ONLYICONIC
The object has no useful content view other than its icon. From the user's perspective, the Display As Icon check box (in the Paste Special dialog box) for this object should always be checked, and should not be uncheckable. Note that such an object should still have a drawable content aspect; it will look the same as its icon view.
OLEMISC_INSERTNOTREPLACE
The object has initialized itself from the data in the container's current selection. Containers should examine this bit after calling IOleObject::InitFromData to initialize an object from the current selection. If set, the container should insert the object beside the current selection rather than replacing the current selection. If this bit is not set, the object being inserted replaces the current selection.
OLEMISC_STATIC
This object is a static object, which is an object that contains only a presentation; it contains no native data. See OleCreateStaticFromData.
OLEMISC_CANTLINKINSIDE
This object cannot be the link source that when bound to activates (runs) the object. If the object is selected and copied to the clipboard, the object's container can offer a link in a clipboard data transfer that, when bound, must connect to the outside of the object. The user would see the object selected in its container, not open for editing. Rather than doing this, the container can simply refuse to offer a link source when transferring objects with this bit set. Examples of objects that have this bit set include OLE1 objects, static objects, and links.
OLEMISC_CANLINKBYOLE1
This object can be linked to by OLE 1 containers. This bit is used in the dwStatus member of the OBJECTDESCRIPTOR structure transferred with the Object and Link Source Descriptor formats. An object can be linked to by OLE 1 containers if it is an untitled document, a file, or a selection of data within a file. Embedded objects or pseudo-objects that are contained within an embedded object cannot be linked to by OLE 1 containers (i.e., OLE 1 containers cannot link to link sources that, when bound, require more than one object server to be run.
OLEMISC_ISLINKOBJECT
This object is a link object. This bit is significant to OLE 1 and is set by the OLE 2 link object; object applications have no need to set this bit.
OLEMISC_INSIDEOUT
This object is capable of activating in-place, without requiring installation of menus and toolbars to run. Several such objects can be active concurrently. Some containers, such as forms, may choose to activate such objects automatically.
OLEMISC_ACTIVATEWHENVISIBLE
This bit is set only when OLEMISC_INSIDEOUT is set, and indicates that this object prefers to be activated whenever it is visible. Some containers may always ignore this hint.
OLEMISC_RENDERINGISDEVICEINDEPENDENT
This object does not pay any attention to target devices. Its presention data will be the same in all cases.
OLEMISC_INVISIBLEATRUNTIME
This value is used with controls. It indicates that the control has no run-time user interface, but that it should be visible at design time. For example, a timer control that fires a specific event periodically would not show itself at run time, but it needs a design-time user interface so a form designer can set the event period and other properties.
OLEMISC_ALWAYSRUN
This value is used with controls. It tells the container that this control always wants to be running. As a result, the container should call OleRun when loading or creating the object.
OLEMISC_ACTSLIKEBUTTON
This value is used with controls. It indicates that the control is buttonlike in that it understands and obeys the container's DisplayAsDefault ambient property.
OLEMISC_ACTSLIKELABEL
This value is used with controls. It marks the control as a label for whatever control comes after it in the form's ordering. Pressing a mnemonic key for a label control activates the control after it.
OLEMISC_NOUIACTIVATE
This value is used with controls. It indicates that the control has no UI active state, meaning that it requires no in-place tools, no shared menu, and no accelerators. It also means that the control never needs the focus.
OLEMISC_ALIGNABLE
This value is used with controls. It indicates that the control understands how to align itself within its display rectangle, according to alignment properties such as left, center, and right.
OLEMISC_SIMPLEFRAME
This value is used with controls. It indicates that the control is a simple grouping of other controls and does little more than pass Windows messages to the control container managing the form. Controls of this sort require the implementation of ISimpleFrameSite on the container's site.
OLEMISC_SETCLIENTSITEFIRST
This value is used with controls. It indicates that the control wants to use IOleObject::SetClientSite as its initialization function, even before a call such as IPersistStreamInit::InitNew or IPersistStorage::InitNew. This allows the control to access a container's ambient properties before loading information from persistent storage. Note that the current implementations of OleCreate, OleCreateFromData, OleCreateFromFile, OleLoad, and the default handler do not understand this value. Control containers that wish to honor this value must currently implement their own versions of these functions in order to establish the correct initialization sequence for the control.
OLEMISC_IMEMODE
Obsolete. A control that works with an Input Method Editor (IME) system component can control the state of the IME through the IMEMode property rather than using this value in the OLEMISC enumeration. You can use an IME component to enter information in Asian character sets with a regular keyboard. A Japanese IME, for example, allows you to type a word such as "sushi," on a regular keyboard and when you hit the spacebar, the IME component converts that word to appropriate kanji or proposes possible choices. The OLEMISC_IMEMODE value was previously used to mark a control as capable of controlling an IME mode system component.
OLEMISC_IGNOREACTIVATEWHENVISIBLE
For new ActiveX controls to work in an older container, the control may need to have the OLEMISC_ACTIVATEWHENVISIBLE value set. However, in a newer container that understands and uses IPointerInactive, the control does not wish to be in-place activated when it becomes visible. To allow the control to work in both kinds of containers, the control can set this value. Then, the container ignores OLEMISC_ACTIVATEWHENVISIBLE and does not in-place activate the control when it becomes visible.
OLEMISC_WANTSTOMENUMERGE
A control that can merge its menu with its container sets this value.
OLEMISC_SUPPORTSMULTILEVELUNDO
A control that supports multi-level undo sets this value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetMiscStatus, OBJECTDESCRIPTOR


OLERENDER

The OLERENDER enumeration constants are used in the various object creation functions to indicate the type of caching requested for the newly created object.

typedef enum tagOLERENDER
{
    OLERENDER_NONE      = 0;
    OLERENDER_DRAW      = 1;
    OLERENDER_FORMAT    = 2;
    OLERENDER_ASIS      = 3
} OLERENDER;

Elements

OLERENDER_NONE
The client is not requesting any locally cached drawing or data retrieval capabilities in the object. The pFormatEtc parameter of the calls is ignored when this value is specified for the renderopts parameter.
OLERENDER_DRAW
The client will draw the content of the object on the screen (a NULL target device) using IViewObject:Draw. The object itself determines the data formats that need to be cached. With this render option, only the ptd and dwAspect members of pFormatEtc are significant, since the object may cache things differently depending on the parameter values. However, pFormatEtc can legally be NULL here, in which case the object is to assume the display target device and the DVASPECT_CONTENT aspect.
OLERENDER_FORMAT
The client will pull one format from the object using IDataObject::GetData (). The format of the data to be cached is passed in pFormatEtc, which may not in this case be NULL.
OLERENDER_ASIS
The client is not requesting any locally cached drawing or data retrieval capabilities in the object. pFormatEtc is ignored for this option. The difference between this and the OLERENDER_FORMAT value is important in such functions as OleCreateFromData () and OleCreateLinkFromData ().

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

OleCreate, OleCreateFromData, OleCreateFromFile, OleCreateLink, OleCreateLinkFromData, OleCreateLinkToFile, OleCreateStaticFromData


OLEUIPASTEFLAG

This enumeration is used to indicate the user options that are available to the user when pasting this format, and within which group or list of choices (Paste, Paste Link, etc.) this entry is to be available. OLEUIPASTEFLAG is used by the OLEUIPASTEENTRY structure.

typedef enum tagOLEUIPASTEFLAG
{
    OLEUIPASTE_ENABLEICON    = 2048,
    OLEUIPASTE_PASTEONLY     = 0,
    OLEUIPASTE_PASTE         = 512,
    OLEUIPASTE_LINKANYTYPE   = 1024,
    OLEUIPASTE_LINKTYPE1     = 1,
    OLEUIPASTE_LINKTYPE2     = 2,
    OLEUIPASTE_LINKTYPE3     = 4,
    OLEUIPASTE_LINKTYPE4     = 8,
    OLEUIPASTE_LINKTYPE5     = 16,
    OLEUIPASTE_LINKTYPE6     = 32,
    OLEUIPASTE_LINKTYPE7     = 64,
    OLEUIPASTE_LINKTYPE8     = 128
} OLEUIPASTEFLAG;

Elements

OLEUIPASTE_ENABLEICON
If the container does not specify this flag for the entry in the OLEUIPASTEENTRY array passed as input to OleUIPasteSpecial, the DisplayAsIcon button will be unchecked and disabled when the user selects the format that corresponds to the entry.
OLEUIPASTE_PASTEONLY
The entry in the OLEUIPASTEENTRY array is valid for pasting only.
OLEUIPASTE_PASTE
The entry in the OLEUIPASTEENTRY array is valid for pasting. It may also be valid for linking if any of the following linking flags are specified. If it is valid for linking, then the following flags indicate which link types are acceptable by OR'ing together the appropriate OLEUIPASTE_LINKTYPE<#> values. These values correspond as follows to the array of link types passed to OleUIPasteSpecial:

OLEUIPASTE_LINKTYPE1=arrLinkTypes[0]
OLEUIPASTE_LINKTYPE2=arrLinkTypes[1]
OLEUIPASTE_LINKTYPE3=arrLinkTypes[2]
OLEUIPASTE_LINKTYPE4=arrLinkTypes[3]
OLEUIPASTE_LINKTYPE5=arrLinkTypes[4]
OLEUIPASTE_LINKTYPE6=arrLinkTypes[5]
OLEUIPASTE_LINKTYPE7=arrLinkTypes[6]
OLEUIPASTE_LINKTYPE8=arrLinkTypes[7]

where,

UINT arrLinkTypes[8] is an array of registered clipboard formats for linking. A maximum of 8 link types is allowed.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oledlg.h.


OLEUPDATE

The OLEUPDATE enumeration constants are used to indicate whether the linked object updates the cached data for the linked object automatically or only when the container calls either the IOleObject::Update or IOleLink::Update methods. The constants are used in the IOleLink interface.

typedef enum tagOLEUPDATE
{
    OLEUPDATE_ALWAYS    = 1,
    OLEUPDATE_ONCALL    = 3
} OLEUPDATE;
typedef OLEUPDATE *LPOLEUPDATE;

Elements

OLEUPDATE_ALWAYS
Update the link object whenever possible, this option corresponds to the Automatic update option in the Links dialog box.
OLEUPDATE_ONCALL
Update the link object only when IOleObject::Update or IOleLink::Update is called, this option corresponds to the Manual update option in the Links dialog box.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleLink::SetUpdateOptions, IOleLink::GetUpdateOptions


OLEVERBATTRIB

The OLEVERBATTRIB enumeration constants are used in the OLEVERB structure to describe the attributes of a specified verb for an object. Values are used in the enumerator (which supports the IEnumOLEVERB interface) that is created by a call to IOleObject::EnumVerbs.

typedef enum tagOLEVERBATTRIB
{
    OLEVERBATTRIB_NEVERDIRTIES       = 1,
    OLEVERBATTRIB_ONCONTAINERMENU    = 2
} OLEVERBATTRIB;

Elements

OLEVERBATTRIB_NEVERDIRTIES
Executing this verb will not cause the object to become dirty and is therefore in need of saving to persistent storage.
OLEVERBATTRIB_ONCONTAINERMENU
Indicates a verb that should appear in the container's menu of verbs for this object. OLEIVERB_HIDE, OLEIVERB_SHOW, and OLEIVERB_OPEN never have this value set.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::EnumVerbs, IEnumOLEVERB, OLEVERB


OLEWHICHMK

The OLEWHICHMK enumeration constants indicate which part of an object's moniker is being set or retrieved. These constants are used in the IOleObject and IOleClientSite interfaces.

typedef enum tagOLEWHICHMK
{
    OLEWHICHMK_CONTAINER    = 1,
    OLEWHICHMK_OBJREL       = 2,
    OLEWHICHMK_OBJFULL      = 3
} OLEWHICHMK;

Elements

OLEWHICHMK_CONTAINER
The moniker of the object's container. Typically, this is a file moniker. This moniker is not persistently stored inside the object, since the container can be renamed even while the object is not loaded.
OLEWHICHMK_OBJREL
The moniker of the object relative to its container. Typically, this is an item moniker, and it is part of the persistent state of the object. If this moniker is composed on to the end of the container's moniker, the resulting moniker is the full moniker of the object.
OLEWHICHMK_OBJFULL
The full moniker of the object. Binding to this moniker results in a connection to the object. This moniker is not persistently stored inside the object, since the container can be renamed even while the object is not loaded.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleClientSite::GetMoniker, IOleObject::GetMoniker, IOleObject::SetMoniker


TYMED

The TYMED enumeration values indicate the type of storage medium being used in a data transfer. They are used in the STGMEDIUM or FORMATETC structures.

typedef [transmit_as(long)] enum tagTYMED
{
    TYMED_HGLOBAL     = 1,
    TYMED_FILE        = 2,
    TYMED_ISTREAM     = 4,
    TYMED_ISTORAGE    = 8,
    TYMED_GDI         = 16,
    TYMED_MFPICT      = 32,
    TYMED_ENHMF       = 64,
    TYMED_NULL        = 0
} TYMED;

Elements

TYMED_HGLOBAL
The storage medium is a global memory handle (HGLOBAL). Allocate the global handle with the GMEM_SHARE flag. If the STGMEDIUM punkForRelease member is NULL, the destination process should use GlobalFree to release the memory.
TYMED_FILE
The storage medium is a disk file identified by a path. If the STGMEDIUM punkForRelease member is NULL, the destination process should use OpenFile to delete the file.
TYMED_ISTREAM
The storage medium is a stream object identified by an IStream pointer. Use ISequentialStream::Read to read the data. If the STGMEDIUM punkForRelease member is not NULL, the destination process should use IStream::Release to release the stream component.
TYMED_ISTORAGE
The storage medium is a storage component identified by an IStorage pointer. The data is in the streams and storages contained by this IStorage instance. If the STGMEDIUM punkForRelease member is not NULL, the destination process should use IStorage::Release to release the storage component.
TYMED_GDI
The storage medium is a GDI component (HBITMAP). If the STGMEDIUM punkForRelease member is NULL, the destination process should use DeleteObject to delete the bitmap.
TYMED_MFPICT
The storage medium is a metafile (HMETAFILE). Use the Windows or WIN32 functions to access the metafile's data. If the STGMEDIUM punkForRelease member is NULL, the destination process should use DeleteMetaFile to delete the bitmap.
TYMED_ENHMF
The storage medium is an enhanced metafile. If the STGMEDIUM punkForRelease member is NULL, the destination process should use DeleteEnhMetaFile to delete the bitmap.
TYMED_NULL
No data is being passed.

Remarks

During data transfer operations, a storage medium is specified. This medium must be released after the data transfer operation. The provider of the medium indicates its choice of ownership scenarios in the value it provides in the STGMEDIUM structure. A NULL value for the IUNKNOWN field indicates that the receiving body of code owns and can free the medium. A non-NULL pointer specifies that ReleaseStgMedium can always be called to free the medium.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in objidl.h.

See Also

FORMATETC, IAdviseSink, IDataObject, IOleCache, ReleaseStgMedium, STGMEDIUM


UASFLAGS

The UASFLAGS enumeration value supplies information about the parent undo unit. It is used in IOleParentUndoUnit::GetParentState.

typedef enum tagUASFLAGS
{
    UAS_NORMAL          = 0,
    UAS_BLOCKED         = 0x1,
    UAS_NOPARENTENABLE  = 0x2,
    UAS_MASK            = 0x3,
} UASFLAGS;

Elements

UAS_NORMAL
The currently open parent undo unit is in a normal, unblocked state and can accept any new units added through calls to its Open or Add methods.
UAS_BLOCKED
The currently open undo unit is blocked and will reject any undo units added through calls to its Open or Add methods. The caller need not create any new units since they will just be rejected.
UAS_NOPARENTENABLE
The currently open undo unit will accept new units, but the caller should act like there is no currently open unit. This means that if the new unit being created requires a parent, then this parent does not satisfy that requirement and the undo stack should be cleared.
UAS_MASK
When checking for a normal state, use this value to mask unused bits in the pdwState parameter to the GetParentState method for future compatibility. For example:
    fNormal = ((pdwState & UAS_MASK) == UAS_NORMAL)

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleParentUndoUnit::GetParentState


USERCLASSTYPE

The USERCLASSTYPE enumeration constants indicate the different variants of the display name associated with a class of objects. They are used in the IOleObject::GetUserType method and the OleRegGetUserType function.

typedef enum tagUSERCLASSTYPE
{
    USERCLASSTYPE_FULL       = 1,
    USERCLASSTYPE_SHORT      = 2,
    USERCLASSTYPE_APPNAME    = 3,
} USERCLASSTYPE;

Elements

USERCLASSTYPE_FULL
The full type name of the class.
USERCLASSTYPE_SHORT
A short name (maximum of 15 characters) that is used for popup menus and the Links dialog box.
USERCLASSTYPE_APPNAME
The name of the application servicing the class and is used in the Result text in dialog boxes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in oleidl.h.

See Also

IOleObject::GetUserType, OleRegGetUserType


VIEWSTATUS

The VIEWSTATUS enumeration is used in IViewObjectEx::GetViewStatus to specify the opacity of the object and the drawing aspects supported by the object.

typedef enum tagVIEWSTATUS
{
    VIEWSTATUS_OPAQUE                 = 1,
    VIEWSTATUS_SOLIDBKGND             = 2,
    VIEWSTATUS_DVASPECTOPAQUE         = 4,
    VIEWSTATUS_DVASPECTTRANSPARENT    = 8
} VIEWSTATUS;

Elements

VIEWSTATUS_OPAQUE
The object is completely opaque. So, for any aspect, it promises to draw the entire rectangle passed to the IViewObject::Draw method. If this value is not set, the object contains transparent parts. If it also support DVASPECT_TRANSPARENT, then this aspect may be used to draw the transparent parts only.

This bit applies only to CONTENT related aspects and not to DVASPECT_ICON or DVASPECT_DOCPRINT.

VIEWSTATUS_SOLIDBKGND
The object has a solid background (consisting in a solid color, not a brush pattern). This bit is meaningful only if VIEWSTATUS_OPAQUE is set.

This bit applies only to CONTENT related aspects and not to DVASPECT_ICON or DVASPECT_DOCPRINT.

VIEWSTATUS_DVASPECTOPAQUE
Object supports DVASPECT_OPAQUE. All IViewObjectEx methods taking a drawing aspect as a parameter can be called with this aspect.
VIEWSTATUS_DVASPECTTRANSPARENT
The object supports DVASPECT_TRANSPARENT. All IViewObjectEx methods taking a drawing aspect as a parameter can be called with this aspect.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IViewObjectEx::GetViewStatus


ActiveX Controls

ActiveX controls technology rests on a foundation consisting of COM, connectable objects, compound documents, property pages, OLE automation, object persistence, and system-provided font and picture objects. As summarized below, each of these core technologies plays a role in controls:

COM
A control is essentially a COM object that exposes the IUnknown interface, through which clients can obtain pointers to its other interfaces. Controls can support licensing through IClassFactory2 and self-registration. See The Component Object Model chapter for more information on COM, licensing, and self-registration.
Connectable objects
Controls can support outgoing interfaces through connectable objects so that the control can communicate with its client. For example, an outgoing interface can trigger an action in the client, can notify the client of some change in the control, or can request permission from the client before the control takes some action. See Events in COM and Connectable Objects for more information on how connectable objects work.
Uniform data transfer
Controls can support being dragged and dropped within a container with help from their container. See IOleInPlaceObjectWindowless::GetDropTarget for more information on drag and drop.
Compound documents
A control can be an in-place active object that can be embedded in a containing client. An end-user activates the control to initiate an action in the container application. See the Compound Documents chapter for more information on in-place activation and other compound document interfaces.
Property pages
Controls can provide property pages so end users can view and change the control’s properties. See the Property Pages and Property Sheets chapter for more information on how property pages work.
OLE automation
Controls can provide programmability through OLE automation so clients can take advantage of the control’s features through a programming language supplied by the client. See the OLE Automation section for more information on OLE automation.
Persistent storage
A control can implement one or more of several persistence interfaces to support persistence of its state. The control implementer must decide what kinds of persistence are most important and implement the appropriate persistence interfaces. The client decides which interface it prefers to use. See The Component Object Model chapter for more information on all the persistence interfaces.
Font and picture objects
Controls can use these system provided objects to provide a visual representation of themselves within the client. The font object implements several interfaces, including IFont and IFontDisp. A font object can be created with OleCreateFontIndirect. The picture object also implements several interfaces, including IPicture and IPictureDisp. A picture object can be created using OleCreatePictureIndirect and can loaded from a stream with OleLoadPicture. The standard font and picture objects are described in this chapter.

It is important to understand that these features can be used in any OLE object. One does not need to implement a control in order to use these features. Also, the only required interface on a control is IUnknown. The control optionally supports other interfaces based on the need to support the related features.

In addition to these features, the following interfaces and API functions are specific to controls technology: IOleControl, IOleControlSite, ISimpleFrameSite, the API function OleTranslateColor. Also specific to controls are a set of standards for properties and methods that a control or a control container can support.

See the ActiveX Control and Control Container Guidelines appendix for more information on controls and their containers.

Notes For new systems (NT 4.0 and above), the system library OLEAUT32.DLL contains implementations of the API functions (OleCreatePropertyFrame, OleCreatePropertyFrameIndirect, OleCreateFontIndirect, OleCreatePictureIndirect, OleIconToPicture, OleLoadPicture, and OleTranslateColor).

In addition, OLEAUT32.DLL contains the implementations of the standard font and picture objects, as well as a type library for all the interfaces used with controls as well as the additional data structures and data types.

For older systems, the redistributable library, OLEPRO32.DLL, contains these implementations. Applications or components for older systems that use these API functions can link with the import library, OLEPRO32.LIB.


ActiveX Controls Architecture

As noted above, ActiveX controls technology builds on a foundation of many lower-level objects and interfaces in OLE. The exact interfaces available on a control vary with its capabilities. This section takes a closer look at the capabilities a control might provide.

Controls are used to provide the building blocks for creating user interfaces in applications. For example, a button that initiates some action in the container application when it is clicked is a simple control. The following aspects are involved in providing these user interface building blocks:

  • A control can be embedded within its container client to support some user interface activity within the client. Thus, a control needs to provide a visual representation of itself when it is embedded within the container and needs to provide a way to save its state, for example, its property values and its position within its container. The client must support being a container with objects embedded in it.
  • By activating the control using a keyboard or mouse, the end user initiates some action in the client application. Thus, a control must respond to keyboard activity and must be able to communicate with its client so it can notify its container of its activities and trigger events in the client.
  • The client also typically provides a programming language through which the end user can initiate actions provided by the control’s properties and methods. Thus, a control must support automation and some set of design-time versus run-time features as well.

As a result of its role in providing user interface building blocks, a control typically supports features in the following areas using OLE technologies as indicated:

Properties and methods
Like any OLE object, a control can provide much of its functionality through a set of incoming interfaces with properties and methods. The container can supply additional ambient properties, and it can support extending the control’s properties through aggregation. These features rest on OLE automation, property pages, connectable objects, and ActiveX control technologies.
Events
In addition to providing properties and methods, an ActiveX control can also provide outgoing interfaces to notify its client of events. The client must support handling of these events. These features use OLE automation and connectable objects.
Visual representation
A control can support positioning and displaying itself within its container. The container positions the control and determines its size. These features use compound document technology, including OLE drag and drop technology.
Keyboard handling
A control can respond to keyboard accelerators so the end-user can initiate actions performed by the control. The container manages keyboard activity for all its embedded controls. These features use control and compound document technologies.
Persistence
A control can save its state. The client manages the persistence of its embedded controls. These features use structured storage and object persistence technologies.
Registration and licensing
A control typically supports self-registration and creates a set of registry entries when it is instantiated. A control can also be licensed to prevent unauthorized use.

Most of these features involve both the control and its client container.

See the following topics for a description of design considerations in each of these areas and for a description of how the OLE technologies are used in controls to support these areas of features:

  • ActiveX Controls Interfaces
  • Properties and Methods
  • Control Events
  • Visual Representation
  • Keyboard Handling for Controls
  • Persistence
  • Registration and Licensing


ActiveX Controls Interfaces

In addition to other mechanisms for communicating between the control and its client, ActiveX controls technology specifies the IOleControl and IOleControlSite interfaces for client-control communication. There is also the ISimpleFrameSite interface for simple control containers.

These three interfaces are, however, specific to controls are not generally useful outside the context of controls. These interfaces are defined as follows:

interface IOleControl : IUnknown
    {
    HRESULT GetControlInfo([out] CONTROLINFO *pCI);
    HRESULT OnMnemonic([in] LPMSG pMsg);
    HRESULT OnAmbientPropertyChange([in] DISPID dispID);
    HRESULT FreezeEvents([in] BOOL bFreeze);
    }

interface IOleControlSite : IUnknown
    {
    HRESULT OnControlInfoChanged(void);
    HRESULT LockInPlaceActive([in] BOOL fLock);
    HRESULT GetExtendedControl([out] IDispatch **ppDisp);
    HRESULT TransformCoords([in-out] POINTL *pptlHimetric
        , [in-out] POINTF *pptfContainer, [in] DWORD dwFlags);
    HRESULT TranslateAccelerator([in] LPMSG pMsg
        , [in] DWORD grfModifiers);
    HRESULT OnFocus([in] BOOL fGotFocus);
    HRESULT ShowPropertyFrame(void);
    }

interface ISimpleFrameSite : IUnknown
    {
    HRESULT PreMessageFilter([in] HWND hWnd, [in] UINT msg
        , [in] WPARAM wp, [in] LPARAM lp, [out] LRESULT *plResult
        , [out] DWORD *pdwCookie);
    HRESULT PostMessageFilter([in] HWND hWnd, [in] UINT msg
        , [in] WPARAM wp, [in] LPARAM lp, [out] LRESULT *plResult
        , [in] DWORD dwCookie);
    }

Some controls, like a group box, are merely a simple container of other controls. In such cases, the simple control, called a simple frame, doesn’t have to implement all the container requirements. It can delegate most of the interface calls from its contained controls to the container that manages the simple frame. Besides interface calls, the simple frame also has to deal with Windows messages that potentially come from controls within it. For this reason, a container supplies ISimpleFrameSite to allow such simple frame controls to pass messages up to the contain. PreMessageFilter gives the container first crack at the message; PostMessageFilter is called after the simple frame has process the message itself.

IOleControl and IOleControlSite are described throughout the following sections.


Properties and Methods

Like any OLE object, a control provides much of its functionality through a set of incoming interfaces with properties and methods.

A control exposes properties and methods through OLE automation so that containers can access them under the control of a container-supplied programming language.

To support access to properties through a user interface, a control provides property pages, support for OLEIVERB_PROPERTIES, per property browsing, and data binding through property change notfications.

  • Through property pages a control can display its properties, independent of its container, if necessary.
  • By supporting OLEIVERB_PROPERTIES, the Properties item is displayed on the container’s menu. Then, the end user can select the Properties item to view the control’s property pages and modify the properties.
  • Per property browsing supports a container that can display the control’s properties as part of a larger property sheet that may include properties from several controls in the container.
  • Through property change notification, a control can notify a client that its properties have change, allowing the client to take any necessary actions as a result.

See the Property Pages and Property Sheets chapter for more information on these features.


Control Properties

In addition to properties defined and implemented by the control itself, ActiveX controls technology also involves:

Ambient properties
These are exposed by the container through a control client site to provide environmental values that apply to all controls embedded in the container. For example, a container can provide a default background color or a default font that the control can use. Ambient properties are exposed through IDispatch implemented on a container’s site object.

The container calls the control’s IOleControl::OnAmbientPropertyChange method when any of its ambient properties change value. In response, a control may need to update its own internal or visual state in response. The container indicates which ambient property changed with the DISPID parameter or may pass DISPID_UNKNOWN to indicate that multiple ambient properties changed.

Extended properties
These are actually implemented by a container to wrap the controls it contains to provide container-managed properties that appear as if they were native control properties. The container can aggregate the control, adding the extended properties to supplement or override the control’s properties.

The aggregated object is called an extended control. To the container, the extended control appears as the control itself and extended properties appear to be exposed by the control. The container supports an extended control through its client site method IOleControlSite::GetExtendedControl. The GetExtendedControl method allows controls to navigate through the site to the extended control object provided for them by the container, if the container supports this feature.

A container can also choose to show property pages for its extended controls in addition to those pages that a control would normally specify through ISpecifyPropertyPages. Because of this, a control has to ask a container to show a property frame before the control attempts to do so itself. The control calls IOleControlSite::ShowPropertyFrame to do this. If the container implements this function then it shows the property frame itself; if the method returns an error then the control can show the property frame.


Standard Properties

OLE defines a set of standard DISPIDs for all three kinds of properties: control, ambient, and extended. The following tables list these standards for control properties, ambient properties, and extended properties.

Control Property | Type | Description
BackColor, ForeColor, FillColor, BorderColor | OLE_COLOR | The control’s color scheme
BackStyle, FillStyle, BorderStyle, BorderWidth, BorderVisible, DrawStyle, DrawWidth | short or long | Bits that define a control's visual behavior, such as being solid or transparent, having thick or thin borders, line styles, and so forth.
Font | IDispatch * | The font used in the control, which is an IDispatch pointer to a standard font object. See Standard Font Object below for more information.
Caption, Text | BSTR | Strings containing the control's label (the caption) or its textual contents (the text). Note that the caption does not necessarily name the control in the container. See the extended Name property in the following table.
Enabled | BOOL | Determines if the control is enabled or disabled. If disabled, the control is probably grayed.
Window | HWND | The window handle of the control, if it has one.
TabStop | BOOL | Determines if this control is a tab stop.
Ambient Property | Type | Description
BackColor, ForeColor | OLE_COLOR | Provides controls with the default background and foreground colors. Use by a control is optional.
Font | IDispatch * | A pointer to a standard font object that defines the default font for the form. Use by a control is optional. See Standard Font Object below for more information.
LocaleID | LCID | The language used in the container. Use by a control is recommended.
UserMode | BOOL | Describes whether the container is in a design mode (FALSE) or run-mode (TRUE), which a control should use to change its available functionality as necessary.
UIDead | BOOL | Describes whether the container is in a mode where controls should ignore user input. This applies irrespective of UserMode. A container might always set UIDead to TRUE in design mode, and may set it to true when it has hit a breakpoint or such during run mode. A control must pay attention to this property.
MessageReflect | BOOL | Specifies whether the container would like to receive Windows messages such as WM_CTLCOLOR, WM_DRAWITEM, WM_PARENTNOTIFY, and so on as events.
SupportsMnemonics | BOOL | Describes whether the container processes mnemonics or not. A control can do whatever it wants with this information, such as not underline characters it would normally use as a mnemonic.
ShowGrabHandles, ShowHatching | BOOL | Describes whether a control should show a hatch border or grab handles (in the hatch border) when in-place active. Controls must obey these properties, giving the container ultimate control over who actually draws these bits of user interface. A control container may want to draw its own instead of relying on each control, in which case these ambients will always be FALSE.
DisplayAsDefault | BOOL | The container will expose a TRUE for this property through whatever site contains what is marked as the default button when the button control should draw itself with a thicker default frame.
Extended Property | Type | Description
Name | BSTR | The container's name for the control.
Visible | BOOL | The control's visibility.
Parent | IDispatch * | The dispinterface of the form containing the control.
Default, Cancel | BOOL | Indicates if this control is the default or cancel button.

All of these standard properties have negative DISPID values, indicating their standard status.

Note that to avoid conflicts in the programmatic symbols for these DISPIDs, all ambient properties are given symbols in the form DISPID_AMBIENT_property as in DISPID_AMBIENT_FORECOLOR. All other symbols use DISPID_property as usual.

Some ambient properties, as well as control properties, involve colors. Controls deal with color in a slightly different way than normal Win32 APIs do. The OLE_COLOR type mentioned in the previous tables can refer to a Win32 standard COLORREF type, an index to a palette, a palette-relative index, or a system color index used with the Win32 API function GetSysColor. The OLE API function OleTranslateColor converts an OLE_COLOR type to a COLORREF type given a palette.


Standard Font Object

The standard ambient font property supplied by the container and the standard font property supplied by the control both provide a standard font object. That is, these standard fonts supply an IDispatch pointer to a standard font object.

The font object is a system-provided implementation of a set of interfaces on top of the underlying GDI font support. A font object is created through the API function OleCreateFontIndirect given a FONTDESC structure. The font object supports a number of read-write properties as well as custom methods through its interface IFont, and supports the same set of properties (but not the methods) through a dispinterface IFontDisp. The dispinterface is used for the font properties mentioned previously. The properties correspond to the GDI font attributes that are described in the LOGFONT structure.

The font object also supports the outgoing interface IPropertyNotifySink so that a client can determine when font properties change. Since the font object supports at least one outgoing interface, it also implements IConnectionPointContainer and one connection point for IPropertyNotifySink for this purpose.

The font object provides an hFont property that is a Windows font handle that conforms to the other attributes specified for the font. The font object delays realizing this GDI hFont when possible, so consecutively setting two properties on a font won’t cause an intermediate font to be realized. In addition, as an optimization, the standard font object maintains a cache of font handles. Two font objects in the same process that have identical properties will return the same font handle. The font object can remove fonts from this cache at will, which introduces special considerations for clients using this hFont property. See IFont::get_hFont for more details.

The font object also supports IPersistStream such that it can save and load itself from an instance of IStream. Any other object that uses a font object internally would normally save and load the font as part of the object’s own persistence handling.

In addition, the font object supports IDataObject through which it provides a property set containing typed values for each font property.


Standard Picture Object

The standard picture object provides a language-neutral abstraction for GDI images: bitmaps, icons, metafiles, and enhanced metafiles. As with the standard font object, the system provides a standard implementation of this object. Its primary interfaces are IPicture and IPictureDisp, the latter being derived from IDispatch to provide access to the picture’s properties through OLE automation. A picture object is created new with OleCreatePictureIndirect.

The picture object also supports the outgoing interface IPropertyNotifySink so that a client can determine when picture properties change. Accordingly, the picture object also supports IConnectionPointContainer and one connection point for IPropertyNotifySink.

The picture object also supports IPersistStream such that it can save and load itself from an instance of IStream. An object that uses a picture object internally would normally save and load the picture as part of the object’s own persistence handling. The API function OleLoadPicture simplifies the creation of a picture object based on stream contents.


Control Methods

There are three standard methods that controls can support: Refresh, DoClick, and AboutBox. All of these standard methods have negative DISPID values, indicating their standard status.


Control Events

In addition to providing properties and methods, a control also provides outgoing interfaces to notify its client of events. The client must support handling of these events. See Events in COM and Connectable Objects for more information on how connectable objects work.

A control can support different outgoing interfaces for different purposes. All outgoing interfaces are marked as source interfaces in the control’s type information, but only one is marked default to indicate that it is the primary outgoing interface.

A container can support one or more of the outgoing interfaces defined by a control. The control should be prepared to deal with containers that only provide support for some of their outgoing interfaces.

Controls support four kinds of events:

  • Request events. A control requests permission from its client to do something by calling a method in the outgoing interface, thus triggering a request event. The client signals the control through a boolean, out-parameter in the method that the control called. The client can thus prevent the control from performing the action.
  • Before events. A control notifies its client hat it is going to do something by calling a method in the outgoing interface, thus triggering a before event. The client does not have the opportunity to prevent the action, but it can take any necessary steps given the action that is about to occur.
  • After events. A control notifies its client that it has just done something by calling a method in the outgoing interface, thus triggering an after event. Again, the client cannot cancel this action, but it can take necessary steps given the action that has occurred.
  • Do events. A control triggers a do event to allow its client to override the control’s action and provide some alternative or supplementary actions. Usually, the method that a control calls for a do event has a number of parameters for negotiating with the client about the actions that will occur.

The following dispids are defined for standard events that controls can support: Click, DblClick, KeyDown, KeyPress, KeyUp, MouseMove, MouseUp, and Error. All of these standard events have negative DISPID values, indicating their standard status.

The IOleControl::FreezeEvents method, when called with TRUE, tells a control whether the container will bother handling events from the control until FreezeEvents is again called with FALSE. During this time control cannot depend on the container actually handling any events. If an event must be handled, the control should queue the event in order to fire it when FreezeEvents(FALSE) is called.


Visual Representation

A control supports positioning and displaying itself within its container through compound document technology and OLE drag and drop technology that involves both the control and its container. The control must be able to draw itself while the container manages the position of the control and its size.

Controls add to the basic functions provided by OLE documents. A control calls its client’s IOleClientSite::RequestNewObjectLayout method to tell its container that it wants to change its size. The client calls the control’s IOleObject::GetExtent to get the new size and calls IOleInPlaceObject::SetObjectRects to set the control to its new size.

Controls that support only IPersistStream[Init] do not support caching through IOleCache2 because the cache requires support for IPersistStorage. However, these controls should provide a way for the client to render the control through IDataObject::GetData so the client can optionally create and manage its own cache of the presentation data for the control.

Controls use the HIMETRIC type for its coordinates. However, different containers can use different coordinate systems. The container wants to receive coordinates in its own system, but the control does not necessarily know what coordinates its container is using. To communicate successfully, the control needs a way to convert values to its container’s coordinates. The container provides a site object with the IOleControlSite::TransformCoords method. The control calls this method on its container’s client site first to convert its coordinates into the appropriate coordinates for the container. Then, it can pass the converted coordinates to the container.

Controls can call IOleControlSite::LockInPlaceActive in the container’s site object to prevent the container from attempting to demote the control out of the in-place active state. Demoting the control in this way causes the control to be deactivated and its window destroyed, so if the control must maintain its window for a known duration it can call LockInPlaceActive to guarantee its state.


Keyboard Handling for Controls

A control responds to keyboard accelerators so the end-user can initiate actions performed by the control. The container manages keyboard activity for all its embedded controls. With compound documents, keyboard accelerators apply only to the currently active object. With controls, a mechanism has been added so that a control can respond to its keyboard mnemonics even if it is not currently UI-active.

The IOleControl::GetControlInfo and IOleControl::OnMnemonic methods and the IOleControlSite::OnControlInfoChanged method handle a control’s keyboard mnemonics. A CONTROLINFO structure describes a control’s mnemonic accelerators, and the flags passed back with it through the IOleControl::GetControlInfo method describe the control’s behavior with the Enter and Esc keys. When a control changes its mnemonics, it calls IOleControlSite::OnControlInfoChanged so the container can reload the structure if necessary.

When a control is UI active, it is also the control with the focus. As controls are activated and deactivated between the in-place active and the UI active states, the control calls IOleControlSite::OnFocus to tell the container of such changes.

In addition, when a control is UI active, it will have first chance to process any keystrokes. To give a container the opportunity to process the keystroke before the control, the control calls IOleControlSite::TranslateAccelerator. If the container does not handle the keystroke, the control then processes it.


Persistence

A control implements one or more of several persistence interfaces to support persistence of its state. For example, the IPersistStreamInit interface supports stream-based persistence of the control’s state. IPersistStreamInit is a replacement for IPersistStream and adds an initialization method, InitNew. The other methods are the same in both interfaces. IPersistStreamInit is not derived from IPersistStream; an object supports only one of the two interfaces based on whether it requires the ability to initialize new instances of itself.

Other persistence interfaces that a control can offer include: IPersistStorage, IPersistMemory, IPersistPropertyBag, IPersistMoniker. The control implementer must decide what kinds of persistence are most important and implement the appropriate persistence interfaces. The control implementer also decides what to save. For example, a control can save the current values of its properties or its location and size within its container. The client decides which interface it prefers to use.

Before loading a control from its persistent state, a client can check the OLEMISC_SETCLIENTSITEFIRST flag to determine if the control supports getting its client site and ambient properties before loading its persistent state. This optimization can save time when instantiating a control since the control is then free to ignore its persistent values rather than loading them only to have them overridden by ambient properties supplied by the client.

A control can also support saving and restoring its state in an OLE property set, a set of identifiers and values in a specified format. This feature can be useful with containers such as Visual Basic which saves its programs in a textual form. A control that wishes to support this feature implements IDataObject::GetData and IDataObject::SetData to pass its property values to and from the container, respectively. It is the container’s job to convert this information to text and save it. The identifiers used by the control correspond to the control’s property names and the values. See the OLE CDK for the definition of this property set.


Registration and Licensing

A control is usually provided as an in-process server (.DLL), although it can also be a local or remote server (.EXE).

A control typically supports self-registration and creates a set of registry entries when it is instantiated. A control can also be licensed to prevent unauthorized use. See The Component Object Model chapter for more information on self registration and licensing.


ActiveX Controls Registry Information

For 32-bit controls, there are a number of registry entries and flags that are used. Additionally, controls can support component categories to classify the features they provide.

Registry keys related to controls are italicized in the following list:

HKEY_CLASSES_ROOT
    CLSID
      {class id of control}
          ProgID = identifier
          InprocServer32 = <filename>.DLL
          DefaultIcon = <filename>.<ext>,resourceID
          ToolboxBitmap32 = <filename>.<ext>,resourceID
          Control
          verb
              n = &Properties...
          MiscStatus = 0
          TypeLib = {typelib ID for the object}
          Version = version number

The DefaultIcon entry is used to identify an icon to be displayed when the control is minimized to an icon. The Windows API function ExtractIcon is used to get the icon from the .DLL or .EXE file specified.

The ToolboxBitmap32 entry identifies the module name and resource identifier for a 16*15 bitmap to use for the face of a toolbar or toolbox button. The standard Windows icon size is too large to be used for this purpose. This entry specifically supports control containers that have a design mode in which one selects controls and places them on a form being designed. For example, in Visual Basic, the control’s icon is displayed in the Visual Basic toolbox during design mode.

The Control entry marks an object as a control. This entry is often used by containers to fill in dialog boxes. The container uses this sub-key to determine whether to include an object in a dialog box that displays controls.

The Insertable sub-key can also be used with controls, depending on whether the object can act only as an in-place embedded object with no special control features. Objects marked with Insertable appear in the Insert Object dialog box of their container. The Insertable entry generally means that the control has been tested with non-control containers.

Both the Insertable and the Control sub-keys are optional. A control can omit the Insertable sub-key if it not designed to work with older containers that do not understand controls. A control can omit the Control key if it is only designed to work with a specific container and thus does not wish to be inserted in other containers.

Controls should have a Properties verb, OLEIVERB_PROPERTIES, along with any other verbs they support. The Properties verb, as well as the standard verb OLEIVERB_PRIMARY, instructs the control to display its property sheet. The Properties verb is displayed as the Properties item on the container’s menu when the control is active. This way, the control can display its own property page allowing some useful functionality to the end user, even if the container does not handle controls.

A control defines the MiscStatus key to describe itself to potential containers. The bits take on the values from OLEMISC, and controls add several values to this enumeration. See the OLEMISC enumeration values for more information. The client can obtain this information by calling IOleObject::GetMiscStatus without having to instantiate the control first.

Finally, Version describes the version of the control which should match the version of the type library associated with this control.

Also in the type information for a control, the attribute control marks a coclass entry as describing a control.


Property Pages and Property Sheets

OLE property pages enable an object to display its properties in a tabbed dialog box known as a property sheet. An end user can then view and change the object’s properties. An object can display its property pages independent of its client, or the client can manage the display of property pages from a number of contained objects in a single property sheet. Property pages also provide a means for notifying a client of changes in an object’s properties.

Any object that wishes to provide a user interface for its changing properties can use this technology.


Property Sheets and Property Pages

An object’s properties are exposed to clients the same as methods through either COM interfaces or the object’s IDispatch implementation, allowing properties to be changed by programs calling these methods. The OLE technology of property pages provides the means to build a user interface for an object’s properties according to Windows user interface standards. Thus, the properties are exposed to end users. An object’s property sheet is a tabbed-dialog where each tab corresponds to a specific property page. The OLE model for working with property pages consists of these features:

  • Each property page is managed by an in-process object that implements either IPropertyPage or IPropertyPage2. Each page is identified with its own unique CLSID.
  • An object specifies its support for property pages by implementing ISpecifyPropertyPages. Through this interface the caller can obtain a list of CLSIDs identifying the specific property pages that the object supports. If the object specifies a property page CLSID, the object must be able to receive property changes from the property page.
  • Any piece of code (client or object) that wishes to display an object’s property sheet passes the object’s IUnknown pointer (or an array if multiple objects are to be affected) along with an array of page CLSIDs to OleCreatePropertyFrame or OleCreatePropertyFrameIndirect, which creates the tabbed-dialog box.
  • The property frame dialog instantiates a single instance of each property page, using CoCreateInstance on each CLSID. The property frame obtains at least an IPropertyPage pointer for each page. In addition, the frame creates a property page site object in itself for each page. Each site implements IPropertyPageSite and this pointer is passed to each page. The page then communicates with the site through this interface pointer.
  • Each page is also made aware of the object or objects for which it has been invoked; that is, the property frame passes the IUnknown pointers of the objects to each page. When instructed to apply changes to the objects, each page queries for the appropriate interface pointer and passes new property values to the objects in whatever way is desired. There are no stipulations on how such communication has to happen.
  • An object can also support per property browsing through the IPerPropertyBrowsing interface permitting the object to specify which property should receive initial focus when the property page is displayed and to specify string’s and values that can be displayed by the client in its own user interface.

These features are illustrated in the following diagram:

These interfaces are defined as follows:

interface ISpecifyPropertyPages : IUnknown
    {
    HRESULT GetPages([out] CAUUID *pPages);
    };

interface IPropertyPage : IUnknown
    {
    HRESULT SetPageSite([in] IPropertyPageSite *pPageSite);
    HRESULT Activate([in] HWND hWndParent, [in] LPCRECT prc
        , [in] BOOL bModal);
    HRESULT Deactivate(void);
    HRESULT GetPageInfo([out] PROPPAGEINFO *pPageInfo);
    HRESULT SetObjects([in] ULONG cObjects
        , [in, max_is(cObjects)] IUnknown **ppunk);
    HRESULT Show([in] UINT nCmdShow);
    HRESULT Move([in] LPCRECT prc);
    HRESULT IsPageDirty(void);
    HRESULT Apply(void);
    HRESULT Help([in] LPCOLESTR pszHelpDir);
    HRESULT TranslateAccelerator([in] LPMSG pMsg);
    }

interface IPropertyPageSite : IUnknown
    {
    HRESULT OnStatusChange([in] DWORD dwFlags);
    HRESULT GetLocaleID([out] LCID *pLocaleID);
    HRESULT GetPageContainer([out] IUnknown **ppUnk);
    HRESULT TranslateAccelerator([in] LPMSG pMsg);
    }

The ISpecifyPropertyPages::GetPages method returns a counted array of UUID (GUID) values each of which describe the CLSID of a property page that the object would like displayed. Whoever invokes the property sheet with OleCreatePropertyFrame or OleCreatePropertyFrameIndirect passes this array to the API function. Note that if the caller wishes to display property pages for multiple objects, it must only pass the intersection of the CLSID lists of all the objects to these API functions. In other words, the caller must only invoke property pages that are common to all objects.

In addition, the caller passes the IUnknown pointers to the affected objects to the API functions as well. Both API functions create a property frame dialog and instantiate a page site with IPropertyPageSite for each page it will load. Through this interface a property page can:

  • retrieve the current language used in the property sheet through GetLocaleID,
  • ask the frame to process keystrokes through TranslateAccelerator,
  • notify the frame of changes in the page through OnStatusChange,
  • obtain an interface pointer for the frame itself through GetPageContainer, although there are no interfaces defined for the frame at this time for this function always returns E_NOTIMPL

The property frame instantiates each property page object and obtain each page’s IPropertyPage interface. Through this interface the frame informs the page of its page site (SetPageSite), retrieves page dimensions and strings (GetPageInfo), passes the interface pointers to the affected objects (SetObjects), tells the page when to create and destroy its controls (Activate and Deactivate), instructs the page to show or reposition itself (Show and Move), instructs the page to apply its current values to the affected objects (Apply), checks on the page’s dirty status (IsPageDirty), invokes help (Help), and passes keystrokes to the page (TranslateAccelerator).

An object can also support per-property browsing which provides:

  1. a way (through IPerPropertyBrowsing and IPropertyPage2) to specify which property on which property page should be given the initial focus when a property sheet is first displayed
  2. a way (through IPerPropertyBrowsing) for the object to specify predefined values and corresponding descriptive strings that could be displayed in a client’s own user interface for properties.

An object can choose to support (b) without supporting (a), such as when the object has no property sheet.

The IPropertyPage2 and IPerPropertyBrowsing interfaces are defined as follows:

interface IPerPropertyBrowsing : IUnknown
    {
    HRESULT GetDisplayString([in] DISPID dispID
        , [out] BSTR *pbstr);
    HRESULT MapPropertyToPage([in] DISPID dispID
        , [out] CLSID *pclsid);
    HRESULT GetPredefinedStrings([in] DISPID dispID
        , [out] CALPOLESTR *pcaStringsOut
        , [out] CADWORD *pcaCookiesOut);
    HRESULT GetPredefinedValue([in] DISPID dispID
        , [in] DWORD dwCookie, [out] VARIANT *pvarOut);
    }

interface IPropertyPage2 : IPropertyPage
    {
    HRESULT EditProperty([in] DISPID dispID);
    }

To specify its support for such capabilities, the object implements IPerPropertyBrowsing. Through this interface, the caller can request the information necessary to achieve the browsing, such as predefined strings (GetPredefinedStrings) and values (GetPredefinedValues) as well as a display string for a given property (GetDisplayString).

In addition, the client can obtain the CLSID of the property page that allows the user to edit a given property identified with a DISPID (MapPropertyToPage). The client then instructs the property frame to activate that page initially by passing the CLSID and the DISPID to OleCreatePropertyFrameIndirect. The frame activates that page first and passes the DISPID to the page through IPropertyPage2::EditProperty. The page then sets the focus to that property’s editing field. In this way, a client can jump from a property name in its own user interface to the property page that can manipulate that property.


Data Binding through IPropertyNotifySink

Objects that support properties, for example, through OLE Automation and the IDispatch interface, may wish to allow clients to be notified when certain properties change value. Such a property is called a bindable property because the notifications allow a client to synchronize its own display of the object’s current property values. In addition, the same objects may wish to allow a client to control when certain properties are allowed to change. Such properties are called request edit properties.

The IPropertyNotifySink is a standard notification interface that supports bindable and request-edit properties. IPropertyNotifySink is supported from an object with properties as an outgoing interface. That is, the interface itself is implemented by a client’s sink object, and the client connects the sink to the supporting object through the connection point mechanism described earlier. The IPropertyNotifySink is defined as follows:

interface IPropertyNotifySink : IUnknown
    {
    HRESULT OnChanged([in] DISPID dispID);
    HRESULT OnRequestEdit([in] DISPID dispID);
    }

When an object wishes to notify its connected sinks that a bindable property identified with a given DISPID has changed, it calls OnChanged. If an object changes multiple properties at once, it can pass DISPID_UNKNOWN to OnChanged in which case a client refreshes its cache of all property values of interest.

When a request edit property is about to change, an object can ask the client whether it will allow that change to occur. The object calls OnRequestEdit passing the DISPID of the property in question (or DISPID_UNKNOWN to identify all properties). The client’s sink returns S_OK to indicate that the change is allowed, or S_FALSE (or an error) to indicate that change is not allowed. When an object calls OnRequestEdit, it is required to obey the client’s wishes by following the exact semantics of S_OK and S_FALSE return values.

Note that OnRequestEdit cannot be used for data validation because at the time of the call, the new value of the property is not yet available. The notification can only be used to control a read-only state for a property.

Objects control which properties are bindable and request edit and mark such properties in the object’s type information. In the type information, the attribute bindable marks a property as supporting IPropertyNotifySink::OnChanged. The attribute requestedit marks a property as supporting IPropertyNotifySink::OnRequestEdit.

One property can support both behaviors in which case OnRequestEdit is called first, and only if change is allowed is OnChanged called.

The one exception to the behavior of such properties is that no notifications are sent as a result of an object’s initialization or loading procedures. At such times, it is assumed that all properties change and that all must be allowed to change. Notifications to this interface are therefore only meaningful in the context of a fully initialized/loaded object.

Two other attributes can be applied to properties in an object’s type information. The defaultbind attribute marks a bindable property as being the one that best represents the state of the object as a whole. The displaybind attribute marks a bindable property as suitable for display in a client’s own user interface.


ActiveX Control and Control Container Guidelines

The purpose of this chapter is to provide guidelines for implementing ActiveX controls and containers that will interoperate well with other controls and containers. This appendix defines the minimum set of interfaces, methods, and features that are required of ActiveX Controls and Containers to accomplish seamless and useful interoperability.


Overview of Control and Control Container Guidelines

An ActiveX Control is essentially a simple OLE object that supports the IUnknown interface. It will usually support a lot more interfaces in order to offer functionality, but all additional interfaces may be viewed as optional and as such, a control container should not rely on any additional interfaces being supported. By not specifying additional interfaces that a control must support, a control may efficiently target a particular area of functionality without having to support particular interfaces to qualify as a control. As always with OLE, whether in a control or a control container, it should never be assumed that an interface is available and standard return-checking conventions should always be followed. It is important for a control or control container to degrade gracefully and offer alternative functionality if an interface required is not available.

An ActiveX control container must be able to host a minimal ActiveX Control as specified in this appendix; it will also support a number of additional interfaces as specified in the Containers section of this appendix. There are a number of interfaces and methods that a container may optionally support, which are grouped into functional areas known as Component Categories. A container may support any combination of component categories, for example, a component category exists for Databinding and a container may or may not support the databinding functionality, depending on the market needs of the container. If a control needs databinding support from a container to function, then it will enter this requirement in the registry. This allows a control container to only offer for insertion those controls that it knows it can successfully host. It is important to note that Component Categories are specified as part of OLE and are not specific to ActiveX Controls, the controls architecture uses Component Categories to identify areas of functionality that an OLE component may support. Component categories are not cumulative or exclusive, so a control container can support one category without necessarily supporting another.

It is important for controls that require optional features, or features specific to a certain container to be clearly packaged and marketed with those requirements. Similarly containers that offer certain features or component categories must be marketed and packaged as offering those levels of support when hosting ActiveX controls. It is recommended that controls target and test with as many containers as possible and degrade gracefully to offer less or alternative functionality if interfaces or methods are not available. In a situation where a control cannot perform its designated job function without the support of a component category, then that category should be entered as a requirement in the registry in order to prevent the control being inserted in an inappropriate container.

These guidelines define those interfaces and methods that a control may expect a control container to support, although as always a control should check the return values when using QueryInterface or other methods to obtain pointers to these interfaces. A container should not expect a control to support anything more than the IUnknown interface, and these guidelines identify what interfaces a control may support and what the presence of a particular interface means.


Why are the ActiveX Control and Control Container Guidelines Important?

ActiveX Controls have become the primary architecture for developing programmable software components for use in a variety of different containers ranging from software development tools to end-user productivity tools. In order for a control to operate well in a variety of containers, the control must be able to assume some minimum level of functionality that it can rely on in all containers.

By following these guidelines, control and container developers make their controls and containers more reliable and interoperable, and ultimately, better and more usable components for building component-based solutions.

This appendix provides guidelines towards good interoperability. It is expected that new interfaces and component categories will develop over time, future versions of this appendix reflecting these changes will be made readily available through Microsoft. It is important to note that this appendix does not cover detailed semantics of the OLE interfaces; this is covered by the Platform SDK documentation.


What to do When an Interface You Need is Not Available

This section states some fundamental rules that apply to all OLE programming. OLE programs should use QueryInterface to acquire interface pointers, and must check the return value. OLE applications cannot safely assume that QueryInterface will succeed, this requirement applies to all OLE applications. If the requested interface is not available (i.e., QueryInterface returns E_NOINTERFACE), the control or container must degrade gracefully, even if that means that it cannot perform its designated job function.


What's New in the Control and Control Container Guidelines?

Platform SDK: COM

What’s New in the Control and Control Container Guidelines?

This release of the guidelines embraces the concept of Component Categories which are a part of the OLE specification. In previous versions of this document component categories were loosely referred to as function groups and were used to identify areas of functionality that a container may optionally support, for this version there has been a definition of how component categories work for ActiveX Controls and some fundamental categories are identified. The use of component categories allows the relaxing of some of the previous rules that identified interfaces as being mandatory, and allows greater flexibility for controls to efficiently target certain areas of functionality without having to provide superfluous additional support in order to qualify as a control. This edition of the guidelines also discusses what the presence or absence of an interface means and what to do in that situation.

The remainder of this appendix is divided into four sections. The first discusses guidelines for implementing controls, the second discusses guidelines for implementing control containers, the third discusses component categories, and the fourth discusses general guidelines, relevant to both control and control container developers.


Controls

An ActiveX control is really just another term for OLE object or more specifically, COM object. In other words, a control, at the very least, is some COM object that supports the IUnknown interface and is also self-registering. Through IUnknown::QueryInterface a container can manage the lifetime of the control as well as dynamically discover the full extent of a control’s functionality based on the available interfaces. This allows a control to implement as little functionality as it needs to, instead of supporting a large number of interfaces that actually don’t do anything. In short, this minimal requirement for nothing more than IUnknown allows any control to be as lightweight as it can.

In short, other than IUnknown and self-registration, there are no other requirements for a control. There are however conventions that should be followed about what the support of an interface means in terms of functionality provided to the container by the control. This section then describes what it means for a control to actually support an interface, as well as methods, properties, and events that a control should provide as a baseline if it has occasion to support methods, properties, and events.


Self Registration for Controls

ActiveX controls must support self-registration by implementing the DllRegisterServer and DllUnregisterServer functions. ActiveX controls must register all of the standard registry entries for embeddable objects and automation servers.

ActiveX Controls must use the component categories API to register themselves as a control and register the component categories that they require a host to support and any categories that the control implements, see the Component Categories section of this appendix. In addition an ActiveX Control may wish to register the control keyword in order to allow older control containers such as VB4 to host them.

ActiveX Controls should also register the ToolBoxBitmap32 registry key, although this is not mandatory.

The Insertable component category should only be registered if the control is suitable for use as a compound document object. It is important to note that a compound document object must support certain interfaces beyond the minimum IUnknown required for an ActiveX Control. Although an ActiveX Control may qualify as a compound document object, the control’s documentation should clearly state what behavior to expect under these circumstances.


What Support for an Interface Means

Besides the IUnknown interface, an ActiveX Control—or COM Object for that matter—expresses whatever optional functionality it supports through additional interfaces. This is to say that no other interfaces are required above IUnknown. To that end, the following table lists the interfaces that an ActiveX Control might support, and what it means to support that interface. Further details about the methods of these interfaces are given in a later section.

Interface | Comments/What it Means to Support the Interface
IOleObject | If the control requires communication with its client site for anything other than events (see IConnectionPointContainer), then IOleObject is a necessity. When implementing this interface, the control must also support the semantics of the following methods: SetHostNames, Close, EnumVerbs, Update, IsUpToDate, GetUserClassID, GetUserType, GetMiscStatus, and the Advise, Unadvise, and EnumAdvise methods that work in conjunction with a container’s IAdviseSink implementation. A control implementing IOleObject must be able to handle IAdviseSink if the container provides one; a container may not, in which case a control ensures, of course, that it does not attempt to call a non-existent sink.
IOleInPlaceObject | Expresses the control’s ability to be in-place activated and possibly UI activated. This interface means that the control has a user interface of some kind that can be activated, and IOleInPlaceActiveObject is supported as well. Required methods are GetWindow, InPlaceActivate, UIDeactivate, SetObjectRects, and ReactivateAndUndo. Support for this interface requires support for IOleObject.
IOleInPlaceActiveObject | An in-place capable object that supports IOleInPlaceObject must also provide this interface as well, though the control itself doesn’t necessarily implement the interface directly.
IOleControl Expresses the control’s ability and desire to deal with (a) mnemonics (GetControlInfo, OnMnemonic methods), (b) ambient properties (OnAmbientPropertyChange), and/or (c) events that the control requires the container to handle (FreezeEvents). Note that mnemonics are different than accelerators that are handled through IOleInPlaceActiveObject: mnemonics have associated UI and are active even when the control is not UI active. A control’s support for mnemonics means that the control also knows how to use the container’s IOleControlSite::OnControlInfoChanged method. Because this requires the control to know the container’s site, support for mnemonics also means support for IOleObject. In addition, knowledge of mnemonics requires in-place support and thus IOleInPlaceObject.

If a control uses any container-ambient properties, then it must also implement this interface to receive change notifications, as following the semantics of changes is required. Because ambient properties are only available through the container site’s IDispatch, ambient property support means that the control supports IOleObject (to get the site) as well as being able to generate IDispatch::Invoke calls.

The FreezeEvents method is necessary for controls that must know when a container is not going to handle an event—this is the only way for control to know this condition. If FreezeEvents is only necessary in isolation, such that other IOleControl methods are not implemented, then IOleControl can stand alone without IOleObject or IOleInPlaceObject.
IDataObject | Indicates that the control can supply at least (a) graphical renderings of the control (CF_METAFILEPICT is the minimum if the control has any visuals at all) and/or (b) property sets, if the control has any properties to provide. The methods GetData, QueryGetData, EnumFormatEtc, DAdvise, DUnadvise, and EnumDAdvise are required. Support for graphical formats other than CF_METAFILEPICT is optional.
IViewObject2 | Indicates that the control has some interesting visuals when it is not in-place active. If implemented, a control must support the methods Draw, GetAdvise, SetAdvise, and GetExtent.
IDispatch Indicates that the control has either (a) custom methods, or (b) custom properties that are both available via late-binding through IDispatch::Invoke. This also requires that the control provides type information through other IDispatch methods. A control may support multiple IDispatch implementations where only one is associated with IID_IDispatch—the others must have their own unique dispinterface identifiers.

A control is encouraged to supply dual interfaces for custom method and property access, but this is optional if methods and properties exist.
IConnectionPointContainer Indicates that a control supports at least one outgoing interface, such as events or property change notifications. All methods of this interface must be implemented if this interface is available at all, including EnumConnectionPoints which requires a separate object with IEnumConnectionPoints.

Support for IConnectionPointContainer means that the object also supports one or more related objects with IConnectionPoint that are available through IConnectionPointContainer methods. Each connection point object itself must implement the full IConnectionPoint interface, including EnumConnections, which requires another enumerator object with the IEnumConnections interface.
IProvideClassInfo[2] | Indicates that the object can provide its own coclass type information directly through IProvideClassInfo::GetClassInfo. If the control supports the later variation IProvideClassInfo2, then it also indicates its ability to provide its primary source IID through IProvideClassInfo2::GetGUID. All methods of this interface must be implemented.
ISpecifyPropertyPages | Indicates that the control has property pages that it can display such that a container can coordinate this control’s property pages with other control’s pages when property pages are to be shown for a multi-control selection. All methods of this interface must be implemented when support exists.
IPerPropertyBrowsing | Indicates the control’s ability to (a) provide a display string for a property, (b) provide pre-defined strings and values for its properties and/or (c) map a property dispID to a specific property page. Support for this interface means that support for properties through IDispatch as above is provided. If (c) is supported, then it also means that the object’s property pages mapped through IPerPropertyBrowsing::MapPropertyToPage themselves implement IPropertyPage2 as opposed to the basic IPropertyPage interface.
IPersistStream | See Persistence Interfaces section.
IPersistStreamInit | See Persistence Interfaces section.
IPersistMemory | See Persistence Interfaces section.
IPersistStorage | See Persistence Interfaces section.
IPersistMoniker | See Persistence Interfaces section.
IPersistPropertyBag | See Persistence Interfaces section.
IOleCache[2] | Indicates support for container caching of control visuals. A control generally obtains caching support itself through the OLE function CreateDataCache. Only controls with meaningful static content should choose to do this (although it is not required). If a control supports caching at all, it should simply aggregate the data cache and expose both IOleCache and IOleCache2 interfaces from the data cache. A control implementing IOleObject must be able to handle IAdviseSink if the container provides one; a container may not, in which case a control ensures, of course, that it does not attempt to call a non-existent sink.
IExternalConnection | Indicates that the control supports external links to itself; that is, the control is not marked with OLEMISC_CANTLINKINSIDE and supports IOleObject::SetMoniker and IOleObject::GetMoniker. A container will never query for this interface itself nor call it directly as calls are generated from inside OLE’s remoting layer.
IRunnableObject | Indicates that the control differentiates being loaded from being running, as some in-process objects do.


Persistence Interfaces

Objects that have a persistent state of any kind must implement at least one IPersist* interface, and preferably multiple interfaces, in order to provide the container with the most flexible choice of how it wishes to save a control’s state.

If a control has any persistent state whatsoever, it must, as a minimum, implement either IPersistStream or IPersistStreamInit (the two are mutually exclusive and shouldn’t be implemented together for the most part). The latter is used when a control wishes to know when it is created new as opposed to reloaded from an existing persistent state (IPersistStream does not have the created new capability). The existence of either interface indicates that the control can save and load its persistent state into a stream, that is, an instance of IStream.

Beyond these two stream-based interfaces, the IPersist* interfaces listed in the following table can be optionally provided in order to support persistence to locations other than an expandable IStream.

A set of component categories is identified to cover the support for persistency interfaces see the Component Categories section of this appendix.

Interface | Usage
IPersistMemory | The object can save and load its state into a fixed-length sequential byte array (in memory).
IPersistStorage | The object can save and load its state into an IStorage instance. Controls that wish to be marked Insertable as other compound document objects (for insertion into non-control aware containers) must support this interface.
IPersistPropertyBag | The object can save and load its state as individual properties written to IPropertyBag which the container implements. This is used for Save As Text functionality in some containers.
IPersistMoniker | The object can save and load its state to a location named by a moniker. The control calls IMoniker::BindToStorage to retrieve the storage interface it requires, such as IStorage, IStream, ILockBytes, IDataObject, etc.

While support for IPersistPropertyBag is optional, it is strongly recommended as an optimization for containers with Save As Text features, such as Visual Basic.

With the exception of IPersistStream[Init]::GetSizeMax and IPersistMemory::GetSizeMax, all methods of each interface must be fully implemented.


Optional Methods in Control Interfaces

Implementing an interface doesn’t necessarily mean implementing all methods of that interface to do anything more than return E_NOTIMPL or S_OK as appropriate. The following table identifies the methods of the interfaces listed in the What Support for an Interface Means section that a control may implement in this manner. Check with the SDK OLE Reference documentation for full syntax and valid return values from these methods. Any method not listed here must be fully implemented if the interface is supported.

Method | Comments
IOleControl |
GetControlInfo, OnMnemonic | Mandatory for controls with mnemonics.
OnAmbientPropertyChange | Mandatory for controls that use ambient properties.
FreezeEvents | See Event Freezing in the General Guidelines section.
IOleObject |
SetMoniker | Mandatory if the control is not marked with OLEMISC_CANTLINKINSIDE
GetMoniker | Mandatory if the control is not marked with OLEMISC_CANTLINKINSIDE
InitFromData | Optional
GetClipboardData | Optional
SetExtent | Mandatory only for DVASPECT_CONTENT
GetExtent | Mandatory only for DVASPECT_CONTENT
SetColorScheme | Optional
DoVerb | See Table Note 1.
IOleInPlaceObject |
ContextSensitiveHelp | Optional
ReactivateAndUndo | Optional
IOleInPlaceActiveObject |
ContextSensitiveHelp | Optional
IViewObject2 |
Freeze | Optional
Unfreeze | Optional
GetColorSet | Optional
IPersistStream[Init], IPersistMemory |
GetSizeMax | See Table Note 2.

Table Notes

  1. A control with property pages must support IOleObject::DoVerbs for the OLEIVERB_PROPERTIES and OLEIVERB_PRIMARY verbs. A control that can be active must support IOleObject::DoVerbs for the OLEIVERB_INPLACEACTIVATE verb. A control that can be UI active must also support IOleObject::DoVerbs for the OLEIVERB_UIACTIVATE verb.
  2. If a control supports IPersistStream[Init] and can return an accurate value, then it should do so.


Class Factory Options

An ActiveX Control, by virtue of being a COM object, must have associated server code that supports control creation through IClassFactory as a minimum.

It is optional, not required, that this class object also supports IClassFactory2 for licensing management. Only those vendors that are concerned about licensing need to support COM’s licensing mechanism. In other words, because IClassFactory2 is the only way to achieve COM-level licensing, this interface is required on the class object for those controls that wish to be licensed.


Properties

Although most controls do have properties, controls are not required to expose any properties and thus the control does not require IDispatch. If the control does have properties, there are no requirements for which properties a control must expose.


Methods (via IDispatch and Other dispinterfaces)

Although most controls do expose and support several methods, controls are not required to expose or support any methods and thus the control does not require IDispatch. If the control does have any methods, there are no requirements for which methods a control must expose.


Events in Controls

Although most controls do expose and fire several events, controls are not required to expose or fire any events and thus the control does not require IConnectionPointContainer. If the control does have any events, there are no requirements for which events a control must expose.


Property Pages

Support for property pages and per-property browsing is strongly recommended, but not required. If a control does implement property pages, then those pages should conform to one of the standard sizes: 250x62 or 250x110 dialog units (DLUs).


Ambient Properties for Controls

If a control supports any ambient properties at all, it must at least respect the values of the following ambient properties under the conditions stated in the following table using the standard dispids.

Ambient Property | Dispid | Comment/Conditions for Use
LocaleID | -705 | If Locale is significant to the control, e.g. for text output
UserMode | -709 | If the control behaves differently in user (design) mode and run mode
UIDead | -710 | If the control reacts to UI events, then it should honor this ambient property
ShowGrabHandles | -711 | If the control support in-place resizing of itself
ShowHatching | -712 | If the control support in-place activation and UI activation
DisplayAsDefault | -713 | Only if the control is marked OLEMISC_ACTSLIKEBUTTON (which means that support for keyboard mnemonics is provided, thus IOleControl::GetControlInfo and IOleControl::OnMnemonic must be implemented).

As described previously, use of ambients requires both IOleControl (for OnAmbientPropertyChange as a minimum) as well as IOleObject (for SetClientSite and GetClientSite).

The OLEMISC_SETCLIENTSITEFIRST bit may not necessarily be supported by a container. In these circumstances, a control must resort to default values for the ambient properties that it requires.


Using the Container's Functionality

Platform SDK: COM

Using the Container’s Functionality

The previous sections have described some of the necessary caller-side support that an ActiveX Control must have in order to access certain features of its container. The following table describes a control’s usage of container-side interfaces and when such usage would occur.

Interface | Container Object | Usage
IOleClientSite | Site | Controls that implement IOleObject call IOleClientSite methods as part of the standard OLE embedding protocol, specifically the methods SaveObject, ShowObject, OnShowWindow (only if a separate-window activation state is supported), RequestNewObjectLayout, and GetContainer (if communication with other controls is desired). The GetMoniker method is only used when the control can be linked to externally, that is, the control is not marked with OLEMISC_CANTLINKINSIDE.
IOleInPlaceSite | Site | Controls that have an in-place activate and possibly a UI active state will call IOleInPlaceSite methods (generally all of them with the exception of ContextSensitiveHelp) as part of the standard OLE in-place activation protocol.
IAdviseSink | Site | Control calls OnDataChange if the control supports IDataObject, OnViewChange if the control supports IViewObject2, and OnClose, OnSave, and OnRename if the control supports IOleObject.
IOleControlSite | Site | If supported, control calls OnControlInfoChanged when mnemonics change, LockInPlaceActive and TransformCoords if events are fired (the latter method is only used if coordinates are passed as event arguments), OnFocus and TranslateAccelerator if the control has a UI active state, and GetExtendedControl if the control wants to look at extended-control (container-owned) properties.
IDispatch (ambient properties) | Site | Used to access ambient properties.
IPropertyNotifySink | Varies | A control must generate OnChanged and OnRequestEdit for any control properties that are marked as [bindable] and [request], respectively.
Other event sink interfaces | Varies | A control that has outgoing interfaces other than IPropertyNotifySink will be handed other interface pointers of the correct IID to the control’s IConnectionPoint::Advise implementations (which are usually found in sub-objects of the control). A control always knows how to call its own event interfaces since the control defines those interfaces.
IOleInPlaceFrame | Frame | Used when a control has an in-place UI active state that requires frame-level tools or menu items.
IOleInPlaceUIWindow | Document | Used only when a control has an in-place UI active state that requires document-level or pane-level UI tools. This is rare.


Containers

An ActiveX Control container is an OLE container that supports the following additional features:

  1. Embedded objects from in-process servers
  2. In Place activation
  3. OLEMISC_ACTIVATEWHENVISIBLE
  4. Event Handling

ActiveX Control Containers must provide support for all of these features.

The following sections describe the specific interfaces, methods, and other features that are required of ActiveX Control Containers. Required Interfaces, Optional Methods, Misc. Status Bits Support, Keyboard Handling, Storage Interfaces, Ambient Properties, Extended Properties, Events, Methods, Message Reflection, and Automatic Clipping. The last section describes how to gracefully degrade when a particular control interface is not supported.


Required Interfaces

The table below lists the ActiveX Control Container interfaces, and denotes which interfaces are optional, and which are mandatory and must be implemented by control containers.

Interface | Required? | Comments
IOleClientSite | Yes |
IAdviseSink | No | Only when the container desires (a) data change notifications (controls with IDataObject), (b) view change notification (controls that are not active and have IViewObject[2]), and (c) other notifications from controls acting as standard embedded objects.
IOleInPlaceSite | Yes |
IOleControlSite | Yes |
IOleInPlaceFrame | Yes |
IOleContainer | Yes | See Table Note 1.
IDispatch for ambient properties | Yes | See Table Note 2 and Ambient Properties for Controls section
Control Event Sets | Yes | See Table Note 2.
ISimpleFrameSite | No | ISimpleFrameSite and support for nested simple frames is optional.
IPropertyNotifySink | No | Only needed for containers that (a) have their own property editing UI which would require updating whenever a control changed a property itself or (b) want to control [requestedit] property changes and other such data-binding features.
IErrorInfo | Yes | Mandatory if container supports dual interfaces. See Table Note 2.
IClassFactory2 | No | Support is strongly recommended.

Table Notes

  1. IOleContainer is implemented on the document or form object (or appropriate analog) that holds the container sites. Controls use IOleContainer to navigate to other controls in the same document or form.
  2. Support for dual interfaces is not mandatory, but is strongly recommended. Writing ActiveX Control Containers to take advantage of dual interfaces will afford better performance with controls that offer dual interface support.

ActiveX Control containers must support OLE Automation exceptions. If a control container supports dual interfaces, then it must capture automation exceptions through IErrorInfo.


Optional Methods

An OLE component can implement an interface without implementing all the semantics of every method in the interface, instead returning E_NOTIMPL or S_OK as appropriate. The following table describes those methods that an ActiveX Control container is not required to implement (i.e. the control container can return E_NOTIMPL).

The table below describes optional methods; note that the method must still exist, but can simply return E_NOTIMPL instead of implementing real semantics. Note that any method from a mandatory interface that is not listed below must be considered mandatory and may not return E_NOTIMPL.

Method | Comments
IOleClientSite |
SaveObject | Necessary for persistence to be successfully supported.
GetMoniker | Necessary only if the container supports linking to controls within its own form or document.
IOleInPlaceSite |
ContextSensitiveHelp | Optional
Scroll | May return S_FALSE with no action.
DiscardUndoState | Can return S_OK with no action.
DeactivateAndUndo | Deactivation is mandatory; Undo is optional.
IOleControlSite |
GetExtendedControl | Necessary for containers that support extended controls.
ShowPropertyFrame | Necessary for containers that wish to include their own property pages to handle extended control properties in addition to those provided by a control.
TranslateAccelerator | May return S_FALSE with no action.
LockInPlaceActive | Optional
IDispatch (Ambient properties) |
GetTypeInfoCount | Necessary for containers that support non-standard ambient properties.
GetTypeInfo | Necessary for containers that support non-standard ambient properties.
GetIDsOfNames | Necessary for containers that support non-standard ambient properties.
IDispatch (Event sink) |
GetTypeInfoCount | The control knows its own type information, so it has no need to call this.
GetTypeInfo | The control knows its own type information, so it has no need to call this.
GetIDsOfNames | The control knows its own type information, so it has no need to call this.
IOleInPlaceFrame |
ContextSensitiveHelp |
GetBorder | Necessary for containers with toolbar UI (which is optional)
RequestBorderSpace | Necessary for containers with toolbar UI (which is optional)
SetBorderSpace | Necessary for containers with toolbar UI (which is optional)
InsertMenus | Necessary for containers with menu UI (which is optional)
SetMenu | Necessary for containers with menu UI (which is optional)
RemoveMenus | Necessary for containers with menu UI (which is optional)
SetStatusText | Necessary only for containers that have a status line
EnableModeless | Optional
TranslateAccelerator | Optional
IOleContainer |
ParseDisplayName | Only if linking to controls or other embeddings in the container is supported, as this is necessary for moniker binding.
LockContainer | As for ParseDisplayName
EnumObjects | Returns all ActiveX Controls through an enumerator with IEnumUnknown, but not necessarily all objects (since there’s no guarantee that all objects are ActiveX Controls; some may be regular Windows controls).


Miscellaneous Status Bits Support

ActiveX Control Containers must recognize and support the following OLEMISC_ status bits:

Status Bit | Required? | Comments
ACTIVATEWHENVISIBLE | Yes |
IGNOREACTIVATEWHENVISIBLE | No | Needed for inactive and windowless control support. The IGNOREACTIVATEWHENVISIBLE bit is for containers hosting inactive and windowless controls. The IGNOREACTIVATEWHENVISIBLE bit is introduced as part of the ActiveX Controls 96 specification, see this documentation for more details.
INSIDEOUT | No | Not generally used with ActiveX Controls but rather with compound document embeddings. Note this is contrary to some SDK documentation that says this bit must be set for the ACTIVATEWHENVISIBLE bit to be set.
INVISIBLEATRUNTIME | Yes | Designates a control that should be visible at design time, but invisible at run time.
ALWAYSRUN | Yes |
ACTSLIKEBUTTON | No | Support is normally mandatory although it is not necessary for document style containers.
ACTSLIKELABEL | No | Support is normally mandatory although it is not necessary for document style containers.
NOUIACTIVATE | Yes |
ALIGNABLE | No |
SIMPLEFRAME | No | See Simple Frame Site Containment section.
SETCLIENTSITEFIRST | No | Support for this bit is recommended but not mandatory.
IMEMODE | No |


Keyboard Handling in Controls

Keyboard handling support for the following functionality is strongly recommended, although it is recognized that it is not applicable to all containers.

  • Support for OLEMISC_ACTSLIKELABEL and OLEMISC_ACTSLIKEBUTTON status bits.
  • Implementing the DisplayAsDefault ambient property (if it exists, it can return FALSE).
  • Implementing tab handling, including tab order.

Some containers will use ActiveX Controls in traditional compound document scenarios. For example, a spreadsheet may allow a user to embed an ActiveX Control into a worksheet. In such scenarios, the container would do keyboard handling differently, because the keyboard interface should remain consistent with the user’s expectations of a spreadsheet. Documentation for such products should inform users of differences in control handling in these different scenarios. Other containers should endeavor to honor the above functionality correctly, including Mnemonic handling.


Storage Interfaces

Control containers must be able to support controls that implement IPersistStorage, IPersistStream, or IPersistStreamInit. Optionally, a container can support any other persistence interfaces such as IPersistMemory, IPersistPropertyBag, and IPersistMoniker for those controls that provide support.

Once an ActiveX Control Container has chosen and initialized a storage interface to use (IPersistStorage, IPersistStream, IPersistStreamInit, etc), that storage interface will remain the primary storage interface for the lifetime of the control, i.e. the control will remain in possession of the storage. This does not preclude the container from saving to other storage interfaces.

ActiveX Control Containers do not need to support a save as text mechanism, thus using IPersistPropertyBag and the associated container-side interface IPropertyBag are optional.


Ambient Properties

At a minimum, ActiveX Control containers must support the following ambient properties using the standard dispids.

Ambient Property | Dispid | Comments/Conditions
LocaleID | -705 |
UserMode | -709 | For containers that have different user and run environments.
DisplayAsDefault | -713 | For those containers where a default button is relevant.


Extended Properties, Events and Methods

ActiveX Control Containers are not required to support extended controls. However, if the control container does support extended properties, then it must support the following minimal set:

Visible

Parent

Default

Cancel

Currently, extended properties, events, and methods do not have standard dispids.


Message Reflection

It is strongly recommended that an ActiveX Control container supports message reflection. This will result in more efficient operation for subclassed controls. If message reflection is supported, the MessageReflect ambient property must be supported and have a value of TRUE. If a container does not implement message reflection, then the OLE CDK creates two windows for every sub-classed control, to provide message reflection on behalf on the control container.


Automatic Clipping

It is strongly recommended that an ActiveX Control container supports automatic clipping of its controls. This will result in more efficient operation for most controls. If automatic clipping is supported, the AutoClip ambient property must be supported and have a value of TRUE.

Automatic clipping is the ability of a container to ensure that a control’s drawn output goes only to the container’s current clipping region. In a container that supports automatic clipping, a control can paint without regard to its clipping region, because the container will automatically clip any painting that occurs outside the control’s area. If a container does not support automatic clipping, then CDK-generated controls will create an extra parent window if a non-null clipping region is passed.


Degrading Gracefully in the Absence of an Interface

Because a control may not support any interface other than IUnknown, a container has to degrade gracefully when it encounters the absence of any particular interface.

One might question the usefulness of a control with nothing more than IUnknown. But consider the advantages that a control receives from a container’s visual programming environment (such as VB) when the container recognizes the object as a control:

  1. A button for the object appears in a toolbox.
  2. One can create an object by dragging it from the toolbox onto a form.
  3. One can give the object a name that is recognized in the visual programming environment.
  4. The same name in (3) above can be used immediately in writing any other code for controls on the same form (or even a different form).
  5. The container can automatically provide code entry points for any events available from that object.
  6. The container provides its own property browsing UI for any available properties.

When an object isn’t recognized as a control, then it potentially loses all of these very powerful and beneficial integration features. For example, in Visual Basic 4.0 it is very difficult to really integrate some random object that is not a control in the complete sense, but may still have properties and events. Because VB 4’s idea of a control is very restrictive the object does not gain any of the integration features above. But even a control with IUnknown, where the mere lifetime of the control determines the existence of some resource, should be able to gain the integration capabilities described above.

As current tools require a large set of control interfaces to gain any advantage, controls are generally led to over-implementation, such that they contain more code than they really need. Controls that could be 7K might end up being 25K, which is a big performance problem in areas such as the Internet. This has also led to the perception that one can only implement a control with one tool like the CDK because of the complexity of implementing all the interfaces—and this has implications when a large DLL like OC30.DLL is required for such a control, increasing the working set. If not all interfaces are required, then this opens up many developers to writing very small and light controls with straight OLE or with other tools as well, minimizing the overhead for each control.

This is why this appendix recognizes a control as any object with a CLSID and an IUnknown interface. Even with nothing more than IUnknown, a container with a programming environment should be able to provide at least features #3 and #4 from the list above. If the object provides a ToolBoxBitmap32 registry entry, it gains #1 and #2. If the object supplies IConnectionPointContainer (and IProvideClassInfo generally) for some event set, it gains #5, and if it supports IDispatch for properties and methods, it gains #6, as well as better code integration in the container.

In short, an object should be able to implement as little as IDispatch and one event set exposed through IConnectionPointContainer to gain all of those visual features above.

With this in mind, the following table describes what a container might do in the absence of any possible interface. Note that only those interfaces are listed that the container will directly obtain through QueryInterface. Other interfaces, like IOleInPlaceActiveObject, are obtained through other means.

Interface | Meaning of Interface Absence
IViewObject2 | The control has no visuals that it will draw itself, so has no definite extents to provide. In run-time, the container simply doesn’t attempt to draw anything when this interface is absent. In design time, the container must at least draw some kind of default rectangle with a name in it for such a control, so a user in a visual programming environment can select the object and check out its properties, methods, and events that exist. Handling the absence of IViewObject2 is critical for good visual programming support.
IOleObject | The control doesn’t need the site whatsoever, nor does it take part in any embedded object layout negotiation. Any information (like control extents) that a container might expect from this interface should be filled in with container-provided defaults.
IOleInPlaceObject | The control doesn’t go in-place active (like a label) and thus never attempts to activate in this manner. Its only activation may be its property pages.
IOleControl | Control has no mnemonics and no use of ambient properties, and doesn’t care if the container ignores events. In the absence of this interface, the container just doesn’t call its methods.
IDataObject | The control provides no property sets nor any visual renderings that could be cached, so the container would choose to cache some default presentation in the absence of this interface (support for CF_METAFILEPICT, specifically) and disable any property-set related functionality.
IDispatch | The control has no custom properties or methods. The container does not need to try to show any control properties in this case, and should disallow any custom method calls that the container doesn’t recognize as belonging to its own extended controls (that may support methods and properties). As extended controls generally delegate certain IDispatch calls to the control, an extended control should not expect the control to have IDispatch at all.
IConnectionPointContainer | The control has no events, so the container doesn’t have to think about handling any.
IProvideClassInfo[2] | The control either doesn’t have type information or events, or the container needs to go into the control’s type information through the control’s registry entries. The existence of this interface is an optimization.
ISpecifyPropertyPages | The control has no property pages, so if the container has any UI that would invoke them, the container should disable that UI.
IPerPropertyBrowsing | The control has no display name itself, no predetermined strings and values, and no property to page mapping. This interface is nearly always used for generating container user interface, so such UI elements would be disabled in the absence of this interface.
IPersist* | The control has no persistent state to speak of, so the container doesn’t have to worry about saving any control-specific data. The container will, of course, save its own information about the control in its own form or document, but the control itself has nothing to contribute to that information.
IOleCache[2] | The object doesn’t support caching. A container can still support caching by just creating a data cache itself using CreateDataCache.


Component Categories

OLE’s component categories allow a software component’s abilities and requirements to be identified by entries in the registry. In a scenario where a container may not wish to or not be able to support an area of functionality, such as databinding for example, the container will not wish to host controls that require databinding in order to perform their job function. Component Categories allow areas of functionality such as databinding to be identified, so that the control container can avoid those controls that state it to be a requirement. Component Categories are specified separately as part of OLE and are not specific to the ActiveX Control architecture, the specification for component categories includes a set of APIs for manipulation of the component category registry keys.


What are Component Categories and how do they work?

Component Categories identify those areas of functionality that a software component supports and requires, a registry entry is used for each category or identified area of functionality. Each component category is identified by a globally unique identifier (GUID), when a control is installed it registers itself as a control in the system registry using the component category ID for control, see the Self Registration for Controls section. Within the control’s self registration it will also register those component categories that it implements and those component categories that it requires a container to support in order to successfully host the control.

When a control container is offering controls to the user to insert, it only allows the user to select and instantiate those controls that will be able to function adequately in that environment. For example, if the control container does not support databinding, then the container will not allow the user to select and instantiate those controls that have an entry in the registry signifying that they require the databinding component category. A common dialog for control insertion and APIs to handle the registry entries are available.

Component categories are not cumulative or exclusive, a control can require any mix of component categories to function. A control that has no required entries for component categories may be expected to be capable of functioning in any control container and not require any specific functionality of a control container to function.

The following component categories are identified here, where necessary more detailed specifications of the categories may be available.

  • ISimpleFrameSite control containment.
  • Simple Databinding through the IPropertyNotifySink interface.
  • Advanced Databinding (as supported by the additional databinding interfaces of VB4.0).
  • Visual Basic private interfaces - IVBFormat, IVBGetControl
  • Internet aware controls.
  • Windowless controls.

This is not a definitive list of categories; further categories are likely to be defined in the future as new requirements are identified. An up-to-date list of component categories is available from Microsoft on their world wide web site, this list reflects those component categories that have been identified by Microsoft and any others that about which vendors have informed Microsoft.

It is important to remember that controls should attempt to work in as many environments as possible. If it is possible, the control should degrade its functionality when placed in a container that does not support certain interfaces. The purpose of component categories is to prevent a situation where the control is placed in an environment that is unsuitable and the control can not achieve its desired task. Generally, a control should degrade gracefully when interfaces are not present, a control may choose to advise the user with a message box that some functionality is not available or clearly document the functionality required of a control container for optimal performance.

Note older controls and containers do not make use of Component Categories and instead rely on the control keyword being present against the control in the registry. In order to be recognized by older containers controls may wish to register the control keyword in the registry, control developers should check that the control can successfully be hosted in such containers before doing this. Containers that use component categories may successfully use them to host older controls as the components category DLL handles the mapping, a separate category exists for older controls CATID_ControlV1 so that a container may optionally exclude them if necessary.

As Component Categories are identified by GUIDs it is possible for containers that offer particular specific functionality to have their own category IDs, generated using a GUID generation tool. However this can possibly undermine the advantage of interoperability of controls and containers so it is preferred that wherever possible existing component categories be used. Vendors are encouraged to consult together when defining new component categories to ensure that they meet the common requirements of the marketplace, and follow the spirit of interoperability of ActiveX Controls.


Simple Frame Site Containment

A container control is an ActiveX Control that is capable of containing other controls. A group box that contains a collection of radio buttons is an example of a container control. Container controls should set the OLEMISC_SIMPLEFRAME status bit, and should call its container’s ISimpleFrameSite implementation. An ActiveX Control container that supports Container Controls must implement ISimpleFrameSite.

CATID - {157083E0-2368-11cf-87B9-00AA006C8166} CATID_SimpleFrameControl


Simple Data Binding

The ActiveX Controls Architecture defines a data-binding mechanism, whereby an ActiveX Control can specify that one or more of its properties are bindable. In most cases, a data-bound control should not absolutely require data binding, so that it could be inserted into a container that does not support data binding. Obviously, in such a situation, the functionality of the control may be reduced.

CATID - {157083E1-2368-11cf-87B9-00AA006C8166} CATID_PropertyNotifyControl


Advanced Data Binding

There is a set of advanced data binding interfaces that allow a more complex databinding scenario to be supported. This component category covers that area of functionality.

CATID - {157083E2-2368-11cf-87B9-00AA006C8166} CATID_VBDataBound


Visual Basic private interfaces

Two interfaces that are implemented by Visual Basic are identified here for component categories. It is not expected that controls should require these categories as it is possible for controls to offer alternative functionality when these are not available.

The IVBFormat interface allows controls to better integrate into the Visual Basic environment when formatting data.

CATID - {02496840-3AC4-11cf-87B9-00AA006C8166} CATID_VBFormat

The IVBGetControl interface allows a control to enumerate other controls on the VB form.

CATID - {02496841-3AC4-11cf-87B9-00AA006C8166} CATID_VBGetControl


Internet-Aware Objects

There are certain categories identified to cover the persistency interfaces, these have been identified as a result of defining how controls function across the internet. A container that does not support the full range of persistency interfaces should ensure that it does not host a control that requires a combination of interfaces that it does not support. Details of the features required for internet aware controls are available in the ActiveX SDK.

The following tables describe the meaning for various categories as both implemented and required categories.

Required Categories | Description
CATID_PersistsToMoniker, CATID_PersistsToStreamInit, CATID_PersisitsToStream, CATID_PersistsToStorage, CATID_PersistsToMemory, CATID_PersistsToFile, CATID_PersistsToPropertyBag | Each of these categories are mutually exclusive and are only used when an object supports only one persistence mechanism at all (hence the mutual exclusion). Containers that do not support the persistence mechanism described by one of these categories should prevent themselves from creating any objects of classes so marked.
CATID_RequiresDataPathHost | The object requires the ability to save data to one or more paths and requires container involvement, therefore requiring container support for IBindHost.
Implemented Categories | Description
CATID_PersistsToMoniker, CATID_PersistsToStreamInit, CATID_PersistsToStream, CATID_PersistsToStorage, CATID_PersistsToMemory, CATID_PersistsToFile, CATID_PersistsToPropertyBag | Object supports the corresponding IPersist* mechanism for the category.

The following table provides the exact CATIDs assigned to each category:

Category | CATID
CATID_RequiresDataPathHost | 0de86a50-2baa-11cf-a229-00aa003d7352
CATID_PersistsToMoniker | 0de86a51-2baa-11cf-a229-00aa003d7352
CATID_PersistsToStorage | 0de86a52-2baa-11cf-a229-00aa003d7352
CATID_PersistsToStreamInit | 0de86a53-2baa-11cf-a229-00aa003d7352
CATID_PersistsToStream | 0de86a54-2baa-11cf-a229-00aa003d7352
CATID_PersistsToMemory | 0de86a55-2baa-11cf-a229-00aa003d7352
CATID_PersistsToFile | 0de86a56-2baa-11cf-a229-00aa003d7352
CATID_PersistsToPropertyBag | 0de86a57-2baa-11cf-a229-00aa003d7352


Windowless Controls

The ActiveX Controls 96 specification includes a definition for windowless controls. Such controls do not operate in their own window and require a container to offer a shared window into which the control may draw, see the ActiveX SDK. Windowless controls are designed to be compatible with older control containers by creating their own window in that situation, windowless control containers should host windowed controls in the traditional way with no problem. It may however be useful for a container to distinguish those controls that can operate in a windowless mode, so an appropriate component category is defined.

CATID - {1D06B600-3AE3-11cf-87B9-00AA006C8166} CATID_WindowlessObject


Categorizing DCOM Proxies and Stubs

As of Windows 2000, a new flag, EOAC_NO_CUSTOM_MARSHAL, can be specified. Specifying this flag contributes to better server security when using DCOM because it reduces the chances of executing arbitrary DLLs. EOAC_NO_CUSTOM_MARSHAL unmarshals CLSIDs implemented only in ole32.dll and Component Services. DCOM marshals references to objects by constructing OBJREFs that contain CLSIDs. CLSIDs are vulnerable to security attacks during unmarshaling because arbitrary DLLs could be loaded. Processes that have declared EOAC_NO_CUSTOM_MARSHAL in their security capabilities by calling CoInitializeSecurity can also use CLSIDs that implement CATID_Marshaler.


General Guidelines

This section describes various features, hints and tips for ActiveX Control and ActiveX Control container developers to help ensure good interoperability between controls and control containers.


Overloading IPropertyNotifySink

Many ActiveX Control Containers implement a modeless property browsing window. If a control’s properties are altered through the control’s property pages, then the control’s properties can get out of sync with the container’s view of those properties (the control is always right, of course). To ensure that it always has the current values for a control’s properties, an ActiveX Control Container can overload the IPropertyNotifySink interface (data binding) and also use it to be notified that a control property has changed. This technique is optional, and is not required of ActiveX Control Containers or ActiveX Controls.

Note that a control should use IPropertyNotifySink::OnRequestEdit only for data binding; it is free to use OnChanged for either or both purposes.


Container-Specific Private Interfaces

Some containers provide container-specific private interfaces for additional functionality or improved performance. Controls that rely on those container-specific interfaces should, if possible, work without those container-specific interfaces present so that the control functions in different containers. For example, Visual Basic® implements private interfaces that provide string formatting functionality to controls. If a control makes use of VB’s private formatting interfaces, it should be able to run with default formatting support if these interfaces are not available. If the control can function without the private interfaces, it should take appropriate action (such as warn the user of reduced functionality) but continue to work. If this is not an option, then a component category should be registered as required to ensure that only containers supporting this functionality can host these controls.


Multi-Threaded Issues

Starting with Microsoft® Windows® 95 and Microsoft Windows NT® 3.51, OLE provides support for multi-threaded applications, allowing applications to make OLE calls from multiple threads. This multi-threaded support is called the apartment model, it is important that all OLE components using multiple threads follow this model. The apartment model requires that interface pointers are marshaled (using CoMarshallInterface, and CoUnmarshalInterface) when passed between threads. For more information about apartment model threading, refer to the Platform SDK documentation, and the OLEAPT sample (in Platform SDK).


Event Freezing

A container can notify a control that it is not ready to respond to events by calling IOleControl::FreezeEvents(TRUE). It can un-freeze the events by calling IOleControl::FreezeEvents(FALSE). When a container freezes events, it is freezing event processing, not event receiving; that is, a container can still receive events while events are frozen. If a container receives an event notification while its events are frozen, the container should ignore the event. No other action is appropriate.

A control should take note of a container’s call to IOleControl::FreezeEvents(TRUE) if it is important to the control that an event is not missed. While a container’s event processing is frozen, a control should implement one of the following techniques:

  1. Fire the events in the full knowledge that the container will take no action.
  2. Discard all events that the control would have fired.
  3. Queue up all pending events and fire them after the container has called IOleControl::FreezeEvents(FALSE).
  4. Queue up only relevant or important events and fire them after the container has called IOleControl::FreezeEvents(FALSE).

Each technique is acceptable and appropriate in different circumstances. The control developer is responsible for determining and implementing the appropriate technique for the control’s functionality.


Container Controls

As described above, container controls are ActiveX Controls that visually contain other controls. The ActiveX Controls Architecture specifies the ISimpleFrameSite interface to enable container controls. Containers may also support container controls without supporting ISimpleFrameSite, although the behavior cannot be guaranteed. For this reason, a component category exists for SimpleFrameSite controls where the full functionality of this interface is required.

In order to support container controls without implementing ISimpleFrameSite, an ActiveX Control Container must:

  • Activate all controls at all times.
  • Reparent the contained controls to the hWnd of the containing control.
  • Remain the parent of the container control.


WS_GROUP and WS_TABSTOP Flags in Controls

A control should not use the WS_GROUP and WS_TABSTOP flags internally; some containers rely on these flags to manage keyboard handling.


Multiple Controls in One DLL

A single .OCX DLL can container any number of ActiveX Controls, thus simplifying the distribution and use of a set of related controls.

If you ship multiple controls in a single DLL, be sure to include the vendor name in each control name in the package. Including the vendors’ names in each control name will enable users to easily identify controls within a package. For example, if you ship a DLL that implements three controls, Con1, Con2 and Con3, then the control names should be:

<Your company name> Con1 Control

<Your company name> Con2 Control

<Your company name> Con3 Control


The IOleContainer::EnumObjects Method

This method is used to enumerate over all the OLE objects contained in a document or form, returning an interface pointer for each OLE object. The container must return pointers to each OLE object that shares the same container. Nested forms or nested controls must also be enumerated.

Some containers implement extender controls, which wrap non-ActiveX Controls, and then return pointers to these extender controls as a form is enumerated. This behavior enables ActiveX Controls and ActiveX Control containers to integrate well with non-ActiveX Controls, and is thus recommended, but not required.


Enhanced Metafiles

Not surprisingly, enhanced metafiles provide more functionality than standard metafiles; using enhanced metafiles generally simplifies rendering code. An enhanced metafile DC is used in exactly the same way as a standard metafile DC. Enhanced metafiles are not available in 16-bit OLE. OLE supports enhanced metafiles, and includes backwards compatibility with standard metafiles and 16-bit applications.

32-bit ActiveX Control containers should use enhanced metafiles instead of standard metafiles.


Licensing

In order to embed licensed controls successfully, ActiveX Control containers must use IClassFactory2 instead of IClassFactory. Several OLE creation and loading helper functions (i.e., OleLoad and CoCreateInstance) explicitly call IClassFactory and not IClassFactory2, and therefore cannot be used to create or load licensed ActiveX Controls. ActiveX Control Containers should explicitly create and load ActiveX Controls using IClassFactory2. In the future, Microsoft will update these standard APIs to use both IClassFactory and IClassFactory2, as appropriate.


Dual Interfaces

OLE Automation enables an object to expose a set of methods in two ways: via the IDispatch interface, and through direct OLE VTable binding. IDispatch is used by most tools available today, and offers support for late binding to properties and methods. VTable binding offers much higher performance because this method is called directly instead of through IDispatch::Invoke. IDispatch offers late bound support, where direct VTable binding offers a significant performance gain; both techniques are valuable and important in different scenarios. By labeling an interface as dual in the type library, an OLE Automation interface can be used either via IDispatch, or it can be bound to directly. Containers can thus choose the most appropriate technique. Support for dual interfaces is strongly recommended for both controls and containers.


IPropertyBag and IPersistPropertyBag

IPropertyBag and IPersistPropertyBag optimize save as text mechanisms, and therefore are recommended for ActiveX Control containers that implement a save as text mechanism. IPropertyBag is implemented by a container, and is roughly analogous to IStream. IPersistPropertyBag is implemented by controls, and is roughly analogous to IPersistStream.


Event Coordinate Translation

The 96 specification for controls requires that coordinates passed for events fired by the control change from being HIMETRIC to being Points based. This change brings the event passing of coordinates in line with properties and methods and thus coordinate translation is no longer the responsibility of the container. This raises certain compatibility issues where a control fires events using a coordinate base that it is not expecting, this should only be an issue where a 96 control container is hosting an older pre-96 control as follows:

  • When an older pre-96 container hosts a 96 control the control will present the event coordinates as points, this should not cause the container any problems as the container should recognize the parameter type.
  • When a 96 container hosts a pre-96 control the control will present the container with coordinates and expect the container to any translation necessary. However the 96 container will be expecting a control to conform to the 96 controls specification and present its coordinates as points. The control uses the TranslateCoordinates method on the IOleControlSite interface provided by the container in the same way as it does for properties and methods to achieve this.

As a result the user of a 96 container hosting pre-96 controls will need to be aware that further translation of coordinates may be necessary when events are fired.


Standard DISPIDS

A number of standard dispids have been defined for the 96 controls specification.

DISPID_MOUSEPOINTER

#define DISPID_MOUSEPOINTER -521

Property of type integer.

The Mousepointer property identifies standard mouse icons:

Value | Description
0 | (Default) Shape determined by the object.
1 | Arrow
2 | Cross (cross-hair pointer)
3 | I Beam
4 | Icon (small square within a square)
5 | Size (four-pointed arrow pointing north, south, east, and west)
6 | Size NE SW (double arrow pointing northeast and southwest)
7 | Size N S (double arrow pointing north and south)
8 | Size NW, SE
9 | Size E W (double arrow pointing east and west)
10 | Up Arrow
11 | Hourglass (wait)
12 | No Drop
13 | Arrow and hourglass
14 | Arrow and question mark
15 | Size all
99 | Custom icon specified by the MouseIcon property

DISPID_MOUSEICON

#define DISPID_MOUSEICON -522

Property of type Picture.

DISPID_PICTURE

#define DISPID_PICTURE -523

Property of type picture.

DISPID_VALID

#define DISPID_VALID -524 // Is data in control valid?

Property of type BOOL.

Used to determine if the control has valid data or not.

DISPID_ AMBIENT_PALETTE

#define DISPID_AMBIENT_PALETTE -726 // Container's HPAL

Used to allow the control to get the container’s HPAL. If the container supplies an ambient palette then that is the only palette that may be realized into the foreground. Controls that wish to realize their own palettes must do so in the background. If their is no ambient palette provided by the container then the active control may realize its palette in the foreground. Palette handling is further discussed in Palette Behaviour for OLE Controls which is in the ActiveX SDK.


Databinding

A new databinding attribute has been added to allow properties distinguish between communicating changes only when focus leaves the control or during all property change notifications.

The new attribute known as ImmediateBind is to allow controls to differentiate two different types of bindable properties. One type of bindable property needs to notify every change to the database, for example with a check box control where every change needs to be sent through to the underlying database even though the control has not lost the focus. However controls such as a listbox only wish to have the change of a property notified to the database when the control loses focus, as the user may have changed the highlighted selection with the arrow keys before finding the desired setting, to have the change notification sent to the database every time that the user hit the arrow key would be give unacceptable performance. The new immediate bind property allows individual bindable properties on a form to have this behavior specified, when this bit is set all changes will be notified.

The new ImmediateBind bit maps through to the new VARFLAG_FIMMEDIATEBIND (0x80) and the FUNCFLAG_FIMMEDIATEBIND (0x80) bits in the VARFLAGS and FUNCFLAGS enumerations for the ITypeInfo interface allowing for the properties attributes to be inspected.


IFont

An COM font object is an object wrapper around a Windows font object. The COM font object supports a number of read-write properties as well as a set of methods through its IFont interface. It supports the same set of properties (but not the methods) through the dispatch interface IFontDisp, which is derived from IDispatch to provide access to the font's properties through Automation. The system provides a standard implementation of the font object with both interfaces.

The font object also supports the outgoing interface IPropertyNotifySink so a client can determine when font properties change. Because the font object supports at least one outgoing interface, it also implements IConnectionPointContainer and related interfaces for this purpose.

The font object provides an hFont property, which is a Windows font handle that conforms to the other attributes specified for the font. The font object delays realizing this hFont object when possible, so consecutively setting two properties on a font won't cause an intermediate font to be realized. In addition, as an optimization, the system-implemented font object maintains a cache of font handles. Two font objects in the same process that have identical properties will return the same font handle. The font object can remove font handles from this cache at will, which introduces special considerations for clients using the hFont property. See the description for IFont::get_hFont for more details.

The font object also supports IPersistStream so it can save and load itself from an instance of IStream. An object that uses a font object internally would normally save and load the font as part of the object's own persistence handling.

In addition, the font object supports IDataObject, which can render a property set containing the font's attributes, allowing a client to save these properties as text.

When to Implement

Typically, you use the COM-provided font object, which implements the IFont interface as its primary interface. It allows the caller to manage font properties and to use that font in graphical rendering. Each property in the IFont interface includes a get_PropertyName method if the property supports read access and a put_PropertyName method if the property supports write access. Most of the properties support both read and write access, and thus expose both "get" and "put" methods for these properties.

Property | Type | Access Allowed | Description
Name | BSTR | RW | The name of the font family, e.g. Arial.
Size | CY | RW | The point size of the font, expressed in a CY type to allow for fractional point sizes.
Bold | BOOL | RW | Indicates whether the font is boldfaced.
Italic | BOOL | RW | Indicates whether the font is italicized.
Underline | BOOL | RW | Indicates whether the font is underlined.
Strikethrough | BOOL | RW | Indicates whether the font is strikethrough.
Weight | short | RW | The boldness or weight of the font.
Charset | short | RW | The character set used in the font, such as ANSI_CHARSET, DEFAULT_CHARSET, or SYMBOL_CHARSET.
hFont | HFONT | R | The Windows font handle that can be selected into a device context for rendering.

When to Use

Use this interface to change or retrieve the properties of a font object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IFont Methods | Description
get_Name | Gets the name of the font family.
put_Name | Sets a new name for the font family.
get_Size | Gets the point size for the font.
put_Size | Sets the point size for the font.
get_Bold | Indicates whether the font is bold or not.
put_Bold | Sets the boldness property for the font.
get_Italic | Indicates whether the font is italic or not.
put_Italic | Sets the italic property for the font.
get_Underline | Indicates whether the font is underlined or not.
put_Underline | Sets the underline property for the font.
get_Strikethrough | Indicates whether the font is strikethrough or not.
put_Strikethrough | Sets the strikethrough property for the font.
get_Weight | Gets the weight (boldness) for the font.
put_Weight | Sets the weight (boldness) for the font.
get_Charset | Gets the font's character set.
put_Charset | Sets the font's character set.
get_hFont | Returns a Windows HFONT handle for the font described by this font object.
Clone | Creates a duplicate font object with a state identical to the current font.
IsEqual | Compares this font object to another for equality.
SetRatio | Converts the scaling factor for this font between logical units and HIMETRIC units (in which is expressed the point size in the Size property).
QueryTextMetrics | Fills a TEXTMETRIC structure describing the font.
AddRefHfont | Notifies the font object that the previously realized font identified with hFont (from IFont::GetHfont) should remain valid until IFont::ReleaseHfont is called or the font object itself is released.
ReleaseHfont | Notifies the font object that the caller that previously locked this font in the cache with IFont::AddRefHfont no longer requires the lock.
SetHdc | Provides a device context handle to the font that describes the logical mapping mode.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont - Ole Implementation, IFontDisp


IFont::AddRefHfont

Notifies the font object that the previously realized font identified with hFont (obtained from IFont::get_hFont) should remain valid until IFont::ReleaseHfont is called or the font object itself is released completely.

HRESULT AddRefHfont(
  HFONT hfont  //Font handle returned from IFont::GetHfont
);

Parameters

hfont
[in] Font handle previously realized through IFont::GetHfont to be locked in the font object's cache.

Return Values

The method supports the standard return values E_UNEXPECTED and E_INVALIDARG, as well as the following:

S_OK
The font was successfully locked in the cache.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get hFont, IFont:ReleaseHfont


IFont::Clone

Creates a duplicate font object with a state identical to the current font.

HRESULT Clone(
  IFont** ppfont  //Address of output variable that receives the
                  //IFont interface pointer
);

Parameters

ppfont
[out] Address of IFont* pointer variable that receives the interface pointer to the new font object. The caller must call IFont::Release when this new font object is no longer needed.

Return Values

The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following:

S_OK
The new font object was successfully created.
E_NOTIMPL
This font object does not support cloning.
E_POINTER
The address in ppfont is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The new font object is entirely independent of the first. The caller is responsible for releasing this new object when it is no longer needed. This method does not affect the reference count of the font being cloned.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IFont::get_Bold

Indicates whether the font is bold or not.

HRESULT get_Bold(
  BOOL* pbold  //Pointer to the font's bold format
);

Parameters

pbold
[out] Pointer to a caller-allocated BOOL variable that indicates whether the font is bold or not.

Return Values

S_OK
The state was retrieved successfully. If the state is indeterminate, a font object should set *pbold to FALSE and return S_OK.
E_POINTER
The address in pbold is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Bold


IFont::get_Charset

Returns the character set used in the font. The character set can be any of those defined for Windows fonts.

HRESULT get_Charset(
  short* pcharset  //Pointer to the character set
);

Parameters

pcharset
[out] Pointer to the caller-allocated short variable that receives the character set value.

Return Values

S_OK
The character set was retrieved successfully.
E_POINTER
The address in pcharset is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Charset


IFont::get_hFont

Returns a Windows HFONT handle for the font described by this font object.

HRESULT get_hFont(
  HFONT* phfont  //Pointer to the font handle
);

Parameters

phfont
[out] Pointer to the caller-allocated HFONT variable that receives the font handle. The caller does not own this resource and must not attempt to destroy the font.

Return Values

The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, as well as the following:

S_OK
The font handle was retrieved successfully.
E_POINTER
The address in phfont is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The font object maintains ownership of the HFONT and can destroy it at any time without prior notification. If the caller needs to secure this font for a limited period of time, it can call IFont::AddRefHfont and IFont::ReleaseHfont.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::AddRefHfont, IFont::ReleaseHfont


IFont::get_Italic

Indicates whether the font is italic or not.

HRESULT get_Italic(
  BOOL* pitalic  //Pointer to the font's italic format
);

Parameters

pitalic
[out] Pointer to the caller-allocated BOOL variable that indicates whether the font is italic.

Return Values

S_OK
The state was retrieved successfully. If the state is indeterminate, a font object should set *pitalic to FALSE and return S_OK.
E_POINTER
The address in pitalic is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Italic


IFont::get_Name

Returns a copy of the name of the font family.

HRESULT get_Name(
  BSTR* pname  //Pointer to the name of the font family
);

Parameters

pname
[out] Pointer to the caller-allocated variable that receives the copy of the name. This name must be freed with SysFreeString when it is no longer needed.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The name was returned successfully.
E_POINTER
The address in pname is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The caller is responsible for freeing the memory allocated for the name with SysFreeString.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Name


IFont::get_Size

Retrieves the point size of the font expressed in a 64-bit CY variable. The upper 32-bits of this value contains the integer point size and the lower 32-bits contains the fractional point size.

HRESULT get_Size(
  CY* psize  //Pointer to the font size.
);

Parameters

psize
[out] Pointer to the caller-allocated variable that receives the size.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The size was retrieved successfully.
E_POINTER
The address in psize is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Size


IFont::get_Strikethrough

Indicates whether the font has the strikethrough property or not.

HRESULT get_Strikethrough(
  BOOL* pstrikethrough  //Pointer to the current strikethrough
                        //property for the font
);

Parameters

pstrikethrough
[out] Pointer to the caller-allocated BOOL variable that indicates the current strikethrough property for the font.

Return Values

S_OK
The state was retrieved successfully. If the state is indeterminate, a font object should set *pstrikethrough to FALSE and return S_OK.
E_POINTER
The address in pstrikethrough is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Strikethrough


IFont::get_Underline

Indicates whether the font is underlined or not.

HRESULT get_Underline(
  BOOL* punderline  //Pointer to the font's underlined property
);

Parameters

punderline
[out] Pointer to the caller-allocated BOOL variable that indicates whether the font is underlined.

Return Values

S_OK
The state was retrieved successfully. If the state is indeterminate, a font object should set *punderline to FALSE and return S_OK.
E_POINTER
The address in punderline is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Underline


IFont::get_Weight

Returns the font weight, where a weight is defined as any of the FW_* values that are valid for Windows fonts.

HRESULT get_Weight(
  short* pweight  //Pointer to the font weight
);

Parameters

pweight
[out] Pointer to the caller-allocated short variable that receives the current font weight.

Return Values

S_OK
The weight was retrieved successfully. If the weight is indeterminate, a font object should store FW_NORMAL in *pweight and return S_OK.
E_POINTER
The address in pweight is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::put_Weight


IFont::IsEqual

Compares this font object to another for equivalence.

HRESULT IsEqual(
  IFont* pFontOther  //Pointer to the font to compare to this font
);

Parameters

pFontOther
[in] Pointer to the IFont interface on the font object to be compared to this font. The reference count of the object referred to by this pointer is not affected by the comparison operation.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The two fonts are equivalent.
S_FALSE
The two fonts are not equivalent.
E_POINTER
The address in pFontOther is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IFont::put_Bold

Sets the font's current bold property. Changing the Bold property may also change the Weight property. Setting Bold to TRUE sets the Weight to FW_BOLD (700); setting Bold to FALSE sets Weight to FW_NORMAL (400).

HRESULT put_Bold(
  BOOL bold  //Bold property for the font
);

Parameters

bold
[in] New bold property for the font.

Return Values

S_OK
The bold state was changed successfully.
S_FALSE
The font does not support a bold state. Note that this is not an error condition.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Bold


IFont::put_Charset

Sets the font's character set.

HRESULT put_Charset(
  short charset  //Character set
);

Parameters

charset
[in] New character set for the font.

Return Values

The method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The character set was changed successfully.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Charset


IFont::put_Italic

Sets the font's current italic property.

HRESULT put_Italic(
  BOOL italic  //Italic property for the font
);

Parameters

italic
[in] New italic property for the font.

Return Values

S_OK
The italic state was changed successfully.
S_FALSE
The font does not support an italic state. This value is not an error condition.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Italic


IFont::put_Name

Specifies a new name for the font family.

HRESULT put_Name(
  BSTR name  //Name of the font family
);

Parameters

name
[in] New name of the font family. This BSTR value is both allocated and freed by the caller.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The name was changed successfully.
E_POINTER
The address in name is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The string value is caller allocated and the caller is responsible for freeing it after this call returns.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Name


IFont::put_Size

Sets the current point size of the font given a CY structure.

HRESULT put_Size(
  CY size  //Size of the font
);

Parameters

size
[in] New size of the font.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The font was resized successfully.
E_POINTER
The value of the size parameter is not valid. For example, it does not contain a usable font size.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Size


IFont::put_Strikethrough

Sets the font's current strikethrough property.

HRESULT put_Strikethrough(
  BOOL strikethrough  //Strikethrough property for the font
);

Parameters

strikethrough
[in] New strikethrough property for the font.

Return Values

S_OK
The strikethrough state was changed successfully.
S_FALSE
The font does not support a strikethrough state. This value is not an error condition.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Strikethrough


IFont::put_Underline

Sets the font's current underline property.

HRESULT put_Underline(
  BOOL underline  //Underline property for the font
);

Parameters

underline
[in] New underline property for the font.

Return Values

S_OK
The underline state was changed successfully.
S_FALSE
The font does not support an underline state. This value is not an error condition.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Underline


IFont::put_Weight

Sets the font's weight. This property may affect the bold property as well. Bold is set to TRUE if the weight is greater than the average of FW_NORMAL (400) and FW_BOLD (700), that is 550.

HRESULT put_Weight(
  short weight  //Weight for the font
);

Parameters

weight
[in] New weight for the font.

Return Values

S_OK
The weight was changed successfully.
S_FALSE
This font does not support different weights. This value is not an error condition.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Weight


IFont::QueryTextMetrics

Fills a caller-allocated TEXTMETRIC structure for the font. The TEXTMETRICOLE structure is defined as a TEXTMETRICW structure on Win32 platforms. For more information on this structure, consult the Platform SDK.

HRESULT QueryTextMetrics(
  TEXTMETRICOLE* ptm  //Pointer to font information structure to be
                      //filled
);

Parameters

ptm
[out] Pointer to the caller-allocated structure that receives the font information.

Return Values

The method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The text metrics were returned successfully.
E_POINTER
The address in ptm is not valid. For example, it may be NULL.

Remarks

Notes to Implementers

E_NOTIMPL is not a valid return value. Font objects must always provide their font information through this call unless other errors occur.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IFont::ReleaseHfont

Notifies the font object that the caller that previously locked this font in the cache with IFont::AddRefHfont no longer requires the lock.

HRESULT ReleaseHfont(
  HFONT hfont  //Font handle returned from IFont::GetHfont
);

Parameters

hfont
[in] Font handle previously realized through IFont::GetHfont. This value was passed to a previous call to IFont::AddRefHfont to lock the font, and the caller would now like to unlock the font in the cache.

Return Values

The method supports the standard return values E_UNEXPECTED and E_INVALIDARG, as well as the following:

S_OK
The font was unlocked successfully.
S_FALSE
The font was not locked in the cache. This return value is a benign notification to the caller that it may have a font reference counting problem.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::AddRefHfont


IFont::SetHdc

Provides a device context to the font that describes the logical mapping mode.

HRESULT SetHdc(
  HDC hdc  //Device context handle
);

Parameters

hdc
[in] Handle to the device context in which to select the font.

Return Values

The method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The font was selected successfully.
E_NOTIMPL
The font selection is not supported through this font object.

Remarks

The logical mapping mode affects the font's internal computation of its point size so that when the caller asks for a font handle by calling IFont::GetHfont, the font is already properly scaled to the device context.

Notes to Callers

The caller retains ownership of this device context which must remain valid for the lifetime of the font object. Thus, the device context passed should be a memory device context (from the Win32 function CreateCompatibleDC) and not a screen device context (from CreateDC, GetDC, or BeginPaint) because screen device contexts are a limited system resource.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IFont::SetRatio

Converts the scaling factor for this font between logical units and HIMETRIC units. HIMETRIC units are used to express the point size in the IFont::get_Size and IFont::put_Size methods. The values passed to IFont::SetRatio are used to compute the display size of the font in logical units from the value in the Size property:

Display Size= (cyLogical/cyHimetric) * Size

HRESULT SetRatio(
  long cyLogical,  //Font size in logical units
  long cyHimetric  //Font size in HIMETRIC units
);

Parameters

cyLogical
[in] Font size in logical units.
cyHimetric
[in] Font size in HIMETRIC units.

Return Values

The method supports the standard return values E_UNEXPECTED and E_INVALIDARG, as well as the following:

S_OK
The ratio was set successfully.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont::get_Size, IFont::put_Size


IFont - Ole Implementation

The system provides a standard implementation of a font object with the IFont interface on top of the underlying system font support. A font object is created through the OleCreateFontIndirect function. A font object supports a number of read-write properties, as well as a set of methods, through its IFont interface and supports the same set of properties (but not the methods) through a dispatch interface IFontDisp which is derived from IDispatch to provide access to the font's properties through Automation. The system implementation of the font object supplies both interfaces.

Remarks

The COM-provided font object implements the complete semantics of the IFont and IFontDisp interfaces.

See Also

IFont


IFontDisp

This interface exposes a font object's properties through Automation. It provides a subset of the IFont methods.

When to Implement

A font object implements this interface along with IFont to provide access to the font's properties through Automation. Typically, it is not necessary to implement this interface on your own object, since there is a COM-provided implementation of the font object.

The following table describes the dispIDs for the various font properties.

Symbol | Value
DISPID_FONT_NAME | 0
DISPID_FONT_SIZE | 2
DISPID_FONT_BOLD | 3
DISPID_FONT_ITALIC | 4
DISPID_FONT_UNDER | 5
DISPID_FONT_STRIKE | 6
DISPID_FONT_WEIGHT | 7
DISPID_FONT_CHARSET | 8

Each property in the IFontDisp interface includes a get_PropertyName method if the property supports read access and a put_PropertyName method if the property supports write access. Most of the properties support both read and write access and thus expose both "get" and "put" methods for these properties.

Property | Type | Access Allowed | Description
Name | BSTR | RW | The facename of the font, e.g. Arial.
Size | CY | RW | The point size of the font, expressed in a CY type to allow for fractional point sizes.
Bold | BOOL | RW | Indicates whether the font is boldfaced.
Italic | BOOL | RW | Indicates whether the font is italicized.
Underline | BOOL | RW | Indicates whether the font is underlined.
Strikethrough | BOOL | RW | Indicates whether the font is strikethrough.
Weight | short | RW | The boldness of the font.
Charset | short | RW | The character set used in the font, such as ANSI_CHARSET, DEFAULT_CHARSET, or SYMBOL_CHARSET.

When to Use

Use this interface to change or retrieve the properties of a font object through the IDispatch::Invoke method in Automation.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IFont


IFontDisp - Ole Implementation

The system provides a standard implementation of a font object with the IFontDisp interface on top of the underlying system font support. A font object is created through the function OleCreateFontIndirect. A font object supports a number of read-write properties as well as a set of methods through its interface IFont and supports the same set of properties (but not the methods) through a dispatch interface IFontDisp which is derived from IDispatch to provide access to the font's properties through Automation. The system implementation of the font object supplies both interfaces.

See Also

IFont, IFontDisp


IOleControl

The IOleControl interface provides the features for supporting keyboard mnemonics (GetControlInfo, OnMnemonic), ambient properties (OnAmbientPropertyChange), and events (FreezeEvents) in control objects.

When to Implement

Implement this interface for a control object to communicate with the control's container, for example, when managing keyboard activity or obtaining the container's ambient properties.

When to Use

A control container uses this interface to work with keyboard mnemonics, ambient properties, and events of a contained control object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleControl Methods | Description
GetControlInfo | Fills in a CONTROLINFO structure with information about the control's keyboard behavior.
OnMnemonic | Informs the control that the user has pressed a keystroke that the control specified through GetControlInfo. The control takes whatever action is appropriate for the keystroke.
OnAmbientPropertyChange | Informs an control that one or more of the container's ambient properties has changed.
FreezeEvents | Indicates whether or not the container ignores or accepts events from the control.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControlSite, ISimpleFrameSite


IOleControl::FreezeEvents

Indicates whether the container is ignoring or accepting events from the control.

HRESULT FreezeEvents(
  BOOL bFreeze  //Indicates whether to ignore or process events
);

Parameters

bFreeze
[out] Indicates whether the container will ignore (TRUE) or now process (FALSE) events from the control.

Return Values

S_OK
Returned in all cases.

Remarks

The control is not required to stop sending events when bFreeze is TRUE. However, the container is not going to process them in this case. If a control depends on the container's processing -- as with request events that return information from the container -- the control must either discard the event or queue the event to send later when IOleControl::FreezeEvents returns FALSE.

Notes to Implementers

As with IOleControl::OnAmbientPropertyChange, S_OK is returned in all cases in order to prevent a container from making assumptions about a control's behavior based on return values.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IOleControl::GetControlInfo

Fills in a CONTROLINFO structure with information about a control's keyboard mnemonics and keyboard behavior.

HRESULT GetControlInfo(
  CONTROLINFO* pCI  //Pointer to structure
);

Parameters

pCI
[in, out] Pointer to the caller-allocated CONTROLINFO structure to be filled in.

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The structure was filled successfully.
E_NOTIMPL
The control has no mnemonics.
E_POINTER
The address in pCI is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CONTROLINFO


IOleControl::OnAmbientPropertyChange

Informs a control that one or more of the container's ambient properties (available through the control site's IDispatch) has changed.

HRESULT OnAmbientPropertyChange(
  DISPID dispID  //Dispatch identifier of the ambient property
);

Parameters

dispID
[in] Dispatch identifier of the ambient property that changed. If the dispID parameter is DISPID_UNKNOWN, it indicates that multiple properties changed. In this case, the control should check all the ambient properties of interest to obtain their current values.

Return Values

S_OK
Returned in all cases.

Remarks

Notes to Implementers

S_OK is returned in all cases even when the control does not support ambient properties or some other failure has occurred. The caller sending the notification cannot attempt to use an error code (such as E_NOTIMPL) to determine whether to send the notification in the future. Such semantics are not part of this interface.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IOleControl::OnMnemonic

Informs a control that the user has pressed a keystroke that matches one of the ACCEL entries in the mnemonic table returned through IOleControl::GetControlInfo. The control takes whatever action is appropriate for the keystroke.

HRESULT OnMnemonic(
  LPMSG pMsg  //Pointer to structure
);

Parameters

pMsg
[in] Pointer to the MSG structure describing the keystroke to be processed.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The control accepted the mnemonic.
E_NOTIMPL
The control doesn't handle any mnemonics. This indicates an unexpected condition and a caller error. For example, the caller has mismatched which control has which mnemonic.

Remarks

Notes to Implementers

If a control changes the contents of its CONTROLINFO structure, it must notify its container by calling IOleControlSite::OnControlInfoChanged.

Notes to Callers

A container of a control is allowed to cache the control's CONTROLINFO structure, provided that the container implements IOleControlSite::OnControlInfoChanged to know when it must update its cached information.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl::GetControlInfo, IOleControlSite::OnControlInfoChanged


IOleControlSite

The IOleControlSite interface provides the methods that enable a site object to manage each embedded control within a container. A site object provides IOleControlSite as well as other site interfaces such as IOleClientSite and IOleInPlaceSite. When a control requires the services expressed through this interface, it will query one of the other client site interfaces for IOleControlSite.

When to Implement

Implement this interface on an in-place capable site object to support the embedding of controls in the site.

When to Use

A control uses this interface to work with a control-aware container.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IOleControlSite Methods | Description
OnControlInfoChanged | Informs the container that the control's CONTROLINFO structure has changed and that the container should call the control's IOleControl::GetControlInfo for an update.
LockInPlaceActive | Indicates whether or not this control should remain in-place active, regardless of possible deactivation events.
GetExtendedControl | Requests an IDispatch pointer to the extended control that the container uses to wrap the real control.
TransformCoords | Converts between a POINTL structure expressed in HIMETRIC units (as is standard in OLE) and a POINTF structure expressed in units the container specifies.
TranslateAccelerator | Instructs the container to process a specified keystroke.
OnFocus | Indicates whether the embedded control in this control site has gained or lost the focus.
ShowPropertyFrame | Instructs the container to show a property page frame for the control object if the container so desires.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl, ISimpleFrameSite


IOleControlSite::GetExtendedControl

Requests an IDispatch pointer to the extended control that the container uses to wrap the real control.

HRESULT GetExtendedControl(
  IDispatch** ppDisp  //Address of output variable that receives the
                      // IDispatch interface pointer
);

Parameters

ppDisp
[out] Address of IDispatch* pointer variable that receives the interface pointer to the extended control. If an error occurs, the implementation must set *ppDisp to NULL. On success, the caller is responsible for calling IDispatch::Release when *ppDisp is no longer needed.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The extended control's IDispatch is returned in ppDisp.
E_NOTIMPL
The container does not implement extended controls.
E_POINTER
The address in ppDisp or *ppDisp is not valid. For example, it may be NULL.

Remarks

This method gives the real control access to whatever properties and methods the container maintains in the extended control. These properties and methods would otherwise be inaccessible to the control.

Notes to Callers

The returned pointer is the responsibility of the caller, which must release it when it is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IOleControlSite::LockInPlaceActive

Indicates whether or not a control should remain in-place active. Calls to this method typically nest an event to ensure that the object's activation state remains stable throughout the processing of the event.

HRESULT LockInPlaceActive(
  BOOL fLock  //Indicates whether to ensure the active state
);

Parameters

fLock
[in] Indicates whether to ensure the in-place active state (TRUE) or to allow activation to change (FALSE). When TRUE, a supporting container must not deactivate the in-place object until this method is called again with FALSE.

Return Values

S_OK
The lock or unlock was made successfully.
E_NOTIMPL
The container does not support in-place locking.

Remarks

This method affects the control's in-place active state but not its UI-active state.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IOleControlSite::OnControlInfoChanged

Informs the container that the control's CONTROLINFO structure has changed and that the container should call the control's IOleControl::GetControlInfo for an update.

HRESULT OnControlInfoChanged(void);

Return Values

S_OK
Returned in all cases.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl::GetControlInfo


IOleControlSite::OnFocus

Indicates whether the control managed by this control site has gained or lost the focus, according to the fGotFocus parameter. The container uses this information to update the state of Default and Cancel buttons according to how the control with the focus processes Return or Esc keys. A control's behavior regarding the Return and Esc keys is specified in the control's CONTROLINFO structure. See IOleControl::GetControlInfo.

HRESULT OnFocus(
  BOOL fGotFocus  //Indicates whether the control gained focus
);

Parameters

fGotFocus
[in] Indicates whether the control gained (TRUE) or lost the focus (FALSE).

Return Values

S_OK
Returned in all cases.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl::GetControlInfo


IOleControlSite::ShowPropertyFrame

Instructs a container to display a property sheet for the control embedded in this site.

HRESULT ShowPropertyFrame(void);

Return Values

This method supports the standard return value E_OUTOFMEMORY, as well as the following:

S_OK
The container successfully displayed property pages for this control and the control must not show its own.
E_NOTIMPL
The container does not need to show property pages itself.

Remarks

A control must always call this method in the container first when it intends to show its own property pages. Calling this method gives the container a chance to have those property pages work with the container's extended controls. The container may include its own property pages as well in such cases, which doesn't affect the control at all. If the container does not implement this method or if it returns a failure of any kind, the control can show its property pages directly. Otherwise, the container has shown the pages.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IOleControlSite::TransformCoords

Converts between a POINTL structure expressed in HIMETRIC units (as is standard in OLE) and a POINTF structure expressed in units specified by the container. By converting the methods, the control can ensure that it sends coordinate information to the container in units that are directly usable in the container without additional conversion.

HRESULT TransformCoords(
  POINTL* pPtlHimetric ,  //Address of POINTL structure
  POINTF* pPtfContainer ,  //Address of POINTF structure
  DWORD dwFlags           //Flags indicating the exact conversion
);

Parameters

pPtlHimetric
[in, out] Address of a POINTL structure containing coordinates expressed in HIMETRIC units. This is an [in] parameter when dwFlags contains XFORMCOORDS_HIMETRICTOCONTAINER; it is [out] with XFORMCOORDS_CONTAINERTOHIMETRIC. In the latter case, the contents are undefined on error.
pPtfContainer
[in, out] Address of a caller-allocated POINTF structure that receives the converted coordinates. This is an [in] parameter when dwFlags contains XFORMCOORDS_CONTAINERTOHIMETRIC; it is [out] with XFORMCOORDS_HIMETRICTOCONTAINER. In the latter case, the contents are undefined on error.
dwFlags
[in] Flags indicating the exact conversion to perform. The dwFlags parameter can be any combination of the following values except where indicated:
Flag Value | Description
XFORMCOORDS_POSITION | The coordinates to convert represent a position point. Cannot be used with XFORMCOORDS_SIZE.
XFORMCOORDS_SIZE | The coordinates to convert represent a set of dimensions. Cannot be used with XFORMCOORDS_POSITION.
XFORMCOORDS_HIMETRICTOCONTAINER | The operation converts pptlHimetric into pptfContainer. Cannot be used with XFORMCOORDS_CONTAINERTOHIMETRIC.
XFORMCOORDS_CONTAINERTOHIMETRIC | The operation converts pptfContainer into pptlHimetric. Cannot be used with XFORMCOORDS_HIMETRICTOCONTAINER.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The conversion was performed successfully.
E_NOTIMPL
The container does not require any special coordinate conversions. The container deals completely in HIMETRIC.
E_POINTER
The address in pptlHimetric or pptfContainer is not valid. For example, it may be NULL.

Remarks

A control uses this method when it has to send coordinates to a container within an event or some other custom call or when the control has container coordinates that it needs to convert into HIMETRIC units.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

POINTF


IOleControlSite::TranslateAccelerator

Instructs the control site to process the keystroke described in pMsg and modified by the flags in grfModifiers.

HRESULT TranslateAccelerator(
  LPMSG pMsg ,        //Pointer to the structure
  DWORD grfModifiers  //Flags describing the state of the keys
);

Parameters

pMsg
[in] Pointer to the MSG structure describing the keystroke to be processed.
grfModifiers
[in] Flags describing the state of the Control, Alt, and Shift keys. The value of the flag can be any valid KEYMODIFIERS enumeration values.

Return Values

S_OK
The container processed the message.
S_FALSE
The container did not process the message. This value must also be returned in all other error cases besides E_NOTIMPL.
E_NOTIMPL
The container does not implement accelerator support.

Remarks

This method is called by a control that can be UI-active. In such cases, a control can process all keystrokes first through IOleInPlaceActiveObject::TranslateAccelerator, according to normal OLE Compound Document rules. Inside that method, the control can give the container certain messages to process first by calling IOleControlSite::TranslateAccelerator and using the return value to determine if any processing took place. Otherwise, the control always processes the message first. If the control does not use the keystroke as an accelerator, it passes the keystroke to the container through this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleInPlaceActiveObject::TranslateAccelerator, KEYMODIFIERS


IPerPropertyBrowsing

The IPerPropertyBrowsing interface accesses the information in the property pages offered by an object.

When to Implement

Implement this interface on all objects that have property pages so that clients can access information about the properties.

When to Use

Use this interface to access information about an object's properties.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPerPropertyBrowsing Methods | Description
GetDisplayString | Returns a text string describing the specified property.
MapPropertyToPage | Returns the CLSID of the property page that allows manipulation of the specified property.
GetPredefinedStrings | Returns a counted array of strings (LPOLESTR pointers) listing the descriptions of the allowable values that the specified property can accept (i.e., the values returned from IPerPropertyBrowsing::GetPredefinedValue.
GetPredefinedValue | Returns a VARIANT containing the value of a property identified with dispID that is associated with a predefined string name as returned from IPerPropertyBrowsing::GetPredefinedStrings.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage, IPropertyPage2, IPropertyPageSite, ISpecifyPropertyPages


IPerPropertyBrowsing::GetDisplayString

Returns a text string describing the property identified with dispID in the caller's user interface. In other words, the returned text is a displayable name describing the property and can be displayed in the caller's user interface. The string itself is a BSTR allocated by this method with SysAllocString. Upon return, the string is the responsibility of the caller, which must free it with SysFreeString when it is no longer needed.

HRESULT GetDisplayString(
  DISPID dispID,  //Dispatch identifier for the property
  BSTR *pbstr     //Receives a pointer to the displayable string
                  //describing the property
);

Parameters

dispID
[in] Dispatch identifier of the property whose display name is requested.
pbstr
[out] Pointer to the BSTR containing the display name for the property identified with dispID.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The display name was successfully returned.
E_NOTIMPL
The object does not provide display names for individual properties. The caller has the recourse to check the object's type information for the text name of the object in this case.
E_POINTER
The address in pbstr is not valid. For example, it may be NULL.

Remarks

Notes to Implementers

The caller is responsible for freeing the pbstr string with SysFreeString when it is no longer needed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPerPropertyBrowsing::GetPredefinedStrings

Returns a counted array of string pointers (LPOLESTR pointers). The strings pointed to provide a list of names that each correspond to values that the property specified with dispID can accept.

HRESULT GetPredefinedStrings(
  DISPID dispID,          //Dispatch identifier for property
  CALPOLESTR *pcaStringsOut,  //Receives a pointer to an array of
                              //strings
  CADWORD *pcaCookiesOut  //Receives a pointer to array of DWORDs
);

Parameters

dispID
[in] Dispatch identifier of the property for which the caller is requesting the string list.
pcaStringsOut
[out] Pointer to a caller-allocated, counted array structure that contains the element count and address of a method-allocated array of string pointers. This method also allocates memory for the string values containing the predefined names, and it stores the string pointers in the array. If the method fails, no memory is allocated, and the contents of the structure are undefined.
pcaCookiesOut
[out] Pointer to the caller-allocated, counted array structure that contains the element count and address of a method-allocated array of DWORDs. The DWORD values in the array can be passed to IPerPropertyBrowsing::GetPredefinedValue to retrieve the value associated with the name in the same array position inside pcaStringsOut. If the method fails, no memory is allocated, and the contents of the structure are undefined.

Return Values

This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The arrays were allocated and filled successfully.
E_NOTIMPL
This method is not implemented and predefined names are not supported.
E_POINTER
The address in pcaStringsOut or pcaCookiesOut is not valid. For example, either may be NULL.

Remarks

Each string returned in the array pointed to by pcaStringsOut has a matching token in the counted array pointed to by pcaCookiesOut, where the token can be passed to IPerPropertyBrowsing::GetPredefinedValue to get the actual value (a VARIANT) corresponding to the string.

Using the predefined strings, a caller can obtain a list of strings for populating user interface elements, such as a drop-down listbox. When the end user selects one of these strings as a value to assign to a property, the caller can then obtain the corresponding value through IPerPropertyBrowsing::GetPredefinedValue.

Notes to Callers

Both the CALPOLESTR and CADWORD structures passed to this method are caller-allocated. The caller is responsible for freeing each string pointed to from the CALPOLESTR array as well as the CALPOLESTR structure.

All memory is allocated with CoTaskMemAlloc. The caller is responsible for freeing the strings and the array with CoTaskMemFree.

Upon return from this method, the caller is responsible for all this memory and must free it when it is no longer needed. Code to achieve this appears as follows:

CALPOLESTR     castr;
CWDWORD        cadw;
ULONG          i;

pIPerPropertyBrowsing->GetPredefinedStrings(dispID, &castr, &cadw);

//...Use the strings and the cookies

CoTaskMemFree((void *)cadw.pElems);

for (i=0; i < castr.cElems; i++)
    CoTaskMemFree((void *)castr.pElems[i]);

CoTaskMemFree((void *)castr.pElems);

Notes to Implementers

Support for predefined names and values is not required. If your object does not support these names, return E_NOTIMPL from this method. If this method is not implemented, IPerPropertyBrowsing::GetPredefinedValue must not be implemented either.

This method fills the cElems and pElems fields of the CADWORD and CALPOLESTR structures. It allocates the arrays pointed to by these structures with CoTaskMemAlloc and fills those arrays. In the CALPOLESTR case, this method also allocates each string with CoTaskMemAlloc, storing each string pointer in the array.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CADWORD, CALPOLESTR, CoTaskMemAlloc, CoTaskMemFree, IPerPropertyBrowsing::GetPredefinedValue


IPerPropertyBrowsing::GetPredefinedValue

Returns a VARIANT containing the value of the property specified by dispID. This property is associated with a predefined string name as returned from IPerPropertyBrowsing::GetPredefinedStrings. The predefined string is identified by a token returned from GetPredefinedStrings.

HRESULT GetPredefinedValue(
  DISPID dispID ,  //Dispatch identifier for property
  DWORD dwCookie , //Token returned
  VARIANT *pVarOut //Receives a pointer to a VARIANT value for the
                   //property
);

Parameters

dispID
[in] Dispatch identifier of the property for which a predefined value is requested.
dwCookie
[in] Token identifying which value to return. The token was previously returned in the pcaCookiesOut array filled by IPerPropertyBrowsing::GetPredefinedStrings.
pVarOut
[out] Pointer to the VARIANT value for the property.

Return Values

This method supports the standard return values E_INVALIDARG,

E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The value was returned successfully.
E_NOTIMPL
This object does not support predefined strings or predefined values.
E_POINTER
The address in pVarOut is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The caller is responsible for freeing any allocations contained in the VARIANT. Unless the vt field of VARIANT is VT_VARIANT, the caller can free memory using a single call to VariantClear. Otherwise, the caller must recursively free the contained VARIANTs before freeing the outer VARIANT.

Notes to Implementers

Support for predefined names and values is not required. If your object does not support these names, return E_NOTIMPL from this method. If this method is not implemented, IPerPropertyBrowsing::GetPredefinedStrings must not be implemented either.

This method allocates any memory needed inside the VARIANT.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing::GetPredefinedStrings


IPerPropertyBrowsing::MapPropertyToPage

Returns the CLSID of the property page associated with the specified property. In other words, this method maps a specified property to the property page that allows a user to manipulate that property. The CLSID returned from this method can be passed to OleCreatePropertyFrameIndirect to specify the initial page to display in the property sheet.

HRESULT MapPropertyToPage(
  DISPID dispID ,  //Dispatch identifier for the property
  CLSID *pclsid    //Receives a pointer to CLSID for property
);

Parameters

dispID
[in] Dispatch identifier of the property of interest.
pclsid
[out] Pointer to the CLSID identifying the property page associated with the property specified by dispID. If this method fails, *pclsid is set to CLSID_NULL.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The CLSID was successfully returned.
E_NOTIMPL
The object does not support property pages at all or doesn't support mapping properties to the page CLSID. In other words, this feature of specific property browsing is not supported.
E_POINTER
The address in pclsid is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture

The IPicture interface manages a picture object and its properties. Picture objects provide a language-neutral abstraction for bitmaps, icons, and metafiles. As with the standard font object, the system provides a standard implementation of the picture object. Its primary interfaces are IPicture and IPictureDisp, the latter being derived from IDispatch to provide access to the picture's properties through Automation. A picture object is created with OleCreatePictureIndirect.

The picture object also supports the outgoing interface IPropertyNotifySink, so a client can determine when picture properties change. Since the picture object supports at least one outgoing interface, it also implements IConnectionPointContainer and its associated interfaces for this purpose.

The picture object also supports IPersistStream so it can save and load itself from an instance of IStream. An object that uses a picture object internally would normally save and load the picture as part of the object's own persistence handling. The function OleLoadPicture simplifies the creation of a picture object based on stream contents.

When to Implement

Typically, you use the OLE-provided picture object, which provides the IPicture and IPictureDisp interfaces for you. The IPicture interface is the primary interface implemented by the OLE-provided picture object. It allows the caller to manage picture properties and to use that picture in graphical rendering. Each property in the IPicture interface includes a get_PropertyName method if the property supports read access and a put_PropertyName method if the property supports write access. Most of the properties support only read access with the exception of hPal.

Property | Type | Access Allowed | Description
HANDLE | OLE_HANDLE (int) | R | The Windows GDI handle of the picture
hPal | OLE_HANDLE (int) | RW | The Windows handle of the palette used by the picture
Type | short | R | The type of picture (see below)
Width | OLE_XSIZE_HIMETRIC (long) | R | The width of the picture
Height | OLE_YSIZE_HIMETRIC (long) | R | The height of the picture
CurDC | HDC | R | The current device context
KeepOriginalFormat | BOOL | RW | If TRUE, the picture object maintains the entire original state of the picture in memory. If FALSE, any state not applicable to the user's machine is discarded
Attributes | DWORD | R | Miscellaneous bit attributes of the picture (see below)

When to Use

Use this interface to change the properties of a picture object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPicture Methods | Description
get_Handle | Returns the Windows GDI handle of the picture managed within this picture object.
get_Hpal | Returns a copy of the palette currently used by the picture object.
get_Type | Returns the current type of the picture.
get_Width | Returns the current width of the picture in the picture object.
get_Height | Returns the current height of the picture in the picture object.
Render | Draws the specified portion of the picture onto the specified device context, positioned at the specified location.
set_Hpal | Sets the current palette of the picture.
get_CurDC | Returns the current device context into which this picture is selected.
SelectPicture | Selects a bitmap picture into a given device context, returning the device context in which the picture was previously selected as well as the picture's GDI handle.
get_KeepOriginalFormat | Returns the current value of the picture object's KeepOriginalFormat property.
put_KeepOriginalFormat | Sets the picture object's KeepOriginalFormat property.
PictureChanged | Notifies the picture object that its picture resource changed.
SaveAsFile | Saves the picture's data into a stream in the same format that it would save itself into a file.
get_Attributes | Returns the current set of the picture's bit attributes.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture - Ole Implementation, IPictureDisp


IPicture::get_Attributes

Returns the current set of the picture's bit attributes.

HRESULT get_Attributes(
  DWORD* pdwAttr  //Receives a pointer to attribute value
);

Parameters

pdwAttr
[out] Pointer to the caller's variable that receives the attribute value.

The Attributes property can contain any combination of the values from the PICTURE enumeration.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The attribute bits were returned successfully.
E_POINTER
The address in pdwAttr is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

PICTURE


IPicture::get_CurDC

Returns the handle of the current device context. This property is valid only for bitmap pictures.

HRESULT get_CurDC(
  HDC* phdcOut  //Receives a pointer to device context
);

Parameters

phdcOut
[out] Pointer to the caller's variable to receive the device context.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The device context was returned successfully.
E_POINTER
The address in phdcOut is not valid. For example, it may be NULL.

Remarks

The CurDC property and the IPicture::SelectPicture method exist to circumvent restrictions in Windows; specifically, that an object can only be selected into exactly one device context at a time. In some cases, a picture object may be permanently selected into a particular device context (for example, a control may use a certain picture for a background). To use this picture property elsewhere, it must be temporarily deselected from its old device context, selected into the new device context for the operation, then reselected back into the old device context. The IPicture::get_CurDC method returns the device context handle into which the picture is currently selected. The IPicture::SelectPicture method selects the picture into a new device context, returning the old device context and the picture's GDI handle. The caller should select the picture back into the old device context when the caller is done with it, as is normal for Windows code.

Notes to Callers

The caller always owns any device contexts passed between it and the picture object. Since the picture object maintains a copy of the HDC, the caller should use a memory device context (created with the Win32 function CreateCompatibleDC) and not a screen device context (from GetDC, CreateDC, or BeginPaint), because the screen device contexts are a limited system resource.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture::SelectPicture


IPicture::get_Handle

Returns the Windows GDI handle of the picture managed within this picture object to a specified location.

HRESULT get_Handle(
  OLE_HANDLE* phandle  //Receives a pointer to GDI handle
);

Parameters

phandle
[out] Pointer to the caller's OLE_HANDLE variable that receives the handle. The caller is responsible for this handle upon successful return. The variable is set to NULL on failure.

Return Values

This method supports the standard return values E_FAIL and E_OUTOFMEMORY, as well as the following:

S_OK
The handle was returned successfully.
E_POINTER
The address in phandle is not valid. For example, it may be NULL.

Remarks

Notes to Callers

The picture object may retain ownership of the picture; however, the caller can be assured that the picture will remain valid until either the caller specifically destroys the picture or the picture object is itself destroyed. The fOwn parameter to OleCreatePictureIndirect determines ownership when the picture object is created. OleLoadPicture forces fOwn to TRUE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::get_Height

Returns the current height of the picture in the picture object.

HRESULT get_Height(
  OLE_YSIZE_HIMETRIC* pheight  //Receives a pointer to height
);

Parameters

pheight
[out] Pointer to the caller's OLE_YSIZE_HIMETRIC variable that receives the height.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The height was returned successfully.
E_POINTER
The address in pheight is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::get_hPal

Returns a copy of the palette currently used by the picture object.

HRESULT get_hPal(
  OLE_HANDLE* phpal  //Receives a pointer to palette handle
);

Parameters

phpal
[out] Pointer to the caller's OLE_HANDLE variable to receive the palette handle. The variable is set to NULL on failure.

Return Values

This method supports the standard return values E_FAIL and E_OUTOFMEMORY, as well as the following:

S_OK
The handle was returned successfully.
S_FALSE
This picture has no palette. The parameter *phpal is set to NULL.
E_POINTER
The address in phpal is not valid. For example, it may be NULL.

Remarks

Notes to Callers

If the picture object has ownership of the picture, it also has ownership of the palette and will destroy it when the object is itself destroyed. Otherwise the caller owns the palette. The fOwn parameter to OleCreatePictureIndirect determines ownership. OleLoadPicture sets fOwn to TRUE to indicate that the picture object owns the palette.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::get_KeepOriginalFormat

Returns the current value of the picture's KeepOriginalFormat property.

HRESULT get_KeepOriginalFormat(
  BOOL* pfkeep  //Receives a pointer to the value of
                //KeepOriginalFormat
);

Parameters

pfkeep
[out] Pointer to the caller's BOOL variable that receives the value of the property.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The value of the KeepOriginalFormat property was returned successfully.
E_POINTER
The address in pfkeep is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture::put_KeepOriginalFormat


IPicture::get_Type

Returns the current type of the picture contained in the picture object.

HRESULT get_Type(
  short* ptype  //Receives a pointer to the picture type
);

Parameters

ptype
[out] Pointer to the caller's short variable to receive the picture type. The Type property can have any one of the values contained in the PICTYPE enumeration.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The type was returned successfully.
E_POINTER
The address in ptype is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

OleCreatePictureIndirect, PICTYPE


IPicture::get_Width

Returns the current width of the picture in the picture object.

HRESULT get_Width(
  OLE_XSIZE_HIMETRIC* pwidth  //Receives a pointer to width
);

Parameters

pwidth
[out] Pointer to the caller's OLE_XSIZE_HIMETRIC variable that receives the width.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The width was returned successfully.
E_POINTER
The address in pwidth is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::PictureChanged

Notifies the picture object that its picture resource has changed. On Win32, this method only sends an IPropertyNotifySink::OnChanged(DISPID_PICT_HANDLE) to any connected sinks.

HRESULT PictureChanged(void);

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
This value is returned in all cases except when the picture object is uninitialized.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::put_KeepOriginalFormat

Sets the value of the picture's KeepOriginalFormat property.

HRESULT put_KeepOriginalFormat(
  BOOL keep  //Specifies the new value of KeepOriginalFormat
);

Parameters

keep
[in] Specifies the new value to assign to the property.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The property was successfully changed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture::get_KeepOriginalFormat


IPicture::Render

Renders (draws) a specified portion of the picture defined by the offset (xSrc,ySrc) of the source picture and the dimensions to copy (cxSrc,xySrc). This picture is rendered onto the specified device context, positioned at the point (x,y), and scaled to the dimensions (cx,cy). The prcWBounds parameter specifies the position of this rendering if the destination device context is itself a metafile. Such information is necessary to place one metafile in another. For more information, see the prcWBounds parameter of IViewObject2::Draw.

HRESULT Render(
  HDC hdc, //Handle of device context on which to render the image
  long x,  //Horizontal position of image in hdc
  long y,  //Vertical position of image in hdc
  long cx, //Horizontal dimension of destination rectangle
  long cy, //Vertical dimension of destination rectangle
  OLE_XPOS_HIMETRIC xSrc,
           //Horizontal offset in source picture
  OLE_YPOS_HIMETRIC ySrc,
           //Vertical offset in source picture
  OLE_XSIZE_HIMETRIC cxSrc,
           //Amount to copy horizontally in source picture
  OLE_YSIZE_HIMETRIC cySrc,
           //Amount to copy vertically in source picture
  LPCRECT prcWBounds
           //Pointer to position of destination for a metafile hdc
);

Parameters

hdc
[in] Handle of the device context on which to render the image.
x
[in] Horizontal coordinate in hdc at which to place the rendered image.
y
[in] Vertical coordinate in hdc at which to place the rendered image.
cx
[in] Horizontal dimension of the destination rectangle.
cy
[in] Vertical dimension of the destination rectangle.
xSrc
[in] Horizontal offset in the source picture from which to start copying.
ySrc
[in] Vertical offset in the source picture from which to start copying.
cxSrc
[in] Horizontal extent to copy from the source picture.
cySrc
[in] Vertical extent to copy from the source picture.
prcWBounds
[in] Pointer to a rectangle containing the position of the destination within a metafile device context if hdc is a metafile DC. Cannot be NULL in such cases.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The picture was rendered successfully.
E_POINTER
The address in prcWBounds is not valid when hdc contains a metafile device context.
CTL_E_INVALIDPROPERTYVALUE
The parameter cx, cy, cxSrc or cySrc has a value of 0.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::SaveAsFile

Saves the picture's data into a stream in the same format that it would save itself into a file. Bitmaps use the BMP file format, metafiles the WMF format, and icons the ICO format. For more information, see the Platform SDK.

HRESULT SaveAsFile(
  IStream * pstream,
                 //Pointer to stream where picture writes its data
  BOOL fSaveMemCopy ,
                 //Indicates whether to save the picture in memory
  LONG* pcbSize  //Receives a pointer to the number of bytes written
                 //to stream
);

Parameters

pstream
[in] Pointer to the stream into which the picture writes its data.
fSaveMemCopy
[in] Flag indicating whether or not to save a copy of the picture in memory.
pcbSize
[out] Pointer to the caller's LONG variable to receive the number of bytes written into the stream. This value can be NULL, indicating that the caller does not require this information.

Return Values

This method supports the standard return values E_FAIL and E_INVALIDARG, as well as the following:

S_OK
The picture was saved successfully.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture::SelectPicture

Selects a bitmap picture into a given device context, and returns the device context in which the picture was previously selected as well as the picture's GDI handle. This method works in conjunction with IPicture::get_CurDC.

HRESULT SelectPicture(
  HDC hdcIn,  //New device context
  HDC* phdcOut,
              //Receives a pointer to the previous device context
  OLE_HANDLE* phbmpOut
              //Receives a pointer to GDI handle of the picture
);

Parameters

hdcIn
[in] Device context in which to select the picture.
phdcOut
[out] Pointer to the caller's HDC variable to receive the previous device context. This parameter can be NULL if the caller does not need this information. Ownership of the device context is always the responsibility of the caller.
phbmpOut
[out] Pointer to the caller's HDC variable to receive the GDI handle of the picture. This parameter can be NULL if the caller does not need the handle. Ownership of this handle is determined by the fOwn parameter passed to OleCreatePictureIndirect. Pictures loaded from a stream always own their resources.

Return Values

This method supports the standard return valuesE_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The picture was selected successfully.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture::get_CurDC


IPicture::set_hPal

Assigns a GDI palette to the picture contained in the picture object.

HRESULT set_hPal(
  OLE_HANDLE hpal  //Handle for GDI palette for the picture
);

Parameters

hpal
[in] Handle to the GDI palette assigned to the picture.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, as well as the following:

S_OK
The palette was assigned successfully.

Remarks

Notes to Implementers

Ownership of the palette passed to this method depends on how the picture object was created, as specified by the fOwn parameter to OleCreatePictureIndirect. OleLoadPicture forces fOwn to TRUE; if the object owns the picture, then it takes over ownership of this palette.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPicture - Ole Implementation

Picture objects provide a language-neutral abstraction for bitmaps, icons, and metafiles. As with the standard font object, the system provides a standard implementation of the picture object. Its primary interfaces are IPicture and IPictureDisp, the latter being derived from IDispatch to provide access to the picture's properties through Automation. A picture object is created with OleCreatePictureIndirect and supports both the IPicture and the IPictureDisp interfaces.

Remarks

The OLE-provided picture object implements the complete semantics of the IPicture and IPictureDisp interfaces.

See Also

IPicture


IPictureDisp

The IPictureDisp interface exposes the picture object's properties through Automation. It provides a subset of the functionality available through IPicture methods.

When to Implement

A picture object implements this interface along with IPicture to provide access to the picture's properties through Automation. Typically, it is not necessary to implement this interface on your own object since there is an OLE-provided picture object.

The following table describes the dispIDs for the various picture properties.

Symbol | Value
DISPID_PICT_HANDLE | 0
DISPID_PICT_HPAL | 2
DISPID_PICT_TYPE | 3
DISPID_PICT_WIDTH | 4
DISPID_PICT_HEIGHT | 5
DISPID_PICT_RENDER | 6

Each property in the IPictureDisp interface includes a get_PropertyName method if the property supports read access and a put_PropertyName method if the property supports write access. Most of the properties support read access only with the exception of the hPal property.

Property | Type | Access Allowed | Description
Handle | OLE_HANDLE (int) | R | The Windows GDI handle of the picture
hPal | OLE_HANDLE (int) | RW | The Windows handle of the palette used by the picture.
Type | short | R | The type of picture (see below).
Width | OLE_XSIZE_HIMETRIC (long) | R | The width of the picture.
Height | OLE_YSIZE_HIMETRIC (long) | R | The height of the picture.

When to Use

Use this interface to change or retrieve the properties of a picture object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPicture


IPictureDisp - Ole Implementation

Picture objects provide a language-neutral abstraction for bitmaps, icons, and metafiles. As with the standard font object, the system provides a standard implementation of the picture object. Its primary interfaces are IPicture and IPictureDisp, the latter being derived from IDispatch to provide access to the picture's properties through Automation. A picture object is created with OleCreatePictureIndirect and supports both the IPicture and the IPictureDisp interfaces.

Remarks

The OLE-provided picture object implements the complete semantics of the IPicture and IPictureDisp interfaces.

See Also

IPicture, IPictureDisp


IPointerInactive

The IPointerInactive interface enables an object to remain inactive most of the time, yet still participate in interaction with the mouse, including drag and drop.

Objects can be active (in-place or UI active) or they can be inactive (loaded or running). An active object creates a window and can receive Windows mouse and keyboard messages. An inactive object can render itself and provide a representation of its data in a given format. While they provide more functionality, active objects also consume more resources than inactive objects. Typically, they are larger and slower than inactive objects. Thus, keeping an object inactive can provide performance improvements.

However, an object, such as a control, needs to be able to control the mouse pointer, fire mouse events, and act as a drop target so it can participate in the user interface of its container application.

When to Implement

Implement this interface on an object, such as a control, so the object can support a minimal level of interaction with the mouse and keyboard while it is in the inactive state. The object can control the mouse pointer, fire mouse events, and act as a drop target without being in the active state at all times. The object does not have to set the OLEMISC_ACTIVATEWHENVISIBLE enumeration value, does not have to have a window, and thus, can increase its performance.

If the object must work with down-level containers, it may have to set the OLEMISC_ACTIVATEWHENVISIBLE enumeration value. However, an updated container that supports objects that implement IPointerInactive can use the OLEMISC_IGNOREACTIVATEWHENVISIBLE enumeration value to override OLEMISC_ACTIVATEWHENVISIBLE.

When to Use

A container calls the methods in this interface for its embedded objects so that the embedded objects can participate in the user interface for the application.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPointerInactive Methods | Description
GetActivationPolicy | Returns the present activation policy for the object.
OnInactiveMouseMove | Notifies the object that the mouse pointer has moved over it so the object can fire mouse events.
OnInactiveSetCursor | Sets the mouse pointer for an inactive object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

OLEMISC


IPointerInactive::GetActivationPolicy

Returns the present activation policy for the object. This method is called by the container on receipt of a WM_SETCURSOR or WM_MOUSEMOVE message when an inactive object is under the mouse pointer.

HRESULT GetActivationPolicy(
  DWORD* pdwPolicy   //Pointer to activation policy
);

Parameter

pdwPolicy
[out] Pointer to the activation policy as specified by the POINTERINACTIVE enumeration values.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The activation policy was successfully returned.

Remarks

A container calls this method when it receives a WM_SETCURSOR or WM_MOUSEMOVE message with the mouse pointer over an inactive object that supports IPointerInactive. The object returns its activation policy through the POINTERINACTIVE enumeration values.

The object can request to be in-place activated as soon as the mouse enters it through the POINTERINACTIVE_ACTIVATEONENTRY value. An object that provides more visual feedback than simply setting the mouse pointer would use this value. For example, if the object supports special visual feedback, it must enter the active state so it can draw the visual feedback that it supports.

An object can also use this method to request activation when the mouse is dragged over them during a drag and drop operation through the POINTERINACTIVE_ACTIVATEONDRAG. See the POINTERINACTIVE enumeration for more information.

If the object returns one of these values, the container should activate the object immediately and forward the Window message that triggered the call. The object then stays active and processes subsequent messages through its own window until the container gets another WM_SETCURSOR or WM_MOUSEMOVE. At this point, the container should deactivate the object.

Note For windowless OLE objects this mechanism is slightly different. See IOleInPlaceSiteWindowless for more information on drag and drop operations for windowless objects.

If the object returns both the POINTERINACTIVE_ACTIVATEONENTRY and the POINTERINACTIVE_DEACTIVATEONLEAVE values, the object is activated only when the mouse is over the object. If the POINTERINACTIVE_ACTIVATEONENTRY value alone is set, the object is activated once when the mouse first enters it, and it remains active.

Note to Callers

The activation policy should not be cached. The container should call this method each time the mouse enters an inactive object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IOleInPlaceSiteWindowless, POINTERINACTIVE


IPointerInactive::OnInactiveMouseMove

Notifies the object that the mouse pointer has moved over it so the object can fire mouse events. This method is called by the container on receipt of a WM_MOUSEMOVE method when an inactive object is under the mouse pointer.

HRESULT OnInactiveMouseMove(
  LPCRECT pRectBounds,  //Object bounding rectangle
  LONG x,               //Horizontal coordinate
  LONG y,               //Vertical coordinate
  DWORD grfKeyState     //
);

Parameter

pRectBounds
[in] The object bounding rectangle, in client coordinates of the containing window. This parameter tells the object its exact position and size on the screen when the WM_MOUSEMOVE message was received. This value is specified in units of the client's coordinate system.
x
[in] Horizontal coordinate of mouse location in units of the client's containing window.
y
[in] Vertical coordinate of mouse location in units of the client's containing window.
grfKeyState
[in] Identifies the current state of the keyboard modifier keys on the keyboard. Valid values can be a combination of any of the values MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The mouse pointer was successfully set.

Remarks

The container calls this method to notify the object that the mouse pointer is over the object after checking the object's activation policy by calling the IPointerInactive::GetActivationPolicy method. If the object has not requested to be activated in-place through that call, the container dispatches subsequent WM_MOUSEMOVE messages to the inactive object by calling OnInactiveMouseMove as long as the mouse pointer stays over the object. The object can then fire mouse move events.

To avoid rounding errors and to make the job easier on the object implementer, this method takes window coordinates in the units of its containing client window, that is, the window in which the object is displayed, instead of the usual HIMETRIC units. Thus, the same coordinates and code path can be used when the object is active and inactive. The window coordinates specify the mouse position. The bounding rectangle is also specified in the same coordinate system.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IPointerInactive::GetActivationPolicy


IPointerInactive::OnInactiveSetCursor

Sets the mouse pointer for an inactive object. This method is called by the container on receipt of a WM_SETCURSOR method when an inactive object is under the mouse pointer.

HRESULT OnInactiveSetCursor(
  LPCRECT pRectBounds,  //Object bounding rectangle
  LONG x,               //Horizontal coordinate
  LONG y,               //Vertical coordinate
  DWORD dwMouseMsg,     //Mouse message identifier
  BOOL fSetAlways       //Indicates whether object must set the
                        //mouse pointer
);

Parameter

pRectBounds
[in] The object bounding rectangle specified in client coordinate units of the containing window. This parameter tells the object its exact position and size on the screen when the WM_SETCURSOR message was received. This value is specified in units of the client's coordinate system.
x
[in] Horizontal coordinate of mouse location in units of the client's containing window.
y
[in] Vertical coordinate of mouse location in units of the client's containing window.
dwMouseMsg
[in] Identifier of the mouse message for which a WM_SETCURSOR occurred.
fSetAlways
[in] If this value is TRUE, the object must set the cursor; if this value is FALSE, the object is not obligated to set the cursor, and should return S_FALSE in that case.

Return Values

This method supports the standard return value E_FAIL, as well as the following:

S_OK
The mouse pointer was successfully set.
S_FALSE
The object did not set the cursor; the container should either set the cursor or call the object again with the parameter fSetAlways set to TRUE.

Remarks

The container calls this method to set the mouse pointer over an inactive object after checking the object's activation policy by calling the IPointerInactive::GetActivationPolicy method. If the object has not requested to be activated in-place through that call, the container dispatches subsequent WM_SETCURSOR messages to the inactive object by calling OnInactiveSetCursor as long as the mouse pointer stays over the object.

To avoid rounding errors and to make the job easier on the object implementer, this method takes window coordinates in the units of its containing client window, that is, the window in which the object is displayed, instead of the usual HIMETRIC units. Thus, the same coordinates and code path can be used when the object is active and inactive. The window coordinates specify the mouse position. The bounding rectangle is also specified in the same coordinate system.

OnInactiveSetCursor takes an additional parameter (fSetAlways) indicating whether the object is obligated to set the cursor or not. Containers should first call this method with this parameter FALSE. The object may return S_FALSE to indicate that it did not set the cursor. In that case, the container should either set the cursor itself, or, if it does not wish to do this, call the OnInactiveSetCursor method again with fSetAlways being TRUE.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IPointerInactive::GetActivationPolicy


IPrint

The IPrint interface enables compound documents in general and active documents in particular to support programmatic printing. Once a document is loaded, containers and other clients can call IPrint::Print to instruct a document to print itself, specifying printing control flags, the target device, the particular pages to print, and other options. The client can control the continuation of printing by calling the IContinueCallback interface.

An object that implements IPrint registers itself with the Printable key stored under its CLSID as follows:

HKEY_CLASSES_ROOT\CLSID\{...}\Printable

Callers determine whether a particular object class supports programmatic printing of its persistent state by looking in the registry for this key.

When to Implement

Implementing IPrint is optional. You usually implement IPrint on a view object.

When to Use

Call the methods of IPrint when you want an object to print its current state. Using this interface, a caller can tell the object to print itself, set the initial page number of the printed document, and obtain both the number of pages and the number of the first page to be printed.

Methods in VTable Order

IUnknown Methods | Description
QueryInterface | Returns a pointer to a specified interface.
AddRef | Increments the reference count.
Release | Decrements the reference count.
IPrint Methods | Description
SetInitialPageNum | Sets number of first page
GetPageInfo | Obtains number of pages and number of first page
Print | Prints object's persistent state

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IPrint::GetPageInfo

Retrieves the number of a document's first page and the total number of pages.

HRESULT GetPageInfo(
  LONG *pnFirstPage,  //Pointer to number of first page
  LONG *pcPages      //Pointer to total number of pages
);

Parameters

pnFirstPage
[out] Pointer to the page number of the first page. May be NULL, indicating that the caller doesn't need this number. If IPrint::SetInitialPageNum has been called, this parameter should contain the same value passed to that method. Otherwise, the value is the document's internal first page number.
pcPages
[out] Pointer to the total number of pages in this document. May be NULL, indicating that the caller doesn't need this number.

Return Value

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The requested page information was obtained.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IPrint::Print

Prints an object on the specified printer, using the specified job requirements.

HRESULT Print(
  DWORD grfFlags,       //Printing options
  DVTARGETDEVICE **pptd,
                        //Address of output variable that receives a
                        //pointer to the TARGETDEVICE structure
  PAGESET **ppPageSet,  //Address of output variable that receives a
                        //pointer to the PAGESET structure
  STGMEDIUM *pStgmOptions,  //Pointer to object-specific printing
                            //options
  IContinueCallback *pCallback,
                        //Pointer to callback interface
  LONG nFirstPage,      //Number of first page to be printed
  LONG *pcPagesPrinted, //Pointer to total number of pages printed
  LONG *pnLastPage      //Pointer to number of last page printed
);

Parameters

grfFlags
[in] A bit field specifying print options from the PRINTFLAG enumeration.
pptd
[in, out] Address of DVTARGETDEVICE pointer variable that receives a pointer to the TARGETDEVICE structure which describes the target print device.
ppPageSet
[in, out] Address of PAGESET pointer variable that receives a pointer to the PAGESET structure that indicates which pages are to be printed.
pStgmOptions
[unique][in, out] Pointer to object-specific printing options in a serialized OLE property set. May be NULL on input or return.
pCallback
[in] Pointer to the IContinueCallback interface on the view site, which is to be periodically polled at human-response speeds to determine whether printing should be abandoned. May be NULL.
nFirstPage
[in] The page number of the first page to be printed. This value overrides any value previously passed to IPrint::SetInitialPageNum.
pcPagesPrinted
[out] Pointer to the actual number of pages that were successfully printed.
pnLastPage
[out] Pointer to the number of the last page that was printed.

Return Values

This method supports the standarde return value E_UNEXPECTED, as well as the following:

S_OK
The printing operation was successfully completed.
PRINT_E_CANCELED
The print process was canceled before completion. *pcPagesPrinted indicates the number of pages that were in fact successfully printed before this error occurred.
PRINT_E_NOSUCHPAGE
A page specified in **pppageset or nFirstPage does not exist.

Remarks

The printer on which the object is to be printed is indicated by the DVTARGETDEVICE structure pointed to by pptd. The DEVMODE structure in the target device indicates whole-job printer-specific options, such as number of copies, paper size, and print quality. The DEVMODE structure may also contain orientation information in the dmOrientation field (this is indicated in the dmFields field). If present, then this paper orientation should be used; if absent, then natural orientation as determined by the object content is to be used.

Due to the possibility of user input, the parameters pptd and ppPageSet are both [in,out] structures. In the absence of user interaction (that is, if the PRINTFLAG_PROMPTUSER flag is not set), both the target device and the page set will necessarily be the same for input and output. However, if the user is prompted for print options, then the object returns target device and page-set information appropriate to what the user has actually chosen.

The ppstgmOptions parameter is also [in,out]. On exit, the object should write to *ppstgmOptions any object-specific information that it would need to reproduce this exact print job. Examples might include whether the user selected "sheet, notes, or both" in a spreadsheet application. The data passed is in the format of a serialized property set. The data is normally useful only when passed back in a subsequent call to the same object. Because a subsequent call may specify different user interaction flags, target device, or other settings, the caller can cause the document to be printed multiple times in the same way in slightly different printing contexts.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IPrint::SetInitialPageNum

Sets the number of the first page of a document.

HRESULT SetInitialPageNum(
  LONG nFirstPage  //Number of first page
);

Parameters

nFirstPage
[in] The desired first page number.

Return Value

This method supports the standard return values E_FAIL and E_UNEXPECTED, as well as the following:

S_OK
The number of the first page was set as requested.

Remarks

Setting the first page to a negative number is valid. This may be useful in printing a portion of the document with page numbers that specify an offset from the usual pagination.

Not all implementations permit the initial page number to be set, as some implementations simply lack the information as to how the page number should be presented in the final output.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in docobj.h.


IPropertyNotifySink

The IPropertyNotifySink interface is implemented by a sink object to receive notifications about property changes from an object that supports IPropertyNotifySink as an "outgoing" interface. The client that needs to receive the notifications in this interface (from a supporting connectable object) creates a sink with this interface and connects it to the connectable object through the connection point mechanism. For more information on connection points, see IConnectionPointContainer.

The object is itself required to call the methods of IPropertyNotifySink only for those properties marked with the [bindable] and [requestedit] attributes in the object's type information. When the object changes a [bindable] property, it is required to call IPropertyNotifySink::OnChanged. When the object is about to change a [requestedit] property, it must call IPropertyNotifySink::OnRequestEdit before changing the property and must also honor the action specified by the sink on return from this call.

The one exception to this rule is that no notifications are sent as a result of an object's initialization or loading procedures. At initialization time, it is assumed that all properties change and that all must be allowed to change. Notifications to this interface are therefore meaningful only in the context of a fully initialized/loaded object.

When to Implement

Implement this interface for a sink object that receives notifications about property changes from an object that supports this kind of notification.

When to Use

Use this interface to notify a sink object about changes in a property.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPropertyNotifySink Methods | Description
OnChanged | Notifies a sink that a bindable property has changed.
OnRequestEdit | Notifies a sink that a requestedit property is about to change.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IConnectionPoint, IConnectionPointContainer

Also, see the Automation branch of the help file for further explanations of type information, ODL files, and type libraries.


IPropertyNotifySink::OnChanged

Notifies a sink that the [bindable] property specified by dispID has changed. If dispID is DISPID_UNKNOWN, then multiple properties have changed together. The client (owner of the sink) should then retrieve the current value of each property of interest from the object that generated the notification.

HRESULT OnChanged(
  DISPID dispID  //Dispatch identifier of the property that changed
);

Parameters

dispID
[in] Dispatch identifier of the property that changed, or DISPID_UNKNOWN if multiple properties have changed.

Return Values

S_OK
This return value is returned in all cases.

Remarks

S_OK is returned in all cases even when the sink does not need [bindable] properties or when some other failure has occurred. In short, the calling object simply sends the notification and cannot attempt to use an error code (such as E_NOTIMPL) to determine whether to not send the notification in the future. Such semantics are not part of this interface.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyNotifySink::OnRequestEdit


IPropertyNotifySink::OnRequestEdit

Notifies a sink that a [requestedit] property is about to change and that the object is asking the sink how to proceed.

HRESULT OnRequestEdit(
  DISPID dispID  //Dispatch identifier of the property that is about
                 //to change
);

Parameters

dispID
[in] Dispatch identifier of the property that is about to change or DISPID_UNKNOWN if multiple properties are about to change.

Return Values

S_OK
The specified property or properties are allowed to change.
S_FALSE
The specified property or properties are not allowed to change. The caller must obey this return value by discarding the new property value(s). This is part of the contract of the [requestedit] attribute and this method.

Remarks

The sink may choose to allow or disallow the change to take place. For example, the sink may enforce a read-only state on the property. DISPID_UNKNOWN is a valid parameter to this method to indicate that multiple properties are about to change. In this case, the sink can enforce a global read-only state for all [requestedit] properties in the object, including any specific ones that the sink otherwise recognizes.

If the sink allows changes, the object must also make IPropertyNotifySink::OnChanged notifications for any properties that are marked [bindable] in addition to [requestedit].

This method cannot be used to implement any sort of data validation. At the time of the call, the desired new value of the property is unavailable and thus cannot be validated. This method's only purpose is to allow the sink to enforce a read-only state on a property.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyNotifySink::OnChanged


IPropertyPage

The IPropertyPage interface provides the main features of a property page object that manages a particular page within a property sheet. A property page implements at least IPropertyPage and can optionally implement IPropertyPage2 if selection of a specific property is supported. See IPerPropertyBrowsing::MapPropertyToPage for more information on specific property browsing. The methods of IPropertyPage2 allow the property sheet or property frame to instruct the page when to perform specific actions, mostly based on user input such as switching between pages or pressing various buttons that the frame itself manages in the dialog box.

A property page manages a dialog box that contains only those controls that should be displayed for that one page within the property sheet itself. This means that the dialog box template used to define the page should only carry the WS_CHILD style and no others. It should not include any style related to a frame, caption, or system menus or controls.

When to Implement

Implement this interface on a property page object.

When to Use

Use this interface to manage a property page object. Typically, this method is called within the OLE-supplied property frame created through OleCreatePropertyFrame or OleCreatePropertyFrameIndirect. Using the methods in this interface, the frame can display the properties and process end-user changes to the property values.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPropertyPage Methods | Description
SetPageSite | Initializes a property page and provides the page with a pointer to the IPropertyPageSite interface through which the property page communicates with the property frame.
Activate | Creates the dialog box window for the property page.
Deactivate | Destroys the window created with Activate.
GetPageInfo | Returns information about the property page.
SetObjects | Provides the property page with an array of IUnknown pointers for objects associated with this property page.
Show | Makes the property page dialog box visible or invisible.
Move | Positions and resizes the property page dialog box within the frame.
IsPageDirty | Indicates whether the property page has changed since activated or since the most recent call to Apply.
Apply | Applies current property page values to underlying objects specified through SetObjects.
Help | Invokes help in response to end-user request.
TranslateAccelerator | Provides a pointer to a MSG structure that specifies a keystroke to process.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing, IPropertyPage2, IPropertyPageSite, ISpecifyPropertyPages, OleCreatePropertyFrame, OleCreatePropertyFrameIndirect


IPropertyPage::Activate

Creates the dialog box for the property page (without a frame, caption, or system menu/controls) using hWndParent as the parent window and prc as the positioning rectangle. The bModal flag indicates the modality of the dialog box frame (in the current implementation of OleCreatePropertyFrame and OleCreatePropertyFrameIndirect, this parameter is always TRUE). The text in the dialog should match the locale obtained through IPropertyPageSite::GetLocaleID.

The property page maintains the window handle created in this process, which it uses to destroy the dialog box within IPropertyPage::Deactivate.

HRESULT Activate(
  HWND hWndParent ,  //Parent window handle
  LPCRECT prc ,      //Pointer to RECT structure
  BOOL bModal        //Dialog box frame is modal or modeless
);

Parameters

hWndParent
[in] Window handle of the parent of the dialog box that is being created.
prc
[in] Pointer to the RECT structure containing the positioning information for the dialog box. This method must create its dialog box with the placement and dimensions described by this rectangle, that is, origin point at (prc->left, prc->top) and dimensions of (prc->right-prc->Left, prc->bottom-prc->top).
bModal
[in] Indicates whether the dialog box frame is modal (TRUE) or modeless (FALSE).

Return Values

This method supports the standard return values E_OUTOFMEMORY and

E_UNEXPECTED, as well as the following:

S_OK
The page dialog box was created successfully.
E_POINTER
The address in prc is not valid. For example, it may be NULL.

Remarks

Notes to Implementers

E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


IPropertyPage::Apply

Applies the current values to the underlying objects associated with the property page as previously passed to IPropertyPage::SetObjects.

HRESULT Apply(void);

Return Values

This method supports the standard return values E_OUTOFMEMORY and

E_UNEXPECTED, as well as the following:

S_OK
Changes were successfully applied and the property page is current with the underlying objects.
S_FALSE
Changes were applied, but the property page cannot determine if its state is current with the objects.

Remarks

The objects to be changed are provided through a previous call to IPropertyPage::SetObjects. By calling IPropertyPage::SetObjects prior to calling this method, the caller ensures that all underlying objects have the correct interfaces through which to communicate changes. Therefore, this method should not fail because of non-existent interfaces.

After applying its values, the property page should determine if its state is now current with the objects in order to properly implement IPropertyPage::IsPageDirty and to provide both S_OK and S_FALSE return values.

Notes to Implementers

E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::IsPageDirty, IPropertyPage::SetObjects


IPropertyPage::Deactivate

Destroys the window created in IPropertyPage::Activate.

HRESULT Deactivate(void);

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The dialog was successfully destroyed.

Remarks

Notes to Implementers

It is important that property pages not keep the dialog box around as an optimization. In a property sheet with many property pages, memory consumption would become excessive if all property pages kept their dialog boxes created at all times. Destroying the dialog box prevents excessive memory consumption due to a very large number of created controls in the dialog boxes. If the frame wishes to keep pages alive while they are not visible, it can use IPropertyPage::Show for that purpose. The decision is ultimately left to the frame.

E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate


IPropertyPage::GetPageInfo

Fills a caller-allocated PROPPAGEINFO structure to provide the caller with information about the property page.

HRESULT GetPageInfo(
  PROPPAGEINFO *pPageInfo  //Receives a pointer to property page
                           //information structure
);

Parameters

pPageInfo
[out] Pointer to the caller-allocated PROPPAGEINFO structure in which the property page stores its page information. All allocations stored in this structure become the responsibility of the caller.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The structure was successfully filled.
E_POINTER
The address in pPageInfo is not valid. For example, it may be NULL.

Remarks

Notes to Implementers

E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

PROPPAGEINFO


IPropertyPage::Help

Invokes the property page help in response to an end-user request.

HRESULT Help(
  LPCOLESTR pszHelpDir  //Pointer to string from HelpDir key
);

Parameters

pszHelpDir
[in] Pointer to the string under the HelpDir key in the property page's CLSID information in the registry. If HelpDir does not exist, this will be the path found in the InProcServer32 entry minus the server file name. (Note that LocalServer32 is not checked in the current implementation, since local property pages are not currently supported).

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The page displayed its own help.
E_NOTIMPL
Help is either not provided or is provided only through the information in PROPPAGEINFO.

Remarks

Notes to Callers

Calls to this method must occur between calls to IPropertyPage::Activate and IPropertyPage::Deactivate.

Notes to Implementers

If the page fails this method (such as E_NOTIMPL), then the frame will attempt to use the pszHelpFile and dwHelpContext fields of the PROPPAGEINFO structure obtained through IPropertyPage::GetPageInfo. Therefore, the page should either implement IPropertyPage::Help or return help information through IPropertyPage::GetPageInfo.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate, IPropertyPage::Deactivate, IPropertyPage::GetPageInfo, PROPPAGEINFO


IPropertyPage::IsPageDirty

Indicates whether the property page has changed its state since activation or since the last call to IPropertyPage::Apply. The property sheet uses this information to enable or disable the Apply button in the dialog box. There is no need to apply the values on a property page if those values are already current with the underlying objects.

HRESULT IsPageDirty(void);

Return Values

S_OK
The value state of the property page is dirty, that is, it has changed and is different from the state of the objects.
S_FALSE
The value state of the page has not changed and is current with that of the objects.

Remarks

Notes to Implementers

This method has no reason to return an error code, since the inability to determine if the page is dirty should return S_OK as a default. In this way, the user has a chance to update the values. The page should not return an error code, since an error code is not the same as S_OK and would indicate that the page is not dirty. Then, the property frame could potentially disable the Apply button, not allowing the user to make sure that the property values are current.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Apply


IPropertyPage::Move

Repositions and resizes the property page dialog box according to the contents of prc. The rectangle specified by prc is treated identically to that passed to IPropertyPage::Activate.

HRESULT Move(
  LPCRECT prc  //Pointer to RECT structure
);

Parameters

prc
[in] Pointer to the RECT structure containing the positioning information for the page dialog box.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The page repositioned itself successfully.
E_POINTER
The address in prc is not valid. For example, it may be NULL.

Remarks

Notes to Callers

Calls to this method must occur after a call to IPropertyPage::Activate and before a corresponding call to IPropertyPage::Deactivate.

Notes to Implementers

The page must create its dialog box with the placement and dimensions described by this rectangle, that is, origin point at (prc->left, prc->top) and dimensions of (prc->right-prc->Left, prc->bottom-prc->top).

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate, IPropertyPage::Deactivate


IPropertyPage::SetObjects

Provides the IUnknown pointers of the objects affected by the property sheet in which this property page is displayed. When the property page receives a call to IPropertyPage::Apply, it must send value changes to these objects through whatever interfaces are appropriate. The property page must query for those interfaces. This method can fail if the objects do not support the interfaces expected by the property page.

HRESULT SetObjects(
  ULONG cObjects ,  //Number of IUnknown pointers in the ppUnk array
  IUnknown **ppUnk  //Pointer to array
);

Parameters

cObjects
[in] Number of IUnknown pointers in the array pointed to by ppUnk. If zero, the property page must release any pointers previously passed to this method.
ppUnk
[in] Pointer to an array of IUnknown interface pointers where each pointer identifies a unique object affected by the property sheet in which this (and possibly other) property pages are displayed. The property page must cache these pointers calling IUnknown::AddRef for each pointer at that time. This array of pointers is the same one that was passed to OleCreatePropertyFrame or OleCreatePropertyFrameIndirect to invoke the property sheet.

Return Values

This method supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The property page successfully saved the pointers it needed.
E_NOINTERFACE
One of the objects in ppUnk did not support the interface expected by this property page, and so this property page cannot communicate with it.
E_POINTER
The address in ppUnk is not valid. For example, it may be NULL.

Remarks

The property page is required to keep the pointers returned by this method or others queried through them. If these specific IUnknown pointers are held, the property page must call IUnknown::AddRef through each when caching them, until the time when IPropertyPage::SetObjects is called with cObjects equal to zero. At that time, the property page must call IUnknown::Release through each pointer, releasing any objects that it held.

The caller must provide the property page with these objects before calling IPropertyPage::Activate, and should call IPropertyPage::SetObjects with zero as the parameter when deactivating the page or when releasing the object entirely. Each call to SetObjects with a non-NULL ppUnk parameter must be matched with a later call to SetObjects with zero in the cObjects parameter.

Notes to Implementers

E_NOTIMPL is not a valid return value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate, OCPFIPARAMS, OleCreatePropertyFrame, OleCreatePropertyFrameIndirect


IPropertyPage::SetPageSite

Initializes a property page and provides the property page object with the IPropertyPageSite interface through which the property page communicates with the property frame.

HRESULT SetPageSite(
  IPropertyPageSite *pPageSite  //Pointer to the site object
);

Parameters

pPageSite
[in] Pointer to the IPropertyPageSite interface of the page site that manages and provides services to this property page within the entire property sheet.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The page site was saved and the page object was fully initialized.

Remarks

Notes to Implementers

If the pPageSite parameter is NULL, this method must call pPageSite->Release on any IPropertyPageSite pointer passed during a previous call to this method. If non-NULL, this method must save the IPropertyPageSite pointer value and call pPageSite->AddRef. Two consecutive calls to this method with a non-NULL site pointer are not allowed and should cause the property page to return E_UNEXPECTED.

E_NOTIMPL is not a valid return value. All property pages must implement this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPageSite


IPropertyPage::Show

Makes the property page dialog box visible or invisible according to the nCmdShow parameter. If the page is made visible, the page should set the focus to itself, specifically to the first property on the page.

HRESULT Show(
  UINT nCmdShow  //Indicates whether to make the page visible or
                 //hidden
);

Parameters

nCmdShow
[in] Command describing whether to become visible (SW_SHOW or SW_SHOWNORMAL) or hidden (SW_HIDE). No other values are valid for this parameter.

Return Values

This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The show command was successfully invoked.

Remarks

Notes to Callers

Calls to this method must occur after a call to IPropertyPage::Activate and before a corresponding call to IPropertyPage::Deactivate.

Notes to Implementers

E_NOTIMPL is not a valid return value. E_OUTOFMEMORY is not a valid return value, since no memory should be used in implementing this method.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate, IPropertyPage::Deactivate


IPropertyPage::TranslateAccelerator

Instructs the property page to process the keystroke described in pMsg.

HRESULT TranslateAccelerator(
  LPMSG pMsg  //Pointer to MSG structure
);

Parameters

pMsg
[in] Pointer to the MSG structure describing the keystroke to process.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The property page handles the accelerator.
S_FALSE
The property page handles accelerators, but this one was not useful to it.
E_NOTIMPL
The property page does not handle accelerators.
E_POINTER
The address in pMsg is not valid. For example, it may be NULL.

Remarks

Notes to Callers

Calls to this method must occur after a call to IPropertyPage::Activate and before the corresponding call to IPropertyPage::Deactivate.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate, IPropertyPage::Deactivate


IPropertyPage2

The IPropertyPage2 interface is an extension to IPropertyPage to support initial selection of a property on a page. It works in conjunction with the implementation of IPerPropertyBrowsing::MapPropertyToPage on an object that supplies properties and specifies property pages through ISpecifyPropertyPages. This interface has only one extra method in addition to those in IPropertyPage. That method, IPropertyPage2::EditProperty tells the page which property to highlight.

When to Implement

Implement this interface if your property page object supports selection of a specific property.

When to Use

Use this interface to select a specific property in a property page.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPropertyPage Methods | Description
SetPageSite | Initializes a property page and provides the page with a pointer to the IPropertyPageSite interface through which the property page communicates with the property frame.
Activate | Creates the dialog box window for the property page.
Deactivate | Destroys the window created with Activate.
GetPageInfo | Returns information about the property page.
SetObjects | Provides the property page with an array of IUnknown pointers for objects associated with this property page.
Show | Makes the property page dialog box visible or invisible.
Move | Positions and resizes the property page dialog box within the frame.
IsPageDirty | Indicates whether the property page has changed since activated or since the most recent call to Apply.
Apply | Applies current property page values to underlying objects specified through SetObjects.
Help | Invokes help in response to end-user request.
TranslateAccelerator | Provides a pointer to a MSG structure that specifies a keystroke to process.
IPropertyPage2 Methods | Description
EditProperty | Specifies which field is to receive the focus when the property page is activated.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing, IPropertyPage, IPropertyPageSite, ISpecifyPropertyPages, IPerPropertyBrowsing::MapPropertyToPage


IPropertyPage2::EditProperty

Specifies in dispID the property's control on the property page to receive the focus when the page is activated.

HRESULT EditProperty(
  DISPID dispID  //Dispatch identifier for property
);

Parameters

dispID
[in] Identifies the property that is to receive the focus.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The property was successfully highlighted.
E_NOTIMPL
This method is not currently implemented; the interface is probably provided in anticipation of future work on this page.

Remarks

Notes to Implementers

If this method is called before a page is activated, the page should store the property and set the focus to it in the next call to IPropertyPage::Activate. If the page is already active, IPropertyPage2::EditProperty should set the focus to the specific property field.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPropertyPage::Activate


IPropertyPageSite

The IPropertyPageSite interface provides the main features for a property page site object. For each property page created within a property frame, the frame creates a property page site to provide information to the property page and to receive notifications from the page when changes occur. This latter notification is used to initiate a call to IPropertyPage::IsPageDirty, the return value of which is then used to enable or disable the frame's Apply button.

When to Implement

Implement this interface on a site object that will manage a property page on behalf of the property frame. Typically, the OLE-provided property frame created through OleCreatePropertyFrame and OleCreatePropertyFrameIndirect implements site objects.

When to Use

Use a site object with this interface to set up communications between the property frame and the property page object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IPropertyPageSite Methods | Description
OnStatusChange | Indicates that the user has modified property values on the property page.
GetLocaleID | Returns the locale identifier so the property page can adjust itself to country-specific settings.
GetPageContainer | Returns an IUnknown pointer for the object representing the entire property frame dialog box that contains all the pages.
TranslateAccelerator | Passes a keystroke to the property frame for processing.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing, IPropertyPage, IPropertyPage2, IPropertyPageSite - Ole Implementation, ISpecifyPropertyPages


IPropertyPageSite::GetLocaleID

Returns the locale identifier (an LCID) that a property page can use to adjust itself to the language in use and other country-specific settings.

HRESULT GetLocaleID(
  LCID* pLocaleID  //Receives a pointer to the locale identifier
);

Parameters

pLocaleID
[out] Pointer to locale identifier.

Return Values

S_OK
The locale was returned successfully.
E_POINTER
The address in pLocaleID is not valid. For example, it may be NULL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

OCPFIPARAMS, PROPPAGEINFO


IPropertyPageSite::GetPageContainer

Returns an IUnknown pointer to the object representing the entire property frame dialog box that contains all the pages. Calling this method could potentially allow one page to navigate to another.

However, there are no "container" interfaces currently defined for this role, so this method always fails in the current property frame implementation.

HRESULT GetPageContainer(
  IUnknown** ppUnk  //Address of output variable that receives the
                    //IUnknown interface pointer to the container
                    //object
);

Parameters

ppUnk
[out] Address of IUnknown* pointer variable that receives the interface pointer to the container object. If an error occurs, the implementation must set *ppUnk to NULL.

Return Values

E_NOTIMPL
This is the only return value allowed at this time.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPropertyPageSite::OnStatusChange

Informs the frame that the property page managed by this site has changed its state, that is, one or more property values have been changed in the page. Property pages should call this method whenever changes occur in their dialog boxes.

HRESULT OnStatusChange(
  DWORD dwFlags  //Indicates what changes have occurred
);

Parameters

dwFlags
[in] Flags to indicate what changes have occurred. The dwFlags parameter can contain either of these two values to indicate the type of status change:
Flag Value | Description
PROPPAGESTATUS_DIRTY | Values in pages have changed so the state of the Apply button should be updated.
PROPPAGESTATUS_VALIDATE | Now is an appropriate time to apply changes.

Return Values

This method supports the standard return value E_INVALIDARG, as well as the following:

S_OK
The status change was noted.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPropertyPageSite::TranslateAccelerator

Instructs the page site to process a keystroke if it desires.

HRESULT TranslateAccelerator(
  LPMSG pMsg  //Pointer to MSG structure
);

Parameters

pMsg
[in] Pointer to the MSG structure to be processed.

Return Values

S_OK
The page site processed the message.
S_FALSE
The page site did not process the message.
E_NOTIMPL
The page site does not support keyboard processing.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.


IPropertyPageSite - Ole Implementation

The system provides an implementation of the IPropertyPageSite interface through the OleCreatePropertyFrame or OleCreatePropertyFrameIndirect functions.

The current frame implementation provided through OleCreatePropertyFrame and OleCreatePropertyFrameIndirect only implements the OnStatusChange and GetLocaleID methods.

Remarks

OnStatusChange
Indicates that the property page has changed.
GetLocaleID
Returns the locale identifier so the property page can adjust itself to country-specific settings.
GetPageContainer
Returns E_NOTIMPL.
TranslateAccelerator
Returns E_NOTIMPL.

See Also

IPropertyPageSite


IQuickActivate

The IQuickActivate interface allows controls and containers to avoid performance bottlenecks on loading controls. It combines the load-time or initialization-time handshaking between the control and its container into a single call.

When to Implement

Implement this interface on controls to achieve performance improvements during activation of the control.

If this interface is supported then all methods of this interface must be implemented.

When to Use

Containers call this interface on controls to achieve performance improvements in activating the control.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
IQuickActivate Methods | Description
QuickActivate | Quick activates a control.
SetContentExtent | Sets the content extent of a control.
GetContentExtent | Gets the content extent of a control.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IQuickActivate::GetContentExtent

Gets the content extent of the control.

HRESULT GetContentExtent(
  LPSIZEL psizel   //Pointer to size of the content extent
);

Parameters

psizel
[out] Pointer to size of the content extent.

Return Values

S_OK
The content extent was successfully returned.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


IQuickActivate::QuickActivate

Quick activates a control.

HRESULT QuickActivate(
  QACONTAINER* pQaContainer,   //Pointer to container information
                               //structure
  QACONTROL* pQaControl        //Pointer to control information
                               //structure
);

Parameters

pQaContainer
[in] Pointer to the QACONTAINER structure containing information about the container.
pQaControl
[out] Pointer to the QACONTROL structure filled in by the control to return information about the control to the container. The container calling this method must reserve memory for this structure.

Return Values

S_OK
The quick activation is proceeding and the QACONTROL structure has been completed.
E_FAIL
An unexpected error occurred. The quick activation is not completed.

Remarks

If the control does not support IQuickActivate, the container performs certain handshaking operations when it loads the control. The container calls certain interfaces on the control and the control, in turn, calls back to certain interfaces on the container's client site. First, the container creates the control object and calls QueryInterface to query for interfaces that it needs. Then, the container calls IOleObjectSetClientSite on the control, passing a pointer to its client site. Next, the control calls QueryInterface on this site, retrieving a pointer to additional necessary interfaces.

Using the new IQuickActivate::QuickActivate method, the container passes a pointer to a QACONTAINER structure. The structure contains pointers to interfaces which are needed by the control and the values of some ambient properties that the control may need. Upon return, the control passes a pointer to a QACONTROL structure that contains pointers to its own interfaces that the container requires, and additional status information.

The IPersist*::Load and IPersist*::InitNew methods should be called after quick activation occurs. The control should establish its connections to the container's sinks during quick activation. However, these connections are not live until IPersist*::Load or IPersist*::InitNew has been called.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

QACONTROL, QACONTAINER


IQuickActivate::SetContentExtent

Sets the content extent of the control.

HRESULT SetContentExtent(
  LPSIZEL psizel   //Size of the content extent
);

Parameter

psizel
[in] Size of the content extent.

Return Values

S_OK
The extent was successfully set.
E_FAIL
The object's size is fixed and the extent cannot be set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.


ISimpleFrameSite

The ISimpleFrameSite interface supports simple frame controls that act as simple containers for other nested controls. Some controls merely contain other controls. In such cases, the simple control container, called a simple frame, doesn't have to implement all container requirements. It can delegate most of the interface calls from its contained controls to the outer container that manages the simple frame. To support what are called simple frame controls, a container implements this interface as well as other site interfaces such as IOleControlSite.

An example of a simple frame control is a group box that only needs to capture a few keystrokes for its contained controls to implement the correct tab or arrow key behavior, but does not want to handle every other message. Through the two methods of this interface, the simple frame control passes messages to its control site both before and after its own processing. If that site is itself a simple frame, it can continue to pass messages up the chain.

When to Implement

Implement this interface on a control object to support nested controls without requiring the control to itself be a full container.

When to Use

Use this interface to pass messages to a container for processing.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISimpleFrameSite Methods | Description
PreMessageFilter | Sends the simple frame site a message that is received by a control's own window before the control itself does any processing.
PostMessageFilter | Sends the simple frame site a message that is received by a control's own window after the control does its own processing.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl, IOleControlSite


ISimpleFrameSite::PostMessageFilter

Sends the simple frame site a message that is received by a control's own window after both ISimpleFrameSite::PreMessageFilter and the control have had a chance to process the message.

HRESULT PostMessageFilter(
  HWND hWnd,          //Handle of window receiving message
  UINT msg,           //Received message
  WPARAM wp,          //WPARAM of message
  LPARAM lp,          //LPARAM of message
  LRESULT* plResult,  //Pointer to variable to receive result
  DWORD dwCookie      //Token returned by PreMessageFilter
);

Parameters

hWnd
[in] Handle of the control window receiving the message.
msg
[in] Message received by the simple frame site.
wp
[in] The WPARAM of the message.
lp
[in] The LPARAM of the message.
plResult
[out] Pointer to the variable that receives the result of the message processing.
dwCookie
[in] The DWORD value that was returned by ISimpleFrameSite::PreMessageFilter through its pdwCookie parameter.

Return Values

S_OK
The site processed the message.
S_FALSE
The site did not process the message.
E_NOTIMPL
The site does not filter any messages.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

ISimpleFrameSite:PreMessageFilter


ISimpleFrameSite::PreMessageFilter

Provides a site with the opportunity to process a message that is received by a control's own window before the control itself does any processing.

HRESULT PreMessageFilter(
  HWND hWnd,          //Handle of window receiving message
  UINT msg,           //Received message
  WPARAM wp,          //WPARAM of message
  LPARAM lp,          //LPARAM of message
  LRESULT* plResult,  //Pointer to variable to receive result of
                      //message processing
  DWORD* pdwCookie    //Pointer to a variable used later
);

Parameters

hWnd
[in] Handle of the control window receiving the message.
msg
[in] Message received by the simple frame site.
wp
[in] The WPARAM of the message.
lp
[in] The LPARAM of the message.
plResult
[out] Pointer to the result variable to receive the result of the message processing.
pdwCookie
[out] Pointer to the DWORD variable that will be passed to PostMessageFilter if it is called later. This parameter should only contain allocated data if this method returns S_OK so it will also receive a call to PostMessageFilter which can free the allocation. The caller is not in any way responsible for anything returned in this parameter.

Return Values

S_OK
The simple frame site will not use the message in this filter so more processing can take place.
S_FALSE
The site has processed the message and no further processing should occur.
E_NOTIMPL
The site does not do any message filtering, indicating that PostMessageFilter need not be called later.
E_POINTER
The addresses in plResultor pdwCookie are not valid.

Remarks

Successful return values indicate whether the site wishes to allow further processing. S_OK indicates further processing, whereas S_FALSE means do not process further. S_OK also indicates that the control must later call ISimpleFrameSite::PostMessageFilter.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

ISimpleFrameSite::PostMessageFilter


ISpecifyPropertyPages

The ISpecifyPropertyPage interface indicates that an object supports property pages. OLE property pages enable an object to display its properties in a tabbed dialog box known as a property sheet. An end user can then view and change the object's properties. An object can display its property pages independent of its client, or the client can manage the display of property pages from a number of contained objects in a single property sheet. Property pages also provide a means for notifying a client of changes in an object's properties.

A property page object manages a particular page within a property sheet. A property page implements at least IPropertyPage and can optionally implement IPropertyPage2 if selection of a specific property is supported.

An object specifies its support for property pages by implementing ISpecifyPropertyPages. Through this interface the caller can obtain a list of CLSIDs identifying the specific property pages that the object supports. If the object specifies a property page CLSID, the object must be able to receive property changes from the property page.

When to Implement

Implement this interface on an object to indicate support for a property sheet and at least one property page.

When to Use

Use this interface to obtain a list of property page CLSIDs that this object supports. The CLSID list can be later passed to OleCreatePropertyFrame or OleCreatePropertyFrameIndirect to invoke a property sheet. If a caller wants to display a property sheet for multiple objects, it must first obtain the CLSID list for each object, then create a list containing only the intersection of the set of CLSID in each separate list. In other words, whoever invokes a property sheet for any number of objects must guarantee that each property page CLSID was specified by all objects for which the sheet is being displayed. This avoids the possibility that a property page is displayed for an object that doesn't understand that page; if this were allowed, it would result in problems when the page sent unknown and unexpected information to an object.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.
ISpecifyPropertyPages Methods | Description
GetPages | Fills an array of CLSIDs for each property page that can be displayed in this object's property sheet.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing, IPropertyPage, IPropertyPage2, IPropertyPageSite, OleCreatePropertyFrame, OleCreatePropertyFrameIndirect


ISpecifyPropertyPages::GetPages

Fills a counted array of GUID values where each GUID specifies the CLSID of each property page that can be displayed in the property sheet for this object.

HRESULT GetPages(
  CAUUID *pPages  //Pointer to structure
);

Parameters

pPages
[out] Pointer to a caller-allocated CAUUID structure that must be initialized and filled before returning. The pElems field in the CAUUID structure is allocated by the callee with CoTaskMemAlloc and freed by the caller with CoTaskMemFree.

Return Values

This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK
The array was filled successfully.
E_POINTER
The address in pPages is not valid. For example, it may be NULL.

Remarks

The CAUUID structure is caller-allocated, but is not initialized by the caller. The ISpecifyPropertyPages::GetPages method fills the cElements field in the CAUUID structure. This method also allocates memory for the array pointed to by the pElems field in CAUUID using CoTaskMemAlloc. Then, it fills the newly allocated array. After this method returns successfully, the structure contains a counted array of UUIDs, each UUID specifying a property page CLSID.

Notes to Callers

The caller must release the memory pointed to by the pElems field of CAUUID, using CoTaskMemFree when it is no longer needed.

Notes to Implementers

E_NOTIMPL is not allowed as a return value since an object with no property pages should not expose the ISpecifyPropertyPages interface at all.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CAUUID, CoTaskMemAlloc, CoTaskMemFree, OleCreatePropertyFrame, OleCreatePropertyFrameIndirect


OleCreatePropertyFrame

Invokes a new property frame, that is, a property sheet dialog box, whose parent is hwndOwner, where the dialog is positioned at the point (x,y) in the parent window and has the caption lpszCaption.

STDAPI OleCreatePropertyFrame(
  HWND hwndOwner,    //Parent window of property sheet dialog box
  UINT x,            //Horizontal position for dialog box
  UINT y,            //Vertical position for dialog box
  LPCOLESTR lpszCaption,
                     //Pointer to the dialog box caption
  ULONG cObjects,    //Number of object pointers in lplpUnk
  LPUNKNOWN FAR* lplpUnk,
                     //Pointer to the objects for property sheet
  ULONG cPages,      //Number of property pages in lpPageClsID
  LPCLSID lpPageClsID,
                     //Array of CLSIDs for each property page
  LCID lcid,         //Locale identifier for property sheet locale
  DWORD dwReserved,  //Reserved
  LPVOID lpvReserved //Reserved
);

Parameters

hwndOwner
[in] Parent window of the resulting property sheet dialog box.
x
[in] Reserved. ‹Horizontal position for the dialog box relative to hwndOwner.›
y
[in] Reserved. ‹Vertical position for the dialog box relative to hwndOwner.›
lpszCaption
[in] Pointer to the string used for the caption of the dialog box.
cObjects
[in] Number of object pointers passed in lplpUnk.
lplpUnk
[in] An array of IUnknown pointers on the objects for which this property sheet is being invoked. The number of elements in the array is specified by cObjects. These pointers are passed to each property page through IPropertyPage::SetObjects.
cPages
[in] Number of property pages specified in lpPageCIsID.
lpPageClsID
[in] Array of size cPages containing the CLSIDs of each property page to display in the property sheet.
lcid
[in] Locale identifier to use for the property sheet. Property pages can retrieve this identifier through IPropertyPageSite::GetLocaleID.
dwReserved
[in] Reserved for future use; must be zero.
lpvReserved
[in] Reserved for future use; must be NULL.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The dialog box was invoked and operated successfully.
E_POINTER
The address in lpszCaption, lplpUnk, or lpPageCIsID is not valid. For example, any one of them may be NULL.

Remarks

The property pages to be displayed are identified with lpPageClsID, which is an array of cPages CLSID values. The objects that are affected by this property sheet are identified in lplpUnk, an array of size cObjects containing IUnknown pointers.

This function always creates a modal dialogbox and does not return until the dialog box is closed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: Included as a resource in olepro32.dll.

See Also

OleCreatePropertyFrameIndirect


OleCreatePropertyFrameIndirect

Creates a property frame, that is, a property sheet dialog box, based on a structure (OCPFIPARAMS) that contains the parameters, rather than specifying separate parameters as when calling OleCreatePropertyFrame.

STDAPI OleCreatePropertyFrameIndirect(
  OCPFIPARAMS* pParams  //Pointer to the structure of parameters for
                        // dialog box
);

Parameters

pParams
[in] Pointer to the caller-allocated structure containing the creation parameters for the dialog box.

Return Values

This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:

S_OK
The dialog box was invoked and operated successfully.
E_POINTER
The address in pParams is not valid. For example, it may be NULL.

Remarks

Besides cbStructSize (the size of the structure) and dispIDInitialProperty, all of the members of the OCPFIPARAMS structure have the same semantics as the parameters for OleCreatePropertyFrame. When dispIDInitialProperty is DISPID_UNKNOWN, the behavior of the two functions is identical.

Working in conjunction with IPerPropertyBrowsing and IPropertyPage2, dispIDInitialProperty allows the caller to specify which single property should be highlighted when the dialog box is made visible. This feature is not available when using OleCreatePropertyFrame. To determine the page and property to show initially, the property frame will do the following:

  1. Call (*lplpUnk)->QueryInterface(IID_IPerPropertyBrowsing, ...) to get an interface pointer to the first object.
  2. Call IPerPropertyBrowsing::MapPropertyToPage(dispIDInitialProperty, ...) to determine which page CLSID contains the property to be highlighted. All objects for which this frame is being invoked must support the set of properties displayed in the frame.
  3. When the dialog box is created, the property page with the CLSID retrieved in Step 2 is activated with IPropertyPage::Activate.
  4. The property frame queries the active page for IPropertyPage2.
  5. If successful, the frame calls IPropertyPage2::EditProperty(dispIDInitialProperty) to highlight the correct field in that dialog box.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: Included as a resource in olepro32.dll.

See Also

OCPFIPARAMS, OleCreatePropertyFrame


OleLoadPicturePath

Creates a new picture object and initializes it from the contents of a stream. This is equivalent to calling OleCreatePictureIndirect(NULL, ...) followed by IPersistStream::Load.

STDAPI OleLoadPicturePath{
LPOLESTR  szURLorPath,
LPUNKNOWN punkCaller,
DWORD     dwReserved,
OLE_COLOR   clrReserved,
REFIID    riid,
LPVOID   *ppvRet
}

Parameters

szURLorPath
[in] The path or url to the file you want to open.
punkCaller
[in] Points to IUnknown for COM aggregation.
dwReserved
[in] Reserved.
clrReserved
[in] The color you want to reserve to be transparent.
riid
[in] Reference to the identifier of the interface describing the type of interface pointer to return in ppvRet.
ppvRet
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvRet contains the requested interface pointer on the storage of the object identified by the moniker. If *ppvRet is non-NULL, this function calls IUnknown::AddRef on the interface; it is the caller's responsibility to call IUnknown::Release. If an error occurs, *ppvRet is set to NULL.

Return Values

This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:

S_OK | The picture was created successfully.
E_POINTER | The address in ppvRet is NULL.
E_NOINTERFACE | The object does not support the interface specified in riid.

Remarks

The stream must be in BMP (bitmap), JPEG, WMF (metafile), ICO (icon) or GIF format.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: Included as a resource in olepro32.dll.

See Also

OleLoadPicture


OleTranslateColor

Converts an OLE_COLOR type to a COLORREF.

STDAPI OleTranslateColor (
  OLE_COLOR clr,       //Color to be converted into a COLORREF
  HPALETTE hpal,       //Palette used for conversion
  COLORREF *pcolorref  //Pointer to the caller's variable that
                       // receives the converted result
);

Parameters

clr
[in] The OLE color to be converted into a COLORREF.
hpal
[in] Palette used as a basis for the conversion.
pcolorref
[out] Pointer to the caller's variable that receives the converted COLORREF result. This can be NULL, indicating that the caller wants only to verify that a converted color exists.

Return Values

This function supports the standard return values E_INVALIDARG and E_UNEXPECTED, as well as the following:

S_OK
The color was translated successfully.

Remarks

The following table describes the color conversion:

OLE_COLOR | hPal | Resulting COLORREF
invalid |  | Undefined (E_INVALIDARG)
0x800000xx, xx is not a valid Win32 GetSysColor index |  | Undefined (E_INVALIDARG)
 | invalid | Undefined (E_INVALIDARG)
0x0100iiii, iiii is not a valid palette index | valid palette | Undefined (E_INVALIDARG)
0x800000xx, xx is a valid GetSysColor index | NULL | 0x00bbggrr
0x0100iiii, iiii is a valid palette index | NULL | 0x0100iiii
0x02bbggrr (palette relative) | NULL | 0x02bbggrr
0x00bbggrr | NULL | 0x00bbggrr
0x800000xx, xx is a valid GetSysColor index | valid palette | 0x00bbggrr
0x0100iiii, iiii is a valid palette index in hPal | valid palette | 0x0100iiii
0x02bbggrr (palette relative) | valid palette | 0x02bbggrr
0x00bbggrr | valid palette | 0x02bbggrr

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.
Library: Included as a resource in olepro32.dll.


CADWORD

The CADWORD structure is a counted array of DWORDs. It is used, for example, in the IPerPropertyBrowsing::GetPredefinedStrings method. The values returned in this counted array can be passed into the IPerPropertyBrowsing::GetPredefinedValue method to obtain the value corresponding to one of the predefined strings for a property.

typedef struct tagCADWORD
{
    ULONG      cElems;
    DWORD FAR* pElems;
} CADWORD;

Members

cElems
Size of the array pointed to by pElems.
pElems
Pointer to an array of DWORD values, each of which can be passed to the IPerPropertyBrowsing::GetPredefinedValue method to obtain the corresponding value for one of the property's predefined strings. This array is allocated by the callee using CoTaskMemAlloc and is freed by the caller using CoTaskMemFree.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CALPOLESTR, IPerPropertyBrowsing::GetPredefinedStrings, IPerPropertyBrowsing::GetPredefinedValue


CALPOLESTR

The CALPOLESTR structure is a counted array of LPOLESTR types, that is, a counted array of pointers to strings. It is used, for example, in the IPerPropertyBrowsing::GetPredefinedStrings method to specify the predefined strings that a property can accept.

typedef struct tagCALPOLESTR
{
    ULONG         cElems;
    LPOLESTR FAR* pElems;
} CALPOLESTR;

Members

cElems
Size of the array pointed to by pElems.
pElems
Pointer to an array of LPOLESTR values, each of which corresponds to an allowable value that a particular property can accept. The caller can use these string values in user interface elements, such as drop-down list boxes. This array, as well as the strings in the array, are allocated by the callee using CoTaskMemAlloc and is freed by the caller using CoTaskMemFree.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IPerPropertyBrowsing::GetPredefinedStrings


CAUUID

The CAUUID structure is a Counted Array of UUID or GUID types. It is used, for example, in the ISpecifyPropertyPages::GetPages method to receive an array of CLSIDs for the property pages that the object wants displayed.

typedef struct tagCAUUID
{
    ULONG     cElems;
    GUID FAR* pElems;
} CAUUID;

Members

cElems
Size of the array pointed to by pElems.
pElems
Pointer to an array of UUID values, each of which specifies a CLSID of a particular property page. This array is allocated by the callee using CoTaskMemAlloc and is freed by the caller using CoTaskMemFree.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

ISpecifyPropertyPages::GetPages


CONTROLINFO

The CONTROLINFO structure contains parameters that describe a control's keyboard mnemonics and keyboard behavior. The structure is filled during the IOleControl::GetControlInfo method.

typedef struct tagCONTROLINFO
    {
    ULONG  cb;
    HACCEL hAccel;
    USHORT cAccel;
    DWORD  dwFlags;
    } CONTROLINFO;

Members

cb
Size of the CONTROLINFO structure.
hAccel
Handle to an array of Windows ACCEL structures, each structure describing a keyboard mnemonic. The array is allocated with the GlobalAlloc function. The control always maintains the memory for this array; the caller of IOleControl::GetControlInfo should not attempt to free the memory.
cAccel
Number of mnemonics described in the hAccel field. This value can be zero to indicate no mnemonics.
dwFlags
Flags that indicate the keyboard behavior of the control. The possible values are:

CTRLINFO_EATS_RETURN

When the control has the focus, it will process the Return key.

CTRLINFO_EATS_ESCAPE

When the control has the focus, it will process the Escape key.

When the control has the focus, the dialog box containing the control cannot use the Return or Escape keys as mnemonics for the default and cancel buttons.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IOleControl::GetControlInfo


FONTDESC

The FONTDESC structure contains parameters used to create a font object through the OleCreateFontIndirect function.

typedef struct tagFONTDESC
    {
    UINT cbSizeofstruct;
    LPOLESTR lpstrName;
    CY cySize;
    SHORT sWeight;
    SHORT sCharset;
    BOOL fItalic;
    BOOL fUnderline;
    BOOL fStrikethrough;
    } FONTDESC;

Members

cbSizeofstruct
Size of the FONTDESC structure.
lpstrName
Pointer to the caller-owned string specifying the font name.
cySize
Initial point size of the font. Use the int64 parameter of the CURRENCY structure and scale your font size (in points) by 10000.
sWeight
Initial weight of the font. If the weight is below 550 (the average of FW_NORMAL, 400, and FW_BOLD, 700), then the Bold property is also initialized to FALSE. If the weight is above 550, the Bold property is set to TRUE.
sCharset
Initial character set of the font.
fItalic
Initial italic state of the font.
fUnderline
Initial underline state of the font.
fStrikethrough
Initial strikethrough state of the font.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.

See Also

OleCreateFontIndirect


LICINFO

The LICINFO structure contains parameters that describe the licensing behavior of a class factory that supports licensing. The structure is filled during the IClassFactory2::GetLicInfo method.

typedef struct tagLICINFO
    {
    ULONG cbLicInfo;
    BOOL  fRuntimeKeyAvail;
    BOOL  fLicVerified;
    } LICINFO;

Members

cbLicInfo
Size of the LICINFO structure.
fRuntimeKeyAvail
Whether this class factory allows the creation of its objects on a unlicensed machine through the use of a license key. If TRUE, IClassFactory2::RequestLicKey can be called to obtain the key. If FALSE, objects can be created only on a fully licensed machine.
fLicVerified
Whether a full machine license exists so that calls to IClassFactory::CreateInstance and IClassFactory2::RequestLicKey will succeed. If TRUE, the full machine license exists. Thus, objects can be created freely. and a license key is available if fRuntimeKeyAvail is also TRUE. If FALSE, this class factory cannot create any instances of objects on this machine unless the proper license key is passed to IClassFactory2::CreateInstanceLic.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

IClassFactory::CreateInstance, IClassFactory2::CreateInstanceLic, IClassFactory2::GetLicInfo, IClassFactory2::RequestLicKey


OCPFIPARAMS

The OCPFIPARAMS structure contains parameters used to invoke a property sheet dialog box through the OleCreatePropertyFrameIndirect function.

typedef struct tagOCPFIPARAMS
{
    ULONG          cbStructSize;
    HWND           hWndOwner;
    int            x;
    int            y;
    LPCOLESTR      lpszCaption;
    ULONG          cObjects;
    LPUNKNOWN FAR* lplpUnk;
    ULONG          cPages;
    CLSID FAR*     lpPages;
    LCID           lcid;
    DISPID         dispIDInitialProperty;
} OCPFIPARAMS;

Members

cbStructsize
Size of the OCPFIPARAMS structure.
hWndOwner
Parent window of the resulting property sheet dialog box.
x
Horizontal position for the dialog box relative to hWndOwner.
y
Vertical position for the dialog box relative to hWndOwner.
lpszCaption
Pointer to the string used for the caption of the dialog.
cObjects
Number of object pointers passed in lplpUnk.
lplpUnk
Array of IUnknown pointers on the objects for which this property sheet is being invoked. The number of elements in the array is specified by cObjects. These pointers are passed to each property page through IPropertyPage::SetObjects.
cPages
Number of property pages specified in lpPages.
lpPages
Pointer to an array of size cPages containing the CLSIDs of each property page to display in the property sheet.
lcid
Locale identifier for the property sheet. This value will be returned through IPropertyPageSite::GetLocaleID.
dispIDInitialProperty
Property that is highlighted when the dialog box is made visible.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.

See Also

IPropertyPage::SetObjects, IPropertyPageSite::GetLocaleID, OleCreatePropertyFrameIndirect


PAGERANGE

The PAGERANGE structure specifies a range of pages. The rgPageRange member of the PAGESET structure is a structure of type PAGERANGE.

typedef struct tagPAGERANGE
{
    LONG nFromPage;
    LONG nToPage;
}PAGERANGE;

Members

nFromPage
The first page of the range. This member can have any page number as a value.If this value is greater than the value specified in nToPage, the document will be printed in reverse page order.
nToPage
The last page of the range. A special value, PAGESET_TOLASTPAGE, indicates that all the remaining pages should be printed. This member can have any page number as a value. If this value is less than the value specified in nFromPage, the document will be printed in reverse page order.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

PAGESET


PAGESET

The PAGESET structure identifies one or more page-ranges to be printed and, optionally, identifies only the even or odd pages as part of a pageset.

typedef struct tagPAGESET
{
    ULONG cbStruct;
    BOOL fOddPages;
    BOOL fEvenPages;
    ULONG cPageRange;
    [size_is(cPageRange)] PAGERANGE rgPages[]
}PAGESET;

Members

cbStruct
The number of bytes in this instance of the PAGESET structure. Must be a multiple of 4.
fOddPages
If TRUE, only the odd-numbered pages in the page-set indicated by rgPages are to be printed.
fEvenPages
If TRUE, only the even-numbered pages in the page-set indicated by rgPages are to be printed.
cPageRange
The number of page-range pairs specified in rgPages.
rgPages
Pointer to an array of PAGERANGE structures specifying the pages to be printed. One or more page ranges can be specified, so long as the number of page ranges is the value of cPageRange. The page ranges must be sorted in ascending order and must be non-overlapping.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in docobj.h.

See Also

PAGERANGE


PICTDESC

The PICTDESC structure contains parameters to create a picture object through the OleCreatePictureIndirect function.

typedef struct tagPICTDESC
{
    UINT cbSizeOfStruct;
    UINT picType;
    union
    {
        struct
        {
            HBITMAP hbitmap;
            HPALETTE hpal;
        } bmp;
        struct
        {
            HMETAFILE hmeta;
            int xExt;
            int yExt;
        } wmf;
        struct
        {
            HICON hicon;
        } icon;
        struct
        {
            HENHMETAFILE hemf;
        } emf;
    } ;
} PICTDESC;

Members

cbSizeOfStruct
Size of the PICTDESC structure.
picType
Type of picture described by this structure, which can be any value from the PICTYPE enumeration.
bmp
Structure containing bitmap information if picType is PICTYPE_BITMAP.
bmp.hbitmap
The HBITMAP identifying the bitmap assigned to the picture object.
bmp.hpal
The HPALETTE identifying the color palette for the bitmap.
wmf
Structure containing metafile information if picType is PICTYPE_METAFILE.
wmf.hmeta
The HMETAFILE handle identifying the metafile assigned to the picture object.
wmf.xExt
Horizontal extent of the metafile in TWIPS units.
wmf.yExt
Vertical extent of the metafile in TWIPS units.
icon
Identifies a structure containing icon information if picType is PICTYPE_ICON.
icon.hicon
The HICON identifying the icon assigned to the picture object.
emf
Structure containing enhanced metafile information if picType is PICTYPE_ENHMETAFILE.
emf.hemf
The HENHMETAFILE identifying the enhanced metafile to assign to the picture object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in olectl.h.

See Also

OleCreatePictureIndirect, PICTYPE


PROPPAGEINFO

The PROPPAGEINFO structure contains parameters used to describe a property page to a property frame. A property page fills a caller-provided structure in the IPropertyPage::GetPageInfo method.

The pszTitle, pszDocString, and the pszHelpFile members specified in this structure should contain text sensitive to the locale obtained through IPropertyPageSite::GetLocaleID.

typedef struct tagPROPPAGEINFO
{
    ULONG     cb;
    LPOLESTR  pszTitle;
    SIZE      size;
    LPOLESTR  pszDocString;
    LPOLESTR  pszHelpFile;
    DWORD     dwHelpContext;
} PROPPAGEINFO;

Members

cb
Size of the PROPPAGEINFO structure.
pszTitle
Pointer to the string that appears in the tab for this page. The string must be allocated with CoTaskMemAlloc. The caller of IPropertyPage::GetPageInfo is responsible for freeing the memory with CoTaskMemFree.
size
Required dimensions of the page's dialog box, in pixels.
pszDocString
Pointer to a text string describing the page, which can be displayed in the property sheet dialog box (current frame implementation doesn't use this field). The text must be allocated with CoTaskMemAlloc. The caller of IPropertyPage::GetPageInfo is responsible for freeing the memory with CoTaskMemFree.
pszHelpFile
Pointer to the simple name of the help file that describes this property page used instead of implementing IPropertyPage::Help. When the user presses Help, the Help method is normally called. If that method fails, the frame will open the help system with this help file (prefixed with the value of the HelpDir key in the property page's registry entries under its CLSID) and will instruct the help system to display the context described by the dwHelpContext field. The path must be allocated with CoTaskMemAlloc. The caller of IPropertyPage::GetPageInfo is responsible for freeing the memory with CoTaskMemFree.
dwHelpContext
Context identifier for the help topic within pszHelpFile that describes this page.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in ocidl.h.

See Also

CoTaskMemAlloc, CoTaskMemFree, IPropertyPageSite::GetLocaleID, IPropertyPage::GetPageInfo, IPropertyPage::Help


QACONTAINER

The QACONTAINER structure is used in IQuickActivate::QuickActivate to specify container information.

typedef struct tagQACONTAINER
{
    ULONG                  cbSize;
    IOleClientSite*        pClientSite;
    IAdviseSinkEx*         pAdviseSink;
    IPropertyNotifySink*   pPropertyNotifySink;
    IUnknown*              pUnkEventSink;
    DWORD                  dwAmbientFlags;
    OLE_COLOR              colorFore;
    OLE_COLOR              colorBack;
    IFont*                 pFont;
    IOleUndoManager*       pUndoMgr;
    DWORD                  dwAppearance;
    LONG                   lcid;
    HPALETTE               hpal;
    struct IBindHost*      pBindHost;
    IOleControlSite*       pOleControlSite;
    IServiceProvider*      pServiceProvider
}    QACONTAINER;

Members

cbsize
Specifies the size of the structure in bytes.
pClientSite
Pointer to an IOleClientSite interface in the container.
pAdviseSink
Pointer to an IAdviseSinkEx interface in the container.
pPropertyNotifySink
Pointer to an IPropertyNotifySink interface in the container.
pUnkEventSink
Pointer to an IUnknown interface on the container's sink object.
dwAmbientFlags
Specifies a number of ambient properties supplied by the container using values from the QACONTAINERFLAGS enumeration.
colorFore
Specifies ForeColor, an ambient property supplied by the container with a DISPID = -704.
colorBack
Specifies BackColor, an ambient property supplied by the container with a DISPID = -701.
pFont
Specifies Font, an ambient property supplied by the container with a DISPID = -703.
pUndoMgr
Pointer to an IOleUndoManager interface in the container.
dwAppearance
Specifies Appearance, an ambient property supplied by the container with a DISPID = -716.
lcid
Specifies LocaleIdentifier, an ambient property supplied by the container with a DISPID = -705.
hPal
Specifies Palette, an ambient property supplied by the container with a DISPID = -726.
pBindHost
Pointer to an IBindHost interface in the container.
pOleControlSite
Pointer to the IOleControlSite interface in the container's site object.
pServiceProvider
Pointer to the IServerProvider interface in the container.

Remarks

If an interface pointer in the QACONTAINER structure is NULL it does not indicate that the interface is not supported. In this situation, the control should use QueryInterface to obtain the interface pointer in the standard manner.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IQuickActivate::QuickActivate, QACONTAINERFLAGS


QACONTROL

The QACONTROL structure is used in IQuickActivate::QuickActivate to specify control information.

typedef struct tagQACONTROL
{
    ULONG    cbSize;
    DWORD    dwMiscStatus;
    DWORD    dwViewStatus;
    DWORD    dwEventCookie;
    DWORD    dwPropNotifyCookie;
    DWORD    dwPointerActivationPolicy;
}QACONTROL;

Members

cbsize
Size of the structure in bytes.
dwMiscStatus
Specifies the control's miscellaneous status bits that can also be returned by IOleObject::GetMiscStatus. See OLEMISC for more information.
dwViewStatus
Specifies the control's view status that can also be returned by IViewObjectEx::GetViewStatus. See VIEWSTATUS for more information.
dwEventCookie
Unique identifier for control-defined events.
dwPropNotifyCookie
Unique identifier for control-defined properties.
dwPointerActivationPolicy
Specifies the control's activation policy that can also be returned by IPointerInactive::GetActivationPolicy. If all the bits of dwPointerActivationPolicy are set, then the IPointerInactive interface may not be supported. The container should QueryInterface to obtain the interface pointer in the standard manner.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IQuickActivate::QuickActivate


GUIDKIND

The GUIDKIND enumeration values are flags used to specify the kind of information requested from an object in the IProvideClassInfo2.

typedef enum tagGUIDKIND
{
    GUIDKIND_DEFAULT_SOURCE_DISP_IID    = 1,
} GUIDKIND;

Elements

GUIDKIND_DEFAULT_SOURCE_DISP_IID
The interface identifier (IID) of the object's outgoing dispinterface, labeled [source, default]. The outgoing interface in question must be derived from IDispatch.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IProvideClassInfo2


KEYMODIFIERS

The KEYMODIFIERS enumeration values are flags used in calls to IOleControlSite::TranslateAccelerator to describe additional keyboard states that can modify the meaning of the keyboard messages that are also passed into IOleControlSite::TranslateAccelerator.

typedef enum tagKEYMODIFIERS
{
    KEYMOD_SHIFT      = 0x00000001,
    KEYMOD_CONTROL    = 0x00000002,
    KEYMOD_ALT        = 0x00000004
} KEYMODIFIERS;

Elements

KEYMOD_SHIFT
The Shift key is currently depressed.
KEYMOD_CONTROL
The Control key is currently depressed.
KEYMOD_ALT
The Alt key is currently depressed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in controls.idl.

See Also

IOleControlSite::TranslateAccelerator


PICTURE

The PICTURE enumeration values describe attributes of a picture object as returned through the IPicture::get_Attributes method.

typedef enum tagPICTURE
{
    PICTURE_SCALABLE       = 0x00000001,
    PICTURE_TRANSPARENT    = 0x00000002
} PICTURE;

Elements

PICTURE_SCALABLE
The picture object is scalable, such that it can be redrawn with a different size than was used to create the picture originally. Metafile-based pictures are considered scalable; icon and bitmap pictures, while they can be scaled, do not express this attribute because both involve bitmap stretching instead of true scaling.
PICTURE_TRANSPARENT
The picture object contains an image that has transparent areas, such that drawing the picture will not necessarily fill in all the spaces in the rectangle it occupies. Metafile and icon pictures have this attribute; bitmap pictures do not.

See Also

IPicture::get_Attributes


PICTYPE

The PICTYPE enumeration values are used to describe the type of a picture object as returned by IPicture::get_Type, as well as to describe the type of picture in the picType field of the PICTDESC structure that is passed to OleCreatePictureIndirect.

typedef enum tagPICTYPE
{
    PICTYPE_UNINITIALIZED    = -1,
    PICTYPE_NONE             = 0,
    PICTYPE_BITMAP           = 1,
    PICTYPE_METAFILE         = 2,
    PICTYPE_ICON             = 3
    PICTYPE_ENHMETAFILE      = 4
} PICTYPE;

Elements

PICTYPE_UNINITIALIZED
The picture object is currently uninitialized. This value is only valid as a return value from IPicture::get_Type and is not valid with the PICTDESC structure.
PICTYPE_NONE
A new picture object is to be created without an initialized state. This value is valid only in the PICTDESC structure.
PICTYPE_BITMAP
The picture type is a bitmap. When this value occurs in the PICTDESC structure, it means that the bmp field of that structure contains the relevant initialization parameters.
PICTYPE_METAFILE
The picture type is a metafile. When this value occurs in the PICTDESC structure, it means that the wmf field of that structure contains the relevant initialization parameters.
PICTYPE_ICON
The picture type is an icon. When this value occurs in the PICTDESC structure, it means that the icon field of that structure contains the relevant initialization parameters.
PICTYPE_ENHMETAFILE
The picture type is a Win32-enhanced metafile. When this value occurs in the PICTDESC structure, it means that the emf field of that structure contains the relevant initialization parameters.

See Also

IPicture::get_Type, OleCreatePictureIndirect, PICTDESC


POINTERINACTIVE

The POINTERINACTIVE enumeration values indicate the activation policy of the object and are used in the IPointerInactive::GetActivationPolicy method.

typedef enum tagPOINTERINACTIVE
{
    POINTERINACTIVE_ACTIVATEONENTRY      = 1,
    POINTERINACTIVE_DEACTIVATEONLEAVE    = 2,
    POINTERINACTIVE_ACTIVATEONDRAG       = 4
} POINTERINACTIVE;

Elements

POINTERINACTIVE_ACTIVATEONENTRY
The object should be in-place activated when the mouse enters it during a mouse move operation.
POINTERINACTIVE_DEACTIVATEONLEAVE
The object should be deactivated when the mouse leaves the object during a mouse move operation.
POINTERINACTIVE_ACTIVATEONDRAG
The object should be in-place activated when the mouse is dragged over it during a drag and drop operation.

Remarks

For more information on using the POINTERINACTIVE_ACTIVATEONENTRY and POINTERINACTIVE_DEACTIVATEONLEAVE values, see the IPointerInactive::GetActivationPolicy method.

The POINTERINACTIVE_ACTIVATEONDRAG value can be used to support drag and drop operations on an inactive object. An inactive object has no window to register itself as a potential drop target. Most containers ignore embedded, inactive objects as drop targets because of the overhead associated with activating them.

As an alternative to activating an object when the mouse pointer is over it during a drag and drop operation, the container can first QueryInterface to determine if the inactive object supports IPointerInactive. Then, if the object does not support IPointerInactive, the container can assume that it is not a drop target. If the object does support IPointerInactive, the container calls the IPointerInactive::GetActivationPolicy method. If the POINTERINACTIVE_ACTIVATEONDRAG value is set, the container activates the object in-place so the object can register its own IDropTarget interface.

The container is processing its own IDropTarget::DragOver method when all these actions occur. To complete that method, the container returns DROPEFFECT_NONE for the pdwEffect parameter. Then, the drag and drop operation continues by calling the container's IDropTarget::DragLeave and then calling the object's IDropTarget::DragEnter.

Note For windowless OLE objects this mechanism is slightly different. See IOleInPlaceSiteWindowless for more information on drag and drop operations for windowless objects.

If the drop occurs on the embedded object, the object is UI-activated and will get UI-deactivated when the focus changes again. If the drop does not occur on the object, the container should deactivate the object the next time it gets a call to its own IDropTarget::DragEnter. It is possible for the drop to occur on a third active object without an intervening call to the container's IDropTarget::DragEnter. In this case, the container should try to deactivate the object as soon as it can, for example, when it UI-activates another object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

IDropTarget, IPointerInactive::GetActivationPolicy


QACONTAINERFLAGS

The QACONTAINERFLAGS enumeration value indicates ambient properties supplied by the container. It is used in the dwAmbientFlags member of the QACONTAINER structure.

typedef enum tagQACONTAINERFLAGS
{
    QACONTAINER_SHOWHATCHING       = 0x1,
    QACONTAINER_SHOWGRABHANDLES    = 0x2,
    QACONTAINER_USERMODE           = 0x4,
    QACONTAINER_DISPLAYASDEFAULT   = 0x8,
    QACONTAINER_UIDEAD             = 0x10,
    QACONTAINER_AUTOCLIP           = 0x20,
    QACONTAINER_MESSAGEREFLECT     = 0x40,
    QACONTAINER_SUPPORTSMNEMONICS  = 0x80
} QACONTAINERFLAGS;

Elements

QACONTAINER_SHOWHATCHING
Specifies the ShowHatching ambient property, which has a standard ambient DISPID of -712.
QACONTAINER_SHOWGRABHANDLES
Specifies the ShowGrabHandles ambient property, which has a standard ambient DISPID of -711.
QACONTAINER_USERMODE
Specifies the UserMode ambient property, which has a standard ambient DISPID of -709.
QACONTAINER_DISPLAYASDEFAULT
Specifies the DisplayAsDefault ambient property, which has a standard ambient DISPID of -713.
QACONTAINER_UIDEAD
Specifies the UIDead ambient property, which has a standard ambient DISPID of -710.
QACONTAINER_AUTOCLIP
Specifies the AutoClip ambient property, which has a standard ambient DISPID of -715.
QACONTAINER_MESSAGEREFLECT
Specifies the MessageReflect ambient property, which has a standard ambient DISPID of -706.
QACONTAINER_SUPPORTSMNEMONICS
Specifies the SupportsMnemonics ambient property, which has a standard ambient DISPID of -714.

Remarks

See the ActiveX Controls chapter in the OLE Programmer's Guide for further information on standard ambient properties.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later. New for OC96.
Windows 95/98: Requires Windows 95 or later. New for OC96.
Header: Declared in ocidl.h.

See Also

QACONTAINER


Structured Storage

Purpose

Structured Storage provides file and data persistence in COM by treating a single file as a structured collection of objects known as storages and streams.

The purpose of Structured Storage is to reduce the performance penalties and overhead associated with storing separate objects in a single file. Structured Storage provides a solution by defining how to treat a single file entity as a structured collection of two types of objects — storages and streams — through a standard implementation called Compound Files. This lets the user interact with and manage a compound file as if it were a single file rather than a nested hierarchy of separate objects.

Where Applicable

Structured Storage is applicable to Microsoft® COM-based systems.: Windows® 2000 , Windows NT® 4.0 or later, Windows 98, and Windows 95.

Developer Audience

The Structured Storage documentation in the Platform SDK is intended for experienced C and C++ programmers and COM-based system developers.

Structured Storage primarily supports C and C++ programming languages, however any COM-based technology will also support any programming language that utilizes interface pointers.

A solid understanding of COM technologies is prerequisite to the developmental use of Structured Storage.

Run-Time Requirements

Windows® NT®/Windows® 2000; requires Windows NT 4.0 or later.

Windows 95/98; requires Windows 95 or later.

See Also

The Component Object Model

Overview

General information about Structured Storage.

Using Structured Storage

Usage guidance for Structured Storage.

Reference

Documentation of Structured Storage specific interfaces, functions, structures, and enumerations.

Samples

Sample programs written in C++. See Names in IStorage, Property Set Header, Section, Storing Property Sets, and Using Structured Storage.

Feedback

Make error reports and feature requests directly to Microsoft.


About Structured Storage

Traditional file systems face challenges when they try to store efficiently multiple kinds of objects in one document. COM provides a solution: a file system within a file. COM structured storage defines how to treat a single file entity as a structured collection of two types of objects — storages and streams — that act like directories and files. This scheme is called structured storage. The purpose of structured storage is to reduce the performance penalties and overhead associated with storing separate objects in a flat file.

This section contains an overview of structured storage benefits and fundamentals, property sets and asynchronous structured storage as well as a historical look at file systems

  • Compound file implementation
  • NTFS file system implementation
  • Stand-alone implementation

This section includes links to The Evolution of File Systems, Storages and Streams, Compound Files, Structured Storage Fundamentals, and structured storage samples available in the Platform SDK.


Benefits of Structured Storage

COM provides a set of services collectively called structured storage. Among the benefits of these services is the reduction of performance penalties and overhead associated with storing separate objects in a flat file. Instead of a flat file, COM stores the separate objects in a single, structured file consisting of two main elements: storage objects and stream objects. Together, they function like a file system within a file.

Structured storage solves performance problems by eliminating the need to totally rewrite a file to storage whenever a new object is added to a compound file, or an existing object increases in size. The new data is written to the next available location in permanent storage, and the storage object updates the table of pointers it maintains to track the locations of its storage objects and stream objects. At the same time, structured storage enables end users to interact and manage a compound file as if it were a single file rather than a nested hierarchy of separate objects.

Structured storage also has other benefits:

  • Incremental access. If a user needs access to an object within a compound file, the user can load and save only that object, rather than the entire file.
  • Multiple use. More than one end user or application can concurrently read and write information in the same compound file.
  • Transaction processing. Users can read or write to COM compound files in transacted mode, where changes made to the file are buffered and can subsequently either be committed to the file or reversed.
  • Low-memory saves. Structured storage provides facilities for saving files in low-memory situations.


The Evolution of File Systems

Years ago, in the days before disk operating systems, each computer was built to run a single, proprietary application, which had complete and exclusive control of the entire machine. The application would write its persistent data directly to a disk, or drum, by sending commands directly to the disk controller. The application was responsible for managing the absolute locations of data on the disk, making sure that it was not overwriting already-existing data. Since only one application was running on the computer, this task was not too difficult.

The advent of computer systems that could run more than one application required a mechanism to ensure that applications did not write over each other's data. Application developers addressed this problem by adopting a single standard for distinguishing disk sectors in use from those that were free by marking them accordingly. In time, these standards coalesced to become a disk operating system, which provided various services to the applications, including a file system for managing persistent storage. With the advent of a file system, applications no longer had to deal directly with the physical storage medium. Instead, they simply told the file system to write blocks of data to the disk and let the file system worry about how to do it. In addition, the file system allowed applications to create data hierarchies through an abstraction known as a directory. A directory could contain not only files but other directories, which in turn could contain their own files and directories, and so on.

The file system provided a single level of indirection between applications and the disk, and the result was that every application saw a file as a single contiguous stream of bytes on the disk even though the file system was actually storing the file in discontiguous sectors. The indirection freed the applications from having to track the absolute position of data on a storage device.

Today, virtually all system APIs for file input and output provide applications for writing information into a flat file. Applications see this file as a single stream of bytes that can grow as large as necessary until the disk is full. For a long time these APIs have been sufficient for applications to store their persistent information. Applications have made significant innovations in how they deal with a single stream of information to provide features like incremental "fast" saves.

However, in a world of component objects, storing data in a single flat file is no longer efficient. Just as file systems arose out of the need for multiple applications to share the same storage medium, so, now, do component objects require a system that allows them to share storage within the conceptual framework of a single file. Even though it is possible to store the separate objects using conventional flat-file storage, if one of the objects increases in size, or if you simply add another object, it becomes necessary to load the entire file into memory, insert the new object, and then save the whole file. This process can be extremely time-consuming.

The solution provided by COM is to implement a second level of indirection: a file system within a file. Flat-file storage requires that a large contiguous sequence of bytes on the disk be manipulated through a single file handle with a single seek pointer. By contrast, COM structured storage defines how to treat a single file system entity as a structured collection of two types of objects — storages and streams — that act like directories and files.


Storages and Streams

A storage object is analogous to a file system directory. Just as a directory can contain other directories and files, a storage object can contain other storage objects and stream objects. Also like a directory, a storage object tracks the locations and sizes of the storage objects and stream objects nested beneath it.

A stream object is analogous to the traditional notion of a file. Like a file, a stream contains data stored as a consecutive sequence of bytes.

A COM compound file consists of a root storage object containing at least one stream object representing its native data along with one or more storage objects corresponding to its linked and embedded objects. The root storage object maps to a file name in whatever file system it happens to reside. Each of the objects inside the document also is represented by a storage object containing one or more stream objects, and perhaps also containing one or more storage objects. In this way, a document can consist of an unlimited number of nested objects. See Compound Files for more information.


Compound Files

Although you can implement your own structured storage objects and interfaces, COM provides a standard implementation called Compound Files. Using Compound Files saves you the work of coding your own implementation of structured storage and confers several additional benefits derived from adhering to a defined standard. These benefits include the following.

  • File-system and platform independence. Because COM's Compound Files implementation runs on top of existing flat-file systems, compound files stored in the FAT file system, NTFS file system, or Macintosh file systems can be opened by applications using any one of the other file systems.
  • Browsability. Because the separate objects in a compound file are saved in a standard format and can be accessed using standard COM interfaces and APIs, any browser utility using these interfaces and APIs can list the objects in the file, even though data within a given object may be in a proprietary format.
  • Access to certain internal data. Because the Compound Files implementation provides standard ways of writing certain types of data — summary information, for example — applications can read this data using COM interfaces and APIs.


Structured Storage Fundamentals

Structured storage provides the equivalent of a complete file system for storing objects through the following elements:

  • Structured Storage Interfaces. The IStorage, IStream, ILockBytes, and IRootStorage interfaces, along with a set of related interfaces — IPersistStorage, IPersist, IPersistFile, and IPersistStream.
  • Structured Storage API Functions. Helper APIs that facilitate your implementation of structured storage, as well as a second set of APIs for compound files, COM's implementation of the structured storage interfaces. COM also provides a set of supporting structures and enumeration values used to organize parameters for interface methods and APIs.
  • Structured Storage Access Modes. A set of access modes for regulating access to compound files.


Storage Object Naming Conventions

Storage and stream objects are named according to a set of conventions.

The name of a root storage object is the actual name of the file in the underlying file system. It conforms to the conventions and restrictions that the file system imposes. File name strings passed to storage-related methods and functions are passed on, uninterpreted and unchanged, to the file system.

The name of a nested element contained within a storage object is managed by the implementation of the particular storage object. All implementations of storage objects must support nested element names 32 characters in length (including the NULL terminator), although some implementations might support longer names. Whether the storage object does any case conversion is implementation-defined. As a result, applications that define element names must choose names that are acceptable whether or not case conversion is performed.. The COM implementation of compound files supports names with a maximum length of 32 characters, and does not perform any case conversion.


Structured Storage Interfaces

Structured storage services are organized into three categories of interfaces. Each set represents a successive level of indirection or abstraction between a compound file, the objects it contains, and the physical media in which these individual components are stored.

The first category of interfaces consists of IStorage, IStream, and IRootStorage. The first two interfaces define how objects are stored within a compound file. These interfaces provide methods for opening storage elements, committing and reverting changes, copying and moving elements, and reading and writing streams. These interfaces do not understand the native data formats of the individual objects and therefore have no methods for saving those objects to persistent storage. The IRootStorage interface has a single method for associating a compound document with an underlying file system name. Clients are responsible for implementing these interfaces on behalf of their compound files.

The second category of interfaces consists of the IPersist interfaces, which objects implement to manage their persistent data. These interfaces provide methods to read the data formats of individual objects and therefore know how to store them. Objects are responsible for implementing these interfaces because clients do not know the native data formats of their nested objects. These interfaces, however, have no knowledge of specific physical storage media.

A third category consists of a single interface, ILockBytes, which provides methods for writing files to specific physical media, such as a hard disk or tape drive. However, most applications will not implement the ILockBytes interface because COM already provides implementations for the two most common situations, which are File-based implementation and Memory-based implementation. The compound file storage object calls the ILockBytes methods — you do not call them directly in the implementation. See ILockBytes for more information.


Structured Storage API Functions

Some of the API functions for structured storage are helper functions that perform a sequence of calls to other API functions and interface methods. You can use these helper functions as short cuts.

Other API functions provide access to COM's implementation of structured storage, Compound Files.

Still another set of API functions enable you to convert OLE 1 objects to structured storage. You can use these functions to determine whether an object class is from OLE 1 and to convert objects between OLE 1 and current COM storage formats.

Finally, there are API functions for converting and emulating other objects. These functions provide services for one server application to work with data from another application. The data can be converted to the native format of the server application by reading the data from its original format but writing it in the native format of the object application. Or the data can remain in its original format while the server application both reads and writes the data in its original format.

See the following section, Structured Storage Access Modes, for further information.


Structured Storage Access Modes

Mechanisms for controlling simultaneous access to an object, by multiple processes and users, are essential. COM provides these mechanisms by defining access modes for both storage and stream objects. The access mode specified for a parent storage object is inherited by its children, though you can place additional restrictions on the child storage or stream. A nested storage or stream object can be opened in the same mode or in a more restricted mode than that of its parent, but it cannot be opened in a less restricted mode than that of its parent.

You specify access modes by using the values listed in the STGM enumeration. These values serve as flags to be passed as arguments to methods in the IStorage interface and associated API functions. Typically, several flags are combined in the parameter grfMode, using a Boolean OR operation.

The flags fall into six groups. Only one flag from each group can be specified at a time:

  • Transaction Flags
  • Storage Creation Flags
  • Temporary Creation Flags
  • Priority Flags
  • Access Permission Flags
  • Shared Access Flags


Transaction Flags

An object can be opened in either direct or transacted mode. When an object is opened in direct mode, changes are made immediately and permanently. When an object is opened in transacted mode, changes are buffered so they can be explicitly committed or reverted once editing is complete. Committed changes are saved to the object while reverted changes are discarded. Direct mode is the default access mode.

Transacted mode is not required on a parent storage object in order to use it on a nested element. A transaction for a nested element, however, is nested within the transaction for its parent storage object. Therefore, changes made to a child object cannot be committed until those made to the parent are committed, and both remain uncommitted until the root storage object (the top-level parent) is actually written to disk. In other words, the changes move outward: inner objects publish changes to the transactions of their immediate containers.


Storage Creation Flags

Storage creation flags specify what COM should do if an existing storage, stream, or lockbytes object has the same name as a new storage or stream object that you are creating. The default is to return an error message and not create the new object. You can use only one of these flags in a given creation call.


Temporary Creation Flags

The temporary creation flag indicates that the underlying file is to be automatically destroyed when the root storage object is released. This capability is most useful for creating temporary files.


Priority Flags

The priority flag opens a storage object in priority mode. When it opens an object, an application usually works from a snapshot copy because other applications may also be using the object at the same time. When opening a storage object in priority mode, however, the application has exclusive rights to commit changes to the object.

Priority mode enables an application to read some streams from storage before opening the object in a mode that would require the system to make a snapshot copy. Since the application has exclusive access, it doesn't have to make a snapshot copy of the object. When the application subsequently opens the object in a mode where a snapshot copy is required, the application can exclude the streams it has already read from the snapshot, thereby reducing the overhead of opening the object.

Since other applications cannot commit changes to an object while it is open in priority mode, applications should keep the object in that mode for as short a time as possible.


Access Permission Flags

Access permission flags specify the type of access a client application has to an open object: read, write, or read/write. Read permission enables an application to read the contents of a stream but not to write changes to the stream. Write permission enables an application to call a function that commits changes to an object's storage but not to read the contents of the stream. Read/write permission enables an application to either read the object's streams or write to the object's storage.


Shared Access Flags

The shared access flags specify the degree of access other applications can have to an object that your application is opening. You can deny read access, deny write access, or deny all access. You can also specify explicitly that no type of access be denied.


Properties and Property Sets

While the kinds of run-time properties that Automation and Microsoft® ActiveX® Controls offer are important, they do not directly address the need to store information with objects persistently stored in the file system. These entities could include files (structured, compound, and so forth), directories, and summary catalogs. COM provides both a standard serialized format for these persistent properties, and a set of interfaces and functions that allow you to create and manipulate the property sets and their properties.

Persistent properties are stored as sets, and one or more sets may be associated with a file system entity. These persistent property sets are intended to be used to store data that is suited to being represented as a collection of fine-grained values. They are not intended to be used as a large data base. They can be used to store summary information about an object on the system, which can then be accessed by any other object that understands how to interpret that property set.

Previous versions of COM specified very little with respect to properties and their usage, but did define a serialized format that allowed developers to store properties and property sets in an IStorage instance. The property identifiers and semantics of a single property set, used for summary information about a document, were also defined. At that time, it was necessary to create and manipulate that structure directly as a data stream. See Structured Storage Serialized Property Set Format.

Now, however, COM defines two primary interfaces to manage property sets:

  • IPropertyStorage
  • IPropertySetStorage

It is no longer necessary to deal with the serialized format directly when these interfaces are implemented on an object that supports the IStorage interface (such as compound files). Writing properties through IPropertySetStorage and IPropertyStorage creates data that exactly conforms to the COM property set format, as viewed through IStorage methods. The converse is also true — properties written to the COM property set format using IStorage are visible through IPropertySetStorage and IPropertyStorage (although you cannot expect to write to IStream and have the properties through IPropertyStorage immediately available, or vice versa).

The IPropertySetStorage interface defines methods that create and manage property sets. The IPropertyStorage interface directly manipulates the properties within a property set. By calling the methods of these interfaces, an application developer can manage whatever property sets are appropriate for a given file system entity. Use of these interfaces provides one tuned reading and writing implementation for properties, rather than having an implementation in each application, where there could be performance bottlenecks such as incessant seeking. You can implement the interfaces to enhance performance, so properties can be read and written more quickly by, for example, more efficient caching. Furthermore, IPropertyStorage and IPropertySetStorage make it possible to manipulate properties on entities that do not support IStorage, although in general, most applications will not do so.

This section contains the following topics:

  • The Summary Information Property Set
  • Predefined Property Set Format Identifiers
  • The DocumentSummaryInformation and UserDefined Property Sets

See Also

Information on the use of properties and property sets can be found at: Property Set Implementations in COM, Property Set Considerations, Managing Properties, Managing Property Sets, Storing Property Sets, and Performance Characteristics.


The Summary Information Property Set

COM defines a standard common property set for storing summary information about documents. The Summary Information property set must be stored in a stream object. That is, this property set must be stored as a simple property set. See Storage vs. Stream for a Property Set.

For example, to create an ANSI simple property set, you would call IPropertySetStorage::Create to create the property set, specifying PROPSETFLAG_ANSI (simple is the default type of property set), then write to it with a call to IPropertyStorage::WriteMultiple. To read the property set, you would call IPropertyStorage::ReadMultiple.

All shared property sets are identified by a stream or storage name with the prefix "\005" (or 0x05) to show that it is a property set that can be shared among applications. The Summary Information property set is no exception. The name of the stream that contains the Summary Information property set is:

"\005SummaryInformation"

It is not necessary to know the stream name of the property set when accessing it by means of the Create or Open methods of the IPropertySetStorage interface; in this case only the format identifier (FMTID) need be known.

The FMTID for the Summary Information property set is:

F29F85E0-4FF9-1068-AB91-08002B27B3D9

The declaration for this value is available in the Win32 API header files as FMTID_SummaryInformation. See the FMTIDS in the Predefined Property Set Format Identifiers.

The following table shows the string property names for the Summary Information property set, along with the respective property identifiers and variable type (VT) indicators. The names are not typically stored in the property set, but are inferred from the Property ID value. The Property ID String entries shown here correspond to definitions found in the Win32 API header files.

Name | Property ID String | Property ID | VT Type
Title | PIDSI_TITLE | 0x00000002 | VT_LPSTR
Subject | PIDSI_SUBJECT | 0x00000003 | VT_LPSTR
Author | PIDSI_AUTHOR | 0x00000004 | VT_LPSTR
Keywords | PIDSI_KEYWORDS | 0x00000005 | VT_LPSTR
Comments | PIDSI_COMMENTS | 0x00000006 | VT_LPSTR
Template | PIDSI_TEMPLATE | 0x00000007 | VT_LPSTR
Last Saved By | PIDSI_LASTAUTHOR | 0x00000008 | VT_LPSTR
Revision Number | PIDSI_REVNUMBER | 0x00000009 | VT_LPSTR
Total Editing Time | PIDSI_EDITTIME | 0x0000000A | VT_FILETIME (UTC)
Last Printed | PIDSI_LASTPRINTED | 0x0000000B | VT_FILETIME (UTC)
Create Time/Date( (*)) | PIDSI_CREATE_DTM | 0x0000000C | VT_FILETIME (UTC)
Last saved Time/Date( (*)) | PIDSI_LASTSAVE_DTM | 0x0000000D | VT_FILETIME (UTC)
Number of Pages Number of Words Number of Characters | PIDSI_PAGECOUNT PIDSI_WORDCOUNT PIDSI_CHARCOUNT | 0x0000000E 0x0000000F 0x00000010 | VT_I4 VT_I4 VT_I4
Thumbnail | PIDSI_THUMBNAIL | 0x00000011 | VT_CF
Name of Creating Application | PIDSI_APPNAME | 0x00000012 | VT_LPSTR
Security | PIDSI_SECURITY | 0x00000013 | VT_I4
* Some methods of file transfer (such as a download from a BBS) do not maintain the file system's version of this information correctly.

See Also

Implementing the Summary Information Property Set


Predefined Property Set Format Identifiers

The following property set Format Identifiers (FMTIDs) are defined in the Win32 in the file UUID.LIB. Declarations for these values are available by including the OLE2.H header file.

Name | Value | Usage
FMTID_SummaryInformation | {F29F85E0-4FF9-1068-AB91-08002B27B3D9} | The Summary Information Property Set
FMTID_DocSummaryInformation | {D5CDD502-2E9C-101B-9397-08002B22CF9AE} | The DocumentSummaryInformation and UserDefined Property Sets
FMTID_UserDefinedProperties | {D5CDD505-2E9C-101B-9397-08002B2CF9AE} | The DocumentSummaryInformation and UserDefined Property Sets


The DocumentSummaryInformation and UserDefined Property Sets

A DocumentSummaryInformation and UserDefined property set is an extension to the Summary Information property set. Both property sets may exist simultaneously.

The name of the stream that contains the DocumentSummaryInformation property set is

    "\005DocumentSummaryInformation"

The format identifier (FMTID) for the DocumentSummary Information property set is

    D5CDD502-2E9C-101B-9397-08002B2CF9AE

The declaration for this value is available in the provided header files as FMTID_DocSummaryInformation. See: Names in IStorage, The Summary Information Property Set, IPropertySetStorage::Create and Format Identifiers.

This stream also has a separate section for the custom-user-defined properties as in the DocumentSummaryInformation and UserDefined Property Sets. This section appears in the IPropertySet Storage interface as a separate property set, with the following FMTID (available as FMTID_UserDefinedProperties):

    D5CDD505-2E9C-101B-9397-08002B2CF9AE

These two property sets are the only ones for which a single stream can hold multiple property sets. The fact that these two property sets are in a single stream affects the behavior of the IPropertySetStorage interface. See IPropertySetStorage for more information.

The following table shows the added properties to the DocumentSummaryInformation and UserDefined property set. As in the SummaryInformation property set, the names are not typically stored in the property set, but are inferred from the property ID.

Name | Property ID String | Property ID | VT Type(*)
Category | PIDDSI_CATEGORY | 0x00000002 | VT_LPSTR
PresentationTarget | PIDDSI_PRESFORMAT | 0x00000003 | VT_LPSTR
Bytes | PIDDSI_BYTECOUNT | 0x00000004 | VT_I4
Lines | PIDDSI_LINECOUNT | 0x00000005 | VT_I4
Paragraphs | PIDDSI_PARCOUNT | 0x00000006 | VT_I4
Slides | PIDDSI_SLIDECOUNT | 0x00000007 | VT_I4
Notes | PIDDSI_NOTECOUNT | 0x00000008 | VT_I4
HiddenSlides | PIDDSI_HIDDENCOUNT | 0x00000009 | VT_I4
MMClips | PIDDSI_MMCLIPCOUNT | 0x0000000A | VT_I4
ScaleCrop | PIDDSI_SCALE | 0x0000000B | VT_BOOL
HeadingPairs | PIDDSI_HEADINGPAIR | 0x0000000C | VT_VARIANT | VT_VECTOR
TitlesofParts | PIDDSI_DOCPARTS | 0x0000000D | VT_VECTOR | (VT_LPSTR)
Manager | PIDDSI_MANAGER | 0x0000000E | VT_LPSTR
Company | PIDDSI_COMPANY | 0x0000000F | VT_LPSTR
LinksUpToDate | PIDDSI_LINKSDIRTY | 0x00000010 | VT_BOOL

These properties have the following uses:

Category
A text string typed by the user indicating what category the file belongs to (memo, proposal, and so forth). It is useful for finding files of same type.
PresentationTarget
Target format for presentation (35mm, printer, video, and so forth).
Bytes
Number of bytes.
Lines
Number of lines.
Paragraphs
Number of paragraphs.
Slides
Number of slides.
Notes
Number of pages that contain notes.
HiddenSlides
Number of slides that are hidden.
MMClips
Number of sound or video clips.
ScaleCrop
Set to True (-1) when scaling of the thumbnail is desired. If not set, cropping is desired.
HeadingPairs
Internally used property indicating the grouping of different document parts and the number of items in each group. The titles of the document parts are stored in the PIDDSI_DOCPARTS property. The HeadingPairs property is stored as a vector of variants, in repeating pairs of VT_LPSTR (or VT_LPWSTR) and VT_I4 values. The VT_LPSTR value represents a heading name, and the VT_I4 value indicates the count of document parts under that heading.
TitlesofParts
Names of document parts.
Manager
Manager of the project.
Company
Company name.
LinksUpToDate
Boolean value to indicate whether the custom links are hampered by excessive noise, for all applications.

Note As described in 12.3. Serialized Format for Property Sets of the OLE 2.0 Design Specification, vector elements in the HeadingPairs and TitlesofParts properties should be aligned on 32 bit boundaries within the property set. However, in the DocumentSummaryInformation and UserDefined property sets, when the code page of the property set is not Unicode, these elements must be packed.

The UserDefined property set can be used to hold any properties. Typically, it is used to store named properties that have been created by a user.


Structured Storage Serialized Property Set Format

Persistent property sets provide a way to store information within file system entities. It is recommended that to create and manage them, you use the IPropertySetStorage and IPropertyStorage interfaces as described in Properties and Property Sets.

Property sets are made up of a tagged section of values, with the section uniquely identified by a format identifier (FMTID). Every property consists of a property identifier and a type indicator that represents a value. Each value stored in a property set has a unique property identifier that distinguishes the property. The type indicator describes the representation of the data in the value.

When you use the IPropertySetStorage and IPropertyStorage interfaces, you do not have to deal directly with the COM serialized property set format structure. See these topics for further detail.

All data elements within a property set are stored in Intel representation (that is, in little-endian byte order).

COM defines a standard, serialized data format for property sets. When you are dealing directly with the serialized format, and not with the interfaces, property sets have the following characteristics:

  • Property sets allow for different applications to create their own independent property sets to serve the application's needs.
  • Property sets can be stored in a single IStream instance or in an IStorage instance containing multiple streams. Property sets are simply another data type that can be stored in many different forms of an in-memory or on-disk storage. For recommended conventions on creating the string name for the storage object, see Storage Object Naming Conventions.
  • Property sets allow for a dictionary of displayable names to be included that further describe the contents. A set of conventions for choosing property names is recommended. For more information on this optional dictionary, see Reserved Property Identifiers, including Property ID 0.

The property set stream is divided into three major parts:

  • Header
  • FORMATID/offset pair
  • Section containing the actual property set values

The overall length of the property set stream must be less than or equal to 256K. The following sections, Property Set Header, Format Identifier/Offset Pair, and Section (including Property Identifiers/Offset Pairs), with supporting topics, describe the individual components that make up the property-set data format.

Note Previous versions of this document described extensions to the property set stream with more than one section allowed, but that has been revised to provide for one section in the property stream. The one exception is The DocumentSummaryInformation and UserDefined Property Sets.


Property Set Header

At the beginning of the property set stream is a header. It consists of a byte-order indicator, a format version, the originating operating system version, the class identifier (CLSID), and a reserved field.

The following pseudo-structure illustrates the header:

typedef struct tagPROPERTYSETHEADER
{
    // Header
    WORD   wByteOrder ;  // Always 0xFFFE
    WORD   wFormat ;     // Always 0
    DWORD   dwOSVer ;    // System version
    CLSID  clsID ;       // Application CLSID
    DWORD  reserved ;    // Should be 1
} PROPERTYSETHEADER;


Byte-Order Indicator

The byte-order indicator is a WORD data type and should always hold the value 0xFFFE. This is the same as the Unicode byte-order indicator. This value is always written in Intel byte order and, thus, appears in the file or stream as 0xFE, 0xFF.


Format Version

The Format Version value is a WORD data type that is used to indicate the format version of this stream. It may be zero or one. The format-version indicator should be checked when reading the property set. If it is not zero or one, then the stream was written to a different specification and cannot be read by code developed according to this specification.

Property sets with Format Version 1 are equivalent to Version 0, with the following additions:

  • Case sensitive property names. Property names are stored in the reserved dictionary property, Property ID 0. In version 1 property sets, these names can be case sensitive. Case sensitivity is determined by the reserved Behavior property, Property ID 0x80000003.
  • Long property names. Version 1 property sets, unlike version 0 property sets, are not limited in the length of property names. See Property ID 0 for more information on property names.
  • Property types. Version 1 property sets can hold all the property types that can be held in a version 0 property set plus some additional types. See the PROPVARIANT structure for more information on these types.


Operating System Versions

This DWORD data type should hold the kind of operating system in the high-order word and the version number of the operating system in the low-order word. Possible values for the operating system are:

Operating System | Value
32-Bit Windows (Win32®) | 0x0002
Macintosh | 0x0001
16-Bit Windows (Win16) | 0x0000

For Windows, the operating system version is the low-order word returned by the GetVersion function. On Windows, the following code would correctly set the version of the originating operating system:

#ifdef WIN32
dwOSVer = (DWORD)MAKELONG( LOWORD(GetVersion()), 2 ) ;
#else
dwOSVer = (DWORD)MAKELONG( LOWORD(GetVersion()), 0 ) ;
#endif


Class Identifier

The class identifier (CLSID) is that of a class that can display and/or provide programmatic access to the property values. If there is no such class, it is recommended that you set this value the same as the FMTID (see below). Alternatively, you can set this value to all zeroes; however, using the FMTID allows more flexibility in the future.


Reserved

The value of the DWORD data type is reserved for future use. Writers of property sets should set this value to 1; readers of property sets should ensure that this value is at least 1. An exception to this guideline is that, for The DocumentSummaryInformation and UserDefined Property Sets, this value may be 2.


Format Identifier/Offset Pair

The second part of the property set stream contains one Format Identifier/Offset Pair. The FMTID is the name of the property set; it uniquely identifies how to interpret the contents of the following section. The Offset is the distance of bytes from the start of the whole stream to where the section begins.

The following structure is helpful in dealing with Format Identifier/Offset Pairs:

typedef struct tagFORMATIDOFFSET
{
    FMTID  fmtid ;     // Name of the section
    DWORD  dwOffset ;  // Offset for the section
} FORMATIDOFFSET;


Format Identifiers

Property set values are stored in a section that is tagged with a unique FMTID. For example, the FMTID for the COM Summary Information property set is:

F29F85E0-4FF9-1068-AB91-08002B27B3D9

To define a FMTID for the Summary Information property set, you would use the DEFINE_GUID macro in an include file for the code that manipulates the property set:

DEFINE_GUID(FMTID_SummaryInformation, 0xF29F85E0, 0x4FF9, 0x1068,
0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9);

Anywhere in your code you need to use the FMTID for the Summary Information property set, you can access it through the FMTID_SummaryInformation variable.

When storing property sets in IStorage instances, you need to convert the FMTID to a string name for the storage object.


Allocating Format Identifiers

FMTIDs are created and represented in the same way as COM CLSIDs and interface identifiers. To create a unique FMTID, use the UUIDGEN.EXE program included in the Win32® API.


Section

The section is the third part of the property set stream, and contains the actual property set values. Specifically, a section contains:

  • Byte count for the section (which is inclusive of the byte count itself)
  • Array of 32-bit Property ID/Offset pairs
  • Array of property Type Indicators/Value pairs

Offsets are the distance from the start of the section to the start of the property (type, value) pair. This allows a section to be copied as an array of bytes without any translation of internal structure.

The following pseudo-structures illustrate the format of a section:

typedef struct tagPROPERTYSECTIONHEADER
{
    DWORD  cbSection ;    // Size of Section
    DWORD  cProperties ;  // Count of Properties in section
} PROPERTYSECTIONHEADER;

typedef struct tagPROPERTYIDOFFSET
{
    DWORD  propid;    // Name of property
    DWORD  dwOffset;  // Offset from start of section to that property
} PROPERTYIDOFFSET;

typedef struct tag SERIALIZEDPROPERTYVALUE
{
    DWORD  dwType;    // Property Type
    BYTE   rgb[];     // Property Value
} SERIALIZEDPROPERTYVALUE ;


Size of Section

This DWORD data type indicates the size (in bytes) of the section. Because the section size is the first 4 bytes, you can copy sections as an array of bytes. The section size should always be a multiple of four.

For example, an empty section (one with zero properties in it) would have a byte count of eight (the DWORD byte count and the DWORD count of properties). The section itself would contain the 8 bytes:

08 00 00 00 00 00 00 00


Count of Properties

This DWORD data type gives a count of the property values in the section. A property set may contain any number of property values. Readers must be able to handle the case where there are zero properties.


Property Identifiers/Offset Pairs

Following the Count of Properties property set value is an array of Property Identifiers/Offset Pairs property set values. Property Identifiers are 32-bit values that uniquely identify a property within a section. The Offset Pairs indicate the distance from the start of the section to the start of the property Type/Value Pair. Because the offsets are relative to the section, sections can be copied as an array of bytes.

Property identifiers are not sorted in any particular order. Properties can be omitted from the stored property set; readers must not rely on a specific order or range of property identifiers.


Type Indicators

After the table of Property Identifiers/Offset Pairs property set values come the actual properties. Each property is stored as a DWORD data type, followed by the data type's value.

Type indicators and their associated values are described in the PROPVARIANT structure reference page.

All Type/Value pairs must begin on a 32-bit boundary. Thus, values may be followed with null bytes to align the subsequent pair on a 32-bit boundary. Given a count of bytes, the following code will calculate how many bytes are needed to align on a 32-bit boundary:

cbAdd = (((cbCurrent + 3) >> 2) << 2) - cbCurrent ;

Within a vector of values, each repetition of a simple scalar value smaller than 32 bits must align with its natural alignment rather than with a 32-bit alignment. In practice, this is only significant for types VT_UI1, VTUI2, VT_I2, and VT_BOOL (which have one- or two-byte natural alignment). All other types have four-byte natural alignment. Some types (VT_R8, etc.) actually have 8-byte natural alignment, but are stored as if they have 4-byte alignment. Therefore, a property value with type indicator VT_I2 | VT_VECTOR would be:

  • A DWORD element count, followed by
  • A sequence of packed two-byte integers with no padding between them.

Note that any 32-bit counts or property types that are stored as a part of a vector property element must also be 32-bit aligned.

A property value of type identifier VT_LPSTR | VT_VECTOR would be:

  • A DWORD element count (DWORD cElems), followed by
  • A sequence of strings (char rgch[]), each preceded by a length-count DWORD data type and possibly followed by null padding to round to a 32-bit boundary.


Reserved Property Identifiers

As a designer of property sets, you can use any property identifier (ID) for your properties except 0, 1, and all values greater than or equal to 0x80000000. These property identifier values are reserved for use by applications.

Of the reserved properties, those with ID values in the range 0x80000000 to 0xBFFFFFFF are considered read-only by implementations that do not understand their meaning. For example, if an implementation does not understand the meaning of property 0x80000000, it should allow that property to be read but not written or deleted. Such an implementation should allow a property in the range 0xC0000000 to 0xFFFFFFFE to be read, written, or deleted. The Property ID 0xFFFFFFFF is a reserved value and should never appear in a serialized property set.

The following sections describe specific reserved property IDs:

  • Property ID 0
  • Property ID 1
  • Property ID 0x80000000
  • Property ID 0x80000003
  • Property ID 0xFFFFFFFF
  • Other Reserved Property Identifiers


Property ID 0

To enable users of property sets to attach meaning to properties beyond those provided by the type indicator, property ID 0 is reserved for an optional dictionary of displayable names for the property set.

The dictionary contains a count of entries in the list, followed by a list of dictionary entries.

typedef struct tagDICTIONARY
{
    DWORD  cEntries ;               // Count of entries in the list
    ENTRY  rgEntry[ cEntries ] ;    // Property ID/String pair
} DICTIONARY ;

Each dictionary entry in the list is a Property Identifier/String pair. The following is a pseudo-structure definition for a dictionary entry. It's a pseudo-structure because the sz[] member is variable in size.

typedef struct tagENTRY
{
    DWORD  propid ;    // Property ID
    DWORD  cch ;       // Count of characters in the string
    char  sz[cch];     // Zero-terminated string
} ENTRY ;

Note the following about property set dictionaries:

  • Property ID 0 does not have a type indicator. The DWORD data type that indicates the count of entries sits in the type-indicator position.
  • The count of characters in the string (cch) includes the zero character that terminates the string. When the codepage of the property set is not Unicode, this field is actually a byte count. For property sets with a format version of 0, this count may not exceed 256. For property sets with a format version of 1, this count may be as large as is allowed by the total property set size.
  • The dictionary is entirely optional. Not all the names of properties in the set need appear in the dictionary. Conversely, not all names in the dictionary need to correspond to properties in the set. The dictionary should omit entries for properties assumed to be universally known by clients that manipulate the property set. Typically, names for the base property sets for widely accepted standards are omitted, but special-purpose property sets may include dictionaries for use by browsers.
  • Property names in the dictionary are stored in the code page indicated by Property ID 1. For ANSI code pages, each dictionary entry is byte-aligned. Thus, there is no padding between property names with Property ID 0. This is the only case where values of DWORD data types (the property ID and property name-length DWORDs) are not required to be aligned on 32-bit boundaries. For Unicode pages, each dictionary entry is 32-bit aligned.
  • Property names that begin with the binary Unicode characters 0x0001 through 0x001F are reserved for future use.
  • The property name associated with Property ID 0 represents the name of the entire property set.


Sample Dictionary

The stock market data transfer example might include a displayable name "Stock Quote" for the entire set, and "Ticker Symbol" for PID_SYMBOL. If a property set contained just a symbol and the dictionary, the property set section would have a byte stream that looked like the following.

Offset     Bytes
; Start of section
0000    5C 01 00 00    ; DWORD size of section
0004    04 00 00 00    ; DWORD number of properties in section

; Start of PropID/Offset pairs
0008    01 00 00 00    ; DWORD Property ID (1 == code page)
000C    28 00 00 00    ; DWORD offset to property ID
0010    00 00 00 80    ; DWORD Property ID (0x80000000 == locale
                                                 ID)
0014    30 00 00 00    ; DWORD offset to property ID
0018    00 00 00 00    ; DWORD Property ID (0 == dictionary)
001C    38 00 00 00    ; DWORD offset to property ID
0020    07 00 00 00    ; DWORD Property ID (3 == PID_SYMBOL)
0024    5C 01 00 00    ; DWORD offset to property ID

; Start of Property 1 (code page)
0028    01 00 00 00    ; DWORD type indicator (VT_12)
002C    B0 04          ; USHORT codepage (0x04b0 == 1200 ==
                                                 unicode)
002E    00 00          ; Pad to 32-bit boundary

; Start of Property 0x80000000 (Local ID)
0030    13 00 00 00    ; DWORD type indicator (VT_U14)
0034    09 04 00 00    ; ULONG locale ID (0x0409 == American
                                                 English)

; Start of Property 0 (the dictionary)
0038    08 00 00 00    ; DWORD number of entries in dictionary
                             (Note:  No type indicator)
003C    00 00 00 00    ; DWORD propid == 0 (the dictionary)
0040    0C 00 00 00    ; DWORD cch == wcslen(L"Stock Quote") +
                                                 sizeof(L'\0') == 12
0044    L"Stock Quote" ; wchar_t wsz(12)
005C    05 00 00 00    ; DWORD propid == 5 (PID_HIGH)
0060    0B 00 00 00    ; DWORD cch == wcslen(L"High Price") +
                                                 sizeof(L'\0') == 11
0064    L"High Price\0"; wchar_t wsz(11)
007A    00 00          ; padding for 32-bit alignment (necessary
                             because the codepage is unicode)
007C    07 00 00 00    ; DWORD propid == 7 (PID_SYMBOL)
0080    0E 00 00 00    ; DWORD cch - wcslen(L"Ticker Symbol\0")
                             == 14
0084    L"Ticker Symbol\0" ; wchar_t wsz(14)

//The dictionary would continue, but may not contain entries
    //for every possible property, and may contain entries for
    //properties that are not present. Also, entries need not be
    //in order.


Property ID 1

Property ID 1 is reserved as an indicator of the code page (Windows) or Script (Macintosh) to use when interpreting the strings in the property set. All string values in the property set must be stored with the same code page. The originating operating system value in the property set header (PROPERTYSETHEADER::dwOSVer) determines whether the code page indicator corresponds to a Windows code page or Macintosh script.

When an application that is not the author of a property set changes a property of type string in the set, it should examine the code page indicator and take one of the following actions:

  • Write the string in the format specified by the code page indicator.
  • Replace and rewrite to change the code page.

If an application cannot understand this indicator, it should not modify the property. All creators of property sets must write a code page indicator; however, if the code page indicator is not present, the prevailing code page on the reader's computer must be assumed.

Note If the IPropertySetStorage interface is used to create a property set, the code page indicator is automatically written.

Possible values for the code page are given in the Win32® API (see the GetACP function) and Inside Macintosh Volume VI, pages 14-111. For example, the code page US ANSI is represented by 0x04E4 (1252 in decimal) while the code page for Unicode is 0x04B0 (1200 in decimal).

It is recommended that the Unicode code page be used whenever possible, and use VT_LPWSTR instead of VT_LPSTR to avoid multibyte <-> Unicode conversions during storage and retrieval. Using the same code page for all property sets is the only way to achieve interoperable property sets on a worldwide basis. In either the Unicode or non-Unicode code page, note that the count at the start of a VT_LPSTR or VT_BSTR is a byte count and not a character count. This byte count includes the one or two zero bytes at the end of the string (the string's NULL terminator).

Property ID 1 is a VT_I2 type and thus starts with a DWORD containing the value VT_I2 followed by a SHORT indicating the code page.


Property ID 0x80000000

Property ID 0x80000000 (Locale Indicator) is reserved as an indication of the locale for which the property set is written. The default locale for a property set is the system's default locale (LOCALE_SYSTEM_DEFAULT). See the Win32 APIs for more information on LOCALE_SYSTEM_DEFAULT. The default is used in the event that the locale indicator does not exist in the property set.

Applications can choose to support the locale or just get the default behavior. It is recommended that applications allow users to specify a working locale. Such applications should write the user-specified locale identifier to the property. Applications that use the user's default locale (LOCALE_USER_DEFAULT) should write the user's default locale identifier to the property. See the Win32 API for more information on LOCALE_USER_DEFAULT.

Note If the IPropertySetStorage interface is used to create a property set, the user's default locale is automatically written as the Locale Indicator.

Applications should also handle the case of a foreign object, which is one where the locale is not the application's locale, the user's locale, or the system's locale.

The locale indicator property is of type VT_U14, and therefore consists of a DWORD containing VT_U14, followed by a DWORD containing the Locale Identifier (LCID), as defined in the Win32 API.


Property ID 0x80000003

Property ID 0x80000003 is reserved as an indicator of property set behaviors. This property ID value is a VT_UI4 and starts with a DWORD data type containing the value VT_UI4 followed by a DWORD indicating property set behavior.

Currently, the only bit in this value that is defined is the low order bit (0x1). If this bit is set, it indicates that property set names, indicated by property ID 0, should be considered case sensitive. If this bit is not set or if the behavior property (ID 0x80000003) does not exist, then the names should be considered case insensitive.


Property ID 0xFFFFFFFF

Property ID 0xFFFFFFF is reserved and may never appear in a serialized property set. It is reserved for the convenience of programmability.


Other Reserved Property Identifiers

Property identifiers with the high bit set (that is, negative values) are reserved for future use by Microsoft.


Asynchronous Storage

Asynchronous storage enhances COM's structured storage specification to support asynchronous downloading of storage objects on high-latency, slow-link networks such as the Internet. Asynchronous storage enables both new and legacy applications that use compound files to efficiently render their content when accessed by means of existing Internet protocols. A single request to a World Wide Web server triggers the download of nested objects contained within a Web page, thereby eliminating the need to separately request each object. An asynchronous download and access mechanism enables an application to render the first page of data before all the data has been received. The exact order in which elements of a page become available can be specified by the Web publisher and is not dependent on random factors of network topology and server availability.

Asynchronous storage works together with asynchronous monikers to provide complete asynchronous binding behavior. (For more information on asynchronous monikers, see the Microsoft® ActiveX® software development kit.) A protocol-specific asynchronous moniker triggers the binding operation and sets up the required components. In the Internet case, this moniker would be one that can parse a URL to bind to an object or storage. If the target of the binding operation is a persistent object, the call to IMoniker::BindToStorage returns an asynchronous storage object.

Note Microsoft's current version of URL monikers does not support asynchronous storage. A future version will do so.

An asynchronous moniker client requests asynchronous binding by implementing a bind-status callback object and registering it with the bind context. The bind-status callback object exposes the IBindStatusCallback interface, which enables the client to specify binding preferences and to receive progress and global data-availability notifications during the course of a binding operation. The asynchronous compound file implementation provides a connection point for IProgressNotify, which clients can use to receive specific availability notifications on individual streams.


Storage Modes

Asynchronous storage supports two storage modes: blocking and nonblocking, which a client (either a browser or the object itself) can specify by returning BINDF_ASYNCSTORAGE from the moniker's call to IBindStatusCallback::GetBindInfo. If a client specifies BINDF_ASYNCSTORAGE, it receives a pointer to a nonblocking asynchronous storage. Otherwise, it receives a pointer to a blocking asynchronous storage. Even if the client does not request an asynchronous binding operation (by not registering IBindStatusCallback with the bind context), the moniker still returns a blocking asynchronous storage, enabling progressive loading for legacy applications.

In nonblocking mode, an asynchronous storage returns E_PENDING when data is unavailable. Upon receiving this message, the client waits for notification that additional data is available before trying again to download it.

In blocking mode, instead of returning E_PENDING, the asynchronous storage blocks the call until new data is available, then unblocks the call and returns the new data. The client must be ready to receive the data. While the thread is blocked, data already passed to the client is fully available to the user.

Blocking mode is necessary because clients unaware of asynchronous storage will not recognize E_PENDING and will assume that an unrecoverable error has occurred. Blocking asynchronous storage enables existing clients to do progressive rendering.


Asynchronous Compound Files

Asynchronous Compound Files, the system-provided implementation of asynchronous storage, enables the efficient downloading of compound files from the Internet in general and the Web in particular. The basic architecture of Asynchronous Compound Files is shown in the following diagram.

The Asynchronous Compound Files implementation can work with new asynchronous moniker types that understand Internet protocols and can bind to an object identified by a URL. Such a moniker would return an asynchronous IStream or IStorage pointer from the client's call to IMoniker::BindToStorage.

Compound Files in general are implemented on top of a byte array object, an abstraction of a file that represents an object's data as a flat byte array. The byte array object exposes its functionality through the ILockBytes interface. If a byte array supports nonblocking asynchronous storage, it returns E_PENDING to the compound-file implementation, which in turn propagates the error back to the caller.

To keep track of the data available during a download, a byte array that supports asynchronous storage exposes the IFillLockBytes interface on a wrapper object provided by the system specifically for this purpose. The downloading code provided by an asynchronous moniker calls this interface to fill the byte array asynchronously, as data is available. The wrapper object also exposes an ILockBytes interface, which the Asynchronous Compound Files implementation uses to read and write data from and to the array.

Asynchronous storage and stream objects provide a connection point for the IProgressNotify interface, which is implemented by the asynchronous moniker's downloading code. The Asynchronous Compound Files implementation calls IProgressNotify to provide the downloader with information about the status of the downloading operation.


How Asynchronous Binding and Storage Work

When a user clicks a link representing a document embedded in a Web page, the following steps occur:

  1. The browser calls the MkParseDisplayName function, passing the link's URL.
  2. MkParseDisplayName parses the URL, creates a corresponding aynchronous moniker, and returns a pointer to the moniker's IMoniker interface.
  3. The browser calls IsAsyncMoniker to determine if the moniker is asynchronous, creates a bind context, registers the IBindStatusCallback interface with the bind context (only if the moniker is asynchronous), and calls IMoniker::BindToObject, passing the bind context.
  4. The moniker binds to the object and queries it for the IPersistMoniker interface, which indicates whether the object supports asynchronous binding and storage. If the object returns a pointer to IPersistMoniker:
    1. The URL moniker calls IPersistMoniker::Load, passing its own IMoniker pointer to the object.
    2. The object modifies the bind context, chooses whether it wants a blocking or nonblocking storage, registers its own IBindStatusCallback and calls IMoniker::BindToStorage on the pointer it received through IPersistMoniker::Load.
    3. The moniker creates an asynchronous storage, keeps a reference to the wrapper object's IFillLockBytes interface, registers the IProgressNotify interface on the root storage, and calls IPersistStorage::Load, passing the asynchronous storage's IStorage pointer. As data arrives (on a background thread) the moniker calls IFillLockBytes to fill the ILockBytes on the temp file.
    4. The object reads data from the storage and returns from IPersistMoniker::Load when it has received sufficient data to consider itself initialized. If the object attempts to read data that has not yet been downloaded, the downloader receives a notification on IProgressNotify. Inside the IProgressNotify::OnProgress method, the downloading thread either blocks in a modal message loop, or causes the asynchronous storage to return E_PENDING, depending on whether the object has requested a blocking or nonblocking storage.
  5. If the object does not implement IPersistMoniker, the moniker queries for IPersistStorage, which indicates that the object's persistent state is stored in a storage object. If the object returns a pointer to IPersistStorage:
    1. The Moniker calls IMoniker::BindToStorage on itself, requesting a blocking IStorage (because the object is not asynchronous-aware), creates an asynchronous storage, keeps a reference to the wrapper object's IFillLockBytes interface, registers the IProgressNotify interface on the root storage, and calls IPersistStorage::Load, passing the asynchronous storage's IStorage pointer. As data arrives (on a background thread) the moniker calls IFillLockBytes to fill the ILockBytes on the temporary file.
    2. The object reads data from storage and returns from IPersistStorage::Load when it has received sufficient data to consider itself initialized. If the object attempts to read data that has not yet been downloaded, it receives a notification on IProgressNotify. Inside the IProgressNotify::OnProgress method, the downloading thread always blocks in a modal message loop.
  6. Regardless of whether the download is synchronous or asynchronous, the moniker returns from IMoniker::BindToObject, and the browser receives the initialized object it requested.
  7. The browser queries for IOleObject and hosts the object as a Document Object. (At this point the object may not be initialized completely, but enough to display something useful, in which case downloading continues in the background.)


Compound File Optimization

Asynchronous storage enables applications to precisely lay out their compound files so that data is available in the order in which applications will need it. If an application requires only part of its data to display a first page of information, this data can be placed at the beginning of the file, even if it logically resides at the end of a stream. Data from different streams can be interleaved. Audio and video data, for example, can be interleaved so that subsequent read operations retrieve both simultaneously, a requirement for multimedia applications.

New applications can optimize their compound files programmatically by calling the StgOpenLayoutDocfile API function. This function calls the ILayoutStorage interface, which is implemented by the root storage of the new compound files implementation. ILayoutStorage has member functions for scripting the layout of data, monitoring downloads to determine the order in which data is accessed, and rewriting the compound file to match the layout specified by scripting or determined by monitoring. The call to rewrite a file must occur before releasing the last pointer to the root storage of the file. Otherwise, the file will not be altered.

Legacy applications can optimize their compound files by using the Compound File Layout Tool (dflayout.exe) included in Win32. This capability, combined with a blocking storage enable legacy applications to progressively download and render their data.


Using Structured Storage

The Platform Software Developer's Kit (SDK) includes a pair of structured storage samples that illustrate a storage server and a storage client. Both samples include tour files that contain build instructions and documentation about the sample.

Click on the buttons below to jump to the structured storage server and client samples.

Structured Storage Server Sample Install the Platform SDK Install the Platform SDK

Structured Storage Client Sample Install the Platform SDK Install the Platform SDK


Storage in COM Objects

One of the most common current uses of persistent properties is to use them to store information about a system object, such as a document, with that object. There is, of course, the potential of storing properties with any object, such as a printer, so it would only be necessary to look at its properties to determine its location, its type, and so on. A user object could have a property set that includes information like First Name, Last Name, Office, and Phone. Applications can be written to query a systemwide set of objects based on their properties, for example, displaying all printers located in a certain building. Current systems, however, most frequently use properties on documents.

The primary property set standard that COM has defined is The Summary Information Property Set. This property set is both simple and commonly used. Most documents created by applications have a common set of attributes that are useful to users of those documents. These attributes include the name of the document's author, the subject of the document, when it was created, and so on. Two other property sets are defined for Office 95 / Office 97. These are the COM Document Summary property set and the User-Defined Properties property set, and are described in more detail in Structured Storage Serialized Property Set Format.

In Windows® 3.1, each application had a different way of storing this information within its documents. To examine the summary information for a given document, the user had to run the application that created the document, open it, and invoke the application's Summary Information dialog box, so only the application could display its summary information.

COM property sets and the property set interfaces make it possible to see a document's properties without running the creating application. For example, all versions of Microsoft® Word, 6.0 or later, and many other COM-enabled applications now save their documents using COM structured storage and the property set standard described here. Thus, other Office Suite applications are able to display The Summary Information Property Set for such a file, as long as that file is a COM structured storage file, and the creating application saved the information in the COM Property Set format. The Windows 95/98 shell, for example, takes advantage of this, and allows the end user to see the properties of any Word 6.0 or later document directly from the shell.

To take advantage of property sets from other applications, the other applications must understand how to interpret the properties within a property set, which implies a standard. COM has pioneered this approach by defining one standard property set, the COM Summary Information Property Set. Any application that has the definition of this property set can easily access the summary information contained in any document created by an COM application that uses that property set specification.


Managing Properties

Every property consists of a property identifier (unique within its property set), a variant type (VT or VarType) tag that represents the type of a value, and the value itself. The variant type tag describes the representation of the data in the value. In addition, a property may also be assigned a string name that can be used to identify the property, rather than using the required numerical property identifier (ID). To create and manage properties, COM defines the IPropertyStorage interface.

The IPropertyStorage interface includes methods to read and write arrays of either properties or property names. The interface includes Commit and Revert methods that are similar to IStorage methods of the same name. There are utility methods that allow you to set the class identifier (CLSID) of the property set, set the times associated with the set, and get statistics about the property set. Finally, the Enum method creates an enumerator and returns a pointer to its IEnumSTATPROPSTG interface. You can call the methods of this interface to enumerate STATPROPSTG structures on your object, which will provide information about all of the properties in the current property set.

The following is an example of how properties are represented. If a specific property in a property set holds an animal's scientific name, that name could be stored as a zero-terminated string. Stored along with the name would be a type indicator to indicate that the value is a zero-terminated string. These properties might have the following characteristics:

Property ID | String Identifier | Type Indicator | Value Represented
02 | PID_ANIMALNAME | VT_LPWSTR | Zero-terminated Unicode string
03 | PID_LEGCOUNT | VT_I2 | WORD

Any application that recognizes the property set format -- identifying it through its format identifier (FMTID) -- can look at the property with an identifier of PID_ANIMALNAME, determine that it is a zero-terminated string, and read and write the value. Although the application can call IPropertyStorage::ReadMultiple to read any or all of a property set (having first obtained a pointer), the application must know how to interpret the property set.

A property value is passed through property interfaces as an instance of the type PROPVARIANT.

It is important to distinguish between these stored (persistent) properties, and run-time properties. Variant-type value constants have names beginning with VT_. The set of valid PROPVARIANTs is, however, not entirely equivalent to the set of VARIANTs used in Automation and ActiveX controls.

The only difference between the two structures is the allowable set of VT_ (Variant Type / VarType) tags in each. Where a certain property type can be used in both a VARIANT and a PROPVARIANT, the type tag (the VT_ value) always has an identical value. Further, for a given VT_ value, the in-memory representation used in VARIANTs and PROPVARIANTs is identical. Taken all together, this approach allows the type system to catch disallowed type tags, while at the same time allowing a knowledgeable client to implement a pointer cast when appropriate.

For more information, see the following section, Property Storage Considerations.


Property Storage Considerations

IPropertyStorage::ReadMultiple reads as many of the properties specified in the rgpspec array as are found in the property set. As long as any of the properties requested is read, a request to retrieve a property that does not exist is not an error. Instead, this must cause VT_EMPTY to be written for that property to the rgvar[] array on return. When none of the requested properties exist, the method should return S_FALSE, and set VT_EMPTY in each PROPVARIANT. If any other error is returned, no property values are retrieved, and the caller need not worry about releasing them.

The rgpspec parameter is an array of PROPSPEC structures, which specify for each property either its property identifier or, if one is assigned, a string identifier. You can map a string to a property identifier by calling IPropertyStorage::WritePropertyNames. The use of property identifiers is, however, likely to be significantly more efficient than the use of strings.

Properties that are requested by string name (PRSPEC_LPWSTR) are mapped case-insensitively to property identifiers (IDs) as they are specified in the current property set (and according to the current system locale).

When the property type is VT_LPSTR and the property is read from an ANSI property set, that is, the code page for the property set is set to something other than Unicode, the value of the property uses the same code page as the property set. When a VT_LPSTR property is read from a Unicode property set, the value of the property uses the system's current default ANSI code page, that is, the code page returned from the GetACP function.

A PROPVARIANT, except for those that are pointers to streams and storages, is called a simple PROPVARIANT. These simple PROPVARIANTs receive data by value, so a call to IPropertyStorage::ReadMultiple supplies a copy of the data that the caller then owns. To create or update these properties, call IPropertyStorage::WriteMultiple.

In contrast, the variant types VT_STREAM, VT_STREAMED_OBJECT, VT_STORAGE, and VT_STORED_OBJECT are non-simple properties, because rather than supplying a value, the method retrieves a pointer to the indicated interface, from which the data can then be read. These types permit the storage of large amounts of information through a single property. There are several issues that arise in using nonsimple properties.

To create these properties, as for the other properties, call IPropertyStorage::WriteMultiple. Rather than calling the same method to update, however, it is more efficient to first call IPropertyStorage::ReadMultiple to get the interface pointer to the stream or storage, then write data using the IStream or IStorage methods. A stream or storage opened through a property is always opened in direct mode, so an additional level of nested transaction is not introduced. There may, however, still be a transaction on the property set as a whole, depending on how it was opened or created through IPropertySetStorage. Further, the access and share mode tags specified when the property set is opened or created, are passed to property-based streams or storages.

The lifetimes of property-based stream or storage pointers, although theoretically independent of their associated IPropertyStorage and IPropertySetStorage pointers, in fact, effectively depend on them. The data visible through the stream or storage is related to the transaction on the property storage object from which it is retrieved, just as for a storage object (supporting IStorage) with contained stream and storage sub-objects. If the transaction on the parent object is aborted, existing IStream and IStorage pointers subordinate to that object enter a "zombie" state. Because IPropertyStorage is the only interface on the property storage object, the useful lifetime of the contained IStream and IStorage pointers is bounded by the lifetime of the IPropertyStorage interface.

The implementation must also deal with the situation where the same stream- or storage-valued property is requested multiple times through the same IPropertyStorage interface instance. For example, in the COM compound file implementation, the open will succeed or fail depending on whether or not the property is already open.

Another issue is multiple opens in transacted mode. The result depends on the isolation level that was specified through a call to IPropertySetStorage methods, (either the Open or Create method, through the STGM flags) at the time that the property storage was opened.

If the call to open the property set specifies read-write access, IStorage- and IStream-valued properties are always opened with read-write access. Data can then be written through these interfaces, changing the value of the property, which is the most efficient way to update these properties. The property value itself does not have an additional level of transaction nesting, so changes are scoped under the transaction (if any) on the property storage object.

Storage and Stream Properties

To write a stream or storage object to a property set, the property set must have been created as nonsimple. For more information on simple and nonsimple property sets, see the section titled Storage vs. Stream for a Property Set. The following property types, as specified in the vt field of the rgvar array elements, are stream or storage types: VT_STREAM, VT_STORAGE, VT_STREAMED_OBJECT, VT_STORED_OBJECT.

To write a stream or storage object as a property in a non-simple property set, call IPropertyStorage::WriteMultiple. While you would also call this method to update simple properties, it is not an efficient way to update stream and storage objects in a property set. This is because updating one of these properties through a call to WriteMultiple creates in the property storage object a copy of the passed-in data, and the IStorage or IStream pointers are not retained beyond the duration of this call. It is usually more efficient to update stream or storage objects directly by first calling IPropertyStorage::ReadMultiple to get the interface pointer to the stream or storage, then writing data through the IStream or IStorage methods.

For example, you can call IPropertyStorage::WriteMultiple to write a NULL stream or storage object. The implementation will then create an empty object in the property set. You can then get access to this object by calling IPropertyStorage::ReadMultiple. When you finish updating this object you need not write it to the property set, as your updates were going directly into the property set.

A stream or storage opened through a property is always opened in direct mode, so an additional level of nested transaction is not introduced. There still may be a transaction on the property set as a whole. (For example, if the IPropertyStorage was obtained by calling IPropertySetStorage::Open with the STGM_TRANSACTED flag set in the grfmode paramenter.) Further, a property-based stream or storage is opened in read-write mode, if possible, given the mode on the property set; otherwise, read mode is used.

As mentioned earlier, when a stream or storage object is written to a property set with the WriteMultiple method, a copy of the object is made. When such a copy is made on a stream object, the copy operation starts at the current seek position of the source. The seek position is undefined on failure, but on success it is at the end of the stream; the seek pointer is not restored to its original position.

If a stream or storage property has been read from a property set with ReadMultiple, is still held open, and a subsequent call to WriteMultiple for the same property is made, the WriteMultiple operation will succeed. The previously opened stream or storage property is placed in the reverted state (all calls to it will return STG_E_REVERTED error).

If the WriteMultiple method returns an error when writing an array of properties, or even individual non-simple properties, the amount of data actually written is undefined.

Reference Properties

If a specified PROPVARIANT structure includes the VT_BYREF flag in its vt member, the associated property is a reference property. A reference property is automatically dereferenced before writing the value to the property set. For example, if the vt member of the PROPVARIANT structure specifies a value of type VT_BYREF | VT_I4, the actual value written is a VT_I4 type. A subsequent call to the IPropertyStorage::ReadMultiple method returns a value as VT_I4. Using reference properties is similar to calling the VariantCopyInd function. VariantCopyInd frees the destination variant and makes a copy of the source VARIANTARG, performing the necessary indirection if the source is specified to be VT_BYREF. This function is useful when a copy of a variant is needed, and to guarantee that it is not VT_BYREF, for example when handling arguments in an implementation of IDispath::Invoke.

Notes to Callers

It is recommended that property sets be created as Unicode, by not setting the PROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage::Create. It is also recommended that you avoid using VT_LPSTR values, and use VT_LPWSTR values instead. When the property set code page is Unicode, VT_LPSTR string values are converted to Unicode when stored, and back to multibyte string values when retrieved. When the code page of the property set is not Unicode, property names, VT_BSTR strings, and non-simple property values are converted to multibyte strings when stored, and converted back to Unicode when retrieved, all using the current system ANSI code page.

Notes to Implementers

When allocating a property identifier, the implementation can choose any value not currently in use in the property set for a property identifier, as long as it is not 0 or 1 or greater than 0x80000000, all of which are reserved values. The propidNameFirst parameter establishes a minimum value for property identifiers within the set, and must be greater than 1 and less than 0x80000000. See Remarks section above.

See Also

IPropertyStorage-Compound File Implementation, IPropertyStorage-NTFS File System Implementation, IPropertyStorage-Stand-alone Implementation


Property Set Implementations in COM

While the potential for uses of persistent property sets is not fully tapped, there are currently two primary uses:

  • Storing summary information with an object such as a document
  • Transferring property data between objects

COM property sets were designed to store data that is suited to representation as a moderately sized collection of fine-grained values. Data sets that are too large for this to be feasible should be broken into separate streams, storages, and/or property sets. The COM property set data format was not meant to provide a substitute for a database of many tiny objects.

COM provides implementations of the property set interfaces for various objects, along with three helper functions. The following section describes some performance characteristics of these implementations. For more information on specific interfaces and how to get a pointer to these interfaces, refer to the following in the COM reference section:

  • IPropertySetStorage–Compound File Implementation

    The compound file implementation, which provides the IStorage and IStream interfaces, also provides the IPropertySetStorage and IPropertyStorage interfaces. Given a compound file implementation of IStorage, the IPropertySetStorage interface can be obtained by calling IStorage::QueryInterface.

  • IPropertySetStorage–NTFS File System Implementation

    The IPropertySetStorage and IPropertyStorage interfaces can also be obtained for NTFS files that are not compound files. Therefore, it is possible to obtain these interfaces for all files on an NTFS volume.

  • IPropertySetStorage–Stand-alone Implementation

    When this implementation of IPropertySetStorage and IPropertyStorage is instantiated, it is given a pointer to an object that supports the IStorage interface. It then manipulates property set storages within that storage object. Thus, it is possible to access and manipulate property sets on any object that supports IStorage.

  • IPropertySetStorage Implementation Considerations

    There are several issues to consider in providing an implementation of the IPropertySetStorage interface. Please refer to these Implementation Considerations in the COM Reference section.

In addition, there are four helper functions, designed to aid in dealing with properties that have been read from a property set into memory (into a PROPVARIANT structure):

  • PropVariantInit
  • PropVariantClear
  • FreePropVariantArray
  • PropVariantCopy

The following sections discuss property set implementations in COM in greater detail:

  • Property Set Considerations
  • Managing Property Sets
  • Storing Property Sets
  • Performance Characteristics
  • Implementing the Summary Information Property Set
  • IPropertySetStorage Implementation Considerations


Property Set Considerations

Because the property set stream is read into memory in its entirety before a single property can be read or written, it is strongly recommended that property sets be kept small. "Small" means less than 32 kilobytes of data. This should not present too much of a problem because typically, "in-line" properties will be small items such as descriptive strings, keywords, time stamps, counts, author names, globally unique identifiers (GUIDs), class identifiers (CLSIDs), and so forth.

To store larger chunks of data, or in cases where the total size of a set of related properties far exceeds the recommended amount, the use of nonsimple types such as VT_STREAM and VT_STORAGE are strongly recommended. These are not stored inside the property set stream, so they do not significantly affect the initial overhead of the first accessing and writing of a property. There is some effect because the property set stream contains the name of the sibling stream- or storage-valued property and this takes a small amount of time to process.


Managing Property Sets

A persistent property set contains related pieces of information in the form of properties. Each property set is identified with an FMTID, and a globally unique identifier (GUID) that allows programs accessing the property set to identify the property set. Through this identification, the program interprets the properties the set contains. Examples of property sets might be the character-formatting properties in a word processor or the rendering attributes of an element in a drawing program.

COM defines the IPropertySetStorage interface to facilitate management of property sets. Through the methods of this interface, you can create a new property set, or open or delete an existing property set. In addition, it provides a method that creates an enumerator and supplies a pointer to its IEnumSTATPROPSETSTG interface. You can call the methods of this interface to enumerate STATPROPSETSTG structures on your object, which will provide information about all of the property sets on the object.

When you create or open an instance of IPropertyStorage, it is similar to opening an object that supports IStorage or IStream, because you need to specify the storage mode in which you are opening the interface. For IStorage, these include the transaction mode, the read/write mode, and the sharing mode.

When you create a property set with a call to IPropertySetStorage::Create, specify whether the property set is to be simple or nonsimple. A simple property set contains types that can be written fully within the property set stream, which is intended to be limited in size, and can be no larger than 256 kilobytes (KB). However, when you need to store a larger amount of information in the property set, you can specify that the property set be nonsimple. This allows you to use one or more of the types that specify only a pointer to a storage or stream object. These types are VT_STREAM, VT_STREAMED OBJECT, VT_STORAGE and VT_STORED_OBJECT. Data stored in these properties is not counted against the 256-KB property-set size limit. However, data about the property, such as its name, does apply. Also, if you need a transacted update, the property set must be nonsimple. There is, of course, a certain performance penalty for opening these types, because it requires opening the stream or storage object to which you have the pointer.

If your application uses compound files, you can use the COM-provided implementation of these interfaces, which are implemented on the COM compound file storage object.

Each property set consists primarily of a logically connected group of properties, as described in Managing Properties.


Storing Property Sets

Applications can expose some of the state of their documents so that other applications can locate and read that information. Some examples are a property set describing the author, title, and keywords of a document created with a word processor, or the list of fonts used in a document. This facility is not restricted to documents; it can also be used on embedded objects. Generally, access to property sets should be through the IPropertySetStorage and IPropertyStorage interfaces, but this section describes the previously recommended way.

Note If you are storing a property set that is internal to your application, you may not want to follow the guidelines described below. If you want to expose your property set to other applications, you need to follow these guidelines.

To store a property set in a compound file

  1. Create an IStorage or IStream instance in the same level of the storage structure as its data streams. Follow the name of your IStorage or IStream instance with "\005." Stream and storage names that begin with 0x05 are reserved for common property sets that can be shared among applications. Also, streams beginning with that value are limited to 256K. The names can be selected from either published names and formats, or by assigning the property set a FMTID and deriving the name from the FMTID according to the conventions described in Storage Object Naming Conventions.
  2. A property set may be stored in a single IStream instance or in an IStorage instance containing multiple streams and storages. In the case of an IStorage instance, the contained stream named Contents is the primary stream containing property values, where some values may be names of other streams or storage instances within the storage for this property set.
  3. Specify the CLSID of the object class that can display or provide programmatic access to the property values. If there is no such class, the CLSID should be set to the format identifier of the property set. For a property set that uses an IStorage instance, either set the CLSID of the IStorage instance to be the same as that stored in the Contents stream or to CLSID_NULL (the value in a newly created IStorage instance).
  4. You have the option of specifying displayable names that form the contents of the dictionary.

Some applications can read only implementations of property sets stored as IStream instances. Applications should be written to expect that a property set may be stored in either an IStorage or IStream instance, unless the property set definition indicates otherwise. For example, the Summary Information property set's definition says that it can only be stored in a named IStream instance. In cases where you are searching for a property set and don't know whether it is a storage or stream, look for an IStream instance with your property set name first. If that fails, look for an IStorage instance.

To better understand storing property sets in an IStorage implementation, suppose there is a class of applications that edit information about animals. First, a CLSID (CLSID_AnimalApp) is defined for this set of applications, so they can indicate that they understand property sets containing animal information (FMTID_AnimalInfo), and others containing medical information (FMTID_MedicalInfo).

IStorage (File): "C:\OLE\REVO.DOC"
  IStorage: "\005AnimalInfo", CLSID = CLSID_AnimalApp
    IStream: "Contents"
      WORD wByteOrder, WORD wFmtVersion, DWORD dwOSVer,
      CLSID CLSID_AnimalApp, DWORD cSections...
      ...
      FMTID = FMTID_AnimalInfo
      Property: Type = PID_ANIMALTYPE, Type = VT_LPWSTR, Value = L"Dog"
      Property: Type = PID_ANIMALNAME, Type = VT_LPWSTR, Value = L"Revo"
      Property: Type = PID_MEDICALHISTORY, Type = VT_STREAMED_OBJECT,
              Value = "MedicalInfo"
      ...
    IStream: "MedicalInfo"
      WORD wByteOrder, WORD wFmtVersion, DWORD dwOSVer,
      CLSID CLSID_AnimalApp, DWORD cSections...
      ...
      FMTID = CLSID_MedicalInfo
      Property: Type = PID_VETNAME, Type = VT_LPWSTR, Value = L"Dr. Woof"
      Property: Type = PID_LASTEXAM, Type = VT_DATE, Value = ...

Note that the CLSID of the IStorage interface and both property sets is CLSID_AnimalApp. This identifies any application that can display and/or provide programmatic access to these property sets. Any application can read the information within the property sets (the point behind property sets), but only applications identified with the class identifier of CLSID_AnimalApp can understand the meaning of the data in the property sets.


Performance Characteristics

A call to the COM compound file implementation of the IPropertySetStorage interface to create a property set causes either a stream or storage to be created through a call to IStorage::CreateStream or IStorage::CreateStorage. A default property set is created in memory, but not flushed to disk. When there is a call to IPropertyStorage::WriteMultiple, it operates within the buffer.

When a property set is opened, IStorage::OpenStream or IStorage::OpenStorage is used. The entire property set stream is read into contiguous memory. IPropertyStorage::ReadMultiple operations then operate by reading the memory buffer. Therefore, the first access is expensive in terms of time (because of disk reads) but subsequent accesses are very efficient. Writes may be slightly more expensive because SetSize operations on the underlying stream may be required to guarantee that disk space is available if data is added.

No guarantees are made as to whether IPropertyStorage::WriteMultiple will flush updates. In general, the client should assume that IPropertyStorage::WriteMultiple only updates the in memory buffer. To flush data, IPropertyStorage::Commit or IPropertyStorage::Release (last release) should be called.

This design means that WriteMultiple may succeed but the data is not actually persistently written.

Note This size of the property set stream cannot exceed 256K bytes.


Implementing the Summary Information Property Set

The following guidelines pertain to implementing The Summary Information Property Set:

  • PID_TEMPLATE refers to an external document containing formatting and styling information. The means by which the template is located is implementation-defined.
  • PID_LASTAUTHOR is the name stored in User Information by the application. For example, suppose Mary creates a document on her machine and gives it to John, who then modifies and saves it. Mary is the author, John is the last saved by value.
  • PID_REVNUMBER is the number of times the File/Save command has been called on this document.
  • Each of the date/time values must be stored in Universal Coordinated Time (UTC).
  • PID_CREATE_DTM is a read-only property; this property should be set when a document is created, but should not be subsequently changed.
  • For PID_THUMBNAIL, applications should store data in CF_DIB or CF_METAFILEPICT format. CF_METAFILEPICT is recommended.
  • PID_SECURITY is the suggested security level for the document. By noting the security level on the document, an application other than the originator of the document can adjust its user interface to the properties. An application should not display any information about a password-protected document or allow modifications to enforced Read-Only or Locked-for-Annotations documents. If the user attempts to modify properties, the application should warn the user about the Read-Only Recommended status.
Security Level | Value
None | 0
Password protected | 1
Read-only recommended | 2
Read-only enforced | 4
Locked for annotations | 8


IPropertySetStorage Implementation Considerations

Several issues arise when considering how to provide an implementation of the IPropertySetStorage interface that reads and writes the COM property set format. The following sections describe these considerations.

  • Names in IStorage
  • Storage vs. Stream for a Property Set
  • Setting the Property Set Class Identifier
  • Synchronization Points
  • Code Pages and Unicode Strings
  • Dictionary
  • Extensions
  • Version 0 vs. Version 1 Property Set Serialization

For more information, see IPropertySetStorage in the Reference section under Interfaces.


Names in IStorage

This is the most complex interoperability issue. In the IPropertySetStorage interface, property sets are identified with format identifiers (FMTIDs), but in IStorage, they are named with strings with a maximum length of 32 characters.

To accomplish this interoperability, the first task is to establish a mapping between FMTIDs and strings. Converting in the one direction, you have a FMTID, and need a corresponding string name. First, check whether the FMTID is one of a fixed set of well-known values, and use the corresponding well-known string name if so:

FMTID | String Name | Semantic
F29F85E0-4FF9-1068-AB91-08002B27B3D9 | "\005SummaryInformation" | COM2 summary information
D5CDD502-2E9C-101B-9397-08002B2CF9AE D5CDD505-2E9C-101B-9397-08002B2CF9AE | "\005DocumentSummaryInformation" | Office document summary information and user-defined properties.

Note The DocumentSummaryInformation and UserDefined property set is uniquie in that it contains two sections. Multiple sections are not permitted in any other property set. This property set is described in more detail in Structured Storage Serialized Property Set Format, and The DocumentSummaryInformation and UserDefined Property Sets. The first section was defined as part of COM; the second was defined by Microsoft Office.

Otherwise, algorithmically form a string name in the following way. First, convert the FMTID to little-endian byte order if necessary. Then, take the 128 bits of the FMTID and consider them as one long bit string by concatenating each of the bytes together. The first bit of the 128 bit value is the least significant bit of the first byte in memory of the FMTID; the last bit of the 128 bit value is the most significant bit of the last byte in memory of the FMTID. Extend these 128 bits to 130 bits by adding two zero bits to the end. Next, chop the 130 bits into groups of five bits; there will be 26 such groups. Consider each group as an integer with reversed bit precedence. For example, the first of the 128 bits is the least significant bit of the first group of five bits; the fifth of the 128 bits is the most significant bit of the first group. Map each of these integers as an index into the array of thirty-two characters:

ABCDEFGHIJKLMNOPQRSTUVWXYZ012345

This yields a sequence of 26 Unicode characters that uses only uppercase characters and numerals. Case sensitive and case insensitive considerations do not apply, causing each character to be unique in any locale. The final string is the concatenation of the string "\005" onto the front of these 26 characters, for a total length of 27 characters.

The following code illustrates one way to map from FMTID to property string:

#define CBIT_BYTE        8
#define CBIT_CHARMASK    5
#define CCH_MAP          (1 << CBIT_CHARMASK)    // 32
#define CHARMASK         (CCH_MAP - 1)           // 0x1f

CHAR awcMap[CCH_MAP + 1] = "abcdefghijklmnopqrstuvwxyz012345";

WCHAR MapChar(ULONG i) {
    return((WCHAR) awcMap[i & CHARMASK]);
    }

VOID GuidToPropertyStringName(GUID *pguid, WCHAR awcname[]) {
    BYTE *pb = (BYTE *) pguid;
    BYTE *pbEnd = pb + sizeof(*pguid);
    ULONG cbitRemain = CBIT_BYTE;
    WCHAR *pwc = awcname;

    *pwc++ = ((WCHAR) 0x0005);
    while (pb < pbEnd) {
        ULONG i = *pb >> (CBIT_BYTE - cbitRemain);
        if (cbitRemain >= CBIT_CHARMASK) {
            *pwc = MapChar(i);
            if (cbitRemain == CBIT_BYTE && *pwc >= L'a' && *pwc <= L'z') {
                *pwc += (WCHAR) (L'A' - L'a');
                }
            pwc++;
            cbitRemain -= CBIT_CHARMASK;
            if (cbitRemain == 0) {
                pb++;
                cbitRemain = CBIT_BYTE;
                }
            }
        else {
            if (++pb < pbEnd) {
                i |= *pb << cbitRemain;
                }
            *pwc++ = MapChar(i);
            cbitRemain += CBIT_BYTE - CBIT_CHARMASK;
            }
        }
    *pwc = L'\0';
    }

Converters of property string names to GUIDs should accept lowercase letters as synonymous with their uppercase counterparts. The following example shows one way to map from the property string to a FMTID:

ULONG
PropertySetNameToGuid(
    IN ULONG cwcname,
    IN WCHAR const awcname[],
    OUT GUID *pguid)
{
    ULONG Status = ERROR_INVALID_PARAMETER;
    WCHAR const *pwc = awcname;

    if (pwc[0] == WC_PROPSET0)
    {
        //Note: cwcname includes the WC_PROPSET0, and
        //sizeof(wsz...) includes the trailing L'\0', but
        //the comparison excludes both the leading
        //WC_PROPSET0 and the trailing L'\0'.

        if (cwcname == sizeof(wszSummary)/sizeof(WCHAR) &&
            wcsnicmp(&pwc[1], wszSummary, cwcname - 1) == 0)
        {
            *pguid = guidSummary;
            return(NO_ERROR);
        }

        if (cwcname == CWC_PROPSET)
        {
            ULONG cbit;
            BYTE *pb = (BYTE *) pguid - 1;

            ZeroMemory(pguid, sizeof(*pguid));
            for (cbit = 0; cbit < CBIT_GUID; cbit +=
            CBIT_CHARMASK)
            {
                ULONG cbitUsed = cibt % CBIT_BYTE;
                ULONG cbitStored;
                WCHAR wc;

                if (cbitUsed == 0)
                {
                    pb++;
                }
                wc = *++pwc - L'A';        //assume uppercase
                if (wc > CALPHACHARS)
                {
                    wc += (WCHAR) (L'A' - L'a')' //try lowercase

                    if (wc > CALPHACHARS)
                    {
                        wc += L'a' - L'0' + CALPHACHARS; //must
                                                      be a digit
                        if (wc > CHARMASK)
                        {
                            goto fail;       //invalid character
                        }
                    }
                }
                *pb |= (BYTE) (wc << cbitUsed);
                cbitStored = min(CBIT_BYTE - cbitUsed,
                CBIT_CHARMASK);
                //If the translated bits wouldn't fit in the
                  current byte

                if (cbitStored < CBIT_CHARMASK)
                {
                    wc >>= CBIT_BYTE - cbitUsed;
                    if (cbit + cbitStored == CBIT_GUID)
                    {
                       if (wc !+0)
                       {
                           goto fail;        //extra bits
                       }
                       break;
                    }
                    pb++;
                    *pb |= (BYTE) wc;
                }
           }
           Status = NO_ERROR
      }
    }
fail:
    return(Status);
}

When attempting to open an existing property set (in IPropertySetStorage::Open) the (root) FMTID is converted to a string as depicted above. If an element of the IStorage of that name exists, it is used. Otherwise, the open fails.

When creating a new property set, the above mapping determines the string name used.


Storage vs. Stream for a Property Set

To provide the proper controls for applications to fully interoperate through the IPropertySetStorage interface with the COM property set, the programmer must specify whether a property set is stored in a storage or a stream when that property set is created. This is indicated with the PROPSETFLAG_NONSIMPLE enumeration value passed in the grfFlags parameter to the IPropertySetStorage::Create method.

If the PROPSETFLAG_NONSIMPLE flag is set, the property set is stored in a storage object, and nonsimple property values can be written to it. Nonsimple values include values with a VARTYPE of VT_STORAGE, VT_STREAM, VT_STORED_OBJECT, or VT_STREAMED_OBJECT. See the PROPVARIANT structure for more information on VARTYPE values and how to use them.

If the PROPSETFLAG_NONSIMPLE flag is not set, only simple values can be written to the property set.

In the storage object of a nonsimple property set, a stream is created named Contents. This is the primary stream of the property set, and holds all simple property values. Nonsimple property values (streams and storages) are stored under the main storage object of the property set as substreams and storages. That is, these nonsimple values are stored as siblings to the Contents stream. The name of the sibling streams and storages is determined by the implementation, and stored in the Contents stream similar to the way a simple string property is stored.


Setting the Property Set Class Identifier

IPropertyStorage::SetClass, when invoked on a nonsimple property storage that has been stored in a structured storage object, sets both the CLSID of the storage object through a call to IStorage::SetClass and the class tag value stored in the COM property set. This provides a consistency and uniformity that creates better interaction with some tools. A property storage object is nonsimple if it was created by calling IPropertySetStorage::Create with the PROPSETFLAG_NONSIMPLE flag set.


Synchronization Points

When property sets are supported on the same object as IStorage, it is important to be aware of synchronization points between the base storage and the property storage. The property set storage holds the property set stream in an internal buffer until that buffer is committed through the IPropertyStorage::Commit method. This is true whether IPropertyStorage was opened in transacted mode or in direct mode.


Code Pages and Unicode Strings

Another consideration in implementing IPropertySetStorage is how Unicode property names are stored in the property ID 0 (the property name dictionary), which is not specified to use Unicode strings.

Unicode officially has a code page value of 1200. To store Unicode values in the property name dictionary, use a code page value of 1200 for the whole property set (in property ID 1), specified by the absence of the PROPSETFLAG_ANSI flag in IPropertySetStorage::Create. Note that this has the side effect of storing all string values in the property set in Unicode. In all code pages, the count found at the start of a VT_LPSTR is a byte count, not a character count. This is necessary to provide for compatibility with earlier-version clients.

The compound file implementation of IPropertySetStorage creates all new property sets completely either in Unicode (code page 1200) or in the current system ANSI code page. This is controlled by the absence or presence of the PROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage::Create.

Microsoft recommends that you create and open property sets as Unicode. To implement this, do not set the PROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage::Create. It is also recommended that you avoid using VT_LPSTR values, and use VT_LPWSTR values instead. When the property set code page is Unicode, VT_LPSTR string values are converted to Unicode when stored, and back to multibyte string values when retrieved. When the code page of the property set is not Unicode, property names, VT_BSTR strings, and nonsimple property references are converted to multibyte strings when stored, and converted back to Unicode when retrieved. If the property set code page is Unicode, VT_LPSTR string values are converted to Unicode when stored, and back to multibyte string values when retrieved.

Setting the PROPSETFLAG_ANSI flag as reported through a call to IPropertyStorage::Stat simply reflects whether the underlying code page is or is not Unicode. Note, though, that Property ID 1 can be explicitly read to learn the code page.

You can access Property ID 1 through a call to IPropertyStorage::ReadMultiple. However, it is read-only, and may not be updated with WriteMultiple. Furthermore, it may not be deleted with DeleteMultiple.


Dictionary

IPropertyStorage::WritePropertyNames is implemented using the Property ID 0 dictionary. Property ID 0 is not accessible through IPropertyStorage::ReadMultiple or IPropertyStorage::WriteMultiple.


Extensions

Property set extensions, as defined in the original COM property set format, have been removed and are not supported, except for those in The DocumentSummaryInformation and UserDefined Property Sets.


Version 0 vs. Version 1 Property Set Serialization

There are two versions of the property set serialization format. The original specification describes version 0 of the format. See Format Version for more information. Version 1 extends the original version. All version 0 property sets are valid as version 1 property sets. The Format Version field in the header of a serialized property set indicates the version.

The following items identify the differences between version 0 and version 1 property set serialization formats.

  • Support for new VARTYPE values. For more information about VARTYPE values and how to use them, see the topic IDispatch Data Types and Structures and the PROPVARIANT structure.

    The following VARTYPE values are not supported in version 0 property sets, but are supported in version 1:

    VT_I1
    VT_VECTOR | VT_I1
    VT_INT
    VT_UINT
    VT_DECIMAL

    Additionally, SafeArrays can be serialized in a property set. The presence of a SafeArray is indicated by the VT_ARRAY bit combined, using an OR operation, with the array elements in the vt member of the PROPVARIANT structure. For example, a SafeArray of 4-byte signed integers has a type of VT_ARRAY | VT_I4.

    The following element types are valid for a SafeArray in a serialized property set:

VT_I1 | VT_UI1 | VT_I2 | VT_UI2
VT_I4 | VT_UI4 | VT_INT | VT_UINT
VT_R4 | VT_R8 | VT_CY | VT_DATE
VT_BSTR | VT_BOOL | VT_DECIMAL | VT_ERROR
VT_VARIANT |  |  |

When the VT_VARIANT data type is specified, it indicates that the SafeArray itself holds PROPVARIANT structures. The types for these elements must be from the preceding list, except they cannot contain nested VT_VARIANT types.

Note that implementations of IPropertyStorage must be able to recover gracefully by returning an error when a new type is encountered; for example, VARENUM types.

  • Case sensitive property names. Property names, for example those specified in the IPropertyStorage::WritePropertyNames method, are not case sensitive in version 0 property sets. In version 1 property sets, property names can be case-sensitive depending on the value of the new Behavior property.

    The Behavior property is Property ID 0x80000003 with a type of VT_UI4. If the lowest bit of this value is set, the property set names are case sensitive. Set the PROPSETFLAG_CASE_SENSITIVE flag in the grfFlags parameter of the IPropertySetStorage::Create method to specify a case-sensitive property set.

  • Long property names. Property names for version 0 property sets must be less than or equal to 256 characters, including the string terminator, for property sets in the Unicode code page. If not in the Unicode code page, they must be less than 256 bytes. Version 1 property sets, on the other hand, can have property names of unlimited length, although they are still limited by the overall property set size limit of 256 kilobytes (KB).

It is recommended that implementations of IPropertyStorage create and maintain version 0 property sets by default. If a caller subsequently requests a feature specific to the version 1 format, only then should the version of the property set be updated. For example, if a property of type VT_ARRAY is written or if a long property name is written, then the implementation should update the property set format to version 1. One exception to this guideline occurs if the PROPSETFLAG_CASE_SENSITIVE enumeration value is specified in the call to IPropertySetStorage::Create. In this case, the property set must be created as a version 1 property set.


Structured Storage Reference

This section provides the reference material for the programming elements of Structured Storage. It contains information about limits on compound file implementation.

The main reference sections are as follows:

  • Interfaces
  • Functions
  • Structures
  • Enumerations
  • Samples


Interfaces

Structured Storage services are organized into three categories of interfaces. Each set represents a successive level of indirection, or abstraction, between a compound file, the objects it contains, and the physical media on which these individual components are stored.

The first category of interfaces consists of IStorage, IStream, and IRootStorage. The first two interfaces define how objects are stored within a compound file. These interfaces provide methods for opening storage elements, committing and reverting changes, copying and moving elements, and reading and writing streams. These interfaces do not understand the native data formats of the individual objects and therefore have no methods for saving those objects to persistent storage. The IRootStorage interface has a single method for associating a compound document with an underlying file system name. Clients are responsible for implementing these interfaces on behalf of their compound files.

The second category of interfaces consists of the IPersist interfaces, which objects implement to manage their persistent data. These interfaces provide methods to read the data formats of individual objects and therefore know how to store them. Objects are responsible for implementing these interfaces because clients do not know the native data formats of their nested objects. These interfaces, however, have no knowledge of specific physical storage media.

A third category consists of a single interface, ILockBytes, which provides methods for writing files to specific physical media, such as a hard disk or tape drive. COM provides an ILockBytes interface for the operating system's file system.

Compound File Implementation Limits

The COM implementation of Structured Storage architecture is called compound files. Storage objects, as implemented in compound files, include an implementation of the IPropertyStorage and IPropertySetStorage interfaces.

Pointers to the compound file implementation of these interfaces are acquired by calling the StgCreateStorageEx function to create a new compound file object, or StgOpenStorageEx to open a previously created compound file.

An alternative method for acquiring a pointer to the compound file implementation of these interfaces is by calling the older and more limited StgCreateDocfile or StgOpenStorage function. All four functions are treated as compound file implementations.

The compound file implementation can be configured to use 512 or 4096 byte sectors, as defined in the STGOPTIONS structure.

The compound file implementation of compound files is subject to the following implementation constraints.

Limit | Compound file
File size limits: | 512: 2 gigabytes (GB) 4096: Up to file system limits
Maximum heap size required for open elements: | 512: 4 megabytes (MB) 4096: Up to virtual memory limits
Concurrent root opens (opens of the same file): | If STGM_READ and STGM_SHARE_DENY_WRITE are specified, limits are dictated by the file-system limits. Otherwise, there is a limit of 20 concurrent root opens of the same file.
Number of elements in a file: | 512: Unlimited, but performance may degrade if elements number in the thousands 4096: Unlimited

Due to the 4-MB heap-size limit, the number of open elements in transacted mode is typically limited to several thousand elements.

The following is a list of hyperlinks for the Interface section of Structured Storage.

  • IDirectWriterLock
  • IDirectWriterLock - Compound File Implementation
  • IEnumSTATPROPSETSTG
  • IEnumSTATPROPSETSTG - Compound File Implementation
  • IEnumSTATPROPSTG
  • IEnumSTATPROPSTG - Compound File Implementation
  • IEnumSTATSTG
  • IFillLockBytes
  • IFillLockBytes - Implementation
  • ILayoutStorage
  • ISequentialStream
  • IStorage
  • IStorage - Compound File Implementation
  • IStream
  • IStream - Compound File Implementation

See Also

Samples


IDirectWriterLock

The IDirectWriterLock interface enables a single writer to obtain exclusive write access to a root storage object opened in direct mode while allowing concurrent access by multiple readers. This single-writer, multiple-reader mode does not require the overhead of making a snapshot copy of the storage for the readers.

When to Implement

Generally, you do not implement this interface unless you are defining a new storage scheme for your system. COM provides a compound file implementation of the IDirectWriterLock interface. For more information on the system-provided implementation, see IDirectWriterLock - Compound File Implementation.

When to Use

Use this interface to obtain exclusive write access to a storage object in direct mode.

Call IDirectWriterLock::WaitForWriteAccess to obtain exclusive write access to the storage object. Call IDirectWriterLock::ReleaseWriteAccess to release exclusive write access.

IDirectWriterLock::HaveWriteAccess indicates whether the storage is currently locked.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

IDirectWriterLock Method | Description
WaitForWriteAccess | Obtains exclusive write access to a storage.
ReleaseWriteAccess | Releases the write lock previously obtained.
HaveWriteAccess | Indicates whether the write lock has been taken.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Objidl.idl.

See Also

IStorage, StgCreateDocfile


IDirectWriterLock::HaveWriteAccess

Indicates whether the write lock has been taken.

HRESULT HaveWriteAccess(void);

Return Values

S_OK
The storage object is currently locked for write access.
S_FALSE
The storage object is not currently locked for write access.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Objidl.idl.

See Also

IDirectWriterLock::WaitForWriteAccess, IDirectWriterLock::ReleaseWriteAccess


IDirectWriterLock::ReleaseWriteAccess

Releases the write lock previously obtained.

HRESULT ReleaseForWriteAccess(void);

Return Values

S_OK
The write lock was successfully released.

Remarks

The writer calls this method to release exclusive access to the storage object previously taken by calling IDirectWriterLock::WaitForWriteAccess.

After the writer calls this method, it is safe to allow readers to reopen the storage again until the next call to IDirectWriterLock::WaitForWriteAccess.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Objidl.idl.

See Also

IDirectWriterLock::WaitForWriteAccess, IDirectWriterLock::HaveWriteAccess


IDirectWriterLock::WaitForWriteAccess

Obtains exclusive write access to a storage object.

HRESULT WaitForWriteAccess(
  DWORD dwTimeout  //Specifies a timeout for blocking
);

Parameter

dwTimeout
[in] Specifies the time in milliseconds that this method blocks while waiting to obtain exclusive write access to the storage object. If dwTimeout is zero, the method does not block waiting for exclusive access for writing. The INFINITE time-out defined in the Platform SDK is allowed for dwTimeout.

Return Values

S_OK
The caller has successfully obtained exclusive write access to the storage.
S_FALSE
This method was called again without an intervening call to IDirectWriterLock::ReleaseWriteAccess.
STG_E_INUSE
The specified time-out expired without obtaining exclusive write access.

Remarks

When a storage is opened in direct mode (STGM_DIRECT) with the STGM_READWRITE|STGM_SHARE_DENY_WRITE, you can call this method to obtain exclusive write access to the storage.

This method returns immediately if no readers have the storage open. If the storage is still open for reading, this method blocks for the specified dwTimeout or until the current readers close the storage.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Objidl.idl.

See Also

IDirectWriterLock::ReleaseWriteAccess, IDirectWriterLock::HaveWriteAccess


IDirectWriterLock - Compound File Implementation

The compound file implementation of IDirectWriterLock provides a way to open a compound file in direct mode with a single writer and multiple readers.

Compound files can be opened in direct mode using the STGM_DIRECT flag. The IDirectWriterLock interface makes the STGM_READWRITE|STGM_SHARE_DENY_WRITE flag valid in direct mode without requiring the overhead of a snapshot copy.

When a compound file is opened in transacted mode using the STGM_TRANSACTED flag, you can also have multiple readers and a single writer using the STGM_READWRITE|STGM_SHARE_DENY_WRITE flag. However, in this case, a snapshot copy of the file is made for the readers. There is often the additional overhead of a scratch copy as well.

When to Use

Use the system-supplied implementation of IDirectWriterLock when you open a storage in direct mode (STGM_DIRECT) with the STGM_READWRITE|STGM_SHARE_DENY_WRITE flags.

To obtain a pointer to IDirectWriterLock, call IStorage::QueryInterface on the root storage object for the compound file.

Call IDirectWriterLock::WaitForWriteAccess to obtain exclusive write access to a compound file. Call IDirectWriterLock::ReleaseWriteAccess to release exclusive write access.

IDirectWriterLock::HaveWriteAccess indicates whether the file is currently locked.

Remarks

The compound file implementation of the single-writer, multiple-reader feature is based on range locking. The writer obtains exclusive access to the storage to write after all current readers have closed the storage. While the writer is active, subsequent readers cannot open the storage. The writer calls IDirectWriterLock::WaitForWriteAccess to obtain exclusive write access. The writer must then call IDirectWriterLock::ReleaseWriteAccess to release the storage.

The call to IDirectWriterLock::WaitForAccess is required before writing in this single-reader, multiple-writer mode. Attempts to write to the file without calling IDirectWriterLock::WaitForAccess first result in STG_E_ACCESSDENIED. This error is returned even if the writer opened the file initially, and no readers currently have the file open.

Marshaling Considerations

Custom marshaling is typically used when a compound file is marshaled to another process on the same machine. When marshaling storages, access rights are not considered, and the IStorage pointer is passed to the new process with the same access modes and rights as the original marshaling process. For more information on access modes, see the STGM enumeration. During marshaling, no locks are taken or checked to ensure exclusive write access. In this case, there is no enforcement of the single-writer policy for compound files opened in the single-writer, multiple-reader mode. Instead, enforcement is handled internally by the compound file implementation.

Since the IStorage pointer is passed to another process during marshaling, it is possible for two processes to have simultaneous access to the same compound file. Even though a caller may have obtained exclusive write access to the storage by calling IDirectWriterLock::WaitForAccess, the marshaled version can also have access simultaneously. The marshaled versions are not forced to close while the single writer accesses the file. In this case, the compound file implementation synchronizes the writes internally.

If a single writer obtains exclusive access by calling, IDirectWriterLock::WaitForAccess, the marshaled storage also has write access and does not have to call IDirectWriterLock::WaitForAccess. Both processes have write access and synchronization is controlled by the internal compound file implementation.

See Also

IDirectWriterLock


IEnumSTATPROPSETSTG

The IEnumSTATPROPSETSTG interface is used to iterate through an array of STATPROPSETSTG structures, which contain statistical information about the property sets managed by the current instance of IPropertySetStorage. IEnumSTATPROPSETSTG has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

The implementation defines the order in which the property sets are enumerated. Property sets that are present when the enumerator is created, and are not removed during the enumeration, will be enumerated only once. Property sets added or deleted while the enumeration is in progress may or may not be enumerated, but, if enumerated, will not be enumerated more than once.

For information on how the COM compound document implementation of IEnumSTATPROPSETSTG:Next supplies members of the STATPROPSETSTG structure, refer to IEnumSTATPROPSETSTG--Compound File Implementation.

When to Implement

IEnumSTATPROPSETSTG is implemented to enumerate the property sets supported by the current property set storage object. If you are using the compound file implementation of the storage object, a pointer to which is available through a call to StgCreateDocfile, IEnumSTATPROPSETSTG is implemented on that object, and a pointer is returned through a call to IPropertySetStorage::Enum. If you are doing a custom implementation of IPropertySetStorage, you need to implement IEnumSTATPROPSETSTG to fill in a caller-allocated array of STATPROPSETSTG structures, each of which contains information about the nested elements in the storage object.

When to Use

Call IPropertySetStorage::Enum to return a pointer to IEnumSTATPROPSETSTG, the methods of which can then be called to enumerate STATPROSETSTG structures so the application can manage its property sets.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  STATPROPSETSTG *rgelt,
  ULONG *pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumSTATPROPSETSTG **ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

IPropertyStorage::Enum


IEnumSTATPROPSETSTG-Compound File Implementation

The compound file implementation of IEnumSTATPROPSETSTG interface is used to enumerate through an array of STATPROPSETSTG structures, which contain statistical information about the properties managed by the compound file implementation of IPropertySetStorage, which is associated with a current compound file storage object.

When to Use

Call the methods of IEnumSTATPROPSETSTG to enumerate STATPROPSETSTG structures, each of which provides information about one of the property sets associated with the compound file storage object.

Remarks

IEnumSTATPROPSETSTG::Next
Gets the next one or more STATPROPSETSTG structures (the number is specified by the celt parameter). The STATPROPSETSTG elements provided through a call to the compound file implementation of IEnumSTATPROPSETSTG::Next follow these rules:
  • If IEnumSTATPROPSETSTG::Next cannot provide STATPROPSETSTG.fmtid, zeros are written to that member. This occurs when the property set does not have a predefined name (such as \005SummaryInformation) and is not a legal value.
  • The DocumentSummaryInformation and UserDefined property set is special, in that it may have two property set sections. This property set is described in the section The DocumentSummaryInformation and UserDefined Property Sets. The second section is referred to as the User-Defined Properties. Each section is identified with a unique format identifier (FMTID). When IPropertySetStorage::Enum is used to enumerate property sets, the User-Defined property set will not be enumerated.

Note If you always create a property set using IPropertySetStorage::Create, then, since a "Character GUID" is created for the storage name, IEnumSTATPROPSETSTG::Next will return a nonzero, valid FMTID for the property set [STATPROPSETSTG.fmtid].

  • The STATPROPSETSTG.grfFlags member does not necessarily reflect whether the property set is ANSI or not. If PROPSETFLAG_ANSI is set, the property set is definitely ANSI. If PROPSETFLAG_ANSI is clear, the property set could be either Unicode or non-Unicode, because it is not possible to tell whether it is ANSI without opening it.
  • The STATPROPSETSTG.grfFlags member does reflect whether the property set is simple or not, so the setting of the PROPSETFLAG_NONSIMPLE flag is always valid.
  • If IEnumSTATPROPSETSTG::Next cannot provide STATPROPSETSTG.clsid, it is set to all zeroes (CLSID_NULL). In the COM compound file implementation, this occurs when the property set is simple (the PROPSETFLAG_NONSIMPLE flag is not set), or is nonsimple but the CLSID was not explicitly set. For nonsimple property sets, the CLSID that is received is the one that is maintained by the underlying IStorage.
  • If IEnumSTATPROPSETSTG::Next cannot provide the time fields [ctime, mtime, atime], each non-supported time will be set to zeroes. In the COM compound file implementation, getting these values depends on retrieving them from the underlying IStorage implementation.
IEnumSTATPROPSETSTG::Skip
Skips the number of elements specified in celt. Returns S_OK if the specified number of elements are skipped, returns S_FALSE if fewer elements than requested are skipped. In any other case, returns the appropriate error.
IEnumSTATPROPSETSTG::Reset
Sets the cursor to the beginning of the enumeration. If successful, returns S_OK, otherwise, returns STG_E_INVALIDHANDLE.
IEnumSTATPROPSETSTG::Clone
Copies the current enumeration state of this enumerator.


IEnumSTATPROPSTG

The IEnumSTATPROPSTG interface is used to iterate through an array of STATPROPSTG structures, which contain statistical information about an open property storage containing a property set. IEnumSTATPROPSTG has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

The implementation defines the order in which the properties in the set are enumerated. Properties that are present when the enumerator is created, and are not removed during the enumeration, will be enumerated only once. Properties added or deleted while the enumeration is in progress may or may not be enumerated, but they will never be enumerated more than once.

Properties with property ID 0 (dictionary), property ID 1 (code page indicator), or a property ID greater than or equal to 0x80000000 are not enumerated.

Enumeration of a nonsimple property does not necessarily indicate that the property can be read successfully through a call to IPropertyStorage::ReadMultiple. This is because the performance overhead of checking existence of the indirect stream or storage is prohibitive during property enumeration. A client of this interface should code accordingly.

When to Implement

Implement IEnumSTATPROPSTG to enumerate the properties within a property set. If you are using the compound file implementation of the storage object, a pointer to which is available through a call to StgCreateDocfile, you can then query for a pointer to IPropertySetStorage. After calling one of its methods either to open or create a property set, you can get a pointer to IEnumSTATPROPSTG through a call to IPropertyStorage::Enum. If you are doing a custom implementation of IPropertyStorage, you also need to implement IEnumSTATPROPSTG to fill in a caller-allocated array of STATPROPSTG structures. Each STATPROPSTG structure contains information about a simple property.

When to Use

Applications that support storage objects and persistent properties within those objects call IPropertyStorage::Enum to return a pointer to IEnumSTATPROPSTG to enumerate the properties in the current property set.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  STATPROPSTG *rgelt,
  ULONG *pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumSTATPROPSTG **ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

IPropertyStorage::Enum, EnumAll Sample, Samples


IEnumSTATPROPSTG-Compound File Implementation

The compound file implementation of the IEnumSTATPROPSTG interface is used to enumerate through properties, resulting in STATPROPSTG structures, which contain statistical information about the properties managed by the compound file implementation of IPropertyStorage, which is associated with a current compound file storage object.

The constructor in the COM implementation of IEnumSTATPROPSTG creates a class that reads the entire property set, and creates a static array which can be shared when IEnumSTATPROPSTG::Clone is called.

When to Use

Call the compound file implementation of IEnumSTATPROPSTG to enumerate the STATPROPSTG structures that contain information about the properties within the current property set. When you are using the compound file implementation of the property storage interfaces, call IPropertyStorage::Enum to return a pointer to IEnumSTATPROPSTG to manage the property storage object and the elements within it.

Remarks

IEnumSTATPROPSTG::Next
Gets the next one or more STATPROPSTG structures (the number is specified by the celt parameter). Returns S_OK if successful.
IEnumSTATPROPSTG::Skip
Skips the number of elements specified in celt. The next element to be enumerated through a call to Next then becomes the element after the ones skipped. Returns S_OK if celt elements were skipped; returns S_FALSE if fewer than celt elements were skipped.
IEnumSTATPROPSTG::Reset
Sets the cursor to the beginning of the enumeration. If successful, returns S_OK, otherwise, returns STG_E_INVALIDHANDLE.
IEnumSTATPROPSTG::Clone
Uses the constructor for the IEnumSTATPROPSTG to create a copy of the array. Because the class that constructs the static array actually contains the object, this function mainly adds to the reference count.

See Also

STATPROPSTG, IPropertyStorage::Enum


IEnumSTATSTG

The IEnumSTATSTG interface is used to enumerate through an array of STATSTG structures, which contain statistical information about an open storage, stream, or byte array object. IEnumSTATSTG has the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. For general information on these methods, refer to IEnumXXXX.

When to Implement

IEnumSTATSTG is implemented to enumerate the elements of a storage object. If you are using the compound file implementation of the storage object, a pointer to which is available through a call to StgCreateDocfile, IEnumSTATSTG is implemented on that object, and a pointer is returned through a call to IStorage::EnumElements. If you are doing a custom implementation of a storage object, you need to implement IEnumSTATSTG to fill in a caller-allocated array of STATSTG structures, each of which contains information about the nested elements in the storage object.

When to Use

Containers call methods that return a pointer to IEnumSTATSTG so the container can manage its storage object and the elements within it. Calls to the IStorage::EnumElements method supplies a pointer to IEnumSTATSTG. The caller allocates an array of STATSTG structures and the IEnumSTATSTG methods fill in each structure with the statistics about one of the nested elements in the storage object. If present, the lpszName member of the STATSTG structure requires additional memory allocations through the IMalloc interface, and the caller is responsible for freeing this memory, if it is allocated, by calling the IMalloc::Free method. If the lpszName member is NULL, no memory is allocated, and, therefore, no memory needs to be freed.

The prototypes of the methods are as follows:

HRESULT Next(
  ULONG celt,
  STATSTG *rgelt,
  ULONG *pceltFetched
);

HRESULT Skip(
  ULONG celt
);

HRESULT Reset(void)

HRESULT Clone(
  IEnumSTATSTG **ppenum
);

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

CoGetMalloc, IEnumXXXX, IStorage::EnumElements, STATSTG, EnumAll Sample, Samples


IFillLockBytes

The IFillLockBytes interface enables downloading code to write data asynchronously to a structured storage byte array. When the downloading code has new data available, it calls IFillLockBytes::FillAppend or IFillLockBytes::FillAt to write the data to the byte array. An application attempting to access this data, through calls to the ILockBytes interface, can do so even as the downloader continues to make calls to IFillLockBytes. If the application attempts to access data that has not already been downloaded through a call to IFillLockBytes, then ILockBytes returns a new error, E_PENDING.

When to Implement

You normally would not implement this interface. A system developer that wants to provide asynchronous storage for a protocol other than HTTP might implement IFillLockBytes as part of the transport layer.

When to Use

You normally would not call this interface. Monikers or other downloading code that provide asynchronous storage use this interface to fill the byte array as data becomes available.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

IFillLockBytes Methods | Description
FillAppend | Writes a new block of bytes to end of byte array.
FillAt | Writes a new block of bytes to specified location in byte array.
SetFillSize | Sets expected size of byte array.
Terminate | Notifies byte array wrapper of successful or unsuccessful termination of download.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IConnectionPoint, IConnectionPointContainer, ILockBytes, IProgressNotify, IStorage, IStream

Also see BINDINFO.


IFillLockBytes::FillAppend

Writes a new block of bytes to the end of a byte array.

HRESULT FillAppend(
  void const *pv,    //Data to be appended to byte array
  ULONG cb,          //Number of bytes to be appended
  ULONG *pcbWritten  //Number of bytes that were
                     // successfully appended
);

Parameters

pv
[in] Pointer to the data to be appended to the end of an existing byte array.
cb
[in] Size of pv in bytes.
pcbWritten
[out] Number of bytes that were successfully written.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL.

Remarks

The FillAppend method is used for sequential downloading, where bytes are written to the end of the byte array in the order in which they are received. This method obtains the current size of the byte array (for example, lockbytes object) and writes a new block of data to the end of the array. As each block of data becomes available, the downloader calls this method to write it to the byte array. Subsequent calls by the compound file implementation to ILockBytes::ReadAt return any available data or return E_PENDING if data is currently unavailable.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

ILockBytes


IFillLockBytes::FillAt

Writes a new block of data to a specified location in the byte array.

HRESULT FillAt(
  ULARGE_INTEGER uIOffset,
                     //Offset from beginning of the byte array
  void const *pv,    //Data to be written
  ULONG cb,          //Number of bytes to be written
  ULONG *pcbWritten  //Number of bytes that were successfully written
);

Parameters

uIOffset
[in] The offset, expressed in number of bytes, from the first element of the byte array.
pv
[in] Pointer to the data to be written at the location specified by uIOffset.
cb
[in] Size of pv in bytes.
pcbWritten
[out] Number of bytes that were successfully written.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL in addition to the following:

E_NOTIMPL
The byte array does not support the FillAt method.

Remarks

The FillAt method is used for nonsequential downloading (for example, HTTP byte range requests). In nonsequential downloading the caller specifies ranges in the byte array where various blocks of data are to be written. Subsequent calls by the compound file implementation to ILockBytes::ReadAt are passed by the byte array wrapper object's own implementation of ILockBytes to the underlying byte array. This method is not currently implemented and will return E_NOTIMPL.

Note The system-supplied IFillLockBytes implementation does not support FillAt and returns E_NOTIMPL.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IFillLockBytes::FillAppend, IFillLockBytes - Implementation, ILockBytes::ReadAt


IFillLockBytes::SetFillSize

Sets the expected size of the byte array.

HRESULT SetFillSize(
  ULARGE_INTEGER uISize  //Size in bytes of a byte array object
);

Parameters

uISize
[in] Size in bytes of the byte array object that is to be filled in subsequent calls to IFillLockBytes::FillAppend.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL.

Remarks

If SetFillSize has not been called, any call to ILockBytes::ReadAt that attempts to access data that has not yet been written using IFillLockBytes::FillAppend or IFillLockBytes::FillAt will return a new error message, E_PENDING. After SetFillSize has been called, any call to ReadAt that attempts to access data beyond the current size, as set by SetFillSize, returns E_FAIL instead of E_PENDING.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IFillLockBytes::FillAppend, IFillLockBytes::FillAt, ILockBytes::ReadAt


IFillLockBytes::Terminate

Informs the byte array that the download has been terminated, either successfully or unsuccessfully.

HRESULT Terminate(
  BOOL bCanceled  //Indicates if download was successful
);

Parameters

bCanceled
[in] Download is complete. If TRUE, the download was terminated unsuccessfully. If FALSE, the download terminated successfully.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL.

Remarks

After this method has been called, the byte array will no longer return E_PENDING.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.


IFillLockBytes - Implementation

The system provides an IFillLockBytes implementation as part of its existing implementation of Compound Files. Downloading code can create an instance of an asynchronous Compound File object by calling StgOpenAsyncDocFileOnIFillLockBytes and can create an instance of an asynchronous byte array wrapper object on an existing file or byte array by calling either the StgGetIFillLockBytesOnFile function or the StgGetIFillLockBytesOnILockBytes function.

When to Use

Currently, URL monikers are the only users of COM's asynchronous storage implementation.

Remarks

The following are the four methods of the IFillLockBytes implementation.

IFillLockBytes::FillAppend
Writes a new block of bytes to the end of a byte array. The size of the block is specified as a parameter to FillAppend.
IFillLockBytes::FillAt
Writes a new block of data to a specified location in the bite array.
IFillLockBytes::SetFillSize
Sets the size of the byte array. Returns E_FAIL from calls to ILockBytes::ReadAt that attempt to access data beyond the upper limit specified by the method.
IFillLockBytes::Terminate
Informs the byte array that a download has been terminated, either successfully or unsuccessfully.


ILayoutStorage

The ILayoutStorage interface enables an application to optimize the layout of its compound files for efficient downloading across a slow link. The goal is to enable a browser or other application to download data in the order in which it will actually be needed.

To optimize a compound file an application first saves it, then calls StgOpenLayoutDocfile to reopen the file using a special Compound Files implementation that exposes ILayoutStorage on its root storage. Once the storage is open, the application queries for a pointer to ILayoutStorage.

Using this pointer, the application can either provide explicit layout instructions or obtain them by monitoring the pattern of access to the compound file as it is loaded into memory.

  • To monitor the order in which a compound file's data is actually accessed, the application calls ILayoutStorage::BeginMonitor, after which the Compound Files implementation assumes that any operation performed on the storage or stream object is part of the desired access pattern. When the specified compound file is completely loaded, the application halts monitoring by calling ILayoutStorage::EndMonitor.

However the desired layout information is obtained, the application calls ILayoutStorage::ReLayoutDocfile to rewrite the compound file using either the explicit layout instructions or the monitored layout access patterns.

When to Implement

You do not need to implement this interface. A special implementation of Compound Files exposes ILayoutStorage on the root storage object.

When to Use

Use ILayoutStorage to optimize your application's compound files. The interface is exposed in dflayout.dll, which is included with the Win32® API. If you are a Web master, you can optimize compound files exposed in your Web site by using the Compound File Layout Tool (dflayout.exe), which is also included with Win32.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

ILayout Storage Method | Description
LayoutScript | Provides explicit layout instructions.
BeginMonitor | Monitors data access to a file.
EndMonitor | Ends monitoring of data access.
ReLayoutDocfile | Rewrites file using layout information.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILayoutStorage

Microsoft Platform SDK, February 2001 Edition.
This content last built on Thursday, February 08, 2001.


ILayoutStorage::LayoutScript

Provides explicit directions for reordering the storages, streams, and controls in a compound file to match the order in which they are accessed during the download.

HRESULT LayoutScript(
  StorageLayout *pStorageLayout,  //Pointer to first element
                                  // in an array of structures
  DWORD nEntries,                 //Number of elements in the array
  DWORD glfInterleavedFlag        //Reserved for future use
);

Parameters

pStorageLayout
[in] Pointer to an array of StorageLayout structures.
nEntries
[in] Number of entries in the array of StorageLayout structures.
glfInterleavedFlag
[in] Reserved for future use.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as the following:

STG_E_INVALIDPOINTER
The storage layout pointer is invalid.
STG_E_INVALIDFLAG
The value of glfInterleavedFlag is invalid.
STG_E_PATHNOTFOUND
The new document file name specified is invalid.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to complete the operation.
STG_E_INVALIDPARAMETER
One of the parameters is invalid.
STG_E_INUSE
The BeginMonitor method was called while ILayoutStorage was already monitoring.

Remarks

To provide explicit layout instructions, the application calls ILayoutStorage::LayoutScript, passing an array of StorageLayout structures. Each structure defines a single storage or stream data block and specifies where the block is to be written in the ILockBytes byte array.

An application can combine scripted layout with monitoring, as the structure of a particular compound file may dictate.

When the optimal data-layout pattern of an entire compound file has been determined, the application calls ILayoutStorage::ReLayoutDocfile to restructure the compound file to match the order in which its data sectors were accessed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILayoutStorage::ReLayoutDocfile, ILockBytes, StorageLayout


ILayoutStorage::BeginMonitor

Allows monitoring of a loading operation to begin. When the operation is complete, the application must call ILayoutStorage::EndMonitor.

HRESULT BeginMonitor(void);

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as the following:

STG_E_INUSE
BeginMonitor was called while ILayoutStorage was already monitoring.

Remarks

Normally an application calls BeginMonitor before the actual loading begins. Once this method has been called, the compound file implementation regards any operation performed on the files storages and streams as part of the desired access pattern. The result is a layout script like that created explicitly by calling ILayoutStorage::LayoutScript.

Applications will usually use monitoring to obtain the access pattern of embedded objects. Monitoring also makes possible generic layout tools, such as the Compound File Layout Tool (dflayout.exe) included in the Win32 API, that launch existing applications and monitor their access patterns.

A call to ILayoutStorage::EndMonitor ends monitoring. Multiple calls to BeginMonitor/EndMonitor are permitted. Monitoring can also be mixed with calls to ILayoutStorage::LayoutScript.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILayoutStorage::EndMonitor, ILayoutStorage::LayoutScript


ILayoutStorage::EndMonitor

Ends monitoring of a compound file. Must be preceded by a call to ILayoutStorage::BeginMonitor.

HRESULT EndMonitor(void);

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as all return values for CloseHandle.

Remarks

A call to EndMonitor is generally followed by a call to ILayoutStorage::RelayoutDocfile, which uses the access pattern detected by the monitoring to restructure the compound file.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILayoutStorage::BeginMonitor, ILayoutStorage::ReLayoutDocfile


ILayoutStorage::ReLayoutDocfile

Rewrites the compound file, using the layout script obtained through monitoring, or provided through explicit layout scripting, to create a new compound file.

HRESULT ReLayoutDocfile(
  OLECHAR *pwcsNewDfName  //Pointer to name of compound file
                          // to be rewritten
);

Parameters

pwcsNewDfName
[in] Pointer to the name of the compound file to be rewritten. This name must be a valid filename, distinct from the name of the original compound file. The original compound file will be optimized and written to the new pwcsNewDfName.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as the following:

STG_E_INVALIDNAME
The name passed to this function is not a valid file name.
STG_E_UNKNOWN
The layout information has been corrupted and cannot be processed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.


ILockBytes

The ILockBytes interface is implemented on a byte array object that is backed by some physical storage, such as a disk file, global memory, or a database. It is used by a COM compound file storage object to give its root storage access to the physical device, while isolating the root storage from the details of accessing the physical storage.

When to Implement

Most applications will not implement the ILockBytes interface because COM provides implementations for the two most common situations:

File-based implementation — If you call StgCreateDocfile function to create a compound file storage object, it contains an implementation of ILockBytes that is associated with a byte array stored in a physical disk file. The compound file storage object calls the ILockBytes methods — you do not call them directly in this implementation.

Memory-based implementation — COM also provides a byte array object based on global memory that supports an implementation of ILockBytes. You can get a pointer through a call to the CreateILockBytesOnHGlobal function). Then, to create a compound file storage object on top of that byte array object, call the StgCreateDocfileOnILockBytes function. The compound file storage object calls the ILockBytes methods — you do not call them directly in this implementation.

There are situations in which it would be useful for an application to provide its own ILockBytes implementation. For example, a database application could implement ILockBytes to create a byte array object backed by the storage of its relational tables. However, it is strongly recommended that you use the COM-provided implementations. For a discussion of the advantages of using the COM implementations rather than creating your own, see the StgCreateDocfileOnILockBytes API function, which creates a compound file storage object on top of a caller-provided byte array object.

If you choose to implement your own ILockBytes interface, you should consider providing custom marshaling by implementing the IMarshal interface as part of your byte array object. The reason for this is that when the COM-provided implementations of IStorage and IStream are marshaled to another process, their ILockBytes interface pointers are also marshaled to the other process. The default marshaling mechanism creates a proxy byte array object (on which is the ILockBytes interface) that transmits method calls back to the original byte array object. Custom marshaling can improve efficiency by creating a remote byte array object that can access the byte array directly.

When to Use

The ILockBytes methods are called by the COM implementations of IStorage and IStream on the compound file object. Unless you are implementing IStorage and IStream, you would not need to call ILockBytes methods directly. If you write your own ILockBytes implementation, you can use the StgCreateDocfileOnILockBytes function to create a compound file storage object backed by your implementation of ILockBytes.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

ILockBytes Methods | Description
ReadAt | Reads a specified number of bytes starting at a specified offset from the beginning of the byte array.
WriteAt | Writes a specified number of bytes to a specified location in the byte array.
Flush | Ensures that any internal buffers maintained by the byte array object are written out to the backing storage.
SetSize | Changes the size of the byte array.
LockRegion | Restricts access to a specified range of bytes in the byte array.
UnlockRegion | Removes the access restriction on a range of bytes previously restricted with ILockBytes::LockRegion.
Stat | Retrieves a STATSTG structure for this byte array object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.


ILockBytes::Flush

Ensures that any internal buffers maintained by the ILockBytes implementation are written out to the underlying physical storage.

HRESULT Flush(void);

Return Values

S_OK
The flush operation was successful.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_MEDIUMFULL
The flush operation is not completed because there is no space left on the storage device.
E_FAIL
General failure writing data.
STG_E_TOOMANYFILESOPEN
Under certain circumstances, the Flush method executes a download-and-closeto flush, which can lead to a return value of STG_E_TOOMANYFILESOPEN if no file handles are available.
STG_E_INVALIDHANDLE
An underlying file has been prematurely closed, or the correct floppy disk has been replaced by an invalid one.

Remarks

ILockBytes::Flush flushes internal buffers to the underlying storage device.

The COM-provided implementation of compound files calls this method during a transacted commit operation to provide a two-phase commit process that protects against loss of data.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

IStorage::Commit, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::LockRegion

Restricts access to a specified range of bytes in the byte array.

HRESULT LockRegion(
  ULARGE_INTEGER libOffset,
                       //Specifies the byte offset for the
                       // beginning of the range
  ULARGE_INTEGER cb,   //Specifies the length of the range in bytes
  DWORD dwLockType     //Specifies the type of restriction
                       // on accessing the specified range
);

Parameters

libOffset
[in] Specifies the byte offset for the beginning of the range.
cb
[in] Specifies, in bytes, the length of the range to be restricted.
dwLockType
[in] Specifies the type of restrictions being requested on accessing the range. This parameter uses one of the values from the LOCKTYPE enumeration.

Return Values

S_OK
The specified range of bytes was locked.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not supported.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_INVALIDHANDLE
An underlying file has been prematurely closed, or the correct floppy disk has been replaced by an invalid one.

Remarks

ILockBytes::LockRegion restricts access to the specified range of bytes. Once a region is locked, attempts by others to gain access to the restricted range must fail with the STG_E_ACCESSDENIED error.

The byte range can extend past the current end of the byte array. Locking beyond the end of an array is useful as a method of communication between different instances of the byte array object without changing data that is actually part of the byte array. For example, an implementation of ILockBytes for compound files could rely on locking past the current end of the array as a means of access control, using specific locked regions to indicate permissions currently granted.

The dwLockType parameter specifies one of three types of locking, using values from the LOCKTYPE enumeration. The types are as follows: locking to exclude other writers, locking to exclude other readers or writers, and locking that allows only one requestor to obtain a lock on the given range. This third type of locking is usually an alias for one of the other two lock types, and permits an Implementer to add other behavior as well. A given byte array might support either of the first two types, or both.

To determine the lock types supported by a particular ILockBytes implementation, you can examine the grfLocksSupported member of the STATSTG structure returned by a call to ILockBytes::Stat.

Any region locked with ILockBytes::LockRegion must later be explicitly unlocked by calling ILockBytes::UnlockRegion with exactly the same values for the libOffset, cb, and dwLockType parameters. The region must be unlocked before the stream is released. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

Notes to Callers

Since the type of locking supported is optional and can vary in different implementations of ILockBytes, you must provide code to deal with the STG_E_INVALIDFUNCTION error.

Notes to Implementers

Support for this method depends on how the storage object built on top of the ILockBytes implementation is used. If you know that only one storage object at any given time can be opened on the storage device that underlies the byte array, then your ILockBytes implementation does not need to support locking. However, if multiple simultaneous openings of a storage object are possible, then region locking is needed to coordinate them.

A LockRegion implementation can choose to support all, some, or none of the lock types. For unsupported lock types, the implementation should return STG_E_INVALIDFUNCTION.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILockBytes::Stat, ILockBytes::UnlockRegion, IStream::LockRegion, LOCKTYPE, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::ReadAt

Reads a specified number of bytes starting at a specified offset from the beginning of the byte array object.

HRESULT ReadAt(
  ULARGE_INTEGER ulOffset,
             //Specifies the starting point for reading data
  void *pv,  //Pointer to the buffer into which the data is read
  ULONG cb,  //Specifies the number of bytes to read
  ULONG *pcbRead
             //Pointer to location that contains actual
             // number of bytes read
);

Parameters

ulOffset
[in] Specifies the starting point from the beginning of the byte array for reading data.
pv
[in] Pointer to the buffer into which the byte array is read.
cb
[in] Specifies the number of bytes of data to attempt to read from the byte array.
pcbRead
[out] Pointer to a location where this method writes the actual number of bytes read from the byte array. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes that were read.

Return Values

S_OK
Indicates that the specified number of bytes were read, or the maximum number of bytes were read to the end of the byte array.
E_FAIL
Data could not be read from the byte array.
E_PENDING
Asynchronous Storage only: Part or all of the data to be read is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_READFAULT
The number of bytes to be read does not equal the number of bytes that were actually read.

Remarks

ILockBytes::ReadAt reads bytes from the byte array object. It reports the number of bytes that were actually read. This value may be less than the number of bytes requested if an error occurs or if the end of the byte array is reached during the read.

It is not an error to read less than the specified number of bytes if the operation encounters the end of the byte array. Note that this is the same end-of-file behavior as found in MS-DOS file allocation table (FAT) file system files.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILockBytes::WriteAt, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::SetSize

Changes the size of the byte array.

HRESULT SetSize(
  ULARGE_INTEGER cb  //Specifies the new size of the byte
                     // array in bytes
);

Parameter

cb
[in] Specifies the new size of the byte array as a number of bytes.

Return Values

S_OK
The size of the byte array was successfully changed.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_MEDIUMFULL
The byte array size is not changed because there is no space left on the storage device.

Remarks

ILockBytes::SetSize changes the size of the byte array. If the cb parameter is larger than the current byte array, the byte array is extended to the indicated size by filling the intervening space with bytes of undefined value, as does ILockBytes::WriteAt, if the seek pointer is past the current end-of-stream.

If the cb parameter is smaller than the current byte array, the byte array is truncated to the indicated size.

Notes to Callers

Callers cannot rely on STG_E_MEDIUMFULL being returned at the appropriate time because of cache buffering in the operating system or network. However, callers must be able to deal with this return code because some ILockBytes implementations might support it.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILockBytes::ReadAt, ILockBytes::WriteAt, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::Stat

Retrieves a STATSTG structure containing information for this byte array object.

HRESULT Stat(
  STATSTG *pstatstg,   //Location for STATSTG structure
  DWORD grfStatFlag    //Values taken from the STATFLAG enumeration
);

Parameters

pstatstg
[out] Pointer to a STATSTG structure in which this method places information about this byte array object. The pointer is NULL if an error occurs.
grfStatFlag
[in] Specifies whether this method should supply the pwcsName member of the STATSTG structure through values taken from the STATFLAG enumeration. If the STATFLAG_NONAME is specified, the pwcsName member of STATSTG is not supplied, thus saving a memory-allocation operation. The other possible value, STATFLAG_DEFAULT, indicates that all members of the STATSTG structure be supplied.

Return Values

S_OK
The STATSTG structure was successfully returned at the specified location.
E_OUTOFMEMORY
The STATSTG structure was not returned due to a lack of memory for the name member in the structure.
STG_E_ACCESSDENIED
The STATSTG structure was not returned because the caller did not have access to the byte array.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned, due to insufficient memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPOINTER
The value for the pStatStg parameter is not valid.

Remarks

ILockBytes::Stat should supply information about the byte array object in a STATSTG structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

STATFLAG, STATSTG, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::UnlockRegion

Removes the access restriction on a previously locked range of bytes.

HRESULT UnlockRegion(
  ULARGE_INTEGER libOffset,
                       //Specifies the byte offset for the
                       // beginning of the range
  ULARGE_INTEGER cb,   //Specifies the length of the range in bytes
  DWORD dwLockType     //Specifies the access restriction
                       // previously placed on the range
);

Parameters

libOffset
[in] Specifies the byte offset for the beginning of the range.
cb
[in] Specifies, in bytes, the length of the range that is restricted.
dwLockType
[in] Specifies the type of access restrictions previously placed on the range. This parameter uses a value from the LOCKTYPE enumeration.

Return Values

S_OK
The byte range was unlocked.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not supported.
STG_E_LOCKVIOLATION
The requested unlock cannot be granted.

Remarks

ILockBytes::UnlockRegion unlocks a region previously locked with a call to ILockBytes::LockRegion. Each region locked must be explicitly unlocked, using the same values for the libOffset, cb, and dwLockType parameters as in the matching calls to ILockBytes::LockRegion. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILockBytes::LockRegion, LOCKTYPE, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes::WriteAt

Writes the specified number of bytes starting at a specified offset from the beginning of the byte array.

HRESULT WriteAt(
  ULARGE_INTEGER ulOffset,
                    //Specifies the starting point for writing data
  void const *pv,   //Pointer to the buffer containing the
                    // data to be written
  ULONG cb,         //Specifies the number of bytes to write
  ULONG *pcbWritten //Pointer to location that contains
                    // actual number of bytes written
);

Parameters

ulOffset
[in] Specifies the starting point from the beginning of the byte array for the data to be written.
pv
[in] Pointer to the buffer containing the data to be written.
cb
[in] Specifies the number of bytes of data to attempt to write into the byte array.
pcbWritten
[out] Pointer to a location where this method specifies the actual number of bytes written to the byte array. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes written.

Return Values

S_OK
Indicates that the specified number of bytes were written.
E_FAIL
A general failure occurred during the write operation.
E_PENDING
Asynchronous Storage only: Part or all of the data to be written is currently unavailable. For more information see IFillLockBytes and [insert jump to asynchronous storage overview, which is to come].
STG_E_ACCESSDENIED
The caller does not have enough permissions for writing this byte array.
STG_E_WRITEFAULT
The number of bytes to be written does not equal the number of bytes that were actually written.
STG_E_MEDIUMFULL
The write operation was not completed because there is no space left on the storage device. The actual number of bytes written is still returned in pcbWritten.

Remarks

ILockBytes::WriteAt writes the specified data at the specified location in the byte array. The number of bytes actually written must always be returned in pcbWritten, even if an error is returned. If the byte count is zero bytes, the write operation has no effect.

If ulOffset is past the end of the byte array and cb is greater than zero, ILockBytes::WriteAt increases the size of the byte array. The fill bytes written to the byte array are not initialized to any particular value.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objidl.h.

See Also

ILockBytes::ReadAt, ILockBytes::SetSize, ILockBytes - File-Based Implementation, ILockBytes - Global Memory Implementation


ILockBytes - File-Based Implementation

Implemented on a byte array object underlying a COM compound file storage object, and designed to read and write directly to a disk file.

When to Use

Methods of ILockBytes are called from the compound file implementations of IStorage and IStream on the compound file storage object created through a call to StgCreateDocfile, so you do not need to call them directly.

Remarks

The following are the methods of the ILockBytes File-Based Implementation.

ILockBytes::ReadAt
Reads a block of bytes from a specified offset at the beginning of the byte-array.
ILockBytes::WriteAt
Writes a block of bytes from a specified offset at the beginning of the byte array.
ILockBytes::Flush
Ensures that any internal buffers maintained by the ILockBytes implementation are written out to the underlying physical storage.
ILockBytes::SetSize
Sets the size of the byte array.
ILockBytes::LockRegion
The dwLockTypes parameter is set to LOCK_ONLYONCE or LOCK_EXCLUSIVE, which will allow or restrict access to locked regions.
ILockBytes::UnlockRegion
This method unlocks the region locked by ILockBytes::LockRegion.
ILockBytes::Stat
The COM-provided IStorage::Stat implementation calls the ILockBytes::Stat method to retrieve information about the byte array object. If there is no reasonable name for the byte array, the COM-provided ILockBytes::Stat method returns NULL in the pwcsName member of the STATSTG structure.

See Also

ILockBytes, IStorage, IStream


ILockBytes - Global Memory Implementation

Implemented on a byte array object underlying a COM compound file storage object, and designed to read and write directly to global memory.

When to Use

Methods of ILockBytes are called from the compound file implementations of IStorage and IStream on the compound file storage object created through a call to StgCreateDocfile.

Remarks

The following are the methods of the ILockBytes Global Memory Implementation.

ILockBytes::ReadAt
Reads a blockof bytes from a specified offset at the beginning of the byte array.
ILockBytes::WriteAt
Writes the block of bytes from a specified offset at the beginning of the byte array.
ILockBytes::Flush
Unlike file-based implementation, calling this method in global memory implementation has no effect.
ILockBytes::SetSize
Sets the size of the byte array.
ILockBytes::LockRegion
This implementation does not support locking, so dwLocksType is set to zero. It is the caller's responsibility to ensure accesses are valid and mutually exclusive.
ILockBytes::UnlockRegion
This implementation does not support locking.
ILockBytes::Stat
The COM-provided IStorage::Stat implementation calls the ILockBytes::Stat method to retrieve information about the byte array object. If there is no reasonable name for the byte array, the COM-provided ILockBytes::Stat method returns NULL in the pwcsName member of the STATSTG structure.

See Also

ILockBytes, IStorage, IStream


IPropertySetStorage

Creates, opens, deletes, and enumerates property set storages that support instances of the IPropertyStorage interface. The IPropertyStorage interface manages a single property set in a property storage subobject; and the IPropertySetStorage interface manages the storage of groups of such property sets. Any file system entity can support IPropertySetStorage that is currently implemented in the COM compound file object.

The IPropertySetStorage and IPropertyStorage interfaces provide a uniform way to create and manage property sets, whether or not these sets reside in a storage object that supports IStorage. When called through an object supporting IStorage (such as structured and compound files) or IStream, the property sets created conform to the COM property set format, described in detail in Structured Storage Serialized Property Set Format. Similarly, properties written using IStorage to the COM property set format are visible through IPropertySetStorage and IPropertyStorage.

IPropertySetStorage methods identify property sets through a globally unique identifier (GUID) called a format identifier (FMTID). The FMTID for a property set identifies the property identifiers in the property set, their meaning, and any constraints on the values. The FMTID of a property set should also provide the means to manipulate that property set. Only one instance of a given FMTID may exist at a time within a single property storage.

When to Implement

Implement IPropertySetStorage to store persistent properties in an object. If you are using the COM compound files implementation, you can use the implementation on the compound file object created through a call to StgCreateStorageEx or StgOpenStorageEx.

When to Use

Call IPropertySetStorage methods to create, open, or delete one or more property sets, or to enumerate the property sets contained in this property set storage.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

IPropertySetStorage Methods | Description
Create | Creates a new property set.
Open | Opens a previously created property set.
Delete | Deletes an existing property set.
Enum | Creates and retrieves a pointer to an object that can be used to enumerate property sets.

Note There is an exception to the above in The DocumentSummaryInformation and UserDefined property set. This property set is unique in that it may have two property set sections in a single underlying stream. This property set is described in The DocumentSummaryInformation and UserDefined Property Sets. The first section is the DocumentSummaryInformation property set. The second section is the UserDefined property set. Each section is identified by a unique format identifier (FMTID); for example, FMTID_DocSummaryInformation and FMTID_UserDefinedProperties. The fact that these two property sets can exist in a single stream affects the behavior of the IPropertySetStorage interface.

When IPropertySetStorage::Create is called to create the UserDefined property set, the first section is created automatically. Thus, once FMTID_UserDefinedProperties is created, FMTID_DocSummaryInformation need not be created, but can be opened with a call to IPropertySetStorage::Open. Creating the first section does not automatically create the second section, and it is not possible to open both sections simultaneously.

Calling IPropertySetStorage::Delete, to delete the first section, causes both sections to be deleted. That is, calling IPropertySetStorage::Delete with FMTID_DocSummaryInformation causes both that section and the FMTID_UserDefinedProperties section to be deleted. However, deleting the second section does not automatically delete the first section.

When IPropertySetStorage::Enum is used to enumerate property sets, the UserDefined property set is not enumerated.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage-Compound File Implementation, IPropertySetStorage-NTFS File System Implementation, IPropertySetStorage-Stand-alone Implementation, IPropertyStorage, IEnumSTATPROPSETSTG, STATPROPSETSTG, PROPVARIANT, EnumAll Sample, Samples


IPropertySetStorage::Create

Creates and opens a new property set in the property set storage object.

HRESULT Create(
  REFFMTID fmtid, //FMTID of the property set to
                  // be created
  CLSID *pclsid,  //Pointer to initial CLSID for this property set
  DWORD grfFlags, //PROPSETFLAG values
  DWORD grfMode,  //Storage mode of new property set
  IPropertyStorage**ppPropStg
                  //Pointer to output variable that receives
                  // the IPropertyStorage interface pointer
);

Parameters

fmtid
[in] FMTID of the property set to be created. For information on FMTIDs that are well known and predefined in the Platform SDK, see Predefined Property Set Format Identifiers.
pclsid
[in] Pointer to the initial class identifier CLSID for this property set. May be NULL, in which case it is set to all zeroes. The CLSID is the CLSID of a class that displays and/or provides programmatic access to the property values. If there is no such class, it is recommended that the FMTID be used.
grfFlags
[in] Values from the PROPSETFLAG enumeration.
grfMode
[in] Access mode in which the newly created property set is to be opened, taken from certain values of the STGM enumeration, as described in the following Remarks section.
ppPropStg
[out] Pointer to IPropertyStorage pointer variable that receives the interface pointer to the new property storage subobject.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The property set was created.
STG_E_FILEALREADYEXISTS
A property set of the indicated name already exists, and STGM_CREATE was not specified.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPARAMETER
A parameter is invalid.

Remarks

IPropertySetStorage::Create creates and opens a new property set subobject (supporting the IPropertyStorage interface) contained in this property set storage object. The property set automatically contains code page and locale ID properties. These are set to the Unicode and the current user default, respectively.

The grfFlags parameter is a combination of values taken from the enumeration PROPSETFLAG. If the PROPSETFLAG_ANSI value from this enumeration is used, the code page is set to the current system default, rather than Unicode.

The grfMode parameter specifies the access mode in which the newly created set is to be opened. Values for this parameter are as in the grfMode parameter to IPropertySetStorage::Open, with the addition of the following values:

Value | Meaning
STGM_FAILIFTHERE | If another property set with the specified fmtid parameter already exists, the call fails. This is the default action; that is, unless STGM_CREATE is specified, STGM_FAILIFTHERE is implied.
STGM_CREATE | If another property set with the specified fmtid parameter already exists, it is removed and replaced with this new one.

The created property set is simple by default, but the caller may request a nonsimple property set by specifying the PROPSETFLAG_NONSIMPLE value in the grfFlags parameter. For more information on simple and nonsimple property sets, see Storage vs. Stream for a Property Set.

This method is subject to the constraints of the underlying IStorage::CreateStream (for simple property sets) or IStorage::CreateStorage (for nonsimple property sets). For example, when using the IPropertySetStorage-Compound File Implementation, you must specify STGM_SHARE_EXCLUSIVE in the grfMode parameter to IPropertySetStorage::Create. On the other hand, if you are using the IPropertySetStorage-Stand-alone Implementation, IPropertySetStorage::Create is subject to whatever constraints apply to the caller-specified IStorage.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage::Open, IPropertySetStorage, PROPVARIANT, WriteRead Sample, StgCreatePropSetStg Sample, Samples


IPropertySetStorage::Delete

Deletes one of the property sets contained in the property set storage object.

HRESULT Delete(
  REFFMTID fmtid  //FMTID of the property set to
                  // be deleted
);

Parameters

fmtid
[in] FMTID of the property set to be deleted.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
The property set was successfully deleted.
STG_E_FILENOTFOUND
The specified property set does not exist.
STG_E_ACCESSDENIED
The requested access to the property set storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid.

Remarks

IPropertySetStorage::Delete deletes the property set specified by its FMTID. Specifying a property set that does not exist returns an error. Open substorages and streams (opened through one of the storage- or stream-valued properties) are put into the reverted state.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.


IPropertySetStorage::Enum

Creates an enumerator object which contains information on the property sets stored in this property set storage. On return, this method supplies a pointer to the IEnumSTATPROPSETSTG pointer on the enumerator object.

HRESULT Enum(
  IEnumSTATPROPSETSTG **ppenum  //Pointer to output variable
                                // that receives the IEnumSTATPROPSETSTG
                                // interface pointer
);

Parameters

ppenum
[out] Pointer to IEnumSTATPROPSETSTG pointer variable that receives the interface pointer to the newly created enumerator object.

Return Values

S_OK
The enumerator object was successfully created.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.

Remarks

IPropertySetStorage::Enum creates an enumerator object that can be used to iterate through STATPROPSETSTG structures. These sometimes provide information on the property sets managed by IPropertySetStorage. This method, on return, supplies a pointer to the IEnumSTATPROPSETSTG interface on this enumerator object on return.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IEnumSTATPROPSETSTG, IEnumSTATPROPSETSTG - Compound File Implementation, EnumAll Sample, Samples


IPropertySetStorage::Open

Opens a property set contained in the property set storage object.

HRESULT Open(
  REFFMTID  fmtid,  //The FMTID of the property
                    // set to be opened
  DWORD grfMode,    //Storage mode in which property set is
                    // to be opened
  IPropertyStorage **ppPropStg
                    //Pointer to output variable that
                    // receives the IPropertyStorage interface pointer
);

Parameters

fmtid
[in] Format identifier (FMTID) of the property set to be opened. For information on FMTIDs that are well known and predefined in the Platform SDK, see Predefined Property Set Format Identifiers.
grfMode
[in] Access mode in which the newly created property set is to be opened. These flags are taken from the STGM enumeration. Flags that may be used and their meanings in the context of this method are described in the following Remarks section.
ppPropStg
[out] Pointer to IPropertyStorage pointer variable that receives the interface pointer to the requested property storage subobject.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
Success.
STG_E_FILENOTFOUND
A property set of the indicated name does not exist.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied, or the property set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPARAMETER
A parameter is not valid.

Remarks

The mode in which the property set is to be opened is specified in the parameter grfMode. These flags are taken from the STGM enumeration, but, for this method, legal values and their meanings are as follows (only certain combinations of these flag values are legal).

Value | Meaning
STGM_DIRECT | Opens the property set without an additional level of transaction nesting. This is the default (the behavior if neither STGM_DIRECT nor STGM_TRANSACTED is specified).
STGM_TRANSACTED | Opens the property set with an additional level of transaction nesting (beyond the transaction, if any, on this property set storage object). Transacted mode is available only on nonsimple property sets. Changes in the property set must be committed with a call to IPropertyStorage::Commit before they are visible to the transaction on this property set storage.
STGM_READ | Opens the property set with read access. Read permission is required on the property set storage.
STGM_WRITE | Opens the property set with write access. Not all implementations of IPropertyStorage support this mode.
STGM_READWRITE | Opens the property set with read and write access. Note that this flag is not the binary OR of the values STGM_READ and STGM_WRITE.
STGM_SHARE_DENY_NONE | Subsequent openings of the property set from this property set storage are not denied read or write access. (Not available in all implementations.)
STGM_SHARE_DENY_READ | Subsequent openings of the property set from this property set storage are denied read access. (Not available in all implementations.)
STGM_SHARE_DENY_WRITE | Subsequent openings of the property set from this property set storage are denied write access. This value is typically used in the transacted mode to prevent making unnecessary copies of an object opened by multiple users. That is, if STGM_TRANSACTED is specified, but this value is not specified, a snapshot is made, whether there are subsequent openings or not. Thus, you can improve performance by specifying this value. (Not available in all implementations.)
STGM_SHARE_EXCLUSIVE | Subsequent openings of the property set from this property set storage are not possible. Note that this value is not a simple binary OR of the STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE elements.

This method is subject to the constraints of the underlying IStorage::OpenStream (for simple property sets) or IStorage::OpenStorage (for nonsimple property sets). For more information on simple and nonsimple property sets, see Storage vs. Stream for a Property Set. For example, when using the IPropertySetStorage-Compound File Implementation, you must specify STGM_SHARE_EXCLUSIVE in the grfMode parameter to IPropertySetStorage::Open. On the other hand, if you are using the IPropertySetStorage-Stand-alone Implementation , IPropertySetStorage::Open is subject to whatever constraints apply to the caller-specified IStorage.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage::Create, IPropertySetStorage, PROPVARIANT, EnumAll Sample, WriteRead Sample, Samples


IPropertySetStorage-Compound File Implementation

The COM compound file storage object implementation includes an implementation of both IPropertyStorage, the interface that manages a single persistent property set, and IPropertySetStorage, the interface that manages groups of persistent property sets.

To get a pointer to the compound file implementation of IPropertySetStorage, specify the header-defined name for the identifier IID_IStorage as the riid parameter, or use the StgCreateStorageEx or StgOpenStorageEx functions. In both cases, specify STGFMT_STORAGE as the stgfmt parameter. (STGFMT_ANY can alternatively be specified in the case of StgOpenStorageEx.) Also, specify the header-defined name for the interface identifier (IID) IID_IPropertySetStorage as the riid parameter. Both functions supply a pointer to the object's IPropertySetStorage interface.

An alternative way to get a pointer to the compound file implementation is to specify the header-defined name for the identifier IID_IStorage as the riid parameter, or to use the StgCreateDocfile or StgOpenStorage functions. This will supply a pointer to the object's IStorage interface. When you want to deal with persistent property sets, call IStorage::QueryInterface for the IPropertySetStorage interface.

When to Use

Call the methods of IPropertySetStorage to create, open, or delete property sets in the current compound-file property set storage. There is also a method, IPropertySetStorage::Enum, that supplies a pointer to an enumerator that can be used to enumerate the property sets in the storage.

Methods

IPropertySetStorage::Create

Creates a new property set in the current compound file storage and, on return, supplies an interface pointer to the IPropertyStorage compound-file implementation. In this implementation, property sets may be transacted only if PROPSETFLAG_NONSIMPLE is specified. This method requires that the sharing mode specified in the grfMode parameter be STGM_SHARE_EXCLUSIVE, and that the access mode be either STGM_READ or STGM_READWRITE (STGM_WRITE mode is not supported.).

IPropertySetStorage::Open

Opens an existing property set in the current property storage. On return, it supplies an interface pointer to the compound file implementation of IPropertyStorage. This method requires that the sharing mode specified in the grfMode parameter be STGM_SHARE_EXCLUSIVE, and that the access mode be either STGM_READ or STGM_READWRITE (STGM_WRITE is not supported.).

IPropertySetStorage::Delete

Deletes a property set in this property storage.

IPropertySetStorage::Enum

Creates an object that can be used to enumerate STATPROPSETSTG structures. Each STATPROPSETSTG structure provides information about a single property set.

Remarks

Starting with Microsoft® Windows® 2000, the compound-file implementation of IPropertySetStorage supports simple mode. Simple mode is indicated by specifying the STGM_SIMPLE flag for the StgCreateStorageEx and StgOpenStorageEx functions. If the compound file is opened in simple mode, the associated IPropertySetStorage implementation is limited as follows:

  • Only simple property sets can be created. That is, specifying the PROPSETFLAG_NONSIMPLE value in the grfFlags parameter for the IPropertySetStorage::Create method results in an error.
  • After you create a compound file with StgCreateStorageEx using STGM_SIMPLE and query for the IPropertySetStorage interface, you can only call IPropertySetStorage::Create once. Then you must release the IPropertyStorage interface before calling the Create method again. See STGM for more information about simple mode.
  • You cannot use the IPropertySetStorage::Open method to open a property set after using StgCreateStorageEx to create the storage object. Instead you must use StgOpenStorageEx before querying for IPropertySetStorage and calling the Open method.
  • After you open a compound file with StgOpenStorageEx using the STGM_SIMPLE flag and query for the IPropertySetStorage interface, you can open one property set at a time using IPropertySetStorage::Open. Also, it may not be possible for the total size of the property set to be increased while the property set is open.

Simple property sets cannot be transacted. You cannot specify STGM_TRANSACTED in the grfmode parameter of the Create and Open methods unless you also specify PROPSETFLAG_NONSIMPLE in the grfFlags parameter. Note that simple and nonsimple property sets are unrelated to the simple-mode property sets described above. For more information on simple and nonsimple property sets, see Storage vs. Stream for a Property Set.

Note The DocumentSummaryInformation and UserDefined property sets are unique in that they may have two property set sections. See The DocumentSummaryInformation and UserDefined Property Sets for more information.

See Also

IPropertySetStorage, IPropertyStorage, IPropertyStorage - Compound File Implementation, STATPROPSETSTG, PROPSETFLAG, IStorage::EnumElements


IPropertySetStorage-NTFS File System Implementation

NTFS file system 5.0 provides an implementation of both IPropertySetStorage and IPropertyStorage for files on an NTFS volume when the files themselves are not compound files. The NTFS implementation is essentially equivalent to the compound file implementation. See the following Remarks section for exceptions.

To get a pointer to the NTFS implementation of IPropertySetStorage, you can make one of the following calls:

  • Call StgCreateStorageEx and specify STGFMT_FILE in the grfFlags parameter to create a new file.
  • Call StgOpenStorageEx and specify either the STGFMT_FILE or STGFMT_ANY enumeration value in the grfFlags parameter to open an existing file.

However, you cannot obtain the NTFS implementation of IPropertySetStorage for a compound file. When opening a compound file with StgOpenStorage, specifying the STGFMT_FILE enumeration value results in an error.

Also, simple property sets cannot be transacted. That is, you cannot specify STGM_TRANSACTED in the grfmode parameter of the Create and Open methods unless you also specify PROPSETFLAG_NONSIMPLE in the grfFlags parameter. The property set storage object itself does not support transaction processing.

When to Use

Call the methods of IPropertySetStorage to create, open, or delete property sets in the current NTFS property set storage. There is also a method, IPropertySetStorage::Enum, that supplies a pointer to an enumerator that can be used to enumerate the property sets in the storage.

Compatibility

The NTFS implementations of IPropertySetStorage and IPropertyStorage are available only on Windows 2000. Windows 95 and Windows NT® 4.0 and earlier versions cannot access these property sets.

The NTFS implementation stores property sets in alternate streams of an NTFS file. The alternate streams must be copied when the main file is copied. However, not all file systems support such streams. For example, if an NTFS file with property sets is copied to a FAT volume, only the data in the file is copied; the property set is lost. The CopyFile function does not return an error in this case. Also, if the computer performing the file copy is not a Windows NT/Windows 2000 computer, the property sets may be lost. For example, if a Windows 95 computer copies an NTFS file, the property set is lost even if the destination file is also on an NTFS volume.

Methods

IPropertySetStorage::Create

Creates a new property set in the current NTFS file storage and, on return, supplies an interface pointer to the IPropertyStorage NTFS file implementation. The sharing mode specified in the grfMode parameter must be STGM_SHARE_EXCLUSIVE.

IPropertySetStorage::Open

Opens an existing property set in the current property storage. On return, it supplies an interface pointer to the NTFS file implementation of IPropertyStorage. The sharing mode specified in the grfMode parameter must be STGM_SHARE_EXCLUSIVE.

IPropertySetStorage::Delete

Deletes a property set in the current property storage.

IPropertySetStorage::Enum

Creates an object that can be used to enumerate STATPROPSETSTG structures. Each STATPROPSETSTG structure provides information about a single property set.

Remarks

The NTFS implementations of IPropertySetStorage and IPropertyStorage store property sets in a file without affecting the contents of that file. For example, if you create a property set in an HTML file named default.htm, that file still displays properly in a Web browser. That is, changes to a file using these two interfaces are undetectable when accessing a file by means of the CreateFile function.

The NTFS implementation of IPropertySetStorage provides a safe implementation when used to write property sets to a file on an NTFS file system 5.0 volume. Such property sets cannot be corrupted by the implementation even in the event of a system failure. For example, if the power to a system fails during a call to IPropertyStorage::Commit while the property set is being flushed to the disk, the property set is never left in an intermediate state. Either the previous version of the property set remains or all of the updates are saved.

The NTFS implementation of IPropertySetStorage differs from the compound file implementation in the following ways:

  • A STATPROPSETSTG structure obtained from the IEnumSTATPROPSETSTG interface contains a clsid member whose value is always zero (CLSID_NULL). With the compound file implementation, the correct clsid member is returned for nonsimple (Storage vs. Stream for a Property Set) property sets.
  • When obtaining an NTFS implementation of IPropertySetStorage interface pointer using the StgCreateStorageEx or StgOpenStorageEx function, the grfMode parameter must follow the same rules as for the compound file implementation.

    Additionally, the following flags may not be used:

    STGM_SIMPLE, STGM_TRANSACTED, STGM_CONVERT, STGM_PRIORITY, and STGM_DELETEONRELEASE.

  • When an NTFS IPropertySetStorage interface is obtained by the StgCreateStorageEx or StgOpenStorageEx functions, the sharing modes primarily apply to other instances of that interface, and not to instances of opening the file itself. For example, if an NTFS IPropertySetStorage interface is opened by calling the StgOpenStorageEx function, with the grfMode parameter set to STGM_READWRITE|STGM_SHARE_EXCLUSIVE, it is still possible to open the file with the CreateFile function.

    Such simultaneous instances of opening this interface are subject to the following constraints: the dwShareMode parameter in the CreateFile function must specify the FILE_SHARE_READ flag, and the dwAccess parameter must not specify the DELETE flag. Also, the DeleteFile and MoveFile functions must not be called on a file for which these property set interfaces are open, as these functions require DELETE access to the file.

  • If an NTFS IPropertySetStorage method is opened on a file in read-only mode, and the file currently has no property sets, the object returned will not actually hold open the file. Consequently, other openings of that file will succeed, even if the sharing mode of the original open operation would otherwise disallow it.

    Example; if an NTFS IPropertySetStorage is opened in the mode STGM_READ|STGM_SHARE_EXCLUSIVE, and the file has no property sets, it will be possible to simultaneously open the file STGM_READWRITE|STGM_SHARE_EXCLUSIVE.

See Also

IPropertySetStorage, IPropertyStorage, IPropertyStorage-NTFS File System Implementation, STATPROPSETSTG structure, PROPSETFLAG enumeration, IStorage::EnumElements


IPropertySetStorage-Stand-alone Implementation

The system-provided, stand-alone implementation of IPropertySetStorage includes an implementation of both IPropertyStorage, the interface that reads and writes properties in a property set storage, and IPropertySetStorage, the interface that creates and opens property sets in a storage. The IEnumSTATPROPSTG and IEnumSTATPROPSETSTG interfaces are also provided in the stand-alone implementation.

To use the stand-alone implementation of IPropertySetStorage, you first must obtain a pointer to the system-provided, stand-alone implementation and associate the system-provided implementation with your storage object. To get a pointer to the stand-alone implementation of IPropertySetStorage, call the StgCreatePropSetStg function and provide the pStorage parameter specifying the storage object that will contain the property set. This function supplies you with a pointer to the new IPropertySetStorage interface for the specified storage object.

The stand-alone implementation of IPropertySetStorage creates property sets on any storage object, not just on compound file storages. The stand-alone implementation does not depend on compound files and can be used with any implementation of structured storages. Any restrictions on the caller-provided structured storages apply to this implementation of property sets. For example, if you provide a simple-mode storage to StgOpenPropStg, the resulting IPropertySetStorage will be restricted by the supplied IStorage.

See the section IPropertySetStorage-Compound File Implementation for more information on the compound file implementation of this interface.

When to Use

Call the methods of IPropertySetStorage to create, open, and delete property sets in any structured storage. There is also a method that supplies a pointer to the IEnumSTATPROPSETSTG enumerator that can be used to enumerate the property sets in the storage.

The stand-alone implementation also provides the StgCreatePropStg and StgOpenPropStg helper functions in addition to the Create and Open methods to create and open property sets. These two functions add support for the PROPSETFLAG_UNBUFFERED value so you can directly write changes to the property set instead of buffering them in a cache. See the PROPSETFLAG enumeration for more information on using this value.

Methods

The stand-alone implementation of IPropertySetStorage supports the following methods:

IPropertySetStorage::Create
Creates a new property set in the storage and returns a pointer to the IPropertyStorage interface on the property set.

If you plan to use the PROPSETFLAG_UNBUFFERED value, use the StgCreatePropStg function instead to create and open the new property set and to obtain a pointer to the stand-alone implementation for the IPropertyStorage interface on the property set.

IPropertySetStorage::Open
Opens an existing property set in the storage and returns a pointer to the IPropertyStorage interface on the property set.

If you plan to use the PROPSETFLAG_UNBUFFERED value, use the StgOpenPropStg function instead to obtain a pointer to the stand-alone implementation of IPropertyStorage on the specified property set.

IPropertySetStorage::Delete
Deletes a property set in this property set storage.
IPropertySetStorage::Enum
Creates an object that can be used to enumerate STATPROPSETSTG structures. Each STATPROPSETSTG structure provides information about a single property set.

Note The DocumentSummaryInformation and UserDefined property set is unique in that it may have two property set sections in a single underlying stream. See The DocumentSummaryInformation and UserDefined Property Sets for more information.

See Also

IPropertySetStorage, IPropertyStorage, IPropertyStorage-Stand-alone Implementation, STATPROPSETSTG, PROPSETFLAG, IStorage::EnumElements, STGM, StgCreatePropStg, StgOpenPropStg, StgCreatePropSetStg


IPropertyStorage

Manages the persistent properties of a single property set. Persistent properties consist of information that can be stored persistently in a property set, such as the summary information associated with a file. This contrasts with run-time properties associated with Controls and Automation, which can be used to affect system behavior. Use the methods of the IPropertySetStorage interface to create or open a persistent property set. An instance of the IPropertySetStorage interface can manage zero or more IPropertyStorage instances.

Each property within a property set is identified by a property identifier (ID), a four-byte ULONG value unique to that set. You can also assign a string name to a property through the IPropertyStorage interface.

Property IDs differ from the dispatch IDs used in Automation dispid property name tags. One difference is that the general-purpose use of property ID values zero and one is prohibited in IPropertyStorage, while no such restriction exists in IDispatch. In addition, while there is significant overlap among the data types for property values that may be used in IPropertyStorage and IDispatch, the property sets are not identical. Persistent property data types used in IPropertyStorage methods are defined in the PROPVARIANT structure.

The IPropertyStorage interface can be used to access both simple and nonsimple property sets. Nonsimple property sets can hold several complex property types that cannot be held in a simple property set. For more information see Storage vs. Stream for a Property Set.

When to Implement

Implement IPropertyStorage when you want to store properties in an object. If you are using the COM compound files implementation, the compound file object created through a call to StgCreateDocfile includes an implementation of IPropertySetStorage, which allows access to the implementation of IPropertyStorage. Once you have a pointer to any of the interface implementations (such as IStorage) on this object, you can call QueryInterface to get a pointer to the IPropertySetStorage interface implementation, and then call either the Open or Create method, as appropriate to obtain a pointer to the IPropertyStorage interface managing the specified property set.

When to Use

Use IPropertyStorage to create and manage properties that are stored in a given property set.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments reference count.
Release | Decrements reference count.

IPropertyStorage Methods | Description
ReadMultiple | Reads property values in a property set.
WriteMultiple | Writes property values in a property set.
DeleteMultiple | Deletes properties in a property set.
ReadPropertyNames | Gets corresponding string names for given property identifiers.
WritePropertyNames | Creates or changes string names corresponding to given property identifiers.
DeletePropertyNames | Deletes string names for given property identifiers.
SetClass | Assigns a CLSID to the property set.
Commit | As in IStorage::Commit, flushes or commits changes to the property storage object.
Revert | When the property storage is opened in transacted mode, discards all changes since the last commit.
Enum | Creates and gets a pointer to an enumerator for properties within this set.
Stat | Receives statistics about this property set.
SetTimes | Sets modification, creation, and access times for the property set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

Managing Properties, Property Storage Considerations, IPropertySetStorage, IEnumSTATPROPSTG, IEnumSTATPROPSETSTG, STATPROPSTG, STATPROPSETSTG, PROPVARIANT, IPropertyStorage-Compound File Implementation, IPropertyStorage-NTFS File System Implementation, IPropertyStorage-Stand-alone Implementation, Samples


IPropertyStorage::Commit

Saves any changes made to a property storage object to the parent storage object.

HRESULT Commit(
  DWORD  grfCommitFlags  //Flags specifying conditions for the commit
);

Parameters

grfCommitFlags
[in] Flags specifying the conditions under which the commit is to be performed. Specific flags and their meanings are described in the following Remarks section.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
The changes were saved successfully.
STG_E_NOTCURRENT
STGC_ONLYIFCURRENT was specified, but the optimistic concurrency control failed.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPARAMETER
One or more flags specified in the grfCommitFlags parameter is invalid.

Remarks

Like IStorage::Commit, the IPropertyStorage::Commit method ensures that any changes made to a property storage object are reflected in the parent storage.

In direct mode in the compound file implementation, a call to this method causes any changes currently in the memory buffers to be flushed to the underlying property stream. In the compound-file implementation for nonsimple property sets, IStorage::Commit is also called on the underlying substorage object with the passed grfCommitFlags parameter.

In transacted mode, this method causes the changes to be permanently reflected in the persistent image of the storage object. The changes that are committed must have been made to this property set since it was opened or since the last commit on this opening of the property set. One could think of the commit action as publishing the changes made on one object level to the next level. Of course, this remains subject to any outer-level transaction that may be present on the object in which this property set is contained. Write permission must be specified when the property set is opened (through IPropertySetStorage) on the property set opening for the commit operation to succeed.

If the commit operation fails for any reason, the state of the property storage object remains as it was before the commit.

This call has no effect on existing storage- or stream-valued properties opened from this property storage, but it does commit them.

Valid values for the grfCommitFlags parameter are as follows:

Value | Meaning
STGC_DEFAULT | Commits per the usual transaction semantics. Last writer wins. This flag may not be specified with other flag values.
STGC_ONLYIFCURRENT | Commits the changes only if the current persistent contents of the property set are the ones on which the changes about to be committed are based. That is, does not commit changes if the contents of the property set have been changed by a commit from another opening of the property set. The error STG_E_NOTCURRENT is returned if the commit does not succeed for this reason.
STGC_OVERWRITE | Only useful when committing a transaction that has no further outer nesting level of transactioning, though legal in all cases. Indicates that the caller is willing to risk some data corruption at the expense of decreased disk usage on the destination volume. This flag is potentially useful in low disk-space scenarios, though it should be used with caution.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::ReadMultiple, IStorage::Commit


IPropertyStorage::DeleteMultiple

Deletes as many of the indicated properties as exist in this property set.

HRESULT DeleteMultiple(
  ULONG cpspec,             //Count of properties to be deleted
  PROPSPEC const rgpspec[]  //Array of properties to be deleted
);

Parameters

cpspec
[in] Count of properties being deleted. May legally be zero, although this has no effect on the outcome of the interface call, because it deletes no properties.
rgpspec[]
[in] Properties to be deleted. A mixture of property identifiers and string-named properties is permitted. There may be duplicates, and there is no requirement that properties be specified in any order.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
All the specified properties that exist in the property set have been deleted.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No properties were deleted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. Some properties may not have been deleted.
STG_E_INVALIDPARAMETER
At least one of the parameters is not valid, as when one of the PROPSPEC structures contains an illegal value of a ulKind member; for example, if the ulKind member is set to a string name instead of a property set value, some properties may not be deleted.
STG_E_INVALIDPOINTER
May be returned when at least one of the pointers passed is not valid. Some properties may not have been written. More frequently, an invalid pointer will result instead in an access violation.

Remarks

IPropertyStorage::DeleteMultiple must delete as many of the indicated properties as are in the current property set. If a deletion of a stream- or storage-valued property occurs while that property is open, the deletion will succeed and place the previously returned IStream or IStorage pointer in the reverted state.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.


IPropertyStorage::DeletePropertyNames

Deletes specified string names from the current property set.

HRESULT DeletePropertyNames(
  ULONG cpropid,           //Size of the rgpropid array
  PROPID const rgpropid[]  //Property identifiers for which
                           // string names are to be deleted
);

Parameters

cpropid
[in] The size on input of the array rgpropid. If 0, no property names are deleted.
rgpropid[]
[in] Property identifiers for which string names are to be deleted.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
Success. The names of all of the indicated properties that exist in this set have been deleted.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No property names were deleted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. Some property names may not have been deleted.
STG_E_INVALIDPARAMETER
At least one of the parameters is not valid. Some property names may not have been deleted.

Remarks

For each property identifier in rgpropid, IPropertyStorage::DeletePropertyNames removes any corresponding name-to-property ID mapping. An attempt is silently ignored to delete the name of a property that either does not exist or does not currently have a string name associated with it. This method has no effect on the properties themselves.

Note All the stored string property names can be deleted by deleting property identifier zero, but cpropid must be equal to 1 for this to be a valid parameter error.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::ReadPropertyNames


IPropertyStorage::Enum

Creates an enumerator object designed to enumerate data of type STATPROPSTG, which contains information on the current property set. On return, this method supplies a pointer to the IEnumSTATPROPSTG pointer on this object.

HRESULT Enum(
  IEnumSTATPROPSTG **ppenum   //Pointer to output variable
                              // that receives the IEnumSTATPROPSTG
                              // interface pointer
);

Parameters

ppenum
[out] Pointer to IEnumSTATPROPSTG pointer variable that receives the interface pointer to the new enumerator object.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
A pointer to the enumerator has been retrieved.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid.
STG_E_READFAULT
Error reading storage.

Remarks

IPropertyStorage::Enum creates an enumeration object that can be used to iterate STATPROPSTG structures. On return, this method supplies a pointer to an instance of the IEnumSTATPROPSTG interface on this object, whose methods you can call to obtain information about the current property set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IEnumSTATPROPSTG, IEnumSTATPROPSTG - Compound File Implementation, EnumAll Sample, Samples


IPropertyStorage::ReadMultiple

Reads specified properties from the current property set.

HRESULT ReadMultiple(
  ULONG cpspec,              //Count of properties being read
  PROPSPEC const rgpspec[],  //Array of the properties to be read
  PROPVARIANT rgvar[]        //Array of PROPVARIANTs containing
                             // the property values on return
);

Parameters

cpspec
[in] Count of properties specified in the rgpspec array. May legally be zero, although this has no effect on the outcome of the interface call because it reads no properties.
rgpspec[]
[in] An array of PROPSPEC structures specifies which properties are to be read. Properties can be specified either by a property ID or by an optional string name. It is not necessary to specify properties in any particular order in the array. The array can contain duplicate properties, resulting in duplicate property values on return for simple properties. Nonsimple properties should return access denied on an attempt to open them a second time. The array can contain a mixture of property IDs and string IDs.
rgvar[]
[in, out] Caller-allocated array of a PROPVARIANT structure that, on return, contains the values of the properties specified by the corresponding elements in the rgpspec array. The array must be at least large enough to hold values of the cpspec parameter of the PROPVARIANT structure. The cpspec parameter contains the number of properties being set in the array. The caller does not need to initialize these PROPVARIANT structure values in any particular way. However, the implementation must fill in all members correctly on return. If there is no other appropriate value, the implementation must set the vt member of each PROPVARIANT structure to VT_EMPTY.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
Success. At least some of the requested properties were retrieved.
S_FALSE
All the property names or IDs had valid syntax, but none of them exist in this property set. Accordingly, no properties were retrieved, and each PROPVARIANT structure is set to VT_EMPTY.
STG_E_ACCESSDENIED
The requested access to the property set has been denied, or, when one or more of the properties is a stream or storage object, access to that substorage or substream has been denied. (The storage or stream may already be open). No properties were retrieved.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. No properties were retrieved.
HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
One of the properties could not be read, because the property type was not supported by this IPropertyStorage implementation.
STG_E_INVALIDPARAMETER
At least one of the parameters is not valid; for example, when one of the PROPSPEC structures contains an illegal value of the ulKind member. If the ulKind member is set to a string name instead of a property set value, some properties may not be retrieved.
STG_E_INVALIDPOINTER
At least one of the pointers passed in is not valid. No properties were retrieved.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from ANSI.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT data type. See Error Handling Strategies.

For more information of this method, please see Property Storage Considerations.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage, IPropertyStorage::WriteMultiple, IPropertyStorage::WritePropertyNames, EnumAll Sample, StgCreatePropSetStg Sample, WriteRead Sample, Samples


IPropertyStorage::ReadPropertyNames

Retrieves any existing string names for the specified property IDs.

HRESULT ReadPropertyNames(
  ULONG cpropid,             //Number of elements in rgpropid
  PROPID const  rgpropid[],  //Property identifiers for
                             // which names are to be retrieved
  LPWSTR rglpwstrName[]      //Array of returned string names
);

Parameters

cpropid
[in] Number of elements on input of the array rgpropid. May legally be zero; however, a value of zero results in no property names being read.
rgpropid[]
[in] Array of property IDs for which names are to be retrieved.
rglpwstrName[]
[in, out] Caller-allocated array of size cpropid of LPWSTR members. On return, the implementation fills in this array. A given entry contains either the corresponding string name of a property ID or it can be empty if the property ID has no string names.

Each LPWSTR member of the array should be freed using the CoTaskMemFree function.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
One or more string names were retrieved and all members of rglpwstrName are valid (either NULL or a valid LPWSTR members).
S_FALSE
No string names were retrieved because none of the requested property identifiers have string names presently associated with them in this property storage object (this result does not address whether the given property identifiers presently exist in the set).
STG_E_INVALIDHEADER
The property name dictionary was not found.
STG_E_READFAULT
Error reading the storage.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No string names were retrieved.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. No string names were retrieved.
STG_E_INVALIDPARAMETER
A parameter is not valid. No string names were retrieved.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
A failed attempt was made to translate a Unicode string to or from ANSI.

Remarks

For each property ID in the list of property IDs supplied in the rgpropid array, IPropertyStorage::ReadPropertyNames retrieves the corresponding string name, if there is one. String names are created either by specifying the names in calls to IPropertyStorage::WriteMultiple when you are creating the property, or through a call to IPropertyStorage::WritePropertyNames. In any case, the string name is optional; all properties must have a property ID.

String names mapped to property IDs must be unique within the set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::WritePropertyNames, IPropertyStorage::WriteMultiple


IPropertyStorage::Revert

Discards all changes to the named property set since it was last opened or discards changes that were last committed to the property set. Has no effect on a direct-mode property set.

HRESULT Revert();    // The property set names with changes to be reverted.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
Success.

Remarks

For transacted-mode property sets, this method discards all changes that have been made in this property set since the set was opened or since the time it was last committed, (whichever is later). After this operation, any existing storage- or stream-valued properties that have been opened from the property set being reverted are no longer valid and cannot be used. The error STG_E_REVERTED will be returned on all calls, except those to Release, using these streams or storages.

For direct-mode property sets, this request is ignored and returns S_OK.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::Commit


IPropertyStorage::Stat

Retrieves information about the current open property set.

HRESULT Stat(
  STATPROPSETSTG *pstatpsstg   //Pointer to a filled-in
                               // STATPROPSETSTG structure
);

Parameters

pstatpsstg
[out] Pointer to a STATPROPSETSTG structure, which contains statistics about the current open property set.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
Statistics were successfully obtained.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is not valid.

Remarks

IPropertyStorage::Stat fills in and returns a pointer to a STATPROPSETSTG structure, containing statistics about the current property set.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

STATPROPSETSTG structure, IPropertySetStorage::Enum, FILETIME structure


IPropertyStorage::SetClass

Assigns a new CLSID to the current property storage object, and persistently stores the CLSID with the object.

HRESULT SetClass(
  REFCLSID clsid  //New CLSID for the property set
);

Parameters

clsid
[in] New CLSID to be associated with the property set.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
The CLSID has been assigned.
STG_E_ACCESSDENIED
The requested access to the IPropertyStorage interface has been denied. The CLSID was not assigned.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. The CLSID was not assigned.
STG_E_INVALIDPARAMETER
The parameter is not valid. The CLSID was not assigned.

Remarks

Assigns a CLSID to the current property storage object. The CLSID has no relationship to the stored property IDs. Assigning a CLSID allows a piece of code to be associated with a given instance of a property set; such code, for example, might manage the user interface (UI). Different CLSIDs can be associated with different property set instances that have the same FMTID.

If the property set is created with the pclsid parameter of the IPropertySetStorage::Create method specified as NULL, the CLSID is set to all zeroes.

The current CLSID on a property storage object can be retrieved with a call to IPropertyStorage::Stat. The initial value for the CLSID can be specified at the time that the storage is created with a call to IPropertySetStorage::Create.

Setting the CLSID on a nonsimple property set (one that can legally contain storage- or stream-valued properties, as described in IPropertySetStorage::Create) also sets the CLSID on the underlying sub-storage.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage::Create, IPropertyStorage::Stat


IPropertyStorage::SetTimes

Sets the modification, access, and creation times of this property set, if supported by the implementation. Not all implementations support all these time values.

HRESULT SetTimes(
  FILETIME const *pctime,  //New creation time for the property set
  FILETIME const *patime,  //New access time for the property set
  FILETIME const *pmtime   //New modification time for the property set
);

Parameters

pctime
[in] Pointer to the new creation time for the property set. May be NULL, indicating that this time is not to be modified by this call.
patime
[in] Pointer to the new access time for the property set. May be NULL, indicating that this time is not to be modified by this call.
pmtime
[in] Pointer to the new modification time for the property set. May be NULL, indicating that this time is not to be modified by this call.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
All the requested times have been successfully updated.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied; no times have been updated.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is not valid. This error is returned if an attempt is made to set a time value not supported by this implementation.

Remarks

Sets the modification, access, and creation times of the current open property set, if supported by the implementation (not all implementations support all these time values). Unsupported time stamps are always reported as zero, enabling the caller to test for support. A call to IPropertyStorage::Stat supplies (among other data) time-stamp information.

Notice that this functionality is provided as an IPropertyStorage method on a property-storage object that is already open, in contrast to being provided as a method in IPropertySetStorage. Normally, when the SetTimes method is not explicitly called, the access and modification times are updated as a side effect of reading and writing the property set. When SetTimes is used, the latest specified times supersede either default times or time values specified in previous calls to SetTimes.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::Stat, FILETIME


IPropertyStorage::WriteMultiple

Writes a specified group of properties to the current property set. If a property with a specified name or property identifier already exists, it is replaced, even when the old and new types for the property value are different. If a property of a given name or property ID does not exist, it is created.

HRESULT WriteMultiple(
  ULONG cpspec,               //The number of properties being set
  PROPSPEC const rgpspec[],   //Property specifiers
  PROPVARIANT const rgvar[],  //Array of PROPVARIANT values
  PROPID propidNameFirst      //Minimum value for property
                              // identifiers when they must be allocated
);

Parameters

cpspec
[in] The number of properties being set. May legally be zero; however, this results in no properties being written.
rgpspec[]
[in] Array of the property IDs (PROPSPEC) to which properties are to be set. These need not be in any particular order, and may legally contain duplicates, however the last specified property ID is the one that takes effect. A mixture of property IDs and string names is permitted.
rgvar[]
[in] An array (of size cpspec) of PROPVARIANT structuresthat contain the property values to be written. The array must be the size specified by cpspec.
propidNameFirst
[in] Specifies the minimum value for the property IDs that the method must assign if the rgpspec parameter specifies string-named properties for which no property IDs currently exist. If all string-named properties specified already exist in this set, and thus already have property IDs, this value is ignored. When not ignored, this value must be greater than or equal to two and less than 0x80000000. Property IDs 0 and 1 and greater than 0x80000000 are reserved for special use.

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
All of the indicated properties were written successfully .
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No properties have been written. The property set was opened in STGM_READ mode.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation. Some properties may or may not have been written.
HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
One of the property types specified in the rgvar array is not a supported type by this IPropertyStorage implementation.
STG_E_INVALIDPARAMETER
At least one of the parameters is not valid. Some properties may not have been written. This error would be returned in several situations, for example: 1) rgvar may be NULL; 2) a stream- or storage-valued property is present in rgpspec but the property set was created without PROPSETFLAG_NONSIMPLE; 3) one of the PROPSPEC structures contains an illegal value of the ulKind member, such as a string name instead of a property ID value.
STG_E_INVALIDPOINTER
May be returned when at least one of the pointers passed in is not valid. Some properties may or may not have been written. More frequently, an invalid pointer will instead result in an access violation.
STG_E_WRITEFAULT
Error writing the storage.
STG_E_REVERTED
The property set was reverted. For example, if the property set is deleted while open (by using IPropertySetStorage::Delete), this status would be returned.
STG_E_MEDIUMFULL
The disk is full or the property-set size limit of 256 kilobytes (KB) has been exceeded. Some properties may or may not have been written. See the following Remarks section for more details.
STG_E_PROPSETMISMATCHED
An attempt was made to write a nonsimple (stream- or storage-valued) property to a simple property set.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from ANSI.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT data type. See Error Handling Strategies.

Remarks

If a specified property already exists, its value is replaced with the one specified in rgpspec, even when the old and new types for the property value are different. If the specified property does not already exist, that property is created.

Property names are stored in a special dictionary section of the property set, which maps such names to property IDs. All properties have an ID, but names are optional. A string name is supplied by specifying PRSPEC_LPWSTR in the ulKind member of the PROPSPEC structure. If a string name is supplied for a property, and the name does not already exist in the dictionary, the method will allocate a property ID, and add the property ID and the name to the dictionary. The property ID is allocated in such a way that it does not conflict with other IDs in the property set. The value of the property ID also is no less than the value specified by the propidNameFirst parameter. If the rgpspec parameter specifies string-named properties for which no property IDs currently exist, the propidNameFirst parameter specifies the minimum value for the property IDs that the WriteMultiple method must assign.

When a new property set is created, the special codepage (Property ID 1) and Locale ID (Property ID 0x80000000) properties are written to the property set automatically. These properties can subsequently be read, using the IPropertyStorage::ReadMultiple method, by specifying property IDs with the header-defined PID_CODEPAGE and PID_LOCALE values, respectively. If a property set is non-empty — has one or more properties in addition to the code page and Locale ID properties or has one or more names in its dictionary — the special code page and Locale ID properties cannot be modified by calling IPropertyStorage::WriteMultiple. However, if the property set is empty, one or both of these special properties can be modified.

If an element in the rgspec array is set with a PRSPEC_PROPID value of 0xffffffff (PID_ILLEGAL), the corresponding value in the rgvar array is ignored by IPropertyStorage::WriteMultiple. For example, if this method is called with the cspec parameter set to 3, but rgpspec[1].prspec is set to PRSPEC_PROPID and rgpspec[1].propid is set to PID_ILLEGAL, only two properties will be written. The rgpropvar[1] element is silently ignored.

Use the PropVariantInit macro to initialize PROPVARIANT structures.

Property sets, not including the data for nonsimple properties, are limited to 256 KB in size. For more information, see Managing Property Sets.

Unless PROPSETFLAG_CASE_SENSITIVE is passed to IPropertySetStorage::Create, property set names are case insensitive.Specifying a property by its name in IPropertyStorage::WriteMultiple will result in a case-insensitive search of the names in the property set. To compare case-insenstive strings, the locale of the strings must be known. See IPropertyStorage::WritePropertyNames.

For more information, see Property Storage Considerations.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertySetStorage::Create, IPropertyStorage::ReadMultiple, PROPVARIANT, Managing Property Sets, StgCreatePropSetStg Sample, WriteRead Sample, Samples


IPropertyStorage::WritePropertyNames

Assigns string names to a specified array of property IDs in the current property set.

HRESULT WritePropertyNames(
  ULONG cpropid,                //Size on input of the array rgpropid
  PROPID const rgpropid[],      //Property identifiers for which
                                // names are to be set
  LPWSTR const  rglpwstrName[]  //New names of the corresponding
                                // property identifiers
);

Parameters

cpropid
[in] Size on input of the array rgpropid. Can legally be zero; however, making it zero causes this method to become non-operational.
rgpropid[]
[in] Array of the property IDs for which names are to be set.
rglpwstrName[]
[in] Array of new names to be assigned to the corresponding property IDs in the rgpropid array. These names may not exceed 255 characters (not including the NULL terminator).

Return Values

This method supports the standard return value E_UNEXPECTED, in addition to the following:

S_OK
Success. All of the indicated string names were successfully set.
STG_E_INVALIDNAME
At least one of the indicated property ID values does not exist in this property set. No names were set.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No property names have been changed in the storage.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation or the property-set size limit of 256-KB has been exceeded. Some names may not have been set. See the following Remarks section.
STG_E_INVALIDPARAMETER
A parameter is invalid. Some names may not have been set.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from ANSI.

Remarks

Property sets, not including the data for nonsimple properties, are limited in size to 256 KB. For more information, see Managing Property Sets.

IPropertyStorage::WritePropertyNames assigns string names to property IDs passed to the method in the rgpropid array. It associates each string name in the rglpwstrName array with the respective property ID in rgpropid. It is explicitly valid to define a name for a property ID that is not currently present in the property storage object.

It is also valid to change the mapping for an existing string name (determined by a case-insensitive match). That is, you can use the WritePropertyNames method to map an existing name to a new property ID, or to map a new name to a property ID that already has a name in the dictionary. In either case, the original mapping is deleted. Property names must be unique (as are property IDs) within the property set.

The storage of string property names preserves the case. Unless PROPSETFLAG_CASE_SENSITIVE is passed to IPropertySetStorage::Create, property set names are case insensitive by default. With case-insensitive property sets, the name strings passed by the caller are interpreted according to the locale of the property set, as specified by the PID_LOCALE property. If the property set has no locale property, the current user is assumed by default. String property names are limited in length to 128 characters. Property names that begin with the binary Unicode characters 0x0001 through 0x001F are reserved for future use.

If the value of an element in the rgpropid array parameter is set to 0xffffffff (PID_ILLEGAL), the corresponding name is ignored by IPropertyStorage::WritePropertyNames. For example, if this method is called with a cpropid parameter of 3, but the first element of the array, rgpropid[1], is set to PID_ILLEGAL, then only two property names are written. The rgpropid[1] element is ignored.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.

See Also

IPropertyStorage::ReadPropertyNames, IPropertyStorage::ReadMultiple, IPropertyStorage::WriteMultiple, WriteRead Sample, Samples


IPropertyStorage-Compound File Implementation

The COM implementation of the Structured Storage architecture is called compound files. Storage objects as implemented in compound files include an implementation of both IPropertyStorage, the interface that manages a single persistent property set, and IPropertySetStorage, the interface that manages groups of persistent property sets. For general information on the IPropertyStorage interface, see the section titled: IPropertyStorage and Property Storage Considerations .

To get a pointer to the compound file implementation of IPropertyStorage, call StgCreateStorageEx to create a new compound file object or StgOpenStorageEx to open a previously created compound file object. In the case of StgCreateStorageEx, the stgfmt parameter should be set to STGFMT_STORAGE. In the case of StgOpenStorageEx, the stgfmt parameter should be set to STGFMT_STORAGE or STGFMT_ANY. In both cases, the riid parameter should be set to IID_IPropertySetStorage. Both functions supply a pointer to the object's IPropertySetStorage interface. By calling either the Create or Open method of that interface, you will get a pointer to the IPropertyStorage interface, which you can use to call any of its methods.

An alternative way to get a pointer to the compound file implementation of IPropertySetStorage is to call the older StgCreateDocfile and StgOpenStorage functions, or to specify an riid parameter of IID_IStorage to the StgCreateStorageEx or StgOpenStorageEx function. In either case, a pointer to the object's IStorage interface is returned. With persistent property sets, call QueryInterface for the IPropertySetStorage interface, specifying the header-defined name for the interface identifier (IID) IID_IPropertySetStorage.

When to Use

Use IPropertyStorage to manage properties within a single property set. Its methods support reading, writing, and deleting both properties and the optional string names that can be associated with property identifiers. Other methods support the standard commit and revert storage operations. There is also a method that allows you to set times associated with the property storage, and another that permits the assignment of a CLSID that can be used to associate other code, such as user interface code, with the property set. Calling the Enum method supplies a pointer to the compound file implementation of IEnumSTATPROPSTG, which allows you to enumerate the properties in the set.

Note If you obtain a pointer to IPropertyStorage by calling StgCreateDocfile, StgCreateStorageEx, StgOpenStorage or StgOpenStorageEx on a simple-mode property set storage, the IPropertyStorage methods adhere to the rules of simple-mode streams. The property set storage is simple mode if it was obtained for a file that was created or opened with the STGM_SIMPLE flag. In this case, it is not always possible to make the underlying stream larger and it is not possible to replace existing properties with larger properties. See IPropertySetStorage-Compound File Implementation for more information.

Remarks

IPropertyStorage and Caching

The compound file implementation of IPropertyStorage caches open property sets in memory in order to improve performance. As a result, changes to a property set are not written to the compound file until the Commit or Release (last reference) methods are called.

Simple Mode Property Sets

A property storage object is in simple mode if it is created from a simple mode property set storage object. For example, a property set storage object would be in simple mode if it were obtained from the StgOpenStorageEx function, with the STGM_SIMPLE flag set in the grfMode parameter. Note that "simple mode" is unrelated to "simple property sets". A property set is simple if it is created by calling IPropertySetStorage::Create with the PROPSETFLAG_NONSIMPLE flag set in the grfFlags parameter. See Storage vs. Stream for a Property Set for more information about simple and nonsimple property sets.

When a simple mode property storage object is created, there are no restrictions on its use. When an existing simple-mode property storage object is opened, the underlying stream object that stores the property set cannot be grown. Consequently, it is not always possible to modify such a property storage object if the change requires a larger stream.

Version 0 and Version 1 Property Set Formats

The compound file implementation of IPropertyStorage supports both the version 0 and the version 1 property set serialization formats. The version 1 format is supported as of Windows 2000. See Version 0 vs. Version 1 Property Set Serialization for more information. Property sets are created in version 0 format and remain in that format unless new features are requested. When that occurs, the format is updated to version 1.

For example, if a property set is created with the PROPSETFLAG_DEFAULT flag, its format is version 0. As long as property types that conform to the version 0 format are written to and read from that property set, the property set remains in version 0 format. If a version 1 property type is written to the property set, the property set is automatically updated to version 1. Subsequently, that property set can no longer be read by implementations that understand only version 0.

IPropertyStorage and Variant Types

The compound file implementation of IPropertyStorage does not support the variant types VT_UNKNOWN or VT_DISPATCH in the vt member of the PROPVARIANT structure.

The following variant types are supported within a SafeArray; that is, these values can be combined with VT_ARRAY in the vt member of the PROPVARIANT structure.

Variant Types Supported within SafeArray by Compound File Implementation of IPropertyStorage
VT_I1 | VT_UI1 | VT_I2
VT_UI2 | VT_I4 | VT_UI4
VT_INT | VT_UINT | VT_R4
VT_R8 | VT_CY | VT_DATE
VT_BSTR | VT_BOOL | VT_DECIMAL
VT_ERROR | VT_VARIANT |

When VT_VARIANT is combined with VT_ARRAY, the SafeArray itself holds PROPVARIANT structures. However, the types of these elements must be taken from the preceding list, cannot be VT_VARIANT, and cannot include the VT_VECTOR, VT_ARRAY, or VT_BYREF indicators.

IPropertyStorage Methods

The compound file implementation of IPropertyStorage supports the following methods:

IPropertyStorage::ReadMultiple
Reads the properties specified in the rgpspec array and supplies the values of all valid properties in the rgvar array of PROPVARIANTs. In the COM compound file implementation, duplicate property identifiers that refer to stream or storage types result in multiple calls to IStorage::OpenStream or IStorage::OpenStorage and the success or failure of ReadMultiple depends on the underlying storage implementation's ability to share opening operations. Because in a compound file STGM_SHARE_EXCLUSIVE is forced, multiple open attempts will fail. Opening the same storage object more than once from the same parent storage is not supported. The STGM_SHARE_EXCLUSIVE flag must be specified.

In addition, to ensure thread-safe operation if the same stream- or storage-valued property is requested several times through the same IPropertyStorage pointer in the COM compound file implementation, the open operation will succeed or fail depending on whether the property is already open and whether the underlying file system handles multiple openings of a stream or storage. Thus, the ReadMultiple operation on a stream- or storage-valued property always results in a call to IStorage::OpenStream, or IStorage::OpenStorage, which passes the access (STGM_READWRITE, and so forth) and share flags (STGM_SHARE_EXCLUSIVE, and so on) specified when the original property set was opened or created.

If the method fails, the values written to rgvar[] are undefined. If some stream- or storage-valued properties are opened successfully but an error occurs before execution is complete, these should be released before the method returns.

IPropertyStorage::WriteMultiple
Writes the properties specified in the rgpspec[] array, assigning them the PROPVARIANT tags and values specified in rgvar[]. Properties that already exist are assigned the specified PROPVARIANT values. Properties that do not currently exist are created.
IPropertyStorage::DeleteMultiple
Deletes the properties specified in the rgpspec[].
IPropertyStorage::ReadPropertyNames
Reads existing string names associated with the property IDs specified in the rgpropid[] array.
IPropertyStorage::WritePropertyNames
Assigns string names specified in the rglpwstrName array to property IDs specified in the rgpropid array.
IPropertyStorage::DeletePropertyNames
Deletes property names for the properties specified in the rgpropid[] array.
IPropertyStorage::SetClass
Sets the CLSID member of the property set stream. In the compound file implementation, setting the CLSID on a nonsimple property set (one that can legally contain storage- or stream-valued properties, as described in IPropertySetStorage::Create) also sets the CLSID on the underlying substorage so that it can be obtained through a call to IStorage::Stat.
IPropertyStorage::Commit
For both simple and nonsimple property sets, flushes the property set memory image to the underlying storage. In addition, for nonsimple transacted-mode property sets, this method performs a commit (as in IStorage::Commit) on the storage which contains the property set.
IPropertyStorage::Revert
For nonsimple property sets only, calls the Revert method of the underlying storage and reopens the 'contents' stream. For simple property sets, this interface always returns S_OK. Nonsimple property sets are those which were created using the PROPSETFLAG_NONSIMPLE flag in the IPropertySetStorage::Create method. See Storage vs. Stream for a Property Set for more information.
IPropertyStorage::Enum
Constructs an instance of IEnumSTATPROPSTG, the methods of which can be called to enumerate the STATPROPSTG structures that provide information about each of the properties in the set. This implementation creates an array into which the entire property set is read and which can be shared when IEnumSTATPROPSTG::Clone is called. Changes to the property set are not reflected in an open IEnumSTATPROPSTG instance. To see such changes, a new instance of this enumerator must be constructed.
IPropertyStorage::Stat
Fills in the members of a STATPROPSETSTG structure, which contains information about the property set as a whole. On return, supplies a pointer to the structure. For nonsimple storage sets, this implementation calls IStorage::Stat (or IStream::Stat) to get the times from the underlying storage or stream. For simple storage sets, no times are maintained.
IPropertyStorage::SetTimes
For nonsimple property sets only, sets the times supported by the underlying storage. The compound file storage implementation supports all three: modification, access, and creation. This implementation of SetTimes calls the IStorage::SetElementTimes method of the underlying storage to retrieve these times.

See Also

IPropertyStorage, IStorage::SetElementTimes


IPropertyStorage-NTFS File System Implementation

NTFS file system 5.0 provides an implementation of both IPropertySetStorage and IPropertyStorage for files on an NTFS volume when the files themselves are not compound files.

To get a pointer to the NTFS file system implementation of IPropertySetStorage, you can make one of the following calls:

  • Call IPropertySetStorage::Create using the NTFS implementation of IPropertySetStorage.
  • Call IPropertySetStorage::Open using the NTFS implementation of IPropertySetStorage.

When to Use

Use IPropertyStorage to manage properties within a single property set. Its methods support reading, writing, and deleting properties and the optional string names that can be associated with property IDs. Another method allows you to set times associated with the property storage, and another permits the assignment of a CLSID, used to associate other code, such as user interface (UI) code, with the property set. Calling the Enum method supplies a pointer to the NTFS implementation of IEnumSTATPROPSTG, which allows you to enumerate the properties in the set.

Remarks

The NTFS implementation provides essentially the same features as the compound file implementation. See IPropertyStorage-Compound File Implementation for more information.

The NTFS implementation differs from the compound file implementation with respect to robustness. An NTFS property set will never be left in an incorrect state. When the content of an NTFS IPropertyStorage is flushed to the underlying NTFS file, either all or none of the state is written to the file as an atomic operation, even if there is a failure during the operation such as an abnormal process termination. To achieve this kind of robustness with the compound file implementation, the parent IPropertySetStorage interface must be opened in transacted mode.

This level of robustness is only possible when accessing an NTFS property set on an NTFS 5.0 volume. It is possible to access NTFS property sets on earlier versions of NTFS (for example, a Windows NT/Windows 2000 computer accessing the property sets on a Windows NT 4.0 file server), but they are not guaranteed to always be in a correct state in the event of an unexpected failure.

Although the NTFS implementation of IPropertySetStorage does not support transactioning, the NTFS implementation of IPropertyStorage supports it. That is, STGM_TRANSACTED may be specified in the grfMode parameter to the Create and Open methods of IPropertySetStorage. As in the compound file implementation, transacted mode is possible only for nonsimple property storages (specifying PROPSETFLAG_NONSIMPLE in the grfFlags parameter).

See Also

IPropertyStorage, IPropertySetStorage-NTFS File System Implementation


IPropertyStorage-Stand-alone Implementation

The system-provided, stand-alone implementation of IPropertySetStorage includes an implementation of IPropertyStorage, the interface that reads and writes properties in a property set storage. The IPropertySetStorage interface creates and opens property sets in a storage. The IEnumSTATPROPSTG and IEnumSTATPROPSETSTG interfaces are also provided in the stand-alone implementation.

To get a pointer to the stand-alone implementation of IPropertyStorage, call the StgCreatePropStg function if you are creating a new property set or StgOpenPropStg if you want to obtain the interface pointer on an existing property set (or call the Create or Open methods of the IPropertySetStorage stand-alone implementation).

The stand-alone implementation of IPropertyStorage creates property sets on any storage or stream object, not just on compound file storages and streams. The stand-alone implementation does not depend on compound files and can be used with any implementation of structured storages. See the section IPropertyStorage-Compound File Implementation for more information on the compound file implementation of this interface.

When to Use

Use IPropertyStorage to manage properties within a single property set. Its methods support reading, writing, and deleting both properties and the optional string names that can be associated with property IDs. Other methods support the standard commit and revert storage operations. There is also a method that sets times associated with the property storage, and another that permits the assignment of a CLSID to be used to associate other code, such as user interface code, with the property set. The Enum method supplies a pointer to the stand-alone implementation of IEnumSTATPROPSTG, which enumerates the properties in the set.

Remarks

Version 0 and Version 1 Property Set Formats

The stand-alone implementation of IPropertyStorage supports both the version 0 and the version 1 property set serialization formats. See Version 0 vs. Version 1 Property Set Serialization for more information. Property sets are created in version 0 format and remain in that format unless new features are requested. At that time, the format is updated to version 1.

For example, if a property set is created with the PROPSETFLAG_DEFAULT flag, its format is version 0. As long as property types that conform to the version 0 format are written to and read from that property set, the property set remains in version 0 format. If a version 1 property type is written to the property set, the property set is automatically updated to version 1. Subsequently, that property set can no longer be read by implementations that only understand version 0.

IPropertyStorage and Variant Types

The stand-alone implementation of IPropertyStorage does not support the variant types VT_UNKNOWN or VT_DISPATCH in the vt member of the PROPVARIANT structure.

The following variant types are supported within a SafeArray; that is, these values can be combined with VT_ARRAY in the vt member of the PROPVARIANT structure.

Variant Types Supported within SafeArray by Compound File Implementation of IPropertyStorage
VT_I1 | VT_UI1 | VT_I2
VT_UI2 | VT_I4 | VT_UI4
VT_INT | VT_UINT | VT_R4
VT_R8 | VT_CY | VT_DATE
VT_BSTR | VT_BOOL | VT_DECIMAL
VT_ERROR | VT_VARIANT |

When VT_VARIANT is combined with VT_ARRAY, the SafeArray itself holds PROPVARIANT structures. However, the types of these elements must be taken from the preceding list, cannot be VT_VARIANT, and cannot include the VT_VECTOR, VT_ARRAY, or VT_BYREF indicators.

IPropertyStorage Methods

The stand-alone implementation of IPropertyStorage supports the following methods:

IPropertyStorage::ReadMultiple
Reads the properties specified in the rgpspec array and supplies the values of all valid properties in the rgvar array of PROPVARIANTs.

In the system-provided, stand-alone implementation, duplicate property identifiers that refer to stream- or storage-types result in multiple calls to IStorage::OpenStream or IStorage::OpenStorage and the success or failure of ReadMultiple depends on the underlying storage implementation's ability to share open storages.

In addition, to ensure thread-safe operation if the same stream- or storage-valued property is requested multiple times through the same IPropertyStorage pointer, the open will succeed or fail depending on whether or not the property is already open and on whether the underlying file system handles multiple opens of a stream or storage. Thus, the ReadMultiple operation on a stream- or storage-valued property always results in a call to IStorage::OpenStream, or IStorage::OpenStorage, passing the access (STGM_READWRITE, for example) and share values (STGM_SHARE_EXCLUSIVE, for example) specified when the property set was originally opened or created.

If the method fails, the values written to rgvar[] are undefined. If some stream- or storage-valued properties are opened successfully but an error occurs before execution is complete, these properties should be released before the method returns.

IPropertyStorage::WriteMultiple
Writes the properties specified in the rgpspec[] array, assigning them the PROPVARIANT tags and values specified in rgvar[]. Properties that already exist are assigned the specified PROPVARIANT values, and properties that do not currently exist are created.
IPropertyStorage::DeleteMultiple
Deletes the properties specified in the rgpspec[].
IPropertyStorage::ReadPropertyNames
Reads existing string names associated with the property IDs specified in the rgpropid[] array.
IPropertyStorage::WritePropertyNames
Assigns string names specified in the rglpwstrName array to property IDs specified in the rgpropid array.
IPropertyStorage::DeletePropertyNames
Deletes the string names of the property IDs specified in the rgpropid array by writing NULL to the property name.
IPropertyStorage::SetClass
Sets the CLSID member of the property set stream. In the stand-alone implementation, setting the CLSID on a nonsimple property set (one that can contain storage- or stream-valued properties, as described in IPropertySetStorage::Create) also sets the CLSID on the underlying substorage so that it can be obtained through a call to IStorage::Stat.
IPropertyStorage::Commit
For both simple and nonsimple property sets, flushes the memory image to the disk subsystem. In addition, for nonsimple transacted-mode property sets, this method calls IStorage::Commit on the property set.
IPropertyStorage::Revert
For nonsimple property sets only, calls the Revert method of the underlying storage and reopens the 'contents' stream. For simple property sets, only returns E_OK.
IPropertyStorage::Enum
Creates an enumerator object that implements IEnumSTATPROPSTG, the methods of which can be called to enumerate the STATPROPSTG structures that provide information about each of the properties in the set.

This implementation creates an array into which the entire property set is read and which can be shared when IEnumSTATPROPSTG::Clone is called.

IPropertyStorage::Stat
Fills in the members of a STATPROPSETSTG structure, which contains information about the property set as a whole. On return, supplies a pointer to the structure.

For nonsimple storage sets, this implementation calls IStorage::Stat (or IStream::Stat) to get the information from the underlying storage or stream.

IPropertyStorage::SetTimes
For nonsimple property sets only, sets the times supported by the underlying storage. This implementation of SetTimes calls the IStorage::SetElementTimes method of the underlying storage to modify the times. It supports the times supported by the underlying method which can be modification time, access time, or creation time.

See Also

IPropertySetStorage-Stand-alone Implementation, IPropertyStorage, IStorage::SetElementTimes, StgOpenPropStg, StgCreatePropStg, StgCreatePropSetStg


Differences between Stand-alone and Compound File Implementations

The stand-alone implementation of the property set interfaces and the compound file implementation differ in some ways. In the compound-file implementation of stream, storage, property set storage, and property storage objects, the various interfaces coordinate with each another because they share a common implementation. In the stand-alone implementation, the interface implementations are distinct.

As a result, the compound-file implementation handles concurrency issues and synchronizes the property set object with the storage or stream object. With the stand-alone implementation, the client is responsible for handling concurrency and synchronization issues between the storage or stream object and the property set. A client can meet these requirements by following two simple rules. First, never manipulate a property set using its stream or storage interfaces while a property storage object is open on it. Second, always commit a property storage object before calling Commit, CopyTo, or MoveElementTo on an ancestor storage object. Specifically, the following items require client attention:

  • In the compound-file implementation, a single mechanism provides concurrency protection for the storage object and its associated property set objects. However, in the stand-alone implementation, the storage object implementation is separate from the property set implementation and each provides its own concurrency mechanisms. Thus, in the stand-alone implementation, the client is responsible for maintaining concurrency protection between the two implementations through a mechanism of mutual exclusion.
  • In the compound file implementation, changes to property sets are buffered in a property-set cache. Then, when the IStorage::Commit method is called on the storage object, the compound files implementation automatically flushes the property-set changes from the property-set buffer before the storage object is committed. Thus, the property-set changes are made visible as part of the transaction being committed.

    In the stand-alone implementation, the client must explicitly flush the property-set buffer by calling IPropertyStorage::Commit before calling the IStorage:Commit method on the storage. Alternatively, the client can use the new PROPSETFLAG_UNBUFFERED value in the stand-alone implementation to write directly to the property set instead of caching changes to the property set's internal buffer. If PROPSETFLAG_UNBUFFERED is used, the client's responsibilities are automatically met. The compound file implementation does not support the PROPSETFLAG_UNBUFFERED value. See the PROPSETFLAG enumeration for more information about using this value.

  • As with transacted storages, the compound file implementation updates the property set by flushing its internal buffer before executing a call to IStorage::CopyTo or IStorage::MoveElementTo. Thus, changes to the property set are reflected in the copied or moved storage element.

    In the stand-alone implementation, the client must explicitly flush the property set buffer by calling IPropertyStorage::Commit before calling IStorage::CopyTo or IStorage::MoveElementTo. Alternatively, the client can use the new PROPSETFLAG_UNBUFFERED value to write directly to the property set instead of caching changes to the property set buffer. See the PROPSETFLAG enumeration for more information about using this value.


IRootStorage

The IRootStorage interface contains a single method that switches a storage object to a different underlying file and saves the storage object to that file. The save operation occurs even with low-memory conditions and uncommitted changes to the storage object. A subsequent call to IStorage::Commit is guaranteed to not consume additional memory.

When to Implement

Storage objects that are based on a file should implement IRootStorage in addition to the IStorage interface. For storage objects that are not file-based, this interface is not necessary.

COM provides an implementation of a storage object, including the IRootStorage interface, as part of its compound file implementation.

When to Use

The primary use for the IRootStorage interface is to save a storage object to a file during low-memory conditions. Typically, the container application calls the IRootStorage interface to switch to a new file. This interface is also useful on older systems where limits on file handles can be exceeded.

If you have an IStorage pointer to a compound file object, you can call IStorage::QueryInterface with IID_IRootStorage to obtain a pointer to the IRootStorage interface.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

IRootStorage Method | Description
SwitchToFile | Copies the file underlying this root storage object, and then associates this storage with the copied file.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage, StgCreateDocfile


IRootStorage::SwitchToFile

Copies the current file associated with the storage object to a new file. The new file is then used for the storage object and any uncommitted changes.

HRESULT SwitchToFile(
  LPOLESTR pszFile  //Filename for the new file
);

Parameter

pszFile
Specifies the file name for the new file. It cannot be the name of an existing file. If NULL, this method creates a temporary file with a unique name, and you can call IStorage::Stat to retrieve the name of the temporary file.

Return Values

S_OK
The file was successfully copied.
STG_E_MEDIUMFULL
The file was not copied because of insufficient space on the storage device.
STG_E_ACCESSDENIED
The file was not copied because the caller does not have permission to access storage device.
STG_E_INVALIDPOINTER
The file was not copied because the pszFile pointer is not valid.
STG_E_FILEALREADYEXISTS
The file was not copied because the new filename (pszFile) points to an existing file.

Remarks

The IRootStorage::SwitchToFile method copies the file associated with the storage object. A COM container calls SwitchToFile to perform a full save on a file in a low-memory situation. Typically, this is done only after a normal, full save operation (that is, save to temporary file, delete original file, rename temporary file) has failed with an E_OUTOFMEMORY error.

It is erroneous to call the SwitchToFile method if the storage object or anything contained within it has been marshaled to another process. Before calling SwitchToFile, the container must call the IPersistStorage::HandsOffStorage method for any element within the storage object that is loaded or running. The HandsOffStorage method forces the element to release its storage pointers and enter the hands-off storage mode. The container must also release all pointers to streams or storages that are contained in this root storage. After the full save operation is completed, the container returns the contained elements to normal storage mode.

Notes to Implementers

If you are implementing your own storage objects, the IRootStorage methods (including QueryInterface, AddRef, and Release) must not consume additional memory or file handles.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IPersistStorage::HandsOffStorage, IPersistStorage::SaveCompleted, IStorage::Commit, IStorage::Stat


IRootStorage - Compound File Implementation

COM's compound file implementation of IRootStorage allows you to support saving files in low-memory or low disk-space situations. For information on how this interface behaves, see IRootStorage.

When to Use

Use the system-supplied implementation of IRootStorage only to support saving files under low-memory conditions.

Remarks

It is possible to call COM's implementation of IRootStorage::SwitchToFile to do a normal save-as operation to another file. However, applications that do this might not be compatible with future generations of COM storage. To avoid this possibility, applications performing a save-as operation should manually create the second compound file and invoke IStorage::CopyTo. The IRootStorage::SwitchToFile method should be used only in emergency (low-memory or disk-space) situations.

See Also

IRootStorage, IRootStorage::SwitchToFile


ISequentialStream

The ISequentialStream interface supports simplified sequential access to stream objects. The IStream interface inherits its Read and Write methods from ISequentialStream.

When to Implement

You can implement ISequentialStream on an object if you require simple sequential access to a stream object. If you implement IStream, you must provide an implementation of the Read and Write methods from ISequentialStream.

Note Most applications do not implement ISequentialStream as a separate interface, and you are not required to provide it separately even if you provide an IStream implementation. For example, the compound file implementation of structured storage does not succeed on a QueryInterface method for ISequentialStream but it includes the Read and Write methods through the IStream interface pointer.

When to Use

Call the methods of the ISequentialStream interface from a container or application to perform sequential read and write operations on data. Most applications call the Read and Write methods through the IStream interface.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

ISequentialStream Methods | Description
Read | Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.
Write | Writes a specified number of bytes to the stream object starting at the current seek pointer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream


ISequentialStream::Read

Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.

HRESULT Read(
  void *pv,  //Pointer to buffer into which the stream is read
  ULONG cb,  //Specifies the number of bytes to read
  ULONG *pcbRead
             //Pointer to location that contains actual
             // number of bytes read
);

Parameters

pv
[out] Pointer to the buffer into which the stream data is read.
cb
[in] Specifies the number of bytes of data to attempt to read from the stream object.
pcbRead
[out] Pointer to a ULONG variable that receives the actual number of bytes read from the stream object. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes read.

Return Values

S_OK
Data was successfully read from the stream object.
S_FALSE
The data could not be read from the stream object.
E_PENDING
Asynchronous storage only: Part or all of the data to be read is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for reading this stream object.
STG_E_INVALIDPOINTER
One of the pointer values is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

This method reads bytes from this stream object into memory. The stream object must be opened in STGM_READ mode. This method adjusts the seek pointer by the actual number of bytes read.

The number of bytes actually read is also returned in the pcbRead parameter.

Notes to Callers

The actual number of bytes read can be less than the number of bytes requested if an error occurs or if the end of the stream is reached during the read operation.

Some implementations might return an error if the end of the stream is reached during the read operation. You must be prepared to deal with the error return or S_OK return values on end-of-stream read operations.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

ISequentialStream::Write, STGMOVE, IStorage::OpenStream, IStream, IStream - Compound File Implementation


ISequentialStream::Write

Writes a specified number of bytes into the stream object starting at the current seek pointer.

HRESULT Write(
  void const *pv,     //Pointer to the buffer address from which the stream is written
  ULONG cb,           //Specifies the number of bytes to write
  ULONG *pcbWritten   //Specifies the actual number of bytes written
);

Parameters

pv
[in] Pointer to the buffer containing the data that is to be written to the stream. A valid pointer must be provided for this parameter even when cb is zero.
cb
[in] The number of bytes of data to attempt to write into the stream. Can be zero.
pcbWritten
[out] Pointer to a ULONG variable where this method writes the actual number of bytes written to the stream object. The caller can set this pointer to NULL, in which case this method does not provide the actual number of bytes written.

Return Values

S_OK
The data was successfully written to the stream object.
E_PENDING
Asynchronous Storage only: Part or all of the data to be written is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_MEDIUMFULL
The write operation was not completed because there is no space left on the storage device.
STG_E_ACCESSDENIED
The caller does not have enough permissions for writing to this stream object.
STG_E_CANTSAVE
Data cannot be written for reasons other than improper access or insufficient space.
STG_E_INVALIDPOINTER
One of the pointer values is not valid. The pv parameter must contain a valid pointer even if cb is zero.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT
The write operation was not completed due to a disk error. This value is also returned when this method attempts to write to a stream that was opened in simple mode (using the STGM_SIMPLE flag).

Remarks

ISequentialStream::Write writes the specified data to a stream object. The seek pointer is adjusted for the number of bytes actually written. The number of bytes actually written is returned in the pcbWritten parameter. If the byte count is zero bytes, the write operation has no effect.

If the seek pointer is currently past the end of the stream and the byte count is nonzero, this method increases the size of the stream to the seek pointer and writes the specified bytes starting at the seek pointer. The fill bytes written to the stream are not initialized to any particular value. This is the same as the end-of-file behavior in the MS-DOS FAT file system.

With a zero byte count and a seek pointer past the end of the stream, this method does not create the fill bytes to increase the stream to the seek pointer. In this case, you must call the IStream::SetSize method to increase the size of the stream and write the fill bytes.

The pcbWritten parameter can have a value even if an error occurs.

In the COM-provided implementation, stream objects are not sparse. Any fill bytes are eventually allocated on the disk and assigned to the stream.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

ISequentialStream::Read, STGMOVE, IStorage::OpenStream, IStream, IStream - Compound File Implementation


IStorage

The IStorage interface supports the creation and management of structured storage objects. Structured storage allows hierarchical storage of information within a single file, and is often referred to as "a file system within a file". Elements of a structured storage object are storages and streams. Storages are analogous to directories, and streams are analogous to files. Within a structured storage there will be a primary storage object that may contain substorages, possibly nested, and streams. Storages provide the structure of the object, and streams contain the data, which is manipulated through the IStream interface.

The IStorage interface provides methods for creating and managing the root storage object, child storage objects, and stream objects. These methods can create, open, enumerate, move, copy, rename, or delete the elements in the storage object.

An application must release its IStorage pointers when it is done with the storage object to deallocate memory used. There are also methods for changing the date and time of an element.

There are a number of different modes in which a storage object and its elements can be opened, determined by setting values from the STGM enumeration. One aspect of this is how changes are committed. You can set direct mode, in which changes to an object are immediately written to it, or transacted mode, in which changes are written to a buffer until explicitly committed. The IStorage interface provides methods for committing changes and reverting to the last-committed version. For example, a stream can be opened in read-only mode or read/write mode. For more information, refer to the STGM enumeration.

Other methods provide access to information about a storage object and its elements through the STATSTG structure.

When to Implement

Generally, you would not implement this interface unless you were defining a new storage scheme for your system. COM provides a compound file implementation of the IStorage interface that supports transacted access. COM provides a set of helper APIs to facilitate using the compound file implementation of storage objects. Refer to IStorage - Compound File Implementation.

When to Use

Call the methods of IStorage to manage substorages or streams within the current storage. This management includes creating, opening, or destroying substorages or streams, as well as managing aspects such as time stamps, names, and so forth. You also can commit changes or revert to previous version for storages opened in transacted mode. The methods of IStorage do not include means to read and write data—this is reserved for IStream, which manages the actual data. While the IStorage and IStream interfaces are used to manipulate the storage object and its elements, the IPersistStorage interface contains methods that are called to serialize the storage object and its elements to a disk file.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

IStorage Methods | Description
CreateStream | Creates and opens a stream object with the specified name contained in this storage object.
OpenStream | Opens an existing stream object within this storage object using the specified access permissions in grfMode.
CreateStorage | Creates and opens a new storage object within this storage object.
OpenStorage | Opens an existing storage object with the specified name according to the specified access mode.
CopyTo | Copies the entire contents of this open storage object into another storage object. The layout of the destination storage object may differ.
MoveElementTo | Copies or moves a substorage or stream from this storage object to another storage object.
Commit | Reflects changes for a transacted storage object to the parent level.
Revert | Discards all changes that have been made to the storage object since the last commit operation.
EnumElements | Returns an enumerator object that can be used to enumerate the storage and stream objects contained within this storage object.
DestroyElement | Removes the specified storage or stream from this storage object.
RenameElement | Renames the specified storage or stream in this storage object.
SetElementTimes | Sets the modification, access, and creation times of the indicated storage element, if supported by the underlying file system.
SetClass | Assigns the specified CLSID to this storage object.
SetStateBits | Stores up to 32 bits of state information in this storage object.
Stat | Returns the STATSTG structure for this open storage object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

EnumAll Sample, Samples


IStorage::Commit

Ensures that any changes made to a storage object open in transacted mode are reflected in the parent storage. For nonroot storage objects in direct mode, this method has no effect. For a root storage, it reflects the changes in the actual device; for example, a file on disk. For a root storage object opened in direct mode, always call the IStorage::Commit method prior to IStorage::Release. IStorage::Commit flushes all memory buffers to the disk for a root storage in direct mode and will return an error code upon failure. Although IStorage::Release also flushes memory buffers to disk, it has no capacity to return any error codes upon failure. Therefore, calling Release without first calling Commit causes indeterminate results.

HRESULT Commit(
  DWORD grfCommitFlags  //Specifies how changes are to be committed
);

Parameter

grfCommitFlags
[in] Controls how the changes are committed to the storage object. See the STGC enumeration for a definition of these values.

Return Values

S_OK
Changes to the storage object were successfully committed to the parent level. If STGC_CONSOLIDATE was specified, the storage was successfully consolidated, or the storage was already too compact to consolidate further.
STG_S_MULTIPLEOPENS
The commit operation succeeded, but the storage could not be consolidated because it had been opened multiple times using the STGM_NOSNAPSHOT flag.
STG_S_CANNOTCONSOLIDATE
The commit operation succeeded, but the storage could not be consolidated due to an incorrect storage mode. For compound files, the storage may have been opened using the STGM_NOSCRATCH flag, or the storage may not be the outermost transacted level.
STG_S_CONSOLIDATIONFAILED
The commit operation succeeded, but the storage could not be consolidated due to an internal error (for example, a memory allocation failure).
E_PENDING
Asynchronous storage only: Part or all of the data to be committed is currently unavailable. For more information, see IFillLockBytes and Asynchronous Storage.
STG_E_INVALIDFLAG
The value for the grfCommitFlags parameter is not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_NOTCURRENT
Another open instance of the storage object has committed changes. As a result, the current commit operation may overwrite previous changes.
STG_E_MEDIUMFULL
No space left on device to commit.
STG_E_TOOMANYOPENFILES
The commit operation could not be completed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.

Remarks

IStorage::Commit makes permanent changes to a storage object that is in transacted mode, in which changes are accumulated in a buffer, and not reflected in the storage object until there is a call to this method. The alternative is to open an object in direct mode, in which changes are immediately reflected in the storage object. An object opened in the direct mode does not require calling IStorage::Commit to make permanent changes in the storage object. Calling the IStorage::Commit method on a nonroot storage opened in direct mode has no effect. Opening a root storage object in direct mode ensures that changes in memory buffers are written to the underlying storage device.

The commit operation publishes the current changes in this storage object and its children to the next level up in the storage hierarchy. To undo current changes before committing them, call IStorage::Revert to roll back to the last-committed version.

Calling IStorage::Commit has no effect on currently opened nested elements of this storage object. They remain valid and can be used. However, the IStorage::Commit method does not automatically commit changes to these nested elements. The commit operation publishes only known changes to the next higher level in the storage hierarchy. Thus, transactions to nested levels must be committed to this storage object before they can be committed to higher levels.

In commit operations, you need to take steps to ensure that data is protected during the commit process:

  • When committing changes to root storage objects, the caller must check the return value to determine whether the operation has been completed successfully, and if not, that the old committed contents of the IStorage are still intact and can be restored.
  • If this storage object was opened with some of its items excluded, the caller is responsible for rewriting them before calling commit. Write mode is required on the storage opening for the commit to succeed.
  • Unless prohibiting multiple simultaneous writers on the same storage object, an application calling this method should specify at least STGC_ONLYIFCURRENT in the grfCommitFlags parameter to prevent the changes made by one writer from inadvertently overwriting the changes made by another.

If the STGC_CONSOLIDATE flag is not supported by a storage implementation, calling IStorage::Commit with STGC_CONSOLIDATE specified in the grfCommitFlags parameter returns the value STG_E_INVALIDFLAG.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, STGC, IStorage::Revert


IStorage::CopyTo

Copies the entire contents of an open storage object to another storage object.

HRESULT CopyTo(
  DWORD ciidExclude,         //Number of elements in rgiidExclude
  IID const *rgiidExclude,   //Array of interface identifiers (IIDs)
  SNB snbExclude,            //Pointer to a block of stream
                             // names in the storage object
  IStorage *pstgDest         //Pointer to destination storage object
);

Parameters

ciidExclude
[in] The number of elements in the array pointed to by rgiidExclude. If rgiidExclude is NULL, then ciidExclude is ignored.
rgiidExclude
[in] An array of interface identifiers (IIDs) that either the caller knows about and does not want copied or that the storage object does not support but whose state the caller will later explicitly copy. The array can include IStorage, indicating that only stream objects are to be copied, and IStream, indicating that only storage objects are to be copied. An array length of zero indicates that only the state exposed by the IStorage object is to be copied; all other interfaces on the object are to be ignored. Passing NULL indicates that all interfaces on the object are to be copied.
snbExclude
[in] A string name block (refer to SNB) that specifies a block of storage or stream objects that are not to be copied to the destination. These elements are not created at the destination. If IID_IStorage is in the rgiidExclude array, this parameter is ignored. This parameter may be NULL.
pstgDest
[in] Pointer to the open storage object into which this storage object is to be copied. The destination storage object can be a different implementation of the IStorage interface from the source storage object. Thus, IStorage::CopyTo can use only publicly available methods of the destination storage object. If pstgDest is open in transacted mode, it can be reverted by calling its IStorage::Revert method.

Return Values

S_OK
The storage object was successfully copied.
E_PENDING
Asynchronous Storage only: Part or all of the data to be copied is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The destination storage object is a child of the source storage object.
STG_E_INSUFFICIENTMEMORY
The copy was not completed due to a lack of memory.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_TOOMANYOPENFILES
The copy was not completed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_MEDIUMFULL
The copy was not completed because the storage medium is full.

Remarks

This method merges elements contained in the source storage object with those already present in the destination. The layout of the destination storage object may differ from the source storage object.

The copy process is recursive, invoking IStorage::CopyTo and IStream::CopyTo on the elements nested inside the source.

When copying a stream on top of an existing stream with the same name, the existing stream is first removed and then replaced with the source stream. When copying a storage on top of an existing storage with the same name, the existing storage is not removed. As a result, after the copy operation, the destination IStorage contains older elements, unless they were replaced by newer ones with the same names.

A storage object may expose interfaces other than IStorage, including IRootStorage, IPropertyStorage, or IPropertySetStorage. The rgiidExclude parameter permits the exclusion of any or all of these additional interfaces from the copy operation.

A caller with a newer or more efficient copy of an existing substorage or stream object may want to exclude the current versions of these objects from the copy operation. The snbExclude and rgiidExclude parameters provide two ways of excluding a storage objects existing storages or streams.

Note to Callers

The most common way to use the IStorage::CopyTo method is to copy everything possible from the source to the destination, as in most full-save and save-as operations. The following example illustrates this call:

pstg->CopyTo(0, Null, Null, pstgDest)

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::MoveElementTo, IStorage::Revert


IStorage::CreateStorage

Creates and opens a new storage object nested within this storage object.

HRESULT CreateStorage(
  const WCHAR *pwcsName,   //Pointer to the name of the new storage object
  DWORD grfMode,           //Access mode for the new storage object
  DWORD reserved1,         //Reserved; must be zero
  DWORD reserved2,         //Reserved; must be zero
  IStorage **ppstg         //Pointer to new storage object
);

Parameters

pwcsName
[in] Pointer to a wide character string that contains the name of the newly created storage object. This name can be used later to reopen the storage object.
grfMode
[in] Specifies the access mode to use when opening the newly created storage object. See the STGM enumeration values for descriptions of the possible values.
reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be zero.
ppstg
[out] When successful, pointer to the location of the IStorage pointer to the newly created storage object. This parameter is set to NULL if an error occurs.

Return Values

S_OK
The storage object was created successfully.
E_PENDING
Asynchronous Storage only: Part or all of the necessary data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
Not enough permissions to create storage object.
STG_E_FILEALREADYEXISTS
The name specified for the storage object already exists in the storage object and the grfmode flag includes the flag STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
The storage object was not created due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_INVALIDNAME
Not a valid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The storage object was not created because there are too many open files.
STG_S_CONVERTED
The existing stream with the specified name was replaced with a new storage object containing a single stream called CONTENTS. The new storage object will be added.

Remarks

If a storage with the name specified in the pwcsName parameter already exists within the parent storage object, and the grfMode parameter includes the STGM_CREATE flag, the existing storage is replaced by the new one. If the grfMode parameter includes the STGM_CONVERT flag, the existing element is converted to a stream object named CONTENTS and the new storage object is created containing the CONTENTS stream object. The destruction of the old element and the creation of the new storage object are both subject to the transaction mode on the parent storage object. Note that you cannot use STGM_CONVERT if you are also using STGM_CREATE.

If a storage object with the same name already exists and grfMode is set to STGM_FAILIFTHERE, this method fails with the return value STG_E_FILEALREADYEXISTS.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::OpenStorage


IStorage::CreateStream

Creates and opens a stream object with the specified name contained in this storage object. All elements within a storage object — both streams and other storage objects — are kept in the same name space.

HRESULT CreateStream(
  const WCHAR *pwcsName,   //Pointer to the name of the new stream
  DWORD grfMode,           //Access mode for the new stream
  DWORD reserved1,         //Reserved; must be zero
  DWORD reserved2,         //Reserved; must be zero
  IStream **ppstm          //Pointer to new stream object
);

Parameters

pwcsName
[in] Pointer to a wide character string that contains the name of the newly created stream. This name can be used later to open or reopen the stream.
grfMode
[in] Specifies the access mode to use when opening the newly created stream. See the STGM enumeration values for descriptions of the possible values.
reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be zero.
ppstm
[out] On return, pointer to the location of the new IStream interface pointer. This is only valid if the operation is successful. When an error occurs, this parameter is set to NULL.

Return Values

S_OK
The new stream was successfully created
E_PENDING
Asynchronous Storage only: Part or all of the necessary data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
Not enough permissions to create stream.
STG_E_FILEALREADYEXISTS
The name specified for the stream already exists in the storage object and the grfmode flag includes the value STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
The stream was not created due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported; for example, when this method is called without the STGM_SHARE_EXCLUSIVE flag.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the stream object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The stream was not created because there are too many open files.

Remarks

If a stream with the name specified in the pwcsName parameter already exists and the grfMode parameter includes the STGM_CREATE flag, the existing stream is replaced by a newly created one. Both the destruction of the old stream and the creation of the new stream object are subject to the transaction mode on the parent storage object.

If the stream already exists and grfMode is set to STGM_FAILIFTHERE, this method fails with the return value STG_E_FILEALREADYEXISTS.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::OpenStream, IStream


IStorage::DestroyElement

Removes the specified storage or stream from this storage object.

HRESULT DestroyElement(
  wchar *pwcsName  //Pointer to the name of the element to be removed
);

Parameter

pwcsName
[in] Pointer to a wide character string that contains the name of the storage or stream to be removed.

Return Values

S_OK
The element was successfully removed.
E_PENDING
Asynchronous Storage only: Part or all of the element's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for removing the element.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The element was not removed due to a lack of memory.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the element was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The element was not removed because there are too many open files.

Remarks

The DestroyElement method deletes a substorage or stream from the current storage object. After a successful call to DestroyElement, any open instance of the destroyed element from the parent storage becomes invalid.

If a storage object is opened in transacted mode, destruction of an element requires that the call to DestroyElement be followed by a call to IStorage::Commit.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation


IStorage::EnumElements

Retrieves a pointer to an enumerator object that can be used to enumerate the storage and stream objects contained within this storage object.

HRESULT EnumElements(
  DWORD reserved1,        //Reserved; must be zero
  void *reserved2,        //Reserved; must be NULL
  DWORD reserved3,        //Reserved; must be zero
  IEnumSTATSTG **ppenum   //Pointer to output variable that
                          // receives the IEnumSTATSTG interface pointer
);

Parameters

reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be NULL.
reserved3
[in] Reserved for future use; must be zero.
ppenum
[out] Pointer to IEnumSTATSTG* pointer variable that receives the interface pointer to the new enumerator object.

Return Values

S_OK
The enumerator object was successfully returned.
E_PENDING
Asynchronous Storage only: Part or all of the element's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INSUFFICIENTMEMORY
The enumerator object could not be created due to lack of memory.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.

Remarks

The enumerator object returned by this method implements the IEnumSTATSTG interface, one of the standard enumerator interfaces that contain the Next, Reset, Clone, and Skip methods. IEnumSTATSTG enumerates the data stored in an array of STATSTG structures.

The storage object must be open in read mode to allow the enumeration of its elements.

The order in which the elements are enumerated and whether the enumerator is a snapshot or always reflects the current state of the storage object, and depends on the IStorage implementation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IEnumXXXX, IEnumSTATSTG, STATSTG


IStorage::MoveElementTo

Copies or moves a substorage or stream from this storage object to another storage object.

HRESULT MoveElementTo(
  const WCHAR *pwcsName,   //Name of the element to be moved
  IStorage *pstgDest,      //Pointer to destination storage object
  LPWSTR pwcsNewName,      //Pointer to new name of element in destination
  DWORD grfFlags           //Specifies a copy or a move
);

Parameters

pwcsName
[in] Pointer to a wide character string that contains the name of the element in this storage object to be moved or copied.
pstgDest
[in] IStorage pointer to the destination storage object.
pwcsNewName
[in] Pointer to a wide character string that contains the new name for the element in its new storage object.
grfFlags
[in] Specifies whether the operation should be a move (STGMOVE_MOVE) or a copy (STGMOVE_COPY). See the STGMOVE enumeration.

Return Values

S_OK
The storage object was successfully copied or moved.
E_PENDING
Asynchronous Storage only: Part or all of the element's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The destination storage object is a child of the source storage object. Or, the destination object and element name are the same as the source object and element name. In other words, you can not move an element to itself.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_FILEALREADYEXISTS
The specified file already exists.
STG_E_INSUFFICIENTMEMORY
The copy or move was not completed due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfFlags parameter is not valid.
STG_E_INVALIDNAME
Not a valid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The copy or move was not completed because there are too many open files.

Remarks

The IStorage::MoveElementTo method is typically the same as invoking the IStorage::CopyTo method on the indicated element and then removing the source element. In this case, the MoveElementTo method uses only the publicly available functions of the destination storage object to carry out the move.

If the source and destination storage objects have special knowledge about each other's implementation (they could, for example, be different instances of the same implementation), this method can be implemented more efficiently.

Before calling this method, the element to be moved must be closed, and the destination storage must be open. Also, the destination object and element cannot be the same storage object/element name as the source of the move. That is, you cannot move an element to itself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, STGMOVE, IStorage::CopyTo


IStorage::OpenStorage

Opens an existing storage object with the specified name in the specified access mode.

HRESULT OpenStorage(
  const WCHAR *pwcsName,   //Pointer to the name of the
                           // storage object to open
  IStorage *pstgPriority,  //Must be NULL.
  DWORD grfMode,           //Access mode for the new storage object
  SNB snbExclude,          //Must be NULL.
  DWORD reserved,          //Reserved; must be zero
  IStorage **ppstg         //Pointer to opened storage object
);

Parameters

pwcsName
[in] Pointer to a wide character string that contains the name of the storage object to open. It is ignored if pstgPriority is non-NULL.
pstgPriority
[in] Must be NULL. A non-NULL value will return STG_E_INVALIDPARAMETER.
grfMode
[in] Specifies the access mode to use when opening the storage object. See the STGM enumeration values for descriptions of the possible values. Whatever other modes you may choose, you must at least specify STGM_SHARE_EXCLUSIVE when calling this method.
snbExclude
[in] Must be NULL. A non-NULL value will return STG_E_INVALIDPARAMETER.
reserved
[in] Reserved for future use; must be zero.
ppstg
[out] When successful, pointer to the location of an IStorage pointer to the opened storage object. This parameter is set to NULL if an error occurs.

Return Values

S_OK
The storage object was opened successfully.
E_PENDING
Asynchronous Storage only: Part or all of the storage's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
Not enough permissions to open storage object.
STG_E_FILENOTFOUND
The storage object with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The storage object was not opened due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_INVALIDNAME
Not a valid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The storage object was not created because there are too many open files.
STG_S_CONVERTED
The existing stream with the specified name was replaced with a new storage object containing a single stream called CONTENTS. In direct mode, the new storage is immediately written to disk. In transacted mode, the new storage is written to a temporary storage in memory and later written to disk when it is committed.

Remarks

If the pstgPriority parameter is NULL, it is ignored. If the pstgPriority parameter is not NULL, it is an IStorage pointer to a previous opening of an element of the storage object, usually one that was opened in priority mode. The storage object should be closed and reopened according to grfMode. When the IStorage::OpenStorage method returns, pstgPriority is no longer valid. Use the value supplied in the ppstg parameter.

Storage objects can be opened with STGM_DELETEONRELEASE, in which case the object is destroyed when it receives its final release. This is useful for creating temporary storage objects.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::CreateStorage


IStorage::OpenStream

Opens an existing stream object within this storage object in the specified access mode.

HRESULT OpenStream(
  const WCHAR *pwcsName,  //Pointer to name of stream to open
  void *reserved1,        //Reserved; must be NULL
  DWORD grfMode,          //Access mode for the new stream
  DWORD reserved2,        //Reserved; must be zero
  IStream **ppstm         //Pointer to output variable
                          // that receives the IStream interface pointer
);

Parameters

pwcsName
[in] Pointer to a wide character string that contains the name of the stream to open.
reserved1
[in] Reserved for future use; must be NULL.
grfMode
[in] Specifies the access mode to be assigned to the open stream. See the STGM enumeration values for descriptions of the possible values. Whatever other modes you may choose, you must at least specify STGM_SHARE_EXCLUSIVE when calling this method in the compound file implementation.
reserved2
[in] Reserved for future use; must be zero.
ppstm
[out] Pointer to IStream pointer variable that receives the interface pointer to the newly opened stream object. If an error occurs, *ppstm must be set to NULL.

Return Values

S_OK
The stream was successfully opened.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
Not enough permissions to open stream.
STG_E_FILENOTFOUND
The stream with specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The stream was not opened due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported; for example, when this method is called without the STGM_SHARE_EXCLUSIVE flag.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the stream object was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The stream was not opened because there are too many open files.

Remarks

IStorage::OpenStream opens an existing stream object within this storage object in the access mode specified in grfMode. There are restrictions on the permissions that can be given in grfMode. For example, the permissions on this storage object restrict the permissions on its streams. In general, access restrictions on streams need to be stricter than those on their parent storages. Compound-file streams must be opened with STGM_SHARE_EXCLUSIVE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::CreateStream, IStream


IStorage::RenameElement

Renames the specified substorage or stream in this storage object.

HRESULT RenameElement(
  const WCHAR *pwcsOldName,  //Pointer to the name of the
                             // element to be changed
  const WCHAR *pwcsNewName   //Pointer to the new name for
                             // the specified element
);

Parameters

pwcsOldName
[in] Pointer to a wide character string that contains the name of the substorage or stream to be changed.
pwcsNewName
[in] Pointer to a wide character string that contains the new name for the specified substorage or stream.

Return Values

S_OK
The element was successfully renamed.
E_PENDING
Asynchronous Storage only: Part or all of the element's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for renaming the element.
STG_E_FILENOTFOUND
The element with the specified old name does not exist.
STG_E_FILEALREADYEXISTS
The element specified by the new name already exists.
STG_E_INSUFFICIENTMEMORY
The element was not renamed due to a lack of memory.
STG_E_INVALIDNAME
Invalid value for one of the names.
STG_E_INVALIDPOINTER
The pointer specified for the element was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.
STG_E_TOOMANYOPENFILES
The element was not renamed because there are too many open files.

Remarks

IStorage::RenameElement renames the specified substorage or stream in this storage object. An element in a storage object cannot be renamed while it is open. The rename operation is subject to committing the changes if the storage is open in transacted mode.

The IStorage::RenameElement method is not guaranteed to work in low memory with storage objects open in transacted mode. It may work in direct mode.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation


IStorage::Revert

Discards all changes that have been made to the storage object since the last commit operation.

HRESULT Revert(void);

Return Values

S_OK
The revert operation was successful.
E_PENDING
Asynchronous Storage only: Part or all of the storage's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INSUFFICIENTMEMORY
The revert operation could not be completed due to a lack of memory.
STG_E_TOOMANYOPENFILES
The revert operation could not be completed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.

Remarks

For storage objects opened in transacted mode, the IStorage::Revert method discards any uncommitted changes to this storage object or changes that have been committed to this storage object from nested elements.

After this method returns, any existing elements (substorages or streams) that were opened from the reverted storage object are invalid and can no longer be used. Specifying these reverted elements in any call except IStorage::Release returns the error STG_E_REVERTED

This method has no effect on storage objects opened in direct mode.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::Commit


IStorage::SetClass

Assigns the specified class identifier (CLSID) to this storage object.

HRESULT SetClass(
  REFCLSID clsid  //CLSID to be assigned to the storage object
);

Parameter

clsid
[in] The CLSID that is to be associated with the storage object.

Return Values

S_OK
The CLSID was successfully assigned.
E_PENDING
Asynchronous Storage only: Part or all of the storage's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for assigning a CLSID to the storage object.
STG_E_MEDIUMFULL
Not enough space was left on device to complete the operation.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.

Remarks

When first created, a storage object has an associated CLSID of CLSID_NULL. Call this method to assign a CLSID to the storage object.

Call the IStorage::Stat method to retrieve the current CLSID of a storage object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::Stat


IStorage::SetElementTimes

Sets the modification, access, and creation times of the specified storage element, if the underlying file system supports this method .

HRESULT SetElementTimes(
  const WCHAR *pwcsName,   //Pointer to name of element to be changed
  FILETIME const *pctime,  //New creation time for element, or NULL
  FILETIME const *patime,  //New access time for element, or NULL
  FILETIME const *pmtime   //New modification time for element, or NULL
);

Parameters

pwcsName
[in] The name of the storage object element whose times are to be modified. If NULL, the time is set on the root storage rather than one of its elements.
pctime
[in] Either the new creation time for the element or NULL if the creation time is not to be modified.
patime
[in] Either the new access time for the element or NULL if the access time is not to be modified.
pmtime
[in] Either the new modification time for the element or NULL if the modification time is not to be modified.

Return Values

S_OK
The time values were successfully set.
E_PENDING
Asynchronous Storage only: Part or all of the element's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for changing the element.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The element was not changed due to a lack of memory.
STG_E_INVALIDNAME
Not a valid value for the element name.
STG_E_INVALIDPOINTER
The pointer specified for the element was not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_TOOMANYOPENFILES
The element was not changed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the transaction tree.

Remarks

This method sets time statistics for the specified storage element within this storage object.

Not all file systems support all the time values. This method sets those times that are supported and ignores the rest. Each time-value parameter can be NULL; indicating that no modification should occur.

Call the IStorage::Stat method to retrieve these time values.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::Stat


IStorage::SetStateBits

Stores up to 32 bits of state information in this storage object.

HRESULT SetStateBits(
  DWORD grfStateBits,  //Specifies new values of bits
  DWORD grfMask        //Specifies mask that indicates which
                       // bits are significant
);

Parameters

grfStateBits
[in] Specifies the new values of the bits to set. No legal values are defined for these bits; they are all reserved for future use and must not be used by applications.
grfMask
[in] A binary mask indicating which bits in grfStateBits are significant in this call.

Return Values

S_OK
The state information was successfully set.
E_PENDING
Asynchronous Storage only: Part or all of the storage's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for changing this storage object.
STG_E_INVALIDFLAG
The value for the grfStateBits or grfMask parameter is not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.

Remarks

This method is reserved for future use. The values for the state bits are not currently defined.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, IStorage::Stat


IStorage::Stat

Retrieves the STATSTG structure for this open storage object.

HRESULT Stat(
  STATSTG *pstatstg,  //Location for STATSTG structure
  DWORD grfStatFlag   //Values taken from the STATFLAG enumeration
);

Parameters

pstatstg
[out] On return, pointer to a STATSTG structure where this method places information about the open storage object. This parameter is NULL if an error occurs.
grfStatFlag
[in] Specifies that some of the members in the STATSTG structure are not returned, thus saving a memory allocation operation. Values are taken from the STATFLAG enumeration.

Return Values

S_OK
The STATSTG structure was successfully returned at the specified location.
E_PENDING
Asynchronous Storage only: Part or all of the storage's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for accessing statistics for this storage object.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.

Remarks

IStorage::Stat retrieves the STATSTG structure for the current storage object. The STATSTG structure contains statistical information about the storage object. IStorage::EnumElements returns a pointer to an enumerator object. The enumerator object returned by this method implements the IEnumSTATSTG interface, through which the data stored in the array of the STATSTG structures is enumerated.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage - Compound File Implementation, STATFLAG, STATSTG, IEnumSTATSTG, IStorage::SetClass, IStorage::SetElementTimes, IStorage::SetStateBits


IStorage-Compound File Implementation

The compound file implementation of IStorage allows you to create and manage substorages and streams within a storage object residing in a compound file object. To create a compound file object and get a \ pointer, call the API function StgCreateStorageEx. To open an existing compound file object and get its root IStorage pointer, call StgOpenStorageEx.

When to Use

Most applications use this implementation to create and manage storages and streams.

Methods

IStorage::CreateStream
Creates and opens a stream object with the specified name contained in this storage object. The name must not exceed 31 characters in length (not including the string terminator). The COM-provided compound file implementation of the IStorage::CreateStream method does not support the following behaviors:
  • The STGM_DELETEONRELEASE flag is not supported.
  • Transacted mode (STGM_TRANSACTED) is not supported for stream objects.
  • Opening the same stream more than once from the same storage is not supported. The STGM_SHARE_EXCLUSIVE sharing-mode flag must be specified in the grfMode parameter.
IStorage::OpenStream
Opens an existing stream object within this storage object using the access modes specified in the grfMode parameter. The COM-provided compound file implementation of the IStorage::OpenStream method does not support the following behavior:
  • The STGM_DELETEONRELEASE flag.
  • Transacted mode (STGM_TRANSACTED) for stream objects.
  • Opening the same stream more than once from the same storage. The STGM_SHARE_EXCLUSIVE flag must be specified.
IStorage::CreateStorage
Creates and opens a new storage object with the specified name in the specified access mode. The name must not exceed 31 characters in length (not including the string terminator). The COM-provided compound file implementation of the IStorage::CreateStorage method does not support the following behavior:
  • The STGM_PRIORITY flag for nonroot storages.
  • Opening the same storage object more than once from the same parent storage. The STGM_SHARE_EXCLUSIVE flag must be specified.
  • The STGM_DELETEONRELEASE flag. If this flag is specified, the function returns STG_E_INVALIDFLAG.
IStorage::OpenStorage
Opens an existing storage object with the specified name in the specified access mode. The COM-provided compound file implementation of the IStorage::OpenStorage method does not support the following behavior:
  • The STGM_PRIORITY flag for nonroot storages.
  • Opening the same storage object more than once from the same parent storage. The STGM_SHARE_EXCLUSIVE flag must be specified.
  • The STGM_DELETEONRELEASE flag. If this flag is specified, the function returns STG_E_INVALIDFUNCTION.
IStorage::CopyTo
Copies only the substorages and streams of this open storage object into another storage object. The rgiidExclude parameter can be set to IID_IStream to copy only substorages, or to IID_IStorage to copy only streams.
IStorage::MoveElementTo
Copies or moves a substorage or stream from this storage object to another storage object.
IStorage::Commit
Ensures that any changes made to a storage object open in transacted mode are reflected in the parent storage; for a root storage, reflects the changes in the actual device; for example, a file on disk. For a root storage object opened in direct mode, this method has no effect except to flush all memory buffers to the disk. For nonroot storage objects in direct mode, this method has no effect.

The COM-provided compound files implementation uses a two-phase commit process unless STGC_OVERWRITE is specified in the grfCommitFlags parameter. This two-phase process ensures the robustness of data, in case the commit operation fails. First, all new data is written to unused space in the underlying file. If necessary, new space is allocated to the file. After this step has been completed, a table in the file is updated using a single-sector write operation to indicate that the new data is to be used in place of the old. The old data becomes free space to be used at the next commit operation. Thus, the old data is available and can be restored if an error occurs when committing changes. If STGC_OVERWRITE is specified, a single phase commit operation is used. See the section on STGM flags for more information about transacted mode.

IStorage::Revert
Discards all changes that have been made to the storage object since the last commit operation.
IStorage::EnumElements
Creates and retrieves a pointer to an enumerator object that can be used to enumerate the storage and stream objects contained within this storage object. The COM-provided compound file implementation takes a snapshot of that information. Therefore, changes to the streams and storages are not reflected in the enumerator until a new enumerator is obtained.
IStorage::DestroyElement
Removes the specified element (substorage or stream) from this storage object.
IStorage::RenameElement
Renames the specified substorage or stream in this storage object.
IStorage::SetElementTimes
Sets the modification, access, and creation times of the specified storage element. The COM-provided compound-file implementation maintains modification and change times for internal storage objects. Root storage objects support whatever is supported by the underlying file system (or by ILockBytes)t. The compound file implementation does not maintain any time stamps for internal streams. Unsupported time stamps are reported as zero, which allows the caller to test for support.
IStorage::SetClass
Assigns the specified CLSID to this storage object.
IStorage::SetStateBits
Stores up to 32 bits of state information in this storage object. The state set by this method is for external use only. The COM-provided compound file implementation does not perform any action based on the state.
IStorage::Stat
Retrieves the STATSTG structure for this open storage object.

Remarks

If the storage object is opened in simple mode, use of the above methods is restricted. A storage is in simple mode if it is opened with the STGM_SIMPLE element specified in the grfMode parameter of the StgCreateStorageEx or StgOpenStorageEx function. See the STGM enumeration for more information on simple-mode storages. If the simple-mode storage object was obtained from the StgCreateStorageEx function, then the CreateStream method can be called but the OpenStream method cannot. If the simple mode storage object was obtained from the StgOpenStorageEx function, then the OpenStream method can be called but the CreateStream method cannot.

When a simple-mode storage object is used to create a stream, the minimum size of that stream typically is 4096 bytes. If less data is written to the stream, the size is rounded up to 4096 bytes.

See Also

IStorage, IStream, StgCreateDocfile, StgCreateStorageEx, StgOpenStorage, StgOpenStorageEx, IFillLockBytes, ILockBytes, IRootStorage, Compound File Implementation Limits


IStream

The IStream interface lets you read and write data to stream objects. Stream objects contain the data in a structured storage object, where storages provide the structure. Simple data can be written directly to a stream but, most frequently, streams are elements nested within a storage object. They are similar to standard files.

The IStream interface defines methods similar to the MS-DOS FAT file functions. For example, each stream object has its own access rights and a seek pointer. The main difference between a DOS file and a stream object is that in the latter case, streams are opened using an IStream interface pointer rather than a file handle.

The methods in this interface present your object's data as a contiguous sequence of bytes that you can read or write. There are also methods for committing and reverting changes on streams that are open in transacted mode and methods for restricting access to a range of bytes in the stream.

Streams can remain open for long periods of time without consuming file-system resources. The IStream::Release method is similar to a close function on a file. Once released, the stream object is no longer valid and cannot be used.

Clients of asynchronous monikers can choose between a data-pull or data-push model for driving an asynchronous IMoniker::BindToStorage operation and for receiving asynchronous notifications. See URL Monikers for more information. The table below compares the behavior of asynchronous ISequentialStream::Read and IStream::Seek calls returned in IBindStatusCallback::OnDataAvailable in these two download models:

Istream method call | Behavior in data-pull model | Behavior in data-push model
Read is called to read partial data (that is, not all the available data) | Returns S_OK. The client must continue to read all available data before returning from IBindStatusCallback::OnDataAvailable or else the bind operation is blocked. (that is, read until S_FALSE or E_PENDING is returned) | Returns S_OK. Even if the client returns from IBindStatusCallback::OnDataAvailable at this point the bind operation continues and IBindStatusCallback::OnDataAvailable will be called again repeatedly until the binding finishes.
Read is called to read all the available data | Returns E_PENDING if the bind operation has not completed, and IBindStatusCallback::OnDataAvailable will be called again when more data is available. | Same as data-pull model.
Read is called to read all the available data and the bind operation is over (end of file) | Returns S_FALSE. There will be a subsequent call to IBindStatusCallback::OnDataAvailable with the grfBSC flag set to BSCF_LASTDATANOTIFICATION. | Same as data-pull model.
Seek is called | Seek doesn't work in data-pull model | Seek doesn't work in data-push model.

For general information on this topic, see Asynchronous Monikers and Data-Pull-Model versus Data Push-Model for more specific information. Also, see Managing Memory Allocation for details on COM's rules for managing memory.

When to Implement

Implement IStream on a container or object application when you require functionality not provided by the COM compound file implementation. The specification of IStream defines more functionality that the COM implementation supports. In addition, if you are creating a stream object that is larger than the heap in your machine's memory and you are using a global memory handle, the compound file implementation calls GlobalRealloc internally whenever it needs more memory, which can be very inefficient. In this case, the preferred solution is to implement an IStream that uses memory allocated by VirtualAlloc instead of GlobalAlloc. This can reserve a large chunk of virtual address space and then commit memory within that address space as required. No data copying occurs and memory is committed only as it is needed. For more information, refer to IStream - Compound File Implementation.

The IStream interface inherits Read and Write methods from ISequentialStream.

Note Most containers do not implement ISequentialStream as a separate interface, and you are not required to provide it separately even if you provide an IStream implementation. For example, the compound file implementation of structured storage does not succeed on a QueryInterface method for ISequentialStream but it includes the Read and Write methods through the IStream interface pointer.

When to Use

Call the methods of the IStream interface from a container or application to read and write the data for an object. Since stream objects can be marshaled to other processes, applications can share the data in storage objects without having to use global memory.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
Release | Decrements the reference count.

ISequentialStream Methods | Description
Read | Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.
Write | Writes a specified number of bytes into the stream object starting at the current seek pointer.

IStream Methods | Description
Seek | Changes the seek pointer to a new location relative to the beginning of the stream, the end of the stream, or the current seek pointer.
SetSize | Changes the size of the stream object.
CopyTo | Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream.
Commit | Ensures that any changes made to a stream object open in transacted mode are reflected in the parent storage object.
Revert | Discards all changes that have been made to a transacted stream since the last call to IStream::Commit.
LockRegion | Restricts access to a specified range of bytes in the stream. Supporting this functionality is optional since some file systems do not provide it.
UnlockRegion | Removes the access restriction on a range of bytes previously restricted with IStream::LockRegion.
Stat | Retrieves the STATSTG structure for this stream.
Clone | Creates a new stream object that references the same bytes as the original stream but provides a separate seek pointer to those bytes.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, ISequentialStream


IStream::Clone

Creates a new stream object with its own seek pointer that references the same bytes as the original stream.

HRESULT Clone(
  IStream **ppstm  //Pointer to location for pointer to the new stream object
);

Parameter

ppstm
[out] When successful, pointer to the location of an IStream pointer to the new stream object. If an error occurs, this parameter is NULL.

Return Values

S_OK
The stream was successfully cloned.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INSUFFICIENT_MEMORY
The stream was not cloned due to a lack of memory.
STG_E_INVALIDPOINTER
The ppStm pointer is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

This method creates a new stream object for accessing the same bytes but using a separate seek pointer. The new stream object sees the same data as the source-stream object. Changes written to one object are immediately visible in the other. Range locking is shared between the stream objects.

The initial setting of the seek pointer in the cloned stream instance is the same as the current setting of the seek pointer in the original stream at the time of the clone operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, IStream::CopyTo


IStream::Commit

Ensures that any changes made to a stream object open in transacted mode are reflected in the parent storage. If the stream object is open in direct mode, IStream::Commit has no effect other than flushing all memory buffers to the next-level storage object. The COM compound file implementation of streams does not support opening streams in transacted mode.

HRESULT Commit(
  DWORD grfCommitFlags  //Specifies how changes are committed
);

Parameter

grfCommitFlags
[in] Controls how the changes for the stream object are committed. See the STGC enumeration for a definition of these values.

Return Values

S_OK
Changes to the stream object were successfully committed to the parent level.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_MEDIUMFULL
The commit operation failed due to lack of space on the storage device.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

This method ensures that changes to a stream object opened in transacted mode are reflected in the parent storage. Changes that have been made to the stream since it was opened or last committed are reflected to the parent storage object. If the parent is opened in transacted mode, the parent may revert at a later time,, rolling back the changes to this stream object. The compound file implementation does not support the opening of streams in transacted mode, so this method has very little effect other than to flush memory buffers. For more information, see IStream - Compound File Implementation.

If the stream is open in direct mode, this method ensures that any memory buffers have been flushed out to the underlying storage object. This is much like a flush in traditional file systems.

The IStream::Commit method is useful on a direct mode stream when the implementation of the IStream interface is a wrapper for underlying file system APIs. In this case, IStream::Commit would be connected to the file system's flush call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, IStorage::Commit


IStream::CopyTo

Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream.

HRESULT CopyTo(
  IStream *pstm,               //Pointer to the destination stream
  ULARGE_INTEGER cb,           //Specifies the number of bytes to copy
  ULARGE_INTEGER *pcbRead,     //Pointer to the actual number of bytes
                               // read from the source
  ULARGE_INTEGER *pcbWritten   //Pointer to the actual number of
                               // bytes written to the destination
);

Parameters

pstm
[in] Pointer to the destination stream. The stream pointed to by pstm can be a new stream or a clone of the source stream.
cb
[in] Specifies the number of bytes to copy from the source stream.
pcbRead
[out] Pointer to the location where this method writes the actual number of bytes read from the source. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes read.
pcbWritten
[out] Pointer to the location where this method writes the actual number of bytes written to the destination. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes written.

Return Values

S_OK
The stream object was successfully copied.
E_PENDING
Asynchronous Storage only: Part or all of the data to be copied is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INVALIDPOINTER
The value of one of the pointer parameters is not valid.
STG_E_MEDIUMFULL
The stream is not copied because there is no space left on the storage device.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

This method copies the specified bytes from one stream to another. It can also be used to copy a stream to itself. The seek pointer in each stream instance is adjusted for the number of bytes read or written. This method is equivalent to reading cb bytes into memory using ISequentialStream::Read and then immediately writing them to the destination stream using ISequentialStream::Write, although IStream::CopyTo will be more efficient.

The destination stream can be a clone of the source stream created by calling the IStream::Clone method.

If IStream::CopyTo returns an error, you cannot assume that the seek pointers are valid for either the source or destination. Additionally, the values of pcbRead and pcbWritten are not meaningful even though they are returned.

If IStream::CopyTo returns successfully, the actual number of bytes read and written are the same.

To copy the remainder of the source from the current seek pointer, specify the maximum large integer value for the cb parameter. If the seek pointer is the beginning of the stream, this technique copies the entire stream.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, ISequentialStream::Read, ISequentialStream::Write, IStream::Clone


IStream::LockRegion

Restricts access to a specified range of bytes in the stream. Supporting this functionality is optional since some file systems do not provide it.

HRESULT LockRegion(
  ULARGE_INTEGER libOffset,  //Specifies the byte offset for
                             // the beginning of the range
  ULARGE_INTEGER cb,         //Specifies the length of the range in bytes
  DWORD dwLockType           //Specifies the restriction on
                             // accessing the specified range
);

Parameters

libOffset
[in] Integer that specifies the byte offset for the beginning of the range.
cb
[in] Integer that specifies the length of the range, in bytes, to be restricted.
dwLockType
[in] Specifies the restrictions being requested on accessing the range.

Return Values

S_OK
The specified range of bytes was locked.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not supported.
STG_E_LOCKVIOLATION
Requested lock is supported, but cannot be granted because of an existing lock.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

The byte range can extend past the current end of the stream. Locking beyond the end of a stream is useful as a method of communication between different instances of the stream without changing data that is actually part of the stream.

Three types of locking can be supported: locking to exclude other writers, locking to exclude other readers or writers, and locking that allows only one requestor to obtain a lock on the given range, which is usually an alias for one of the other two lock types. A given stream instance might support either of the first two types, or both. The lock type is specified by dwLockType, using a value from the LOCKTYPE enumeration.

Any region locked with IStream::LockRegion must later be explicitly unlocked by calling IStream::UnlockRegion with exactly the same values for the libOffset, cb, and dwLockType parameters. The region must be unlocked before the stream is released. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

Notes to Callers

Since the type of locking supported is optional and can vary in different implementations of IStream, you must provide code to deal with the STG_E_INVALIDFUNCTION error.

This method has no effect in the compound file implementation, because the implementation does not support range locking.

Notes to Implementers

Support for this method is optional for implementations of stream objects since it may not be supported by the underlying file system. The type of locking supported is also optional. The STG_E_INVALIDFUNCTION error is returned if the requested type of locking is not supported.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, LOCKTYPE, IStream::UnlockRegion


IStream::Revert

Discards all changes that have been made to a transacted stream since the last IStream::Commit call. On streams open in direct mode and streams using the COM compound file implementation of IStream::Revert, this method has no effect.

HRESULT Revert(void);

Return Values

S_OK
The stream was successfully reverted to its previous version.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.

Remarks

This method discards changes made to a transacted stream since the last commit operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, IStream::Commit


IStream::Seek

Changes the seek pointer to a new location relative to the beginning of the stream, to the end of the stream, or to the current seek pointer.

HRESULT Seek(
  LARGE_INTEGER dlibMove,          //Offset relative to dwOrigin
  DWORD dwOrigin,                  //Specifies the origin for the offset
  ULARGE_INTEGER *plibNewPosition  //Pointer to location containing
                                   // new seek pointer
);

Parameters

dlibMove
[in] Displacement to be added to the location indicated by the dwOrigin parameter. If dwOrigin is STREAM_SEEK_SET, this is interpreted as an unsigned value rather than a signed value.
dwOrigin
[in] Specifies the origin for the displacement specified in dlibMove. The origin can be the beginning of the file, the current seek pointer, or the end of the file. See the STREAM_SEEK enumeration for the values.
plibNewPosition
[out] Pointer to the location where this method writes the value of the new seek pointer from the beginning of the stream. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the new seek pointer.

Return Values

S_OK
The seek pointer has been successfully adjusted.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INVALIDPOINTER
Indicates that the [out] parameter plibNewPosition points to invalid memory, because plibNewPosition is not read.
STG_E_INVALIDFUNCTION
The dwOrigin parameter contains an invalid value, or the dlibMove parameter contains a bad offset value. For example, the result of the seek pointer is a negative offset value.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

IStream::Seek changes the seek pointer so that subsequent read and write operations can take place at a different location in the stream object. It is an error to seek before the beginning of the stream. It is not, however, an error to seek past the end of the stream. Seeking past the end of the stream is useful for subsequent write operations, as the stream will at that time be extended to the seek position immediately before the write is done.

You can also use this method to obtain the current value of the seek pointer by calling this method with the dwOrigin parameter set to STREAM_SEEK_CUR and the dlibMove parameter set to 0 so that the seek pointer is not changed. The current seek pointer is returned in the plibNewPosition parameter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, STREAM_SEEK, ISequentialStream::Read, ISequentialStream::Write


IStream::SetSize

Changes the size of the stream object.

HRESULT SetSize(
  ULARGE_INTEGER libNewSize  //Specifies the new size of the stream object
);

Parameter

libNewSize
[in] Specifies the new size of the stream as a number of bytes.

Return Values

S_OK
The size of the stream object was successfully changed.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_MEDIUMFULL
The stream size is not changed because there is no space left on the storage device.
STG_E_INVALIDFUNCTION
The value of the libNewSize parameter is not valid. Since streams cannot be greater than 232 bytes in the COM-provided implementation, the high DWORD data type of libNewSize must be 0. If it is nonzero, this parameter is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

IStream::SetSize changes the size of the stream object. Call this method to preallocate space for the stream. If the libNewSize parameter is larger than the current stream size, the stream is extended to the indicated size by filling the intervening space with bytes of undefined value. This operation is similar to the ISequentialStream::Write method if the seek pointer is past the current end of stream.

If the libNewSize parameter is smaller than the current stream, the stream is truncated to the indicated size.

The seek pointer is not affected by the change in stream size.

Calling IStream::SetSize can be an effective way to obtain a large chunk of contiguous space.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, ISequentialStream::Write


IStream::Stat

Retrieves the STATSTG structure for this stream.

HRESULT Stat(
  STATSTG *pstatstg,  //Location for STATSTG structure
  DWORD grfStatFlag   //Values taken from the STATFLAG enumeration
);

Parameters

pstatstg
[out] Pointer to a STATSTG structure where this method places information about this stream object. This pointer is NULL if an error occurs.
grfStatFlag
[in] Specifies that this method does not return some of the members in the STATSTG structure, thus saving a memory allocation operation. Values are taken from the STATFLAG enumeration.

Return Value

S_OK
The STATSTG structure was successfully returned at the specified location.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have enough permissions for accessing statistics for this storage object.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPOINTER
The pStatStg pointer is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

IStream::Stat retrieves a pointer to the STATSTG structure that contains information about this open stream. When this stream is within a structured storage and IStorage::EnumElements is called, it creates an enumerator object with the IEnumSTATSTG interface on it, which can be called to enumerate the storages and streams through the STATSTG structures associated with each of them.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, STATFLAG, STATSTG


IStream::UnlockRegion

Removes the access restriction on a range of bytes previously restricted with IStream::LockRegion.

HRESULT UnlockRegion(
  ULARGE_INTEGER libOffset,  //Specifies the byte offset for
                             // the beginning of the range
  ULARGE_INTEGER cb,         //Specifies the length of the range in bytes
  DWORD dwLockType           //Specifies the access restriction
                             // previously placed on the range
);

Parameters

libOffset
[in] Specifies the byte offset for the beginning of the range.
cb
[in] Specifies, in bytes, the length of the range to be restricted.
dwLockType
[in] Specifies the access restrictions previously placed on the range.

Return Values

S_OK
The byte range was unlocked.
E_PENDING
Asynchronous Storage only: Part or all of the stream's data is currently unavailable. For more information see IFillLockBytes and Asynchronous Storage.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not supported.
STG_E_LOCKVIOLATION
The requested unlock operation cannot be granted.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transaction tree.

Remarks

IStream::UnlockRegion unlocks a region previously locked with the IStream::LockRegion method. Locked regions must later be explicitly unlocked by calling IStream::UnlockRegion with exactly the same values for the libOffset, cb, and dwLockType parameters. The region must be unlocked before the stream is released. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream - Compound File Implementation, LOCKTYPE, IStream::LockRegion


IStream - Compound File Implementation

The IStream interface supports reading and writing data to stream objects. Stream objects contain the data in a structured storage object, where storages provide the structure. Simple data can be written directly to a stream, but most frequently, streams are elements nested within a storage object. They are similar to standard files.

The specification of IStream defines more functionality that the COM implementation supports. For example, the IStream interface defines streams up to 264 bytes in length requiring a 64-bit seek pointer. However, the COM implementation only supports streams up to 232 bytes in length (4 gigabytes) and read and write operations are always limited to 232 bytes at a time. The COM implementation also does not support stream transactioning or region locking.

When you want to create a simple stream based on global memory, you can get an IStream pointer by calling the API function CreateStreamOnHGlobal. To get an IStream pointer within a compound file object, call either StgCreateDocfile or StgOpenStorage. These functions retrieve an IStorage pointer, with which you can then call CreateStream /OpenStream for an IStream pointer. In either case, the same IStream implementation code is used.

Note The compound file implementation of structured storage does not succeed on a QueryInterface method for ISequentialStream but it includes the Read and Write methods through the IStream interface pointer.

When to Use

Call the methods of IStream to read and write data to a stream.

Since stream objects can be marshaled to other processes, applications can share the data in storage objects without having to use global memory. In the COM compound file implementation of stream objects, the custom marshaling facilities in COM create a remote version of the original object in the new process when the two processes have shared-memory access. Thus, the remote version does not need to communicate with the original process to carry out its functions.

The remote version of the stream object shares the same seek pointer as the original stream. If you do not want to share the seek pointer, use the IStream::Clone method to provide a copy of the stream object for the remote process.

Note If you are creating a stream object that is larger than the heap in your machine's memory and you are using an HGLOBAL handle to a global memory object, the stream object calls the GlobalRealloc method internally whenever it needs more memory. Because GlobalRealloc always copies data from the source to the destination, increasing a stream object from 20 megabytes (MB) to 25 MB, for example, consumes immense amounts of time. This is due to the size of the increments copied and is worsened if there is less than 45 MB of memory on the machine because of disk swapping.

The preferred solution is to implement an IStream method that uses memory allocated by VirtualAlloc instead of GlobalAlloc. This can reserve a large chunk of virtual address space and then commit memory within that address space as required. No data copying occurs and memory is committed only as it is needed.

Another alternative to GlobalRealloc is to call the IStream::SetSize method on the stream object to increase the memory allocation in advance. This is not, however, as efficient as using VirtualAlloc, as described above.

Methods

ISequentialStream::Read
Reads a specified number of bytes from the stream object into memory starting at the current seek pointer. This implementation returns S_OK if the end of the stream was reached during the read. (This is the same as the "end of file" behavior found in the MS-DOS FAT file system.)
ISequentialStream::Write
Writes a specified number from bytes into the stream object starting at the current seek pointer. In this implementation, stream objects are not sparse. Any fill bytes are eventually allocated on the disk and assigned to the stream.
IStream::Seek
Changes the seek pointer to a new location relative to the beginning of the stream, to the end of the stream, or to the current seek pointer.
IStream::SetSize
Changes the size of the stream object. In this implementation, there is no guarantee that the space allocated will be contiguous.
IStream::CopyTo
Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream.
IStream::Commit
The compound file implementation of IStream supports opening streams only in direct mode, not transacted mode. Therefore, the method has no effect when called other than to flush all memory buffers to the next storage level.

In this implementation, it does not matter if you commit changes to streams, you need only commit changes for storage objects.

IStream::Revert
This implementation does not support transacted streams, so a call to this method has no effect.
IStream::LockRegion
Range-locking is not supported by this implementation, so a call to this method has no effect.
IStream::UnlockRegion
Removes the access restriction on a range of bytes previously restricted with IStream::LockRegion.
IStream::Stat
Retrieves the STATSTG structure for this stream
IStream::Clone
Creates a new stream object with its own seek pointer that references the same bytes as the original stream.

A simple-mode IStream is subject to the following constraints.

  • A stream is simple mode if it was created or opened from a simple-mode storage. A storage is simple mode if it is created or opened with the STGM_SIMPLE flag set in the grfMode parameter.
  • The Clone and CopyTo methods are not supported.
  • The Stat method is supported, but the STATFLAG_NONAME value must be specified.

See Also

IStream, IStorage, CreateStreamOnHGlobal, StgCreateDocfile, StgOpenStorage


Functions

Functions are routines or subroutines that return a specific value or values. Structured Storage functions are described in the following sections:

  • CreateILockBytesOnHGlobal
  • CreateStreamOnHGlobal
  • FmtIdToPropStgName
  • FreePropVariantArray
  • GetConvertStg
  • GetHGlobalFromILockBytes
  • GetHGlobalFromStream
  • OleConvertIStorageToOLESTREAM
  • OleConvertIStorageToOLESTREAMEx
  • OleConvertOLESTREAMToIStorage
  • OleConvertOLESTREAMToIStorageEx
  • PropStgNameToFmtId
  • PropVariantClear
  • PropVariantCopy
  • PropVariantInit
  • ReadClassStg
  • ReadClassStm
  • ReadFmtUserTypeStg
  • SetConvertStg
  • StgCreateDocfile
  • StgCreateDocfileOnILockBytes
  • StgCreatePropSetStg
  • StgCreatePropStg
  • StgCreateStorageEx
  • StgGetIFillLockBytesOnFile
  • StgGetIFillLockBytesOnILockBytes
  • StgIsStorageFile
  • StgIsStorageILockBytes
  • StgOpenAsyncDocfileOnIFillLockBytes
  • StgOpenLayoutDocfile
  • StgOpenPropStg
  • StgOpenStorage
  • StgOpenStorageEx
  • StgOpenStorageOnILockBytes
  • StgSetTimes
  • WriteClassStg
  • WriteClassStm
  • WriteFmtUserTypeStg


CreateILockBytesOnHGlobal

Creates a byte array object that allows you to use global memory as the physical device underneath a compound file implementation. This object supports a COM implementation of the ILockBytes interface.

WINOLEAPI CreateILockBytesOnHGlobal(
  HGLOBAL hGlobal,        //Memory handle for the byte array
                          // object
  BOOL fDeleteOnRelease,  //Whether to free memory when the
                          // object is released
  ILockBytes **ppLkbyt    //Address of output variable that
                          // receives the ILockBytes
                          // interface pointer
);

Parameters

hGlobal
[in] Memory handle allocated by the GlobalAlloc function. The handle must be allocated as moveable and nondiscardable. If the handle is to be shared between processes, it must also be allocated as shared. New handles should be allocated with a size of zero. If hGlobal is NULL, CreateILockBytesOnHGlobal internally allocates a new shared memory block of size zero.
fDeleteOnRelease
[in] Determines whether the underlying handle for this byte array object should be automatically freed when the object is released.
ppLkbyt
[out] Address of ILockBytes pointer variable that receives the interface pointer to the new byte array object.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The byte array object was created successfully.

Remarks

The CreateILockBytesOnHGlobal function creates a byte array object based on global memory. This object supports a COM implementation of the ILockBytes interface, and is intended as the basis for a compound file. You can then use the supplied ILockBytes pointer in a call to the StgCreateDocfileOnILockBytes function to build a compound file on top of this byte array object. The ILockBytes instance calls the GlobalReAlloc function to grow the memory block as needed.

The current contents of the memory block are undisturbed by the creation of the new byte array object. After creating the ILockBytes instance, you can use the StgOpenStorageOnILockBytes function to reopen a previously existing storage object that is already contained in the memory block. You can also call GetHGlobalFromILockBytes to get the global memory handle associated with the byte array object created by CreateILockBytesOnHGlobal.

Note If you free the hGlobal memory handle, the byte array object is no longer valid. You must call the ILockBytes::Release method before freeing the memory handle.

The value of the hGlobal parameter can be changed by a subsequent call to the GlobalReAlloc function; thus, you cannot rely on this value after the byte array object is created.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

StgOpenStorageOnILockBytes, GetHGlobalFromILockBytes, ILockBytes


CreateStreamOnHGlobal

Creates a stream object stored in global memory.

WINOLEAPI CreateStreamOnHGlobal(
  HGLOBAL hGlobal,         //Memory handle for the stream object
  BOOL fDeleteOnRelease,   //Whether to free memory when the
                           // object is released
  LPSTREAM *ppstm          //Address of output variable that
                           // receives the IStream interface pointer
);

Parameters

hGlobal
[in] Memory handle allocated by the GlobalAlloc function. The handle must be allocated as movable and nondiscardable. If the handle is to be shared between processes, it must also be allocated as shared. New handles should be allocated with a size of zero. If hGlobal is NULL, the CreateStreamOnHGlobal function internally allocates a new shared memory block of size zero.
fDeleteOnRelease
[in] Whether the underlying handle for this stream object should be automatically freed when the stream object is released.
ppstm
[out] Address of IStream* pointer variable that receives the interface pointer to the new stream object. Its value cannot be NULL.

Return Values

This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY, as well as the following:

S_OK
The stream object was created successfully.

Remarks

The CreateStreamOnHGlobal function creates a stream object in memory that supports the OLE implementation of the IStream interface. The returned stream object supports both reading and writing, is not transacted, and does not support locking.

The initial contents of the stream are the current contents of the memory block provided in the hGlobal parameter. If the hGlobal parameter is NULL, this function internally allocates memory.

The current contents of the memory block are undisturbed by the creation of the new stream object. Thus, you can use this function to open an existing stream in memory.

The initial size of the stream is the size of the memory handle returned by the Microsoft Win32® GlobalSize function. Because of rounding, this is not necessarily the same size that was originally allocated for the handle. If the logical size of the stream is important, you should follow the call to this function with a call to the IStream::SetSize method.

After you have created the stream object with CreateStreamOnHGlobal, you can call GetHGlobalFromStream to get the global memory handle associated with the stream object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

GetHGlobalFromStream, IStream::SetSize, GlobalSize


FmtIdToPropStgName

Converts a property set's format identifier (FMTID) to its storage or stream name.

HRESULT FmtIdToPropStgName(
  const FMTID *pfmtid,  //Pointer to the format identifier
                        // of the property set
  LPOLESTR oszName      //Pointer to a string receiving the
                        // property set name
);

Parameters

pfmtid
[in] Pointer to the FMTID of the property set.
oszName
[out] Pointer to a string that receives the storage or stream name of the property set identified by pfmtid. The array allocated for this string must be at least CCH_MAX_PROPSTG_NAME (32) characters in length.

Return Values

This function supports the standard return value E_INVALIDARG as well as the following:

S_OK
The storage or stream name of the property set was successfully returned.

Remarks

FmtIdToPropStgName maps a property set's FMTID to its stream name for a simple property set or to its storage name for a nonsimple property set.

This function is useful in creating or opening a property set using the PROPSETFLAG_UNBUFFERED value with the StgCreatePropStg and StgOpenPropStg functions. See the PROPSETFLAG enumeration for more information on the use of PROPSETFLAG_UNBUFFERED.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

PROPSETFLAG, PropStgNameToFmtId, StgCreatePropStg, StgOpenPropStg


FreePropVariantArray

Calls PropVariantClear on each of the PROPVARIANT structures in the rgvar array to make the value zero for each of the members of the array.

WINOLEAPI FreePropVariantArray(
  ULONG cVariants,     //Count of elements in the structure
  PROPVARIANT *rgvars  //Pointer to the PROPVARIANT structure
);

Parameters

cVariants
[in] Count of elements in the PROPVARIANT array (rgvar).
rgvars
[in] Pointer to an initialized array of PROPVARIANT structures for which any deallocatable elements are to be freed. On exit, all zeroes are written to the PROPVARIANT structure (thus tagging them as VT_EMPTY).

Return Values

S_OK
The variant types are recognized and all items that can be freed have been freed.
STG_E_INVALID_PARAMETER
One or more PROPVARIANT structures has an unknown type.

Remarks

FreePropVariantArray calls PropVariantClear on an array of PROPVARIANT structures to clear all the valid members. All valid PROPVARIANT structures are freed. If any of the PROPVARIANT structures contain illegal VT types, valid members are freed and the function returns STG_E_INVALIDPARAMETER.

Passing NULL for rgvars is legal, and produces a return code of S_OK.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

PropVariantClear


GetConvertStg

Returns the current value of the convert bit for the specified storage object.

WINOLEAPI GetConvertStg(
  IStorage *pStg   //Pointer to the IStorage interface on the
                   // storage object
);

Parameter

pStg
[in] IStorage pointer to the storage object from which the convert bit is to be retrieved.

Return Values

S_OK
Indicates that the convert bit is set to TRUE.
S_FALSE
COM the convert bit is cleared (FALSE).
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.

IStorage::OpenStream, IStorage::OpenStorage, and ISequentialStream::Read storage and stream access errors.

Remarks

The GetConvertStg function is called by object servers that support the conversion of an object from one format to another. The server must be able to read the storage object using the format of its previous class identifier (CLSID) and write the object using the format of its new CLSID to support the object's conversion. For example, a spreadsheet created by one application can be converted to the format used by a different application.

The convert bit is set by a call to the SetConvertStg function. A container application can call this function on the request of an end user, or a setup program can call it when installing a new version of an application. An end user requests converting an object through the Convert To dialog box. When an object is converted, the new CLSID is permanently assigned to the object, so the object is subsequently associated with the new CLSID.

Then, when the object is activated, its server calls the GetConvertStg function to retrieve the value of the convert bit from the storage object. If the bit is set, the object's CLSID has been changed, and the server must read the old format and write the new format for the storage object.

After retrieving the bit value, the object application should clear the convert bit by calling the SetConvertStg function with its fConvert parameter set to FALSE.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

SetConvertStg


GetHGlobalFromILockBytes

Retrieves a global memory handle to a byte array object created using the CreateILockBytesOnHGlobal function.

WINOLEAPI GetHGlobalFromILockBytes(
  ILockBytes *pLkbyt,   //Pointer to the byte array object
  HGLOBAL *phglobal     //Pointer to the current memory
                        // handle for the specified byte array
);

Parameters

pLkbyt
[in] Pointer to the ILockBytes interface on the byte-array object previously created by a call to the CreateILockBytesOnHGlobal function.
phglobal
[out] Pointer to the current memory handle used by the specified byte-array object.

Return Values

S_OK
Indicates that the handle was returned successfully.
E_INVALIDARG
Indicates value specified for the pLkbyt parameter is not valid. It can also indicate that the byte array object passed in is not one created by the CreateILockBytesOnHGlobal function.

Remarks

After a call to CreateILockBytesOnHGlobal, which creates a byte array object on global memory, GetHGlobalFromILockBytes retrieves a pointer to the handle of the global memory underlying the byte array object. The handle this function returns might be different from the original handle due to intervening calls to the GlobalRealloc function.

The contents of the returned memory handle can be written to a clean disk file, and then opened as a storage object using the StgOpenStorage function.

This function only works within the same process from which the byte array was created.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

StgOpenStorage, CreateILockBytesOnHGlobal


GetHGlobalFromStream

Retrieves the global memory handle to a stream that was created through a call to the CreateStreamOnHGlobal function.

WINOLEAPI GetHGlobalFromStream(
  IStream *pstm,      //Pointer to the stream object
  HGLOBAL *phglobal   //Pointer to the current memory handle
                      // for the specified stream
);

Parameters

pstm
[in] IStream pointer to the stream object previously created by a call to the CreateStreamOnHGlobal function.
phglobal
[out] Pointer to the current memory handle used by the specified stream object.

Return Values

S_OK
Indicates that the handle was successfully returned.
E_INVALIDARG
Indicates invalid value specified for the pstm parameter. It can also indicate that the stream object passed in is not one created by a call to the CreateStreamOnHGlobal function.

Remarks

The handle this function returns may be different from the original handle due to intervening GlobalRealloc calls.

This function can be called only from within the same process from which the byte array was created.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CreateStreamOnHGlobal, GlobalRealloc in Win32


OleConvertIStorageToOLESTREAM

Converts the specified storage object from OLE 2 structured storage to the OLE 1 storage object model but does not include the presentation data. This is one of several compatibility functions.

WINOLEAPI OleConvertIStorageToOLESTREAM(
  IStorage *pStg,          //Pointer to the OLE 2 storage
                           // object to be converted
  LPOLESTREAM lpolestream  //Pointer to the stream where the
                           // OLE1 storage is written
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object to be converted to an OLE 1 storage.
lpolestream
[out] Pointer to an OLE 1 stream structure where the persistent representation of the object is saved using the OLE 1 storage model.

Return Values

This function supports the standard return value E_INVALIDARG, in addition to the following:

S_OK
The storage object was successfully converted and the OLESTREAM structure contains the persistent representation of an OLE 1 object.
CONVERT10_E_STG_NO_STD_STREAM
Object cannot be converted because its storage is missing a stream.
CONVERT10_S_NO_PRESENTATION
The specified storage object contains a Paintbrush object in DIB format and there is no presentation data in the OLESTREAM structure.

Remarks

This function converts an OLE 2 storage object to OLE 1 format. The OLESTREAM structure code implemented for OLE 1 must be available.

On entry, the stream to which lpolestm points should be created and positioned just as it would be for an OleSaveToStream call. On exit, the stream contains the persistent representation of the object using OLE 1 storage.

Note Paintbrush objects are dealt with differently from other objects because their native data is in device-independent bitmap (DIB) format. When Paintbrush objects are converted using OleConvertIStorageToOLESTREAM, no presentation data is added to the OLESTREAM structure. To include presentation data, use the OleConvertIStorageToOLESTREAMEx function instead.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CoIsOle1Class, OleConvertIStorageToOLESTREAMEx, OleConvertOLESTREAMToIStorage, OleConvertOLESTREAMToIStorageEx


OleConvertIStorageToOLESTREAMEx

Converts the specified storage object from OLE 2 structured storage to the OLE 1 storage object model, including the presentation data. This is one of several functions included in Structured Storage to ensure compatibility between OLE1 and OLE2.

WINOLEAPI OleConvertIStorageToOLESTREAMEx(
  IStorage *pStg,       //Pointer to the OLE 2 storage
                        // object to be converted
  CLIPFORMAT cfFormat,  //Presentation data format
  LONG lWidth,          //Width in HIMETRIC
  LONG lHeight,         //Height in HIMETRIC
  DWORD dwSize,         //Size of data
  STGMEDIUM pmedium,    //Pointer to data
  LPOLESTREAM lpolestm  //Pointer to the stream where the
                        // OLE1 storage is written
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object to be converted to an OLE 1 storage.
cfFormat
[in] Format of the presentation data. May be NULL, in which case the lWidth, lHeight, dwSize, and pmedium parameters are ignored.
lWidth
[in] Width of the object presentation data in HIMETRIC units.
lHeight
[in] Height of the object presentation data in HIMETRIC units.
dwSize
[in] Size of the data, in bytes, to be converted.
pmedium
[in] Pointer to the STGMEDIUM structure for the serialized data to be converted.
lpolestm
[out] Pointer to a stream where the persistent representation of the object is saved using the OLE 1 storage model.

Return Values

This function supports the standard return value E_INVALIDARG, in addition to the following:

S_OK
The conversion was completed successfully.
DV_E_STGMEDIUM
The hGlobal member of STGMEDIUM is NULL.
DV_E_TYMED
The tymed member of the STGMEDIUM structure is not TYMED_HGLOBAL or TYMED_ISTREAM.

Remarks

The OleConvertIStorageToOLESTREAMEx function converts an OLE 2 storage object to OLE 1 format. It differs from the OleConvertIStorageToOLESTREAM function in that the OleConvertIStorageToOLESTREAMEx function also passes the presentation data to the OLE 1 storage object, whereas the OleConvertIStorageToOLESTREAM function does not.

Because OleConvertIStorageToOLESTREAMEx can specify which presentation data to convert, it can be used by applications that do not use OLE default caching resources but do use OLE's conversion resources.

The value of the tymed member of STGMEDIUM must be either TYMED_HGLOBAL or TYMED_ISTREAM; refer to the TYMED enumeration for more information. The medium is not released by the OleConvertIStorageToOLESTREAMEx function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CoIsOle1Class, OleConvertIStorageToOLESTREAM, OleConvertOLESTREAMToIStorage, OleConvertOLESTREAMToIStorageEx, STGMEDIUM, TYMED


OleConvertOLESTREAMToIStorage

Converts the specified object from the OLE 1 storage model to an OLE 2 structured storage object without specifying presentation data. This is one of several compatibility functions.

WINOLEAPI OleConvertOLESTREAMToIStorage(
  LPOLESTREAM lpolestream,    //Pointer to the stream where
                              // the OLE 1 storage is written
  IStorage *pstg,             //Pointer to OLE 2 storage object
  const DVTARGETDEVICE *ptd   //Pointer to target device
);

Parameters

lpolestream
[in] Pointer to a stream that contains the persistent representation of the object in the OLE 1 storage format.
pstg
[out] Pointer to the IStorage interface on the OLE 2 structured storage object.
ptd
[in] Pointer to the DVTARGETDEVICE structure specifying the target device for which the OLE 1 object is rendered.

Return Values

This function supports the standard return value E_INVALIDARG, in addition to the following:

S_OK
The object was successfully converted.
CONVERT10_S_NO_PRESENTATION
The object either has no presentation data or uses native data for its presentation.
DV_E_DVTARGETDEVICE or DV_E_DVTARGETDEVICE_SIZE
Invalid value for ptd.

Remarks

This function converts an OLE 1 object to an OLE 2 structured storage object. You can use this function to update OLE 1 objects to OLE 2 objects when a new version of the object application supports OLE 2.

On entry, the lpolestm parameter should be created and positioned just as it would be for an OleLoadFromStream function call. On exit, the lpolestm parameter is positioned just as it would be on exit from an OleLoadFromStream function, and the pstg parameter contains the uncommitted persistent representation of the OLE 2 storage object.

For OLE 1 objects that use native data for their presentation, the OleConvertOLESTREAMToIStorage function returns CONVERT10_S_NO_PRESENTATION. On receiving this return value, callers should call IOleObject::Update to get the presentation data so it can be written to storage.

Applications that do not use OLE's default caching resources, but use the conversion resources, can use an alternate function, OleConvertOLESTREAMToIStorageEx, which can specify the presentation data to convert. In the OleConvertOLESTREAMToIStorageEx function, the presentation data read from the OLESTREAM structure is passed out and the newly created OLE 2 storage object does not contain a presentation stream.

The following steps describe the conversion process using OleConvertOLESTREAMToIStorage:

  1. Create a root IStorage object by calling the StgCreateDocfile function (..., &pstg).
  2. Open the OLE 1 file (using OpenFile or another OLE 1 technique).
  3. Using the OLE 1 procedure for reading files, read from the file until an OLE object is encountered.
  4. Allocate an IStorage object from the root IStorage created in step 1:
    pstg->lpVtbl->CreateStorage(...&pStgChild);
hRes = OleConvertIStorageToOLESTREAM(polestm, pStgChild);
hRes = OleLoad(pStgChild, &IID_IOleObject, pClientSite, ppvObj);
  5. Repeat step 3 until the file is completely read.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CoIsOle1Class, OleConvertIStorageToOLESTREAM, OleConvertIStorageToOLESTREAMEx, OleConvertOLESTREAMToIStorageEx, DVTARGETDEVICE, STGMEDIUM, TYMED


OleConvertOLESTREAMToIStorageEx

Converts the specified object from the OLE 1 storage model to an OLE 2 structured storage object including presentation data. This is one of several compatibility functions.

WINOLEAPI OleConvertOLESTREAMToIStorageEx(
  LPOLESTREAM lpolestm,    //Pointer to the stream where the
                           // OLE1 storage is written
  IStorage *pstg,          //Pointer to OLE 2 storage object
  CLIPFORMAT *pcfFormat,   //Pointer to presentation data
  LONG *plWidth,           //Pointer to width value
  LONG *plHeight,          //Pointer to height value
  DWORD *pdwSize,          //Pointer to size
  STGMEDIUM pmedium        //Pointer to the structure
);

Parameters

lpolestm
[in] Pointer to the stream that contains the persistent representation of the object in the OLE 1 storage format.
pstg
[out] Pointer to the OLE 2 structured storage object.
pcfFormat
[out] Pointer to where the format of the presentation data is returned. May be NULL, indicating the absence of presentation data.
plWidth
[out] Pointer to where the width value (in HIMETRIC) of the presentation data is returned.
plHeight
[out] Pointer to where the height value (in HIMETRIC) of the presentation data is returned.
pdwSize
[out] Pointer to where the size in bytes of the converted data is returned.
pmedium
[out] Pointer to where the STGMEDIUM structure for the converted serialized data is returned.

Return Values

S_OK
The conversion was completed successfully.
DV_E_TYMED|
Value of the tymed member of STGMEDIUM is not TYMED_ISTREAM or TYMED_NULL.

Remarks

This function converts an OLE 1 object to an OLE 2 structured storage object. You can use this function to update OLE 1 objects to OLE 2 objects when a new version of the object application supports OLE 2.

This function differs from the OleConvertOLESTREAMToIStorage function in that the presentation data read from the OLESTREAM structure is passed out and the newly created OLE 2 storage object does not contain a presentation stream.

Since this function can specify which presentation data to convert, it can be used by applications that do not use OLE's default caching resources but do use the conversion resources.

The tymed member of STGMEDIUM can only be TYMED_NULL or TYMED_ISTREAM. If it is TYMED_NULL, the data will be returned in a global handle through the hGlobal member of STGMEDIUM, otherwise data will be written into the pstm member of this structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CoIsOle1Class, OleConvertIStorageToOLESTREAM, OleConvertIStorageToOLESTREAMEx, OleConvertOLESTREAMToIStorage, STGMEDIUM, TYMED


PropStgNameToFmtId

Converts a property set's storage or stream name to its format identifier.

HRESULT PropStgNameToFmtId(
  const LPOLESTR oszName,  //Pointer to a string containing
                           // the property set name
  FMTID *pfmtid            //Pointer to the format
                           // identifier of the property set
);

Parameters

oszName
[in] Pointer to a string containing the stream name of a simple property set or the storage name of a nonsimple property set.
pfmtid
[out] Pointer to a FMTID variable that receives the format identifier of the property set specified by oszName.

Return Values

This function supports the standard return value E_INVALIDARG as well as the following:

S_OK
The format identifier of the property set was successfully returned.
STG_E_INVALIDNAME
The oszName parameter was invalid.

Remarks

The ProgStgNameToFmtId function maps the stream name of a simple property set or the storage name of a nonsimple property set to its format identifier.

This function is useful in creating or opening a property set using the PROPSETFLAG_UNBUFFERED value with the StgCreatePropStg and StgOpenPropStg functions. See the PROPSETFLAG enumeration for more information on the use of PROPSETFLAG_UNBUFFERED.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

FmtIdToPropStgName, PROPSETFLAG, StgCreatePropStg, StgOpenPropStg


PropVariantClear

Frees all elements that can be freed in a given PROPVARIANT structure.

WINOLEAPI PropVariantClear(
  PROPVARIANT *pvar  //Pointer to a PROPVARIANT structure
);

Parameters

pvar
[in] Pointer to an initialized PROPVARIANT structure for which any deallocatable elements are to be freed. On return, all zeroes are written to the PROPVARIANT structure.

Return Values

S_OK
The VT types are recognized and all items that can be freed have been freed.
STG_E_INVALID_PARAMETER
The variant has an unknown VT type.

Remarks

At any level of indirection, a NULL pointer is ignored. For example, in a VT_CF PROPVARIANT, the pvar—pclipdata—pClipData could be NULL. In this case, the pvar—pclipdata—pClipData pointer would be ignored, but the pvar—pclipdata pointer would be freed.

On return, this function writes zeroes to the specified PROPVARIANT structure, so the VT-type is VT_EMPTY.

Passing NULL as the pvar parameter produces a return code of S_OK.

Note Do not use this function to initialize PROPVARIANT structures. Instead, initialize these structures using the PropVariantInit macro (defined in propidl.h).

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

FreePropVariantArray


PropVariantCopy

Copies the contents of one PROPVARIANT structure to another.

WINOLEAPI PropVariantCopy(
  PROPVARIANT *pvarDest        //Pointer to uninitialized
                               // PROPVARIANT that is filled on return
  const PROPVARIANT *pvarSrc   //PROPVARIANT to be copied
);

Parameters

pvarDest
[in, out] Pointer to an uninitialized PROPVARIANT structure that receives the copy.
pvarSrc
[in] Pointer to the PROPVARIANT structure to be copied.

Return Values

S_OK
The copy was successfully completed.
STG_E_INVALID_PARAMETER
The variant has an unknown type.

Remarks

Copies a PROPVARIANT structure by value so the original pvarSrc and new pvarDest parameters may be freed independently with calls to PropVariantClear. PropVariantCopy does not free the destination as the VariantCopy function does. For nonsimple PROPVARIANT types such as VT_STREAM, VT_STORAGE, and so forth, which require a subobject, the copy is made by reference. The pointer is copied and IUnknown::AddRef is called on it. It is illegal to pass NULL for either pvarDest or pvarSrc.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

PROPVARIANT, PropVariantClear


PropVariantInit

Initializes a PROPVARIANT structure.

Note This function is implemented as a macro, available by including the provided ole2.h header file. This function is not exported from any system-provided DLL.

void PropVariantInit(
  PROPVARIANT *pvar
);

Parameters

pvar
[out] Pointer to an uninitialized PROPVARIANT structure that is initialized.

Return Values

None

Remarks

Initializes a PROPVARIANT structure, and sets its type to VT_EMPTY. This function should be not be used to clear a PROPVARIANT structure that contains data; for example, when it contains the result of a call to IPropertyStorage::ReadMultiple. Such a PROPVARIANT structure should be cleared using the PropVariantClear function.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

PROPVARIANT, PropVariantClear


ReadClassStg

Reads the CLSID previously written to a storage object with the WriteClassStg function.

WINOLEAPI ReadClassStg(
  IStorage *pStg,   //Pointer to the storage object
                    // containing the CLSID
  CLSID *pclsid     //Pointer to return the CLSID
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object containing the CLSID to be retrieved.
pclsid
[out] Pointer to where the CLSID is written. May return CLSID_NULL.

Return Values

This function supports the standard return value E_OUTOFMEMORY, in addition to the following:

S_OK
The CLSID was returned successfully.

This function also returns any of the error values returned by the IStorage::Stat method.

Remarks

This function is simply a helper function that calls the IStorage::Stat method and retrieves the CLSID previously written to the storage object with a call to WriteClassStg from the STATSTG structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

OleLoad, WriteClassStg, IStorage::Stat, STATSTG.


ReadClassStm

Reads the CLSID previously written to a stream object with the WriteClassStm function.

WINOLEAPI ReadClassStm(
  IStream *pStm,  //Pointer to the stream holding the CLSID
  CLSID *pclsid   //Pointer to where the CLSID is to be written
);

Parameters

pStm
[in] Pointer to the IStream interface on the stream object containing the CLSID to be read. This CLSID must have been previously written to the stream object using WriteClassStm.
pclsid
[out] Pointer to where the CLSID is to be written.

Return Values

S_OK
The CLSID was successfully retrieved.
STG_E_READFAULT
End of file was reached.

This function also returns any of the error values returned by the ISequentialStream::Read method.

Remarks

Most applications do not call the ReadClassStm method directly. COM calls it before making a call to an object's IPersistStream::Load implementation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

WriteClassStm, ReadClassStg, WriteClassStg


ReadFmtUserTypeStg

Returns the clipboard format and user type previously saved with the WriteFmtUserTypeStg function.

WINOLEAPI ReadFmtUserTypeStg(
  IStorage *pStg,    //Pointer to storage object holding the values
  CLIPFORMAT *pcf,   //Pointer to return the clipboard format
  LPWSTR *lplpszUserType
                     //Pointer to output variable that receives
                     // a pointer to the requested user type string
);

Parameters

pStg
[in] Pointer to the IStorage interface on the storage object from which the information is to be read.
pcf
[out] Pointer to where the clipboard format is to be written on return. It can be NULL, indicating the format is of no interest to the caller.
lplpszUserType
[out] Address of LPWSTR pointer variable that receives a pointer to the user-type string. The caller can specify NULL for this parameter, indicating that the user type is of no interest. This function allocates memory for the string. The caller is responsible for freeing the memory with CoTaskMemFree.

Return Values

This function supports the standard return values E_FAIL, E_INVALIDARG, and E_OUTOFMEMORY, in addition to the following:

S_OK
The requested information was read successfully.

This function also returns any of the error values returned by the ISequentialStream::Read method.

Remarks

This function returns the clipboard format and the user type string from the specified storage object. The WriteClassStg function must have been called before calling the ReadFmtUserTypeStg function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

CoTaskMemFree, WriteFmtUserTypeStg


SetConvertStg

Sets the convert bit in a storage object to indicate that the object is to be converted to a new class when it is opened. The setting can be retrieved with a call to the GetConvertStg function.

WINOLEAPI SetConvertStg(
  IStorage *pStg,  //Pointer to storage object where the
                   // conversion bit is to be set
  BOOL fConvert    //Indicates whether an object is to be converted
);

Parameters

pStg
IStorage pointer to the storage object in which to set the conversion bit.
fConvert
If TRUE, sets the conversion bit for the object to indicate the object is to be converted when opened. If FALSE, clears the conversion bit.

Return Values

S_OK
Indicates that the object's conversion bit was set successfully.
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
E_OUTOFMEMORY
Indicates that the conversion bit was not set due to a lack of memory.
E_INVALIDARG
Indicates that one or more arguments are not valid.
E_UNEXPECTED
Indicates that an unexpected error occurred.

See the IStorage::CreateStream, IStorage::OpenStream, ISequentialStream::Read, and ISequentialStream::Write methods for possible storage and stream access errors.

Remarks

The SetConvertStg function determines the status of the convert bit in a contained object. It is called by both the container application and the server in the process of converting an object from one class to another. When a user specifies through a Convert To dialog (which the container produces with a call to the OleUIConvert function) that an object is to be converted, the container must take the following steps:

  1. Unload the object if it is currently loaded.
  2. Call WriteClassStg to write the new CLSID to the object storage.
  3. Call WriteFmtUserTypeStg to write the new user-type name and the existing main format to the storage.
  4. Call SetConvertStg with the fConvert parameter set to TRUE to indicate that the object has been tagged for conversion to a new class the next time it is loaded.
  5. Just before the object is loaded, call OleDoAutoConvert to handle any needed object conversion, unless you call OleLoad, which calls it internally.

When an object is initialized from a storage object and the server is the destination of a convert-to operation, the object server should do the following:

  1. Call the GetConvertStg function to retrieve the value of the conversion bit.
  2. If the bit is set, the server reads the data out of the object according to the format associated with the new CLSID.
  3. When the object is asked to save itself, the object should call the WriteFmtUserTypeStg function using the normal native format and user type of the object.
  4. The object should then call SetConvertStg with the fConvert parameter set to FALSE to reset the object's conversion bit.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

GetConvertStg


StgCreateDocfile

StgCreateDocfile creates a new compound file storage object using the COM-provided compound file implementation for the IStorage interface.

Note All Microsoft® Windows® 2000 applications should use the new function, StgCreateStorageEx, instead of StgCreateDocfile, to take advantage of enhanced Windows 2000 Structured Storage features. This function, StgCreateDocfile, still exists for compatibility with pre-Windows 2000 applications.

HRESULT StgCreateDocfile(
  const WCHAR *pwcsName,   //Pointer to path of compound file
                           // to create
  DWORD grfMode,           //Specifies the access mode for
                           // opening the storage object
  DWORD reserved,          //Reserved; must be zero
  IStorage **ppstgOpen     //Pointer to location for
                           // returning the new storage object
);

Parameters

pwcsName
[in] Pointer to the path of the compound file to create. It is passed uninterpreted to the file system. This can be a relative name or NULL. If NULL, a temporary compound file is allocated with a unique name.
grfMode
[in] Specifies the access mode to use when opening the new storage object. For more information, see the STGM enumeration. If the caller specifies transacted mode together with STGM_CREATE or STGM_CONVERT, the overwrite or conversion takes place when the commit operation is called for the root storage. If IStorage::Commit is not called for the root storage object, previous contents of the file will be restored. STGM_CREATE and STGM_CONVERT cannot be combined with the STGM_NOSNAPSHOT flag, because a snapshot copy is required when a file is overwritten or converted in the transacted mode.
reserved
[in] Reserved for future use; must be zero.
ppstgOpen
[out] Pointer to the location of the IStorage pointer to the new storage object.

Return Values

S_OK
Indicates that the compound file was successfully created.
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates that the compound file already exists and grfMode is set to STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
Indicates that the compound file was not created due to inadequate memory.
STG_E_INVALIDFLAG
Indicates a non-valid flag combination in the grfMode parameter.
STG_E_INVALIDNAME
Indicates a non-valid name in the pwcsName parameter.
STG_E_INVALIDPOINTER
Indicates a non-valid pointer in the pwcsName parameter or the ppStgOpen parameter.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_TOOMANYOPENFILES
Indicates that the compound file was not created due to a lack of file handles.
STG_S_CONVERTED
Indicates that the specified file was successfully converted to storage format.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

The StgCreateDocfile function creates a new storage object using the COM-provided, compound-file implementation for the IStorage interface. The name of the open compound file can be retrieved by calling the IStorage::Stat method.

StgCreateDocfile creates the file if it does not exist. If it does exist, the use of the STGM_CREATE, STGM_CONVERT, and STGM_FAILIFTHERE flags in the grfMode parameter indicate how to proceed. See the STGM enumeration for more information on these values.

If the compound file is opened in transacted mode (the grfMode parameter specifies STGM_TRANSACTED) and a file with this name already exists, the existing file is not altered until all outstanding changes are committed. If the calling process lacks write access to the existing file (because of access control in the file system), the grfMode parameter can only specify STGM_READ and not STGM_WRITE or STGM_READWRITE. The resulting new open compound file can still be written to, but a subsequent commit operation will fail (in transacted mode, write permissions are enforced at commit time).

Specifying STGM_SIMPLE provides a much faster implementation of a compound file object in a limited, but frequently used case. This can be used by applications that require a compound-file implementation with multiple streams and no storages. The simple mode does not support all of the methods on IStorage. For more information, refer to the STGM enumeration.

If the grfMode parameter specifies STGM_TRANSACTED and no file yet exists with the name specified by the pwcsName parameter, the file is created immediately. In an access-controlled file system, the caller must have write permissions in the file system directory in which the compound file is created. If STGM_TRANSACTED is not specified, and STGM_CREATE is specified, an existing file with the same name is destroyed before the new file is created.

StgCreateDocfile can be used to create a temporary compound file by passing a NULL value for the pwcsName parameter. However, these files are temporary only in the sense that they have a system-provided unique name — likely one that is meaningless to the user. The caller is responsible for deleting the temporary file when finished with it, unless STGM_DELETEONRELEASE was specified for the grfMode parameter.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

StgCreateDocFileOnILockBytes, STGM, StgCreateStorageEx


StgCreateDocfileOnILockBytes

Creates and opens a new compound file storage object on top of a byte-array object provided by the caller. The storage object supports the COM-provided, compound-file implementation for the IStorage interface.

WINOLEAPI StgCreateDocfileOnILockBytes(
  ILockBytes *plkbyt,    //Pointer to the ILockBytes
                         // interface on the byte array object
  DWORD grfMode,         //Specifies the access mode
  DWORD reserved,        //Reserved; must be zero
  IStorage **ppstgOpen   //Pointer to location for returning
                         // the new storage object
);

Parameters

plkbyt
[in] Pointer to the ILockBytes interface on the underlying byte-array object on which to create a compound file.
grfMode
[in] Specifies the access mode to use when opening the new compound file. For more information, see the STGM enumeration.
reserved
[in] Reserved for future use; must be zero.
ppstgOpen
[out] Pointer to the location of the IStorage pointer on the new storage object.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Return Values

S_OK
Indicates that the compound file was successfully created.
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates that the compound file already exists and the grfMode parameter is set to STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
Indicates that the storage object was not created due to inadequate memory.
STG_E_INVALIDPOINTER
Indicates that a non-valid pointer was in the pLkbyt parameter or the ppStgOpen parameter.
STG_E_INVALIDFLAG
Indicates that a non-valid flag combination was in the grfMode parameter.
STG_E_TOOMANYOPENFILES
Indicates that the storage object was not created due to a lack of file handles.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_S_CONVERTED
Indicates that the compound file was successfully converted. The original byte-array object was successfully converted to IStorage format.

This function can also return any file system errors, or Win32 errors wrapped in an HRESULT, or ILockBytes interface error return values. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

The StgCreateDocfileOnILockBytes function creates a storage object on top of a byte array object using the COM-provided, compound-file implementation of the IStorage interface. StgCreateDocfileOnILockBytes can be used to store a document in a relational database. The byte array (indicated by the pLkbyt parameter, which points to the ILockBytes interface on the object) is used for the underlying storage in place of a disk file.

Except for specifying a programmer-provided byte-array object, StgCreateDocfileOnILockBytes is similar to the StgCreateDocfile function. For more information, refer to StgCreateDocfile.

The newly created compound file is opened according to the access modes in the grfMode parameter. For conversion purposes, the file is considered to already exist. As a result, it is not useful to use the STGM_FAILIFTHERE value, because it causes an error to be returned. However, both STGM_CREATE and STGM_CONVERT remain useful.

The ability to build a compound file on top of a byte-array object is provided to support having the data (underneath an IStorage and IStream tree structure) live in a nonpersistent space. Given this capability, there is nothing preventing a document that is stored in a file from using this facility. For example, a container might do this to minimize the impact on its file format caused by adopting COM. However, it is recommended that COM documents adopt the IStorage interface for their own outer-level storage. This has the following advantages:

  • The storage structure of the document is the same as its storage structure when it is an embedded object, reducing the number of cases the application needs to handle.
  • One can write tools to access the OLE embedded and linked objects within the document without special knowledge of the document's file format. An example of such a tool is a copy utility that copies all the documents included in a container containing linked objects. A copy utility like this needs access to the contained links to determine the extent of files to be copied.
  • The IStorage implementation addresses the problem of how to commit the changes to the file. An application using the ILockBytes interface must handle these issues itself.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

StgCreateDocfile


StgCreatePropSetStg

Creates a property set storage object from a specified storage object. The property set storage object supplies the system-provided, stand-alone implementation of the IPropertySetStorage interface.

HRESULT StgCreatePropSetStg(
  IStorage *pStorage,   //Pointer to a storage object
  DWORD dwReserved,     //Reserved; must be zero
  IPropertySetStorage **ppPropSetStg
                        //Pointer to the output variable that
                        // receives IPropertySetStorage interface pointer
);

Parameter

pStorage
[in] Pointer to the storage object that contains or is to contain one or more property sets.
dwReserved
Reserved for future use; must be zero.
ppPropSetStg
[out] Pointer to IPropertySetStorage* pointer variable that receives the interface pointer to the property-set storage object.

Return Values

This function supports the standard return value E_INVALIDARG as well as the following:

S_OK
The property set storage object was successfully created.

Remarks

The StgCreatePropSetStg function creates an IPropertySetStorage interface that will act on the given IStorage interface specified by the pStorage parameter. This function does not modify this IStorage by itself, although subsequent calls to the IPropertySetStorage interface might.

StgCreatePropSetStg calls IUnknown::AddRef on the storage object specified by pStorage. The caller is responsible for releasing the object when it is no longer needed.

The following code sample shows how this function could be used to create a property set within a storage object.

IPropertyStorage*
CreatePropertySetInStorage( IStorage *pStg, const FMTID &fmtid )
{
    HRESULT hr = S_OK;
    IPropertySetStorage *pPropSetStg = NULL;
    IPropertyStorage *pPropStg = NULL;

    try
    {
        hr = StgCreatePropSetStg( pStg, 0, &pPropSetStg );
        if( FAILED(hr) ) throw L"Failed StgCreatePropSetStg (%08x)";

        hr = pPropSetStg->Create( fmtid, NULL,
            PROPSETFLAG_DEFAULT,
            STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
            &pPropStg );
        if( FAILED(hr) ) throw L"Failed IPropertySetStorage::Create (%08x)";

        // Success. The caller must now call Release on both
        // pPropSetStg and pStg.

    }
    catch( const WCHAR *pwszError )
    {
        wprintf( L"Error: %s (%08x)\n", pwszError, hr );
    }

    if( NULL != pPropSetStg )
        pPropSetStg->Release();

    return( pPropStg );
}

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.
Library: Use Ole32.lib.

See Also

IPropertySetStorage-Stand-alone Implementation, StgCreatePropSetStg Sample, Samples


StgCreatePropStg

Creates and opens a property set in a specified storage or stream object. The property set supplies the system-provided, stand-alone implementation of the IPropertyStorage interface.

HRESULT StgCreatePropStg(
  IUnknown *pUnk,       //Interface pointer for object
                        // containing the new property set
  REFFMTID fmtid,       //FMTID of the property
                        // set to be created
  const CLSID *pclsid,  //Initial CLSID for the new property set
  DWORD grfFlags,       //PROPSETFLAG values
  DWORD dwReserved,     //Reserved; must be zero
  IPropertyStorage **ppPropStg
                        //Pointer to output variable that
                        // receives the IPropertyStorage interface pointer
);

Parameters

pUnk
[in] Pointer to the IUnknown interface on the storage or stream object that stores the new property set.
fmtid
[in] FMTID of the property set to be created.
pclsid
[in] Pointer to the initial CLSID for this property set. May be NULL, in which case pclsid is set to all zeroes.
grfFlags
[in] Values from the PROPSETFLAG enumeration that determine how the property set is created and opened.
dwReserved
[in] Reserved; must be zero.
ppPropStg
[out] Address of IPropertyStorage* pointer variable that receives the interface pointer to the new property set.

Return Values

This function supports the standard return values E_INVALIDARG and E_UNEXPECTED, in addition to the following:

S_OK
The property set was created.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INVALIDHEADER
The property set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFLAG
A flag in the grfMode or grfFlags parameter was not valid.
STG_E_INVALIDPARAMETER
A parameter is not valid.
STG_E_MEDIUMFULL
The storage object could not be increased in size to hold the default property set.

Remarks

StgCreatePropStg creates and opens a new property set which supplies the system-provided, stand-alone implementation of the IPropertyStorage interface. The new property set is contained in the storage or stream object specified by pUnk. The value of the grfFlags parameter indicates whether pUnk specifies a storage or stream object. For example, if PROPSETFLAG_NONSIMPLE is set, then pUnk can be queried for an IStorage interface on a storage object.

In either case, this function calls pUnk->AddRef for the storage or stream object containing the property set. It is the responsibility of the caller to release the object when it is no longer needed.

This function is similar to the IPropertySetStorage::Create method. However, StgCreatePropStg adds the pUnk parameter and supports the PROPSETFLAG_UNBUFFERED value for the grfFlags parameter. Use this function instead of the Create method if you have an IStorage interface that does not support the IPropertySetStorage interface, or if you want to use the PROPSETFLAG_UNBUFFERED value. See the PROPSETFLAG enumeration for more information on using this PROPSETFLAG_UNBUFFERED enumeration value.

The property set automatically contains code page and locale identifier (ID) properties. These are set to the current system default and the current user default, respectively.

The grfFlags parameter is a combination of values taken from the enumeration PROPSETFLAG. The new enumeration value PROPSETFLAG_UNBUFFERED is supported. See the PROPSETFLAG enumeration for more information on the use of this value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

IPropertySetStorage, IPropertySetStorage-Stand-alone Implementation, IPropertyStorage, IPropertyStorage-Stand-alone Implementation, PROPSETFLAG, StgOpenPropStg, StgCreatePropSetStg


StgCreateStorageEx

Creates a new storage object using a provided implementation for the IStorage or IPropertySetStorage interfaces. To open an existing file, use the StgOpenStorageEx function instead.

All Microsoft Windows 2000 applications should use this new function, StgCreateStorageEx, rather than StgCreateDocfile, to take advatage of the enhanced Windows 2000 Structured Storage features. The StgCreateDocfile function still exists for compatibility with pre-Windows 2000 applications.

WINOLEAPI StgCreateStorageEx(
  const WCHAR *pwcsName,     //Pointer to path of compound
                             // file to create
  DWORD grfMode,             //Specifies the access mode for
                             // opening the storage object
  STGFMT stgfmt,             //Specifies the storage file format
  DWORD grfAttrs,            //Reserved; must be zero
  STGOPTIONS *pStgOptions,   //Pointer to STGOPTIONS
                             // structure which specifies
                             // features of the storage object
  void *reserved2,           //Reserved; must be zero
  REFIID riid,               //Specifies the GUID of the interface pointer
  void **ppObjectOpen,       //Pointer to an interface pointer
);

Parameters

pwcsName
[in] Points to the path of the file to create. It is passed uninterpreted to the file system. This can be a relative name or NULL. If NULL, a temporary file is allocated with a unique name. If non-NULL, the string size must not exceed MAX_PATH characters.

Microsoft Windows 2000: Unlike the CreateFile function, you cannot exceed the MAX_PATH limit by using the "\\?\" prefix.

grfMode
[in] Specifies the access mode to use when opening the new storage object. For more information, see the STGM enumeration. If the caller specifies transacted mode together with STGM_CREATE or STGM_CONVERT, the overwrite or conversion takes place when the commit operation is called for the root storage. If IStorage::Commit is not called for the root storage object, previous contents of the file will be restored. STGM_CREATE and STGM_CONVERT cannot be combined with the STGM_NOSNAPSHOT flag, because a snapshot copy is required when a file is overwritten or converted in the transacted mode.
stgfmt
[in] Specifies the storage file format. For more information, see the STGFMT enumeration.
grfAttrs
[in] Depends on the value of the stgfmt parameter.
Parameter Values | Description
STGFMT_DOCFILE | 0, or FILE_FLAG_NO_BUFFERING. See the CreateFile function for information on this value. If the sector size of the file (specified in pStgOptions) is not an integer multiple of the underlying disk's physical sector size, this operation will fail.
All other values of stgfmt | Must be 0

pStgOptions
[in] The pStgOptions parameter is valid only if the stgfmt parameter is set to STGFMT_DOCFILE. If the stgfmt parameter is set to STGFMT_DOCFILE, pStgOptions points to the STGOPTIONS structure, which specifies features of the storage object, such as the sector size. This parameter may be NULL, which creates a storage object with a default sector size of 512 bytes. If non-NULL, the ulSectorSize member must be set to either 512 or 4096. If set to 4096, STGM_SIMPLE may not be specified in the grfMode parameter. The usVersion member must be set before calling StgCreateStorageEx. See the STGOPTIONS structure for more information.
reserved2
[in] Reserved for future use; must be zero.
riid
[in] Specifies the interface identifier (IID) of the interface pointer to return. This IID may be for the IStorage interface or the IPropertySetStorage interface
ppObjectOpen
[out] Pointer to an interface pointer variable that receives a pointer for an interface on the new storage object; contains NULL if operation failed.

Return Values

S_OK
Indicates that the compound file was successfully created.
STG_E_INVALIDPOINTER
Indicates a non-valid pointer in the ppObjectOpen parameter.
STG_E_INVALIDPARAMETER
Indicates a non-valid value for the grfAttrs, reserved1, reserved2, grfMode, or stgfmt parameters. Can occur if the FILE_FLAG_NO_BUFFERING flag is specified for grfAttrs, but the sector size of the file is not an integer multiple of the underlying disk's sector size.
STG_E_INVALIDFLAG
Indicates a non-valid flag combination in the grfMode pointer.
STG_E_INVALIDNAME
Indicates a non-valid name in the pwcsName parameter.
STG_E_FILEALREADYEXISTS
Indicates that the compound file already exists and grfMode is set to STGM_FAILIFTHERE.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_NOTIMPL
Indicates that the StgCreateStorageEx function is not implemented by the operating system. In this case, use the StgCreateDocfile function.
STG_E_ACCESSDENIED
Indicates that the file could not be created because the underlying storage device does not allow such access to the current user. When opening the storage object in transacted mode (STGM_TRANSACTED), this error may also indicate that a temporary file could not be created in the temporary directory as specified by the GetTempPath function. The GetTempPath function retrieves the path of the directory designated for temporary files.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

StgCreateStorageEx is a superset of the StgCreateDocfile function, and should be used by new code. Future enhancements to Structured Storage will be exposed through the StgCreateStorageEx function. See the following Requirements section for information on supported platforms.

The StgCreateStorageEx function creates a new storage object using one of the system-provided, structured-storage implementations. This function can be used to obtain a compound file implementation of the IStorage or IPropertySetStorage, or to obtain an NTFS implementation of IPropertySetStorage.

When a new file is created, the storage implementation used depends on the STGFMT flag that you specify and on the type of drive on which the file is stored. For more information, see the STGFMT enumeration.

StgCreateStorageEx creates the file if it does not exist. If it does exist, the use of the STGM_CREATE, STGM_CONVERT, and STGM_FAILIFTHERE flags in the grfMode parameter indicate how to proceed. For more information on these values, see the STGM enumeration. It is not valid, in direct mode, to specify the STGM_READ mode in the grfMode parameter (direct mode is indicated by not specifying the STGM_TRANSACTED flag). This function cannot be used to open an existing file; use the StgOpenStorageEx function instead.

You can use the StgCreateStorageEx function to get access to the root storage of a structured-storage document or the property set storage of any file that supports property sets. See the STGFMT documentation for information about which IIDs are supported for different STGFMT values.

When a file is created with this function to access the NTFS property set implementation, special sharing rules apply. See IPropertySetStorage-NTFS Implementation for more information.

If a compound file is created in transacted mode (by specifying STGM_TRANSACTED) and read-only mode (by specifying STGM_READ), it is possible to make changes to the returned storage object. For example, it is possible to call IStorage::CreateStream. However, it is not possible to commit those changes by calling IStorage::Commit. Therefore, such changes will be lost.

Specifying STGM_SIMPLE provides a much faster implementation of a compound file object in a limited, but frequently used case involving applications that require a compound file implementation with multiple streams and no storages. For more information, see the STGM enumeration. It is not valid to specify that STGM_TRANSACTED if STGM_SIMPLE is specified.

The simple mode does not support all the methods on IStorage. Specifically, in simple mode, supported IStorage methods are QueryInterface, AddRef, Release, CreateStream, Commit and SetClass. In addition, SetElementTimes is supported with a NULL name, allowing applications to set times on a root storage. All the other methods of IStorage return STG_E_INVALIDFUNCTION.

If the grfMode parameter specifies STGM_TRANSACTED and no file yet exists with the name specified by the pwcsName parameter, the file is created immediately. In an access-controlled file system, the caller must have write permissions for the file system directory in which the compound file is created. If STGM_TRANSACTED is not specified, and STGM_CREATE is specified, an existing file with the same name is destroyed before creating the new file.

You can also use StgCreateStorageEx to create a temporary compound file by passing a NULL value for the pwcsName parameter. However, these files are temporary only in the sense that they have a unique system-provided name – one that is probably meaningless to the user. The caller is responsible for deleting the temporary file when finished with it, unless STGM_DELETEONRELEASE was specified for the grfMode parameter. For more information on these flags, see STGM.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in Objbase.h.
Library: Use Ole32.lib.

See Also

StgOpenStorageEx StgCreateDocFileOnILockBytes, StgCreateDocfile, STGM, STGFMT, STGOPTIONS


StgGetIFillLockBytesOnFile

Opens a wrapper object on a temporary file.

WINOLEAPI StgGetIFillLockBytes(
  OLECHAR *pwcsName,      //Pointer to file specified by filename
  IFillLockBytes **ppflb  //Pointer to output variable that
                          // receives the IFillLockBytes interface pointer
);

Parameters

pwcsName
[in] Pointer to the name of the file for which a wrapper object is created.
ppflb
[out] Pointer to IFillLockBytes* pointer variable that receives the interface pointer to the new byte array wrapper object.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, in addition to the following:

S_OK
Indicates that the wrapper object was successfully created.
STG_E_FILENOTFOUND
Indicates that the file specified by pwcsName could not be found.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPOINTER
Indicates non-valid pointer in the ppflb parameter.

This function can also return any file system errors.

Remarks

The moniker that manages the downloading of the file specified in pwcsName calls this function in the course of creating the asynchronous storage necessary to manage the asynchronous downloading of data. The moniker first creates a temporary file, then calls this function to create the wrapper object on that file. Finally, the moniker calls StgOpenAsyncDocfileOnIFillLockBytes to open the root storage of the compound file that is to be downloaded into the temporary file.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

IFillLockBytes, ILockBytes, StgOpenAsyncDocfileOnIFillLockBytes


StgGetIFillLockBytesOnILockBytes

Creates a new wrapper object on a byte array object provided by the caller.

WINOLEAPI StgOpenAsyncDocFileOnIFillLockBytes(
  ILockBytes *pilb,       //Pointer to existing byte array object
  IFillLockBytes **ppflb  //Pointer to output variable that
                          // receives the IFillLockBytes interface pointer
);

Parameters

pilb
[in] Pointer to an existing byte array object.
ppflb
[out] Pointer to IFillLockBytes* pointer variable that receives the interface pointer to the new byte array wrapper object.

Return Values

This function supports the standard return values E_UNEXPECTED and E_FAIL, as well as the following:

S_OK
Indicates that the wrapper object was successfully created.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDPOINTER
Indicates non-valid pointer in the pilb parameter.

Remarks

The StgGetIFillLockBytesOnILockBytes function makes it possible to create an asynchronous storage wrapper object on a custom byte-array object. For example, if you wanted to implement asynchronous storage on a database for which you have already created a byte-array object, you would call this function to create the wrapper object for the byte array. To do so, the function creates a new wrapper object and then initializes it by passing it a pointer to the existing byte-array object.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

IFillLockBytes, ILockBytes


StgIsStorageFile

Indicates whether a particular disk file contains a storage object.

WINOLEAPI StgIsStorageFile(
  const WCHAR *pwcsName  //Points to a path of the file to check
);

Parameter

pwcsName
[in] Pointer to the name of the disk file to be examined. The pwcsName parameter is passed uninterpreted to the underlying file system.

Return Values

S_OK
Indicates that the file contains a storage object.
S_FALSE
Indicates that the file does not contain a storage object.
STG_E_FILENOTFOUND
Indicates that the file was not found.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors

Remarks

At the beginning of the disk file underlying a storage object is a signature distinguishing a storage object from other file formats. The StgIsStorageFile function is useful to applications whose documents use a disk file format that might or might not use storage objects.

If a root compound file has been created in transacted mode but not yet committed, this method still return S_OK.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

StgIsStorageILockBytes


StgIsStorageILockBytes

Indicates whether the specified byte array contains a storage object.

WINOLEAPI StgIsStorageILockBytes(
  ILockBytes *plkbyt   //ILockBytes pointer to the byte
                       // array to be examined
);

Parameter

plkbyt
ILockBytes pointer to the byte array to be examined.

Return Values

S_OK
Indicates that the specified byte array contains a storage object.
S_FALSE
Indicates that the specified byte array does not contain a storage object.

This function can also return any file system errors, or Win32 errors wrapped in an HRESULT, or ILockBytes interface error return values. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors

Remarks

At the beginning of the byte array underlying a storage object is a signature distinguishing a storage object (supporting the IStorage interface) from other file formats. The StgIsStorageILockBytes function is useful to applications whose documents use a byte array (a byte array object supports the ILockBytes interface) that might or might not use storage objects.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

StgIsStorageFile, ILockBytes


StgOpenAsyncDocfileOnIFillLockBytes

Opens an existing root asynchronous storage object on a byte-array wrapper object provided by the caller.

WINOLEAPI StgOpenAsyncDocFileOnIFillLockBytes(
  IFillLockBytes *ppflb, //Pointer to byte-array wrapper object
  DWORD grfmode,         //Storage access mode
  DWORD asyncFlags,      //Asynchronous storage flags
  IStorage **ppstgOpen   //Pointer to output variable that
                         // receives the IStorage interface pointer
);

Parameters

ppflb
[in] IFillLockBytes pointer to the byte-array wrapper object that contains the storage object to be opened.
grfmode
[in] Specifies the access mode to use to open the storage object. The most common access mode, taken from the STGM enumeration, is STGM_READ.
asyncFlags
[in] Indicates whether a connection point on a storage is inherited by its substorages and streams. ASYNC_MODE_COMPATIBILITY indicates that the connection point is inherited; ASYNC_MODE_DEFAULT indicates that the connection point is not inherited.
ppstgOpen
[out] Pointer to IStorage* pointer variable that receives the interface pointer to the root asynchronous storage object.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, as well as the following:

E_PENDING
Data is currently unavailable.
E_NOINTERFACE
A pointer was not returned to the requested interface.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to complete this operation.

Remarks

The root storage of the asynchronous storage object is opened according to the access mode in the grfMode parameter. A pointer to the IStorage interface on the opened storage object is supplied through the ppstgOpen parameter.

The byte array wrapper object must have been previously instantiated through a call to the StgGetIFillLockBytesOnFile function.

StgOpenAsyncDocFileOnIFillLockBytes does not support priority access mode or exclusions. Otherwise, it works in much the same way as the StgOpenStorageOnILockBytes function.

The returned storage object has a connection point for IProgressNotify.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

IFillLockBytes, ILockBytes, StgGetIFillLockBytesOnFile, StgOpenStorageOnILockBytes.


StgOpenLayoutDocfile

Opens a compound file on an ILockBytes implementation that is capable of monitoring sector information.

WINOLEAPI StgOpenLayoutDocfile(
  OLECHAR *pwcsName,    //Pointer to name of compound file
                        // to be opened
  DWORD grfMode,        //Access mode for the new storage object
  IStorage **ppStgOpen  //Pointer to output variable that
                        // receives the IStorage interface pointer
);

Parameters

pwcsName
[in] Pointer to the name of the compound file to be opened.
grfMode
[in] Access mode to use when opening the newly created storage object. Values are taken from the STGM enumeration. Note that priority mode and exclusions are not supported. The most common access mode is likely to be STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE.
ppStgOpen
[out] Pointer to IStorage pointer variable that receives the interface pointer to the root object of the newly created root storage object.

Return Values

This function supports the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_INVALIDARG, and E_FAIL, in addition to the following:

STG_E_INVALIDPARAMETER
One of the parameters is not valid.
STG_E_INVALIDNAME
The name passed to this function is not a valid file name.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to complete this operation.

This function can also return any of the error values returned by the StgOpenStorageOnILockBytes function.

Remarks

The compound file implementation created by this function exposes the ILayoutStorage interface on its root storage. Applications use this interface to express the optimal layout of their compound files in order to download and render data more rapidly over a slow link. StgOpenLayoutDocfile returns a pointer to the IStorage interface on the root storage of the newly created compound file. Using this pointer, applications call QueryInterface to obtain a pointer to ILayoutStorage.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.

See Also

ILockBytes, IStorage, STGM


StgOpenPropStg

Opens a specified property set in a specified storage or stream object. The property set supplies the system-provided, stand-alone implementation of the IPropertyStorage interface.

HRESULT StgOpenPropStg(
  IUnknown *pUnk,    //Interface pointer for object
                     // containing the requested property set
  REFFMTID fmtid,    //FMTID of the property set
                     // to be opened
  DWORD grfFlags,    //PROPSETFLAG values
  DWORD dwReserved,  //Reserved; must be zero
  IPropertyStorage **ppPropStg
                     //Pointer to output variable that
                     // receives the IPropertyStorage interface pointer
);

Parameters

pUnk
[in] Interface pointer for IUnknown interface on the storage or stream object that contains the requested property set object.
fmtid
[in] FMTID of the property set to be opened.
grfFlags
[in] Values from the PROPSETFLAG enumeration.
dwReserved
[in] Reserved for future use; must be zero.
ppPropStg
[out] Pointer to IPropertyStorage* pointer variable that receives the interface pointer to the requested property set.

Return Values

This function supports the standard return values E_INVALIDARG and E_UNEXPECTED, in addition to the following:

S_OK
The property set was opened.
STG_E_FILENOTFOUND
A property set was not found in the specified object (pUnk).
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied, or the property set is corrupted.
STG_E_INVALIDHEADER
The property set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFLAG
A flag in the grfMode or grfFlags parameter was not valid.
STG_E_INVALIDPARAMETER
A parameter is not valid.

Remarks

StgOpenPropStg opens the requested property set and supplies the system-provided, stand-alone implementation of the IPropertyStorage interface. The requested property set is contained in the storage or stream object specified by pUnk. The value of the grfFlags parameter indicates whether pUnk specifies a storage or stream object. For example, if PROPSETFLAG_NONSIMPLE is set, then pUnk can be queried for an IStorage interface on a storage object.

In either case, this function calls pUnk->AddRef for the storage or stream object containing the property set. It is the responsibility of the caller to release the object when it is no longer needed.

This function is similar to the IPropertySetStorage::Open method. However, StgOpenPropStg adds the pUnk and grfFlags parameters, including the PROPSETFLAG_UNBUFFERED value for the grfFlags parameter. Use this function instead of the Open method if you have an IStorage interface that does not support the IPropertySetStorage interface, or if you want to use the PROPSETFLAG_UNBUFFERED value. See the PROPSETFLAG enumeration for more information on using PROPSETFLAG_UNBUFFERED.

The grfFlags parameter is a combination of values taken from the enumeration PROPSETFLAG. The new enumeration value PROPSETFLAG_UNBUFFERED is supported. See PROPSETFLAG for more information on the use of this value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.
Library: Use Ole32.lib.

See Also

IPropertySetStorage, IPropertySetStorage-Stand-alone Implementation, IPropertyStorage, IPropertyStorage-Stand-alone Implementation, PROPSETFLAG, StgCreatePropStg, StgCreatePropSetStg


StgOpenStorage

StgOpenStorage opens an existing root storage object in the file system. You can use this function to open compound files, but you cannot use it to open directories, files, or summary catalogs. Nested storage objects can only be opened using their parent's IStorage::OpenStorage method.

Note All Microsoft Windows 2000 applications should use the new function, StgOpenStorageEx, instead of StgOpenStorage, to take advantage of the enhanced Windows 2000 Structured Storage features. This function, StgOpenStorage, still exists for compatibility with pre-Windows 2000 applications.

HRESULT StgOpenStorage(
  const WCHAR *pwcsName,    //Pointer to the path of the file
                            // containing storage object
  IStorage *pstgPriority,   //Pointer to a previous opening
                            // of a root storage object
  DWORD grfMode,            //Specifies the access mode for the object
  SNB snbExclude,           //Pointer to an SNB structure
                            // specifying elements to be excluded
  DWORD reserved,           //Reserved; must be zero
  IStorage **ppstgOpen      //Pointer to output variable
                            // that receives the IStorage interface pointer
);

Parameters

pwcsName
[in] Pointer to the path of the file containing the storage object to open. This parameter is ignored if the pStgPriority parameter is not NULL.
pstgPriority
[in] Most often NULL. If not NULL, this parameter is used instead of the pwcsName parameter to specify the pointer to the IStorage interface on the storage object to open. It points to a previous opening of a root storage object, most often one that was opened in priority mode.

After the StgOpenStorage function returns, the storage object specified in the pStgPriority parameter on function entry is not valid, and can no longer be used. Instead, use the storage object specified in the ppStgOpen parameter.

grfMode
[in] Specifies the access mode to use to open the storage object.
snbExclude
[in] If not NULL, pointer to a block of elements in the storage that are to be excluded as the storage object is opened. The exclusion occurs regardless of whether a snapshot copy happens on the open. May be NULL.
reserved
[in] Indicates reserved for future use; must be zero.
ppstgOpen
[out] Pointer IStorage* pointer variable that receives the interface pointer to the opened storage.

Return Values

S_OK
Indicates that the storage object was successfully opened.
STG_E_FILENOTFOUND
Indicates that the specified file does not exist.
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates that the file exists but is not a storage object.
STG_E_TOOMANYOPENFILES
Indicates that the storage object was not opened because there are too many open files.
STG_E_INSUFFICIENTMEMORY
Indicates that the storage object was not opened due to inadequate memory.
STG_E_INVALIDNAME
Indicates a non-valid name in the pwcsName parameter.
STG_E_INVALIDPOINTER
Indicates a non-valid pointer in one of the parameters: snbExclude, pwcsName, pstgPriority, or ppStgOpen.
STG_E_INVALIDFLAG
Indicates a non-valid flag combination in the grfMode parameter.
STG_E_INVALIDFUNCTION
Indicates STGM_DELETEONRELEASE specified in the grfMode parameter.
STG_E_OLDFORMAT
Indicates that the storage object being opened was created by the Beta 1 storage provider. This format is no longer supported.
STG_E_NOTSIMPLEFORMAT
Indicates that the STGM_SIMPLE flag was specified in the grfMode parameter and the storage object being opened was not written in simple mode.
STG_E_OLDDLL
The DLL being used to open this storage object is a version of the DLL that is older than the one used to create it.
STG_E_PATHNOTFOUND
Specified path does not exist.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors

Remarks

The StgOpenStorage function opens the specified root storage object according to the access mode in the grfMode parameter, and, if successful, supplies an IStorage pointer to the opened storage object in the ppstgOpen parameter.

To support the simple mode for saving a storage object with no substorages, the StgOpenStorage function accepts the following flag combinations as valid modes in the grfMode parameter:

    STGM_SIMPLE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE

and

    STGM_SIMPLE | STGM_READ | STGM_SHARE_EXCLUSIVE

To support the single-writer, multireader, direct mode, the following flag combination is the valid grfMode parameter for the writer:

    STGM_DIRECT_SWMR | STGM_READWRITE | STGM_SHARE_DENY_WRITE

And the following combination is valid for readers:

    STGM_DIRECT_SWMR | STGM_READ | STGM_SHARE_DENY_NONE

In direct mode, the following combinations are valid:

    STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE

and

    STGM_DIRECT | STGM_READ | STGM_SHARE_DENY_WRITE

and

    STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE

Note Opening a storage object in read and/or write mode without denying write permission to others (the grfMode parameter specifies STGM_SHARE_DENY_WRITE) can be a time-consuming operation because the StgOpenStorage call must make a snapshot of the entire storage object.

Applications often try to open storage objects with the following access permissions:

STGM_READWRITE | STGM_SHARE_DENY_WRITE
    // transacted versus direct mode omitted for exposition

If the application succeeds, it never needs to make a snapshot copy. If it fails, the application can revert to using the permissions and make a snapshot copy:

STGM_READWRITE
    // transacted versus direct mode omitted for exposition

In this case, the application should prompt the user before making a time-consuming copy. Alternatively, if the document-sharing semantics implied by the access modes are appropriate, the application could try to open the storage as follows:

STGM_READ | STGM_SHARE_DENY_WRITE
    // transacted versus direct mode omitted for exposition

In this case, if the application succeeds, a snapshot copy will not have been made (because STGM_SHARE_DENY_WRITE was specified, denying others write access).

To reduce the expense of making a snapshot copy, applications can open storage objects in priority mode (grfMode specifies STGM_PRIORITY).

The snbExclude parameter specifies a set of element names in this storage object that are to be emptied as the storage object is opened: streams are set to a length of zero; storage objects have all their elements removed. By excluding certain streams, the expense of making a snapshot copy can be significantly reduced. Almost always, this approach is used after first opening the storage object in priority mode, then completely reading the now-excluded elements into memory. This earlier priority-mode opening of the storage object should be passed through the pstgPriority parameter to remove the exclusion implied by priority mode. The calling application is responsible for rewriting the contents of excluded items before committing. Thus, this technique is most likely useful only to applications whose documents do not require constant access to their storage objects while they are active.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Use Ole32.lib.

See Also

IStorage, StgCreateDocfile, StgOpenStorageEx


StgOpenStorageEx

Opens an existing root storage object in the file system. You can use this function to open compound files and regular files. To create a new file, use the StgCreateStorageEx function.

Note All Microsoft Windows 2000 applications should use this new function, StgOpenStorageEx, instead of StgOpenStorage, to take advantage of the enhanced Windows 2000 Structured Storage features. The StgOpenStorage function still exists for compatibility with pre-Windows 2000 applications.

HRESULT StgOpenStorageEx(
  const WCHAR *pwcsName,     //Pointer to the path of the
                             // file containing storage object
  DWORD grfMode,             //Specifies the access mode for the object
  STGFMT stgfmt,             //Specifies the storage file format
  DWORD grfAttrs,            //Reserved; must be zero
  STGOPTIONS *pStgOptions,   //Address of STGOPTIONS pointer
  void *reserved2,           //Reserved; must be zero
  REFIID riid,               //Specifies the GUID of the interface pointer
  void **ppObjectOpen        //Address of an interface pointer
);

Parameters

pwcsName
[in] Pointer to the path of the file containing the storage object. This string size must not exceed MAX_PATH characters.

Windows 2000: Unlike the CreateFile function, the MAX_PATH limit cannot be exceeded by using the "\\?\" prefix.

grfMode
[in] Specifies the access mode to open the new storage object. For more information, see the STGM enumeration. If the caller specifies transacted mode together with STGM_CREATE or STGM_CONVERT, the overwrite or conversion takes place when the commit operation is called for the root storage. If IStorage::Commit is not called for the root storage object, previous contents of the file will be restored. STGM_CREATE and STGM_CONVERT cannot be combined with the STGM_NOSNAPSHOT flag, because a snapshot copy is required when a file is overwritten or converted in the transacted mode.

If the storage object is opened in direct mode (STGM_DIRECT), and with access of either STGM_WRITE or STGM_READWRITE, the sharing mode must be either STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE, respectively.

The mode in which a file is opened can affect implementation performance. See Compound File Implementation Limits for more information

stgfmt
[in] Specifies the storage file format. For more information, see the STGFMT enumeration.
grfAttrs
[in] Depends on the value of the stgfmt parameter.
STGFMT_DOCFILE | All other values of stgfmt
Zero (0) or FILE_FLAG_NO_BUFFERING. See the CreateFile function for information on this value. If the sector size of the file (specified in ppStgOptions) is not an integer multiple of the underlying disk's physical sector size, then this operation will fail. | Must be 0

pStgOptions
[in, out] Pointer to a STGOPTIONS structure that contains information about the storage object being opened. The pStgOptions parameter is valid only if the stgfmt parameter is set to STGFMT_DOCFILE. The usVersion member must be set before calling StgOpenStorageEx. See STGOPTIONS structure.
reserved2
[in] Reserved for future use; must be zero.
riid
[in] Specifies the GUID of the interface pointer to return. Can also be the header-specified value for IID IStorage to obtain the IStorage interface or for IID IPropertySetStorage to obtain the IPropertySetStorage interface.
ppObjectOpen
[out] Address of an interface pointer variable that receives a pointer for an interface on the storage object being opened; contains NULL if operation failed.

Return Values

S_OK
Indicates that the storage object was successfully opened.
STG_E_INVALIDPOINTER
Indicates a non-valid pointer in the ppObjectOpen parameter.
STG_E_INVALIDPARAMETER
Indicates a non-valid value for the grfAttrs, reserved1, reserved2, grfMode, or stgfmt parameters. Can occur if the FILE_FLAG_NO_BUFFERING flag is specified for grfAttrs, but the sector size of the file is not an integer multiple of the underlying disk's sector size.
E_NOINTERFACE
Indicates that the specified interface is not supported.
STG_E_INVALIDFLAG
Indicates an non-valid flag combination in the grfMode pointer (includes both STGM_DELETEONRELEASE and STGM_CONVERT flags).
STG_E_INVALIDNAME
Indicates a non-valid name in the pwcsName parameter.
STG_E_INVALIDFUNCTION
Indicates that the grfMode is set to STGM_DELETEONRELEASE.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_UNIMPLEMENTEDFUNCTION
Indicates that the StgOpenStorageEx function is not implemented by the operating system. In this case, use the StgOpenStorage function instead.
STG_E_INCOMPLETE
Indicates that the file could not be opened because it is on a high-latency device. This can only occur if the riid parameter is IID_IPropertySetStorage, and the stgfmt parameter is STGFMT_FILE.
STG_E_ACCESSDENIED
Indicates that the file could not be opened because the underlying storage device does not allow such access to the current user. When opening the storage object in transacted mode (STGM_TRANSACTED), this error may also indicate that a temporary file could not be created in the temporary directory as specified by the GetTempPath function. The GetTempPath function retrieves the path of the directory designated for temporary files.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

StgOpenStorageEx is a superset of the StgOpenStorage function, and should be used by new code. Future enhancements to Windows structured storage will be exposed through this function. See the following Requirements section for information on supported platforms.

The StgOpenStorageEx function opens the specified root storage object according to the access mode in the grfMode parameter, and, if successful, supplies an interface pointer for the opened storage object in the ppObjectOpen parameter. This function can be used to obtain a compound file implementation of the IStorage or IPropertySetStorage, or to obtain an NTFS file system implementation of IPropertySetStorage.

When you open a file, the system selects a structured storage implementation depending on which STGFMT flag you specify on the file type and on the type of drive where the file is stored.

You can use the StgOpenStorageEx function to get access to the root storage of a structured storage document or the property set storage of any file that supports property sets. For information about which interface identifiers (IIDs) are supported for the different STGFMT values, see STGFMT.

When a file is opened with this function to access the NTFS property set implementation, special sharing rules apply. See IPropertySetStorage-NTFS Implementation.

If a compound file is opened in transcacted mode (by specifying STGM_TRANSACTED) and read-only mode (by specifying STGM_READ), it is possible to make changes to the returned storage object. For example, it is possible to call IStorage::CreateStream. However, it is not possible to commit those changes by calling IStorage::Commit. Therefore, such changes will be lost.

It is not valid to use the STGM_CREATE, STGM_DELETEONRELEASE, or STGM_CONVERT flags in the grfMode parameter for this function.

To support the simple mode for saving a storage object with no substorages, the StgOpenStorageEx function accepts the following flag combinations as valid modes in the grfMode parameter:

    STGM_SIMPLE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE

and

    STGM_SIMPLE | STGM_READ | STGM_SHARE_EXCLUSIVE

To support the single-writer, multireader, direct mode, the following flag combination is the valid grfMode parameter for the writer:

    STGM_DIRECT_SWMR | STGM_READWRITE | STGM_SHARE_DENY_WRITE

And the following combination is valid for readers:

    STGM_DIRECT_SWMR | STGM_READ | STGM_SHARE_DENY_NONE

For more information on simple mode and single-writer/multiple-reader modes, see the STGM documentation.

Note Opening a transacted mode storage object in read and/or write mode without denying write permissions to others (the grfMode parameter specifies STGM_SHARE_DENY_WRITE) can be a time-consuming operation since the StgOpenStorageEx call must make a snapshot copy of the entire storage object.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in Objbase.h.
Library: Use Ole32.lib.

See Also

IStorage, StgCreateDocfile, StgCreateStorageEx, StgOpenStorage, STGFMT, STGM, STGOPTIONS


StgOpenStorageOnILockBytes

Opens an existing storage object that does not reside in a disk file, but instead has an underlying byte array provided by the caller.

WINOLEAPI StgOpenStorageOnILockBytes(
  ILockBytes *plkbyt,       //Pointer to the ILockBytes
                            // interface on the underlying byte array
  IStorage *pStgPriority,   //Pointer to a previous opening
                            // of a root storage object
  DWORD grfMode,            //Specifies the access mode for the object
  SNB snbExclude,           //Pointer to an SNB structure
                            // specifying elements to be excluded
  DWORD reserved,           //Reserved, must be zero
  IStorage **ppstgOpen      //Pointer to location for
                            // returning the storage object
);

Parameters

plkbyt
[in] ILockBytes pointer to the underlying byte array object that contains the storage object to be opened.
pStgPriority
[in] Most often NULL. If not NULL, this parameter is used instead of the plkbyt parameter to specify the storage object to open. In this case, it points to the IStorage interface on a previously opened root storage object, most often one that was opened in priority mode.

After the StgOpenStorageOnILockBytes function returns, the storage object specified in the pStgPriority parameter on function entry is not valid, and can no longer be used; use the one specified in the ppStgOpen parameter instead.

grfMode
[in] Specifies the access mode to use to open the storage object.
snbExclude
[in] Can be NULL. If not NULL, this parameter points to a block of elements in this storage that are to be excluded as the storage object is opened. This exclusion occurs independently of whether a snapshot copy happens on the open.
reserved
[in] Indicates reserved for future use; must be zero.
ppstgOpen
[out] Points to the location of an IStorage pointer to the opened storage on successful return.

Return Values

S_OK
The storage object was successfully opened.
STG_E_FILENOTFOUND
The specified byte array does not exist.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
The byte array exists but is not a storage object.
STG_E_TOOMANYOPENFILES
The storage object was not opened because there are too many open files.
STG_E_INSUFFICIENTMEMORY
The storage object was not opened due to a lack of memory.
STG_E_INVALIDNAME
Either pwcsName or snbExclude contains an invalid name.
STG_E_INVALIDPOINTER
Either snbExclude, pwcsName, pstgPriority, or ppStgOpen contains a non-valid pointer.
STG_E_INVALIDFLAG
The grfMode parameter contains a non-valid flag combination.
STG_E_INVALIDFUNCTION
The access mode STGM_DELETEONRELEASE was specified in the grfMode parameter.
STG_E_OLDDLL
The DLL being used to open this storage object is a version of the DLL that is older than the one used to create it.
STG_E_OLDFORMAT
The storage object being opened was created by the Beta 1 storage provider. This format is no longer supported.

This function can also return any file system errors, or Win32 errors wrapped in an HRESULT, or ILockBytes interface error return values. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

StgOpenStorageOnILockBytes opens the specified root storage object. The storage object is opened according to the access mode in the grfMode parameter; a pointer to the IStorage interface on the opened storage object is supplied through the ppstgOpen parameter.

The storage object must have been previously created by the StgCreateDocfileOnILockBytes function.

Except for specifying a programmer-provided byte-array object, StgOpenStorageOnILockBytes is similar to the StgOpenStorage function. For more information, refer to StgOpenStorage.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Use Ole32.lib.

See Also

StgOpenStorage, StgCreateDocfileOnILockBytes


StgSetTimes

Sets the creation, access, and modification times of the indicated file, if supported by the underlying file system.

WINOLEAPI StgSetTimes(
  WCHAR const *lpszName,    //Pointer to the name of the file
                            // to be changed
  FILETIME const *pctime,   //Pointer to the new value for
                            // the creation time
  FILETIME const *patime,   //Pointer to the new value for
                            // the access time
  FILETIME const *pmtime    //Pointer to the new value for
                            // the modification time
);

Parameters

lpszName
[in] Pointer to the name of the file to be changed.
pctime
[in] Pointer to the new value for the creation time.
patime
[in] Pointer to the new value for the access time.
pmtime
[in] Pointer to the new value for the modification time.

Return Values

S_OK
Indicates time values successfully set.
STG_E_FILENOTFOUND
Indicates that the element does not exist.
STG_E_INVALIDNAME
Indicates a non-valid name passed in the lpszName parameter, or a file system error.
STG_E_ACCESSDENIED
Access denied because the caller does not have enough permissions, or another caller has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.

This function can also return any file system errors or Win32 errors wrapped in an HRESULT. See Error Handling Strategies, HRESULTs, Win32 and Network Errors, and Handling Unknown Errors.

Remarks

The StgSetTimes function sets the time values for the specified file. Each of the time value parameters can be NULL, indicating that no modification should occur.

It is possible that one or more of these time values are not supported by the underlying file system. This function sets the times that can be set and ignores the rest.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.
Library: Included as a resource in Ole32.dll.


WriteClassStg

Stores the specified class identifier (CLSID) in a storage object.

WINOLEAPI WriteClassStg(
  IStorage *pStg,   //Pointer to the IStorage interface on
                    // the storage object
  REFCLSID rclsid   //Specifies the CLSID to be stored in
                    // the storage object
);

Parameters

pStg
[in] IStorage pointer to the storage object that gets a new CLSID.
rclsid
[in] Pointer to the CLSID to be stored with the object.

Return Values

S_OK
Indicates that the CLSID was successfully written to the file.
STG_E_MEDIUMFULL
Indicates that the CLSID could not be written due to lack of memory.

IStorage::SetClass method error return values.

Remarks

The WriteClassStg function writes a CLSID to the specified storage object so that it can be read by the ReadClassStg function. Container applications typically call this function before calling the IPersistStorage::Save method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

OleSave, ReadClassStg


WriteClassStm

Stores the specified CLSID in the stream.

WINOLEAPI WriteClassStm(
  IStream *pStm,   //Pointer to the IStream interface on the
                   // stream object
  REFCLSID rclsid  //Specifies the CLSID to be stored in the
                   // stream object
);

Parameters

pStm
[in] IStream pointer to the stream into which the CLSID is to be written.
rclsid
[in] Specifies the CLSID to write to the stream.

Return Values

S_OK
Indicates that the CLSID was successfully written.
STG_E_MEDIUMFULL
The CLSID could not be written because there is no space left on device.

IStorage::SetClass method error return values.

Remarks

The WriteClassStm function writes a CLSID to the specified stream object so it can be read by the ReadClassStm function. Most applications do not call WriteClassStm directly. OLE calls it before making a call to an object's IPersistStream::Save method.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

ReadClassStm, WriteClassStg, ReadClassStg


WriteFmtUserTypeStg

Writes a clipboard format and user type to the storage object.

WINOLEAPI WriteFmtUserTypeStg(
  IStorage *pStg,        //Pointer to the IStorage interface
                         // on the storage object
  CLIPFORMAT cf,         //Specifies the clipboard format
  LPWSTR *lpszUserType   //Pointer to the current user type
);

Parameters

pStg
[in] IStorage pointer to the storage object where the information is to be written.
cf
[in] Specifies the clipboard format that describes the structure of the native area of the storage object. The format tag includes the policy for the names of streams and substorages within this storage object and the rules for interpreting data within those streams.
lpszUserType
[in] Pointer to the object's current user type. It cannot be NULL. This is the type returned by the IOleObject::GetUserType method. If this function is transported to a remote machine where the object class does not exist, this persistently stored user type can be shown to the user in dialog boxes.

Return Values

S_OK
Indicates that the information was written successfully.
STG_E_MEDIUMFULL
Indicates that information could not be written due to lack of space on the storage medium.

Remarks

The WriteFmtUserTypeStg function must be called in an object's implementation of the IPersistStorage::Save method. It must also be called by document-level objects that use structured storage for their persistent representation in their save sequence.

To read the information saved, applications call the ReadFmtUserTypeStg function.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Ole2.h.
Library: Included as a resource in Ole32.dll.

See Also

IPersistStorage::Save, ReadFmtUserTypeStg


Structures

Structures are primarily collections of data elements or members that facilitate an organizational scheme for interpreting data or performing operations on it. The Structured Storage structures are described in the following sections.

  • PROPSPEC
  • PROPVARIANT
  • RemSNB
  • SNB
  • STATPROPSETSTG
  • STATPROPSTG
  • STATSTG
  • STGOPTIONS
  • StorageLayout


PROPSPEC

The PROPSPEC structure is used by many of the methods of IPropertyStorage to specify a property either by its property identifier (ID) or the associated string name. The structure and related definitions are defined as follows in the header files:

const ULONG    PRSPEC_LPWSTR = 0
const ULONG    PRSPEC_PROPID = 1

typedef ULONG    PROPID

typedef struct tagPROPSPEC
{
  ULONG ulKind;        // PRSPEC_LPWSTR or PRSPEC_PROPID
  union
  {
  PROPID      propid;
  LPOLESTR    lpwstr;
  }
} PROPSPEC

Members

ulKind
If ulKind is set to PRSPEC_LPWSTR, the lpwstr member is used and set to a string name. If ulKind is set to PRSPEC_PROPID, propid is used and set to a property ID value.
propid
Specifies the value of the property ID. Use either this value or the following lpwstr, not both.
lpwstr
Specifies the string name of the property as a null-terminated Unicode string.

Remarks

String names are optional and can be assigned to a set of properties when the property is created with a call to IPropertyStorage::WriteMultiple or, later, with a call to IPropertyStorage::WritePropertyNames.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

IPropertyStorage


PROPVARIANT

The PROPVARIANT structure is used in the ReadMultiple and WriteMultiple methods of IPropertyStorage to define the type tag and the value of a property in a property set. There are five members. The first, the value-type tag, and the last, the value of the property, are significant. The middle three are reserved for future use.

Note The bool member in previous definitions of this structure has been renamed to boolVal, since some compilers now recognize bool as a keyword.

Note The PROPVARIANT structure, defined below, includes types that can be serialized in the version 1 property set serialization format. The version 1 format supports all types allowed in the version 0 format plus some additional types. The added types include "Version 1" in the comment field below. Use these types only if a version 1 property set is intended. See Version 0 vs. Version 1 Property Set Serialization for more information.

The PROPVARIANT structure is defined as follows:

struct PROPVARIANT{
  VARTYPE        vt;          // value type tag
  WORD           wReserved1;
  WORD           wReserved2;
  WORD           wReserved3;
  union {
  // none                     // VT_EMPTY, VT_NULL, VT_ILLEGAL
  CHAR           cVal;        // VT_I1, Version 1
  UCHAR          bVal;        // VT_UI1
  SHORT          iVal;        // VT_I2
  USHORT         uiVal;       // VT_UI2
  LONG           lVal;        // VT_I4
  INT            intVal;      // VT_INT, Version 1
  ULONG          ulVal;       // VT_UI4
  UINT           uintVal;     // VT_UINT, Version 1
  LARGE_INTEGER  hVal;        // VT_I8
  ULARGE_INTEGER uhVal;       // VT_UI8
  FLOAT          fltVal;      // VT_R4
  DOUBLE         dblVal;      // VT_R8
  CY             cyVal;       // VT_CY
  DATE           date;        // VT_DATE
  BSTR           bstrVal;     // VT_BSTR
  VARIANT_BOOL   boolVal;     // VT_BOOL
  SCODE          scode;       // VT_ERROR
  FILETIME       filetime;    // VT_FILETIME
  LPSTR          pszVal;      // VT_LPSTR
  LPWSTR         pwszVal;     // VT_LPWSTR
  CLSID*         puuid;       // VT_CLSID
  CLIPDATA*      pclipdata;   // VT_CF

  BLOB           blob;        // VT_BLOB, VT_BLOBOBJECT
  IStream*       pStream;     // VT_STREAM, VT_STREAMED_OBJECT
  IStorage*      pStorage;    // VT_STORAGE, VT_STORED_OBJECT
  IUnknown*      punkVal;     // VT_UNKNOWN
  IDispatch*     pdispVal;    // VT_DISPATCH
  LPSAFEARRAY    parray;      // VT_ARRAY | VT_*, Version 1

  CAC            cac;         // VT_VECTOR | VT_I1, Version 1
  CAUB           caub;        // VT_VECTOR | VT_UI1
  CAI            cai;         // VT_VECTOR | VT_I2
  CAUI           caui;        // VT_VECTOR | VT_UI2
  CAL            cal;         // VT_VECTOR | VT_I4
  CAUL           caul;        // VT_VECTOR | VT_UI4
  CAH            cah;         // VT_VECTOR | VT_I8
  CAUH           cauh;        // VT_VECTOR | VT_UI8
  CAFLT          caflt;       // VT_VECTOR | VT_R4
  CADBL          cadbl;       // VT_VECTOR | VT_R8
  CACY           cacy;        // VT_VECTOR | VT_CY
  CADATE         cadate;      // VT_VECTOR | VT_DATE
  CABSTR         cabstr;      // VT_VECTOR | VT_BSTR
  CABOOL         cabool;      // VT_VECTOR | VT_BOOL
  CASCODE        cascode;     // VT_VECTOR | VT_ERROR
  CALPSTR        calpstr;     // VT_VECTOR | VT_LPSTR
  CALPWSTR       calpwstr;    // VT_VECTOR | VT_LPWSTR
  CAFILETIME     cafiletime;  // VT_VECTOR | VT_FILETIME
  CACLSID        cauuid;      // VT_VECTOR | VT_CLSID
  CACLIPDATA     caclipdata;  // VT_VECTOR | VT_CF
  CAPROPVARIANT  capropvar;   // VT_VECTOR | VT_VARIANT

  CHAR*          pcVal;       // VT_BYREF | VT_I1, Version 1
  UCHAR*         pbVal;       // VT_BYREF | VT_UI1, Version 1
  SHORT*         piVal;       // VT_BYREF | VT_I2, Version 1
  USHORT*        puiVal;      // VT_BYREF | VT_UI2, Version 1
  LONG*          plVal;       // VT_BYREF | VT_I4, Version 1
  ULONG*         pulVal;      // VT_BYREF | VT_UI4, Version 1
  INT*           pintVal;     // VT_BYREF | VT_INT, Version 1
  UINT*          puintVal;    // VT_BYREF | VT_UINT, Version 1
  FLOAT*         pfltVal;     // VT_BYREF | VT_R4, Version 1
  DOUBLE*        pdblVal;     // VT_BYREF | VT_R8, Version 1
  VARIANT_BOOL*  pboolVal;    // VT_BYREF | VT_BOOL, Version 1
  DECIMAL*       pdecVal;     // VT_BYREF | VT_DECIMAL, Version 1
  SCODE*         pscode;      // VT_BYREF | VT_ERROR, Version 1
  CY*            pcyVal;      // VT_BYREF | VT_CY, Version 1
  PROPVARIANT*   pvarVal;     // VT_BYREF | VT_VARIANT, Version 1

  }} PROPVARIANT

The PROPVARIANT structure can also hold a value of VT_DECIMAL:

    DECIMAL       decVal;        //VT_DECIMAL

However, the value of the DECIMAL structure requires special handling. The DECIMAL structure is the same size as an entire PROPVARIANT structure and does not fit into the union that holds all other types of values. Instead, the value of the DECIMAL structure occupies the entire PROPVARIANT structure, including the reserved fields and the vt member. However, the first member of the DECIMAL structure is not used and is equal in size to the vt member of the PROPVARIANT structure. Therefore, the PROPVARIANT structure declaration in the propidl.h header file of Microsoft® Win32® defines the decVal member in such a way that it corresponds to the beginning of the PROPVARIANT structure. Therefore, to put the value of the DECIMAL structure into a PROPVARIANT structure, the value must be loaded into the decVal member and the vt member is set to VT_DECIMAL, just as for any other value.

Remarks

PROPVARIANT is the fundamental data type by which property values are read and written through the IPropertyStorage interface.

The data type PROPVARIANT is related to the data type VARIANT, defined as part of Automation in OLE2. Several definitions are reused from Automation, as follows:

typedef struct  tagCY {
    unsigned long      Lo;
    long               Hi;
    } CY;

typedef struct  tagDEC {
    USHORT             wReserved;
    BYTE               scale;
    BYTE               sign;
    ULONG              Hi32;
    ULONGLONG          Lo64;
    } DECIMAL;

typedef struct  tagSAFEARRAYBOUND {
    ULONG              cElements;
    LONG               lLbound;
    } SAFEARRAYBOUND;

typedef struct  tagSAFEARRAY {
    USHORT             cDims;
    USHORT             fFeatures;
    ULONG              cbElements;
    ULONG              cLocks;
    PVOID              pvData;
    SAFEARRAYBOUND     rgsabound [ * ];
    } SAFEARRAY;

typedef CY             CURRENCY;
typedef short          VARIANT_BOOL;
typedef unsigned short VARTYPE;
typedef double         DATE;
typedef OLECHAR*       BSTR;

In addition, some types are unique to the PROPVARIANT structure:

typedef struct  tagCLIPDATA {
    // cbSize is the size of the buffer pointed to
    // by pClipData, plus sizeof(ulClipFmt)
    ULONG              cbSize;
    long               ulClipFmt;
    BYTE*              pClipData;
    } CLIPDATA;

Among the unique PROPVARIANT types are several data types that define counted arrays of other data types. The data types of all counted arrays begin with the letters CA (such as CAUB) and have an OR operator vt value (the VarType of the element and an OR operator with VT VECTOR). The counted array structure has the following form (where name is the specific name of the counted array).

#define TYPEDEF_CA(type, name)

    typedef struct tag ## name {\
        ULONG cElems;\
        type *pElems;\
        } name
Propvariant Type | Code | Propvariant Member | Value Representation
VT_EMPTY | 0 | None | A property with a type indicator of VT_EMPTY has no data associated with it; that is, the size of the value is zero.
VT_NULL | 1 | None | This is like a pointer to NULL.
VT_I1 | 16 | cVal | 1-byte signed integer.
VT_UI1 | 17 | bVal | 1-byte unsigned integer.
VT_I2 | 2 | iVal | Two bytes representing a 2-byte signed integer value.
VT_UI2 | 18 | uiVal | 2-byte unsigned integer.
VT_I4 | 3 | lVal | 4-byte signed integer value.
VT_INT | 22 | intVal | 4-byte signed integer value (equivalent to VT_I4).
VT_UI4 | 19 | ulVal | 4-byte unsigned integer.
VT_UINT | 23 | uintVal | 4-byte unsigned integer (equivalent to VT_UI4).
VT_I8 | 20 | hVal | 8-byte signed integer.
VT_UI8 | 21 | uhVal | 8-byte unsigned integer.
VT_R4 | 4 | fltVal | 32-bit IEEE floating point value.
VT_R8 | 5 | dblVal | 64-bit IEEE floating point value.
VT_CY | 6 | cyVal | 8-byte two's complement integer (scaled by 10,000). This type is commonly used for currency amounts.
VT_DATE | 7 | date | A 64-bit floating point number representing the number of days (not seconds) since December 31, 1899. For example, January 1, 1900, is 2.0, January 2, 1900, is 3.0, and so on). This is stored in the same representation as VT_R8.
VT_BSTR | 8 | bstrVal | Pointer to a null-terminated Unicode string. The string is immediately preceded by a DWORD representing the byte count, but bstrVal points past this DWORD to the first character of the string. BSTRs must be allocated and freed using the Automation SysAllocString and SysFreeString calls.
VT_BOOL | 11 | boolVal (bool in earlier designs) | Boolean value, a WORD containing 0 (FALSE) or -1 (TRUE).
VT_ERROR | 10 | scode | A DWORD containing a status code.
VT_FILETIME | 64 | filetime | 64-bit FILETIME structure as defined by Win32. It is recommended that all times be stored in Universal Coordinate Time (UTC).
VT_LPSTR | 30 | pszVal | Pointer to a null-terminated ANSI string in the system default code page.
VT_LPWSTR | 31 | pwszVal | Pointer to a null-terminated Unicode string in the user's default locale.
VT_CLSID | 72 | puuid | Pointer to a class identifier (CLSID) (or other globally unique identifier (GUID)).
VT_CF | 71 | pclipdata | Pointer to a CLIPDATA structure, described above.
VT_BLOB | 65 | blob | DWORD count of bytes, followed by that many bytes of data. The byte count does not include the four bytes for the length of the count itself; an empty blob member would have a count of zero, followed by zero bytes. This is similar to the value VT_BSTR but does not guarantee a null byte at the end of the data.
VT_BLOBOBJECT 70 blob A blob member containing a serialized object in the same representation that would appear in VT_STREAMED_OBJECT. That is, a DWORD byte count (where the byte count does not include the size of itself) which is in the format of a class identifier followed by initialization data for that class.

The only significant difference between VT_BLOB_OBJECT and VT_STREAMED_OBJECT is that the former does not have the system-level storage overhead that the latter would have, and is therefore more suitable for scenarios involving numbers of small objects.
VT_STREAM | 66 | pStream | Pointer to an IStream interface, representing a stream which is a sibling to the "Contents" stream.
VT_STREAMED_OBJECT | 68 | pStream | As in VT_STREAM, but indicates that the stream contains a serialized object, which is a CLSID followed by initialization data for the class. The stream is a sibling to the "Contents" stream that contains the property set.
VT_STORAGE | 67 | pStorage | Pointer to an IStorage interface, representing a storage object that is a sibling to the "Contents" stream.
VT_STORED_OBJECT | 69 | pStorage | As in VT_STORAGE, but indicates that the designated IStorage contains a loadable object.
VT_DECIMAL | 14 | decVal | A DECIMAL structure.
VT_VECTOR 0x1000 ca* If the type indicator is combined with VT_VECTOR by using an OR operator, the value is one of the counted array values. This creates a DWORD count of elements, followed by a pointer to the specified repetitions of the value.

For example, a type indicator of VT_LPSTR|VT_VECTOR has a DWORD element count, followed by a pointer to an array of LPSTR elements.

VT_VECTOR can be combined by an OR operator with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_BOOL, VT_I4, VT_UI4, VT_R4, VT_R8, VT_ERROR, VT_I8, VT_UI8, VT_CY, VT_DATE, VT_FILETIME, VT_CLSID, VT_CF, VT_BSTR, VT_LPSTR, VT_LPWSTR, and VT_VARIANT.
VT_ARRAY | 0x2000 | Parray | If the type indicator is combined with VT_ARRAY by an OR operator, the value is a pointer to a SAFEARRAY. VT_ARRAY can be ORed with the following data types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_INT, VT_UINT, VT_R4, VT_R8, VT_BOOL, VT_DECIMAL, VT_ERROR, VT_CY, VT_DATE, and VT_BSTR. VT_ARRAY cannot be ORed with VT_VECTOR.
VT_BYREF 0x4000 p* If the type indicator is combined with VT_BYREF by an OR operator, the value is a reference. Reference types are interpreted as a reference to data, similar to the reference type in C++ (for example, "int&").

VT_BYREF can be ORed with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_INT, VT_UINT, VT_R4, VT_R8, VT_BOOL, VT_DECIMAL, VT_ERROR, VT_CY, VT_DATE, VT_BSTR, VT_ARRAY, and VT_VARIANT.
VT_VARIANT | 12 | capropvar | A DWORD type indicator followed by the corresponding value. VT_VARIANT can be used only with VT_VECTOR or VT_BYREF.
VT_TYPEMASK | 0xFFF | Used as a mask for VT_VECTOR and other modifiers to extract the raw VT value.

Clipboard format identifiers, stored with the tag VT_CF, use one of five representations, identified in the ulClipFmt member of the CLIPDATA structure using the pClipData pointer to the particular data type.

ulClipFmt Value | pClipData Value
-1L | A DWORD containing a built-in Windows® clipboard format value.
-2L | A DWORD containing a Macintosh clipboard format value.
-3L | A GUID containing a format identifier (FMTID). This is rarely used.
any positive value | A null-terminated string containing a Windows clipboard format name, one suitable for passing to the RegisterClipboardFormat function. This function registers a new clipboard format. If a registered format with the specified name already exists, a new format is not registered and the return value identifies the existing format. This enables more than one application to copy and paste data using the same registered clipboard format. The format name comparison is case insensitive and is identified by values in the range of 0xC000 through 0xFFFF. The code page used for characters in the string is per the code-page indicator. The "positive value" here is the length of the string, including the null byte at the end. When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL data-type value, which provides the handle to the object..
0L | No data (rarely used).

ClipBoard Format Values

If the value of the ulClipFmt member is -1, the data is in the form of a built-in Windows format. In this case, the first DWORD of the buffer pointed to by pClipData is the clipboard format identifier, for example CF_METAFILEPICT. In the case of CF_METAFILEPCT, what follows is a variation on the METAFILEPICT structure (it uses WORD, rather than DWORD data types). That is, this data is in the following form:

struct PACKEDMETA
{
    WORD mm;
    WORD xExt;
    WORD yExt
    WORD reserved;
};

After the METAFILEPICT structure is the metafile data, suitable to be passed to the SetMetaFileBitsEx function. This function creates a memory-based, Windows-format metafile from the supplied data. This function is provided for compatibility with 16-bit versions of Windows. Win32-based applications should use the SetEnhMetaFileBits function. This function retrieves the contents of the specified enhanced-format metafile and copies them into a buffer. If the function succeeds and the buffer pointer is NULL, the return value is the size of the enhanced metafile in bytes. If the function succeeds and the buffer pointer is a valid pointer, the return value is the number of bytes copied to the buffer. If the function fails, the return value is zero.

When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL value.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Objbase.h.


RemSNB

The RemSNB structure is used for marshaling the SNB data type.

Defined in the IStorage interface (storag.idl).

typedef struct tagRemSNB {
  unsigned                long     ulCntStr;
  unsigned                long     ulCntChar;
  [size_is(ulCntChar)]    wchar_t  rgString[];
} RemSNB;
typedef [transmit_as(RemSNB)] wchar_t  **SNB;

Members

ulCntStr
Number of strings in the rgString buffer.
ulCntChar
Size in bytes of the rgString buffer.
rgString
Pointer to an array of bytes containing the stream name strings from the SNB structure.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage


SNB

A string name block (SNB) is a pointer to an array of pointers to strings, that ends in a NULL pointer. String name blocks are used by the IStorage interface and by function calls that open storage objects. The strings point to contained storage objects or streams that are to be excluded in the open calls.

typedef [wire_marshal(wireSNB)] OLESTR **SNB

Remarks

The SNB should be created by allocating a contiguous block of memory in which the pointers to strings are followed by a NULL pointer, which is then followed by the actual strings.

The marshaling of an SNB is based on the assumption that the SNB that was passed in was created in this way. Although it could be stored in other ways, the SNB created in this manner has the advantage of requiring only one allocation operation and one freeing of memory for all the strings.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage


STATPROPSETSTG

Contains information about a property set. To get this information, call IPropertyStorage::Stat, which fills in a buffer containing the information describing the current property set. To enumerate the STATPROPSETSTG structures for the property sets in the current property-set storage, call IPropertySetStorage::Enum to get a pointer to an enumerator. You can then call the enumeration methods of the IEnumSTATPROPSETSTG interface on the enumerator. The structure is defined as follows:

typedef struct tagSTATPROPSETSTG {
  FMTID       fmtid;
  CLSID       clsid;
  DWORD       grfFlags;
  FILETIME    mtime;
  FILETIME    ctime;
  FILETIME    atime;
  } STATPROPSETSTG

Members

fmtid
FMTID of the current property set, specified when the property set was initially created.
clsid
CLSID associated with this property set, specified when the property set was initially created and possibly modified thereafter with IPropertyStorage::SetClass. If not set, the value will be CLSID_NULL.
grfFlags
Flag values of the property set, as specified in IPropertySetStorage::Create.
mtime
Time in Universal Coordinated Time (UTC) when the property set was last modified.
ctime
Time in UTC when this property set was created.
atime
Time in UTC when this property set was last accessed.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

IPropertySetStorage::Create, IEnumSTATPROPSETSTG, IPropertyStorage::Stat, FILETIME structure


STATPROPSTG

Each STATPROPSTG structure contains information about a single property in a property set. This information is the property ID and type tag, and the optional string name that may be associated with the property.

IPropertyStorage::Enum supplies a pointer to the IEnumSTATPROPSTG interface on an enumerator object that can be used to enumerate through the STATPROPSTG structures for the properties in the current property set. STATPROPSTG is defined as follows:

typedef struct    tagSTATPROPSTG {
  LPWSTR        lpwstrName;
  PROPID        propid;
  VARTYPE       vt;
} STATPROPSTG

Members

lpwstrName
Wide-character string containing the optional string name that can be associated with the property. May be NULL. This member must be freed using CoTaskMemFree.
propid
A 32-bit identifier that uniquely identifies the property within the property set. All properties within property sets must have unique property identifiers.
vt
Type of the property.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

IPropertyStorage::Enum, IEnumSTATPROPSTG


STATSTG

The STATSTG structure contains statistical information about an open storage, stream, or byte-array object. This structure is used in the IEnumSTATSTG, ILockBytes, IStorage, and IStream interfaces.

typedef struct tagSTATSTG
{
  LPWSTR          pwcsName;
  DWORD           type;
  ULARGE_INTEGER  cbSize;
  FILETIME        mtime;
  FILETIME        ctime;
  FILETIME        atime;
  DWORD           grfMode;
  DWORD           grfLocksSupported;
  CLSID           clsid;
  DWORD           grfStateBits;
  DWORD           reserved;
} STATSTG;

Members

pwcsName
Pointer to a NULL-terminated string containing the name. Space for this string is allocated by the method called and freed by the caller (see CoTaskMemFree). You can specify not to return this member by specifying the STATFLAG_NONAME value when you call a method that returns a STATSTG structure, except for calls to IEnumSTATSTG::Next, which provides no way to specify this value.
type
Indicates the type of storage object. This is one of the values from the STGTY enumeration.
cbSize
Specifies the size in bytes of the stream or byte array.
mtime
Indicates the last modification time for this storage, stream, or byte array.
ctime
Indicates the creation time for this storage, stream, or byte array.
atime
Indicates the last access time for this storage, stream or byte array.
grfMode
Indicates the access mode specified when the object was opened. This member is only valid in calls to Stat methods.
grfLocksSupported
Indicates the types of region locking supported by the stream or byte array. See the LOCKTYPE enumeration for the values available. This member is not used for storage objects.
clsid
Indicates the class identifier for the storage object; set to CLSID_NULL for new storage objects. This member is not used for streams or byte arrays.
grfStateBits
Indicates the current state bits of the storage object; that is, the value most recently set by the IStorage::SetStateBits method. This member is not valid for streams or byte arrays.
reserved
Reserved for future use.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStorage::SetElementTimes


STGOPTIONS

The STGOPTIONS structure is used to specify features of the storage object, such as the sector size, in the StgCreateStorageEx and StgOpenStorageEx functions.

typedef struct tagSTGOPTIONS
{
  USHORT      usVersion;
  USHORT      reserved;
  ULONG       ulSectorSize;
} STGOPTIONS;

Members

usVersion
Specifies the version of the STGOPTIONS structure. Currently, it should be set to STGOPTIONS_VERSION, which is version 1.
reserved
Reserved for future use; must be zero.
ulSectorSize
Specifies the sector size of the storage object. The default is 512 bytes.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Declared in Objbase.h.

See Also

StgCreateStorageEx, StgOpenStorageEx, Compound File Implementation Limits


StorageLayout

The StorageLayout structure describes a single block of data, including its name, location, and length. To optimize a compound file, an application or layout tool passes an array of StorageLayout structures in a call to ILayoutStorage::LayoutScript.

typedef struct tagStorageLayout
{
  DWORD           LayoutType;
  OLECHAR*        pwcsElementName;
  LARGE_INTEGER   cOffset;
  LARGE_INTEGER   cBytes;
} StorageLayout;

Members

LayoutType
The type of element to be written. Values are taken from the STGTY enumeration. STGTY_STREAM means read the block of data named by pwcsElementName. STGTY_STORAGE means open the storage specified in pwcsElementName. STGTY_REPEAT is used in multimedia applications to interface audio, video, text, and other elements. An opening STGTY_REPEAT value means that the elements that follow are to be repeated a specified number of times. The closing STGTY_REPEAT value marks the end of those elements that are to be repeated. Nested STGY_REPEAT value pairs are permitted.
pwcsElementName
The name of the storage or stream. If the element is a substorage or embedded object, the fully qualified storage path must be specified; for example, RootStorageName\SubStorageName\Substream.
cOffset
Where the value of the LayoutType member is STGTY_STREAM, this flag specifies the beginning offset into the steam named in the pwscElementName member.

Where LayoutType is STGTY_STORAGE, this flag should be set to zero.

Where LayoutType is STGTY_REPEAT, this flag should be set to zero.

cBytes
Length in bytes of the data block named in pwcsElementName.

Where LayoutType is STGTY_STREAM, cBytes specifies the number of bytes to read at cOffset from the stream named in pwcsElementName.

Where LayoutType is STGTY_STORAGE, this flag is ignored.

Where LayoutType is STGTY_REPEAT, a positive cBytes specifies the beginning of a repeat block. STGTY_REPEAT with zero cBytes marks the end of a repeat block.

A beginning block value of STG_TOEND specifies that elements in a following block are to be repeated after each stream has been completely read.

An array of StorageLayout structures might appear as follows:

StorageLayout arrScript[]=
    // Read first 2k of "WordDocument" stream
    {STGTY_STREAM,L"WordDocument",{0,0},{0,2048}},

    //Test if "ObjectPool\88112233" storage exists
    {STGTY_STORAGE,L"ObjectPool\\88112233",{0,0},{0,0}},

    //Read 2k at offset 1048 of "WordDocument" stream
    {STGTY_STREAM,L"WordDocument",{0,10480},{0,2048}},

    //Interlace "Audio", "Video", and "Caption" streams
    {STGTY_REPEAT,NULL,0,STG_TOEND},
        {STGTY_STREAM,L"Audio", {0,0},{0,2048}},    // 2k of Audio
        {STGTY_STREAM,L"Video", {0,0},{0,65536}},   // 64k of Video
        {STGTY_STREAM,L"Caption", {0,0},{0,128}},   // 128b of text
    {STGTY_REPEAT,NULL, {0,0},{0,0}}
};

Note The parameters cOffset and cBytes are LARGE_INTEGER structures, used to represent a 64-bit signed integer value as a union of two 32-bit members. The two 32-bit members must be represented as a LARGE_INTEGER structure with DWORD LowPart AND LONG HighPart as the structure members. (LowPart specifies the low-order 32 bits and HighPart specifies the high-order 32 bits.)}. If your compiler has built-in support for 64-bit integers, use the QuadPart member of the LARGE_INTEGER structure to store the 64-bit integer.

Requirements

Windows NT/2000: Requires Windows NT 4.0 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

ILayoutStorage::LayoutScript


Enumerations

Enumerations in Structured Storage are data types consisting of a sequence of named values in a given order. An enumerated data type allows a member of a class of objects to be described in a natural language form. Enumerations are most useful in the context where all possible values of a variable are known and the expression of that variable in the programming language can therefore be expressed in natural-language words.

The Structured Storage enumerations are described in the following sections.

  • LOCKTYPE
  • PROPSETFLAG
  • STATFLAG
  • STGC
  • STGFMT
  • STGM
  • STGMOVE
  • STGTY
  • STREAM_SEEK


LOCKTYPE

The LOCKTYPE enumeration values indicate the type of locking requested for the specified range of bytes. The values are used in the ILockBytes::LockRegion and IStream::LockRegion methods.

typedef enum tagLOCKTYPE
{
  LOCK_WRITE        = 1,
  LOCK_EXCLUSIVE    = 2,
  LOCK_ONLYONCE     = 4
} LOCKTYPE;

Elements

LOCK_WRITE
If this lock is granted, the specified range of bytes can be opened and read any number of times, but writing to the locked range is prohibited except for the owner that was granted this lock.
LOCK_EXCLUSIVE
If this lock is granted, writing to the specified range of bytes is prohibited except by the owner that was granted this lock.
LOCK_ONLYONCE
If this lock is granted, no other LOCK_ONLYONCE lock can be obtained on the range. Usually this lock type is an alias for some other lock type. Thus, specific implementations can have additional behavior associated with this lock type.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.


PROPSETFLAG

The PROPSETFLAG enumeration values define characteristics of a property set. The values are used in the grfFlags parameter of IPropertySetStorage methods, the StgCreatePropStg function, and the StgOpenPropStg function.

typedef enum PROPSETFLAG {
  PROPSETFLAG_DEFAULT         = 0,
  PROPSETFLAG_NONSIMPLE       = 1,
  PROPSETFLAG_ANSI            = 2,
  PROPSETFLAG_UNBUFFERED      = 4,
  PROPSETFLAG_CASE_SENSITIVE  = 8
  } PROPSETFLAG

Elements

PROPSETFLAG_DEFAULT
If left unspecified, by default only simple property values may be written to the property set. Using simple property values prevents property sets from being transacted in the compound file and stand-alone implementations of IPropertySetStorage. Nonsimple property values must be used for this purpose.
PROPSETFLAG_NONSIMPLE
If specified, nonsimple property values can be written to the property set and the property set is saved in a storage object. Nonsimple property values include those with a VARTYPE of VT_STORAGE, VT_STREAM, VT_STORED_OBJECT, or VT_STREAMED_OBJECT. If this flag is not specified, nonsimple types cannot be written into the property set. In the compound file and stand-alone implementations, property sets may be transacted only if PROPSETFLAG_NONSIMPLE is specified.
PROPSETFLAG_ANSI
If specified, all string values in the property set that are not explicitly Unicode (those other than VT_LPWSTR) are stored with the current system ANSI code page (see the Win32® function GetACP). Use of this value is not recommended, as described in the following Remarks section.

If this value is absent, string values in the new property set are stored in Unicode. The degree of control that this value affords is necessary so that clients using the property-related interfaces can interoperate well with standard property sets such as the OLE2 summary information, which may exist in the ANSI code page.

PROPSETFLAG_UNBUFFERED
Used only with the StgCreatePropStg and StgOpenPropStg functions; that is, in the stand-alone implementations of property set interfaces. If specified in these functions, changes to the property set are not buffered. Instead, changes are always written directly to the property set. Calls to a property set's IPropertyStorage methods will change it. However, by default, changes are buffered in an internal property set cache and are subsequently written to the property set when the IPropertyStorage::Commit method is called.

Setting PROPSETFLAG_UNBUFFERED reduces performance because the property set's internal buffer is automatically flushed after every change to the property set. However, writing changes directly will prevent coordination problems. For example, suppose the storage object is opened in transacted mode, and the property set is buffered. Then, if you call the IStorage::Commit method on the storage object, the property set changes will not be picked up as part of the transaction, because they are in a buffer that has not been flushed yet. You must call IPropertyStorage::Commit prior to calling IStorage::Commit to flush the property set buffer before committing changes to the storage. As an alternative to making two calls, you can set PROPSETFLAG_UNBUFFERED so that changes are always written directly to the property set and are never buffered in the property set's internal cache. Then, the changes will be picked up when the transacted storage is committed.

PROPSETFLAG_CASE_SENSITIVE
If specified, property names are case sensitive. Case-sensitive property names are only possible in the version 1 property set serialization format. See Version 0 vs. Version 1 Property Set Serialization for more information.

Remarks

These values can be set and checked using bitwise operations that determine how property sets are created and opened. Property sets are created using the IPropertySetStorage::Create method or the StgCreatePropStg function. They are opened using the IPropertySetStorage::Open method or the StgOpenPropStg function.

It is recommended that property sets be created as Unicode by not setting the PROPSETFLAG_ANSI flag in the grfFlags parameter. It is also recommended that you avoid using VT_LPSTR values, and use VT_LPWSTR values instead. When the property set code page is Unicode, VT_LPSTR string values are converted to Unicode when stored, and converted back to multibyte string values when retrieved. When the code page of the property set is not Unicode, property names, VT_BSTR strings, and nonsimple property values are converted to multibyte strings when stored, and converted back to Unicode when retrieved, all using the current system ANSI code page.

Requirements

Windows NT/2000: Requires Windows NT 4.0 SP2 or later.
Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
Header: Declared in Propidl.h.

See Also

FmtIdToPropStgName, IPropertySetStorage::Create, IPropertySetStorage::Open, PropStgNameToFmtId, StgCreatePropSetStg, StgCreatePropStg, StgOpenPropStg


STATFLAG

The STATFLAG enumeration values indicate whether the method should try to return a name in the pwcsName member of the STATSTG structure. The values are used in the ILockBytes::Stat, IStorage::Stat, and IStream::Stat methods to save memory when the pwcsName member is not needed.

Defined in the IOLETypes pseudo-interface (oletyp.idl).

typedef enum tagSTATFLAG
{
  STATFLAG_DEFAULT    = 0,
  STATFLAG_NONAME     = 1
} STATFLAG;

Elements

STATFLAG_DEFAULT
Requests that the statistics include the pwcsName member of the STATSTG structure.
STATFLAG_NONAME
Requests that the statistics not include the pwcsName member of the STATSTG structure. If the name is omitted, there is no need for the Stat methods to allocate and free memory for the string value of the name, therefore the method reduces time and resources used in an allocation and free operation.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Wtypes.idl.

See Also

ILockBytes::Stat, IStorage::Stat, IStream::Stat


STGC

The STGC enumeration constants specify the conditions for performing the commit operation in the IStorage::Commit and IStream::Commit methods.

typedef enum tagSTGC
{
  STGC_DEFAULT                               = 0,
  STGC_OVERWRITE                             = 1,
  STGC_ONLYIFCURRENT                         = 2,
  STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE    = 4,
  STGC_CONSOLIDATE                           = 8
} STGC;

Elements

STGC_DEFAULT
You can specify this condition with STGC_CONSOLIDATE, or some combination of the other three flags in this list of elements. You would use this value mainly to make your code more readable.
STGC_OVERWRITE
The commit operation can overwrite existing data to reduce overall space requirements. This value is not recommended for typical usage because it is not as robust as the default value. In this case, it is possible for the commit operation to fail after the old data is overwritten but before the new data is completely committed. Then, neither the old version nor the new version of the storage object will be intact.

You can use this value in the following cases:

  • The user has indicated a willingness to risk losing the data.
  • The low-memory save sequence will be used to safely save the storage object to a smaller file.
  • A previous commit returned STG_E_MEDIUMFULL but overwriting the existing data would provide enough space to commit changes to the storage object.

Note that the commit operation checks for adequate space before any overwriting occurs. Thus, even with this value specified, if the commit operation fails due to space requirements, the old data will remain safe. It is possible, however, for data loss to occur with the STGC_OVERWRITE value specified if the commit operation fails for any reason other than lack of disk space.

STGC_ONLYIFCURRENT
Prevents multiple users of a storage object from overwriting each another's changes. The commit operation occurs only if there have been no changes to the saved storage object since the user most recently opened it. Thus, the saved version of the storage object is the same version that the user has been editing. If other users have changed the storage object, the commit operation fails and returns the STG_E_NOTCURRENT value. You can override this behavior by calling the IStorage::Commit or IStream::Commit method again using the STGC_DEFAULT value.
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE
Commits the changes to a write-behind disk cache, but does not save the cache to the disk. In a write-behind disk cache, the operation that writes to disk actually writes to a disk cache, thus increasing performance. The cache is eventually written to the disk, but usually not until after the write operation has already returned. The performance increase comes at the expense of an increased risk of losing data if a problem occurs before the cache is saved and the data in the cache is lost.

If you do not specify this value, then committing changes to root-level storage objects is robust even if a disk cache is used. The two-phase commit process ensures that data is stored on the disk and not just to the disk cache.

STGC_CONSOLIDATE
Microsoft® Windows® 2000: Indicates that a storage should be consolidated after it is committed, resulting in a smaller file on disk. This flag is valid only on the outermost storage object that has been opened in transacted mode. It is not valid for streams. The STGC_CONSOLIDATE flag can be combined with any other STGC flags.

Remarks

You can specify STGC_DEFAULT or some combination of STGC_OVERWRITE, STGC_ONLYIFCURRENT, and STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE for normal commit operations. You can specify STGC_CONSOLIDATE with any other STGC flags.

Typically, you would use STGC_ONLYIFCURRENT to protect the storage object in cases where more than one user can edit the object simultaneously.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Wtypes.idl.

See Also

IPropertyStorage, IStorage, IStream


STGFMT

The STGFMT enumeration values specify the format of a storage object and are used in the StgCreateStorageEx and StgOpenStorageEx functions in the stgfmt parameter. This value, in combination with the value in the riid parameter, is used to determine the file format and the interface implementation to use.

typedef enum tagSTGFMT
{
  STGFMT_STORAGE     = 0,
  STGFMT_FILE        = 3,
  STGFMT_ANY         = 4,
  STGFMT_DOCFILE     = 5
} STGFMT;

Elements

STGFMT_ANY
Indicates that the system will determine the file type and use the appropriate structured storage or property set implementation. This value cannot be used with the StgCreateStorageEx function.
STGFMT_STORAGE
Indicates that the file must be a compound file.
STGFMT_FILE
Indicates that the file must not be a compound file. This element is only valid when using the StgCreateStorageEx or StgOpenStorageEx functions to access the NTFS file system implementation of the IPropertySetStorage interface. Therefore, these functions return an error if the riid parameter does not specify the IPropertySetStorage interface, or if the specified file is not located on an NTFS file system volume.
STGFMT_DOCFILE
Indicates that the file must be a compound file, and is similar to the STGFMT_STORAGE flag, but indicates that the compound-file form of the compound-file implementation must be used. For more information, see Compound File Implementation Limits.

Remarks

If the STGFMT value, specified to the StgCreateStorageEx or StgOpenStorageEx function, is STGFMT_FILE, the riid parameter must be the header-defined value for IID_IPropertySetStorage. It is not possible to get an IStorage interface for noncompound files. Similarly, if the STGFMT_ANY flag is specified to StgOpenStorageEx, and the file is not a compound file, the riid parameter must be IID_IPropertySetStorage.

Note The STGM enumeration values STGM_CONVERT, STGM_DELETEONRELEASE, STGM_PRIORITY, STGM_SIMPLE, and STGM_TRANSACTED cannot currently be used in the following situations.

  • In combination with the STGFMT_FILE flag.
  • In combination with the STGFMT_ANY flag if the file is not a compound file.

Requirements

Windows NT/2000: Requires Windows 2000.
Windows 95/98: Unsupported.
Header: Objidl.idl.

See Also

StgCreateStorageEx, StgOpenStorageEx


STGM

The STGM enumeration values are used in the IStorage, IStream, and IPropertySetStorage interfaces, and in the StgCreateDocfile, StgCreateStorageEx, StgCreateDocfileOnILockBytes, StgOpenStorage, StgOpenStorageEx functions to indicate the conditions for creating and deleting the object and to indicate access modes for the object.

STGM values are as follows:

Group | Flag | Value
Access | STGM_READ | 0x00000000L
 | STGM_WRITE | 0x00000001L
 | STGM_READWRITE | 0x00000002L
Sharing | STGM_SHARE_DENY_NONE | 0x00000040L
 | STGM_SHARE_DENY_READ | 0x00000030L
 | STGM_SHARE_DENY_WRITE | 0x00000020L
 | STGM_SHARE_EXCLUSIVE | 0x00000010L
 | STGM_PRIORITY | 0x00040000L
Creation | STGM_CREATE | 0x00001000L
 | STGM_CONVERT | 0x00020000L
 | STGM_FAILIFTHERE | 0x00000000L
Transactioning | STGM_DIRECT | 0x00000000L
 | STGM_TRANSACTED | 0x00010000L
Transactioning Performance | STGM_NOSCRATCH | 0x00100000L
 | STGM_NOSNAPSHOT | 0x00200000L
Direct SWMR and Simple | STGM_SIMPLE | 0x08000000L
 | STGM_DIRECT_SWMR | 0x00400000L
Delete On Release | STGM_DELETEONRELEASE | 0x04000000L

Elements

These elements are often combined using an OR operator.They are interpreted in groups as follows. It is never valid to use more than one element from a single group.

Access group

STGM_READ
Indicates that the object is read-only, meaning that modifications cannot be made. For example, if a stream object is opened with STGM_READ, the IStream::Read method may be called, but the IStream::Write method may not. Similarly, if a storage object opened with STGM_READ, the IStorage::OpenStream and IStorage::OpenStorage methods may be called, but the IStorage::CreateStream and IStorage::CreateStorage methods may not.
STGM_WRITE
STGM_WRITE lets you save changes to the object, but does not permit access to its data. The provided implementations of the IPropertyStorage and IPropertySetStorage interfaces do not support this write-only mode.
STGM_READWRITE
STGM_READWRITE lets you both access and modify an object's data. For example, if a stream object is created or opened in this mode, it is possible to call both IStream::Read and IStream::Write. Note that this element of the enumeration is not a simple binary OR operation of the STGM_WRITE and STGM_READ elements.

Sharing group

STGM_SHARE_DENY_NONE
Specifies that subsequent openings of the object are not denied read or write access. If no flag from the sharing group is specified, this flag is assumed.
STGM_SHARE_DENY_READ
Prevents others from subsequently opening the object in STGM_READ mode. It is typically used on a root storage object.
STGM_SHARE_DENY_WRITE
Prevents others from subsequently opening the object for STGM_WRITE or STGM_READWRITE access. In transacted mode, sharing of STGM_SHARE_DENY_WRITE or STGM_SHARE_EXCLUSIVE can significantly improve performance since they don't require you to make snapshots. See the following Remarks section for more information about transactioning.
STGM_SHARE_EXCLUSIVE
Prevents others from subsequently opening the object in any mode. Note that this value is not a simple bitwise OR operation of the STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE values. In transacted mode, sharing of STGM_SHARE_DENY_WRITE or STGM_SHARE_EXCLUSIVE can significantly improve performance since they don't require snapshotting. See the following Remarks section for more information about transactioning.
STGM_PRIORITY
Opens the storage object with exclusive access to the most recently committed version. Thus, other users cannot commit changes to the object while you have it open in priority mode. You gain performance benefits for copy operations, but you prevent others from committing changes. So, you should limit the time you keep objects open in priority mode. You must specify STGM_DIRECT and STGM_READ with priority mode, and you cannot specify STGM_DELETEONRELEASE. This flag is only valid when opening a root object, such as with StgOpenStorageEx. It is not valid when opening a subelement, such as with IStorage::OpenStorage.

Creation Group

Use a flag from this group when creating an object, such as with StgCreateStorageEx or IStorage::CreateStream.

STGM_CREATE
Indicates that an existing storage object or stream should be removed before the new one replaces it. A new object is created when this flag is specified only if the existing object has been successfully removed.

This flag is used in three situations:

  • When you are trying to create a storage object on disk but a file of that name already exists.
  • When you are trying to create an object inside a storage object but a object with the specified name already exists.
  • When you are creating a byte array object but one with the specified name already exists.

This flag cannot be used with open operations, such as StgOpenStorageEx or IStorage::OpenStream.

STGM_CONVERT
Creates the new object while preserving existing data in a stream named "Contents". In the case of a storage object or a byte array, the old data is flattened to a stream regardless of whether the existing file or byte array currently contains a layered storage object. This flag can only be used when creating a root storage object. It cannot be used within a storage object; for example, in IStorage::CreateStream. It is also not valid to use this flag and the STGM_DELETEONRELEASE flag simultaneously.
STGM_FAILIFTHERE
Causes the create operation to fail if an existing object with the specified name exists. In this case, STG_E_FILEALREADYEXISTS is returned. This is the default creation mode; that is, if no other create flag is specified, STGM_FAILIFTHERE is implied.

Transactioning group

See the following Remarks section for more information on transactioning.

STGM_DIRECT
In direct mode, each change to a storage or stream element is written as it occurs. This is the default if neither STGM_DIRECT nor STGM_TRANSACTED is specified.
STGM_TRANSACTED
In transacted mode, changes are buffered and written only if an explicit commit operation is called. To ignore the changes, call the Revert method in the IStream, IStorage, or IPropertyStorage interface. The COM compound file implementation of IStorage does not support transacted streams, which means that streams can be opened only in direct mode, and you cannot revert changes to them, however transacted storages are supported. The compound file, stand-alone, and NTFS file system implementations of IPropertySetStorage similarly do not support transacted, simple property sets because these property sets are stored in streams. However, transactioning of nonsimple property sets, which can be created by specifying the PROPSETFLAG_NONSIMPLE flag in the grfFlags parameter of IPropertySetStorage::Create, are supported.

Transactioning Performance group

See the following Remarks section for more information on transactioning.

STGM_NOSCRATCH
In transacted mode, a temporary scratch file is usually used to save modifications until the Commit method is called. Specifying STGM_NOSCRATCH permits the unused portion of the original file to be used as scratch space instead of creating a scratch file for that purpose. This does not affect the data in the original file, and in certain cases can result in improved performance. It is not valid to specify this flag without also specifying STGM_TRANSACTED, and this flag may only be used in a root open. See the following Remarks section for more information about NoScratch mode.
STGM_NOSNAPSHOT
This flag is used when opening a storage object with STGM_TRANSACTED and without STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE. In this case, specifying STGM_NOSNAPSHOT prevents the system-provided implementation from creating a snapshot copy of the file. Instead, changes to the file are written to the end of the file. Unused space is not reclaimed unless consolidation is done during the commit, and there is only one current writer on the file. When the file is opened in no snapshot mode, another open operation cannot be done without specifying STGM_NOSNAPSHOT. This flag may only be used in a root open operation. See the following Remarks section for more information about NoSnapshot mode.

Delete On Release groups

STGM_DELETEONRELEASE
Indicates that the underlying file is to be automatically destroyed when the root storage object is released. This capability is most useful for creating temporary files. This flag can only be used when creating or opening a root object, such as with StgCreateStorageEx. It is not valid when creating or opening a subelement, such as with IStorage::CreateStream. It is also not valid to use this flag and the STGM_CONVERT flag simultaneously.

Direct SWMR and Simple group

STGM_SIMPLE
STGM_SIMPLE is a mode that provides a much faster implementation of a compound file in a limited, but frequently used case. It is described in detail in the following Remarks section.
STGM_DIRECT_SWMR
The STGM_DIRECT_SWMR supports direct mode for single-writer, multireader file operations. It is described in detail in the following Remarks section.

Remarks

You can combine these flags but you can only choose one flag from each group of related flags. Typically one flag from each of the access and sharing groups must be specified for all functions and methods which use this enumeration. Flags from other groups are optional.

Transacted Mode

When the STGM_DIRECT flag is specified, only the following combination of flags may be specified from the access and sharing groups:

    STGM_READ | STGM_SHARE_DENY_WRITE

or

    STGM_READWRITE | STGM_SHARE_EXCLUSIVE

or

    STGM_READ | STGM_PRIORITY

Note that direct mode is implied by the absence of STGM_TRANSACTED. That is, if neither STGM_DIRECT nor STGM_TRANSACTED is specified, STGM_DIRECT is assumed.

When the STGM_TRANSACTED flag is specified, objects are created or opened in transacted mode. In this mode, changes to an object do not persist until they are committed. For example, changes to a transacted storage object are not persisted until the IStorage::Commit method is called. Changes to such a storage object will be lost if the storage object is released (final release) before the Commit method is called, or if the IStorage::Revert method is called.

When an object is created or opened in transacted mode, it is necessary for the implementation to keep both the original data and updates to this data, so that updates can be reverted if necessary. This is typically done by writing changes to a scratch area until they are committed, or by creating a copy, called a snapshot, of the most recently committed data.

When a root storage object is opened in transacted mode, the location and behavior of the scratch data and the snapshot copies can be controlled to optimize performance with the STGM_NOSCRATCH and STGM_NOSNAPSHOT flags. (A root storage object is obtained from, for example, the StgOpenStorageEx function; a storage object obtained from the IStorage::OpenStorage method is a substorage object.) Typically, the scratch data and snapshots are stored in temporary files, separate from the storage.

The effect of these flags depends on the number of readers and/or writers accessing the root storage at one time.

In the "single-writer" case, a transacted mode storage object is opened for write access and there can be no other access to the file. That is, the file is opened with STGM_TRANSACTED, access of STGM_WRITE or STGM_READWRITE, and sharing of STGM_SHARE_EXCLUSIVE. In this case, changes to the storage object are written to the scratch area. When those changes are committed, they are copied to the original storage. Therefore, if no changes are actually made to the storage object, there will be no unnecessary data transfer.

In the "multiple-writer" case, a transacted storage object is opened for write access, but is shared in such as way as to allow other writers. That is, the storage object is opened with STGM_TRANSACTED, access of STGM_WRITE or STGM_READWRITE, and sharing of STGM_SHARE_DENY_READ. If sharing of STGM_SHARE_DENY_NONE is specified instead, then the case is "multiple-writer, multiple-reader". In these cases, a snapshot of the original data will be made during the open operation. Therefore, even if no changes are actually made to the storage and/or it is not actually opened by another writer simultaneously, data transfer is still necessary during the open. As a result the best open-time performance can be obtained by opening the storage object in STGM_SHARE_DENY_WRITE or STGM_SHARE_EXCLUSIVE modes. See the IStorage::Commit documentation for information on how changes are committed when there are multiple writers.

In the "single-writer, multiple-reader" case, a transacted storage object is opened for write access, but is shared with readers. That is, the storage object is opened by the writer with STGM_TRANSACTED, access of STGM_READWRITE or STGM_WRITE, and sharing of STGM_SHARE_DENY_WRITE. The storage is opened by readers with STGM_TRANSACTED, access of STGM_READ, and sharing of STGM_SHARE_DENY_NONE. In this case the writer uses the scratch area to store uncommitted changes. As in the cases above, the reader incurs an open-time performance penalty while a snapshot copy of the data is made.

Typically, the scratch area is a temporary file, separate from the original data. So when uncommitted changes are committed back to the original object, the data must be transferred from the temporary file. To avoid this data transfer, the STGM_NOSCRATCH flag may be specified. When this flag is specified, portions of the storage object's file itself are used for the scratch area, rather than a separate temporary file. As a result, committing changes can be performed quickly, since very little data transfer is necessary. The disadvantage is that the storage file can become larger than it would otherwise be, since it must be grown to be large enough for both the original data and the scratch area. To consolidate the data and remove this unnecessary area, reopen the root storage in transacted mode but without setting the STGM_NOSCRATCH flag. Then, call IStorage::Commit with the STGC_CONSOLIDATE flag set.

The snapshot area, like the scratch area, is also typically a temporary file, and this too can be affected with a STGM flag. By specifying the STGM_NOSNAPSHOT flag, a separate temporary snapshot file is not created. Instead, the original data is never modified, even if there are one or more writers per object. When a writer's changes are committed, they are added to the file, but the original data remains intact. This mode increases efficiency because it reduces run time by eliminating the need to create a snapshot during the open operation. However, using this mode may result in a very large storage file because data in the file can never be overwritten. The size of files opened in NoSnapshot mode is unlimited..

Direct Single-Writer, Multiple-Reader Mode

As described above, it is possible to have a single writer and multiple readers of a storage object, if that object is opened in transacted mode. It is also possible to achieve the single-writer, multireader case in direct mode, by specifying the STGM_DIRECT_SWMR flag.

In STGM_DIRECT_SWMR mode, it is possible for one caller to open an object for read/write access, while other callers simultaneously have the file open for read-only access. It is invalid to use this flag in combination with the STGM_TRANSACTED flag. In this mode, the writer opens the object with the following flags:

STGM_DIRECT_SWMR | STGM_READWRITE | STGM_SHARE_DENYWRITE

and each of the readers opens the object with these flags:

STGM_DIRECT_SWMR | STGM_READ | STGM_SHARE_DENY_NONE

In this mode, to modify the storage object, the writer must get exclusive access to the object. This is possible when all the readers have closed it. The writer uses the IDirectWriterLock interface to obtain this exclusive access.

Simple Mode

Simple mode (STGM_SIMPLE) is useful for applications that perform complete save operations. It is highly efficient, but has the following constraints:

  1. There is no support for substorages.
  2. The storage object, and stream objects obtained from it, cannot be marshaled. Marshaling is the process of packaging, unpackaging, and sending interface method parameters across thread or process boundaries within a Remote Procedure Call (RPC). See also: Marshaling Details and Interface Marshaling.
  3. Each stream has a minimum size. If fewer than the minimum bytes are written into a stream by the time the stream is released, the stream will be extended to the minimum size. For example, the minimum size for a particular IStream implementation is 4 kilobytes (KB). A stream is created and 1 KB is written to it. On the final release of that IStream, the stream size will be automatically extended to 4 KB. Subsequently, opening the stream and calling the IStream::Stat method will show a size of 4 KB.
  4. Not all methods of IStorage or IStream will necessarily be supported by the implementation. See IStorage-Compound File Implementation, and IStream - Compound File Implementation .

When a storage object is obtained by a Create operation in simple mode:

  • Stream elements can be created, but not opened.
  • When a stream element is created by calling IStorage::CreateStream, it is not possible to create another stream until that stream object is released.
  • After all streams are written, you must call IStorage::Commit to flush changes.

When a storage object is obtained by an Open operation in simple mode (this capability is introduced in Windows 2000):

  • It is possible to open only one stream element at a time.
  • It is not possible to change the size of a stream by calling the IStream::SetSize method or by seeking or writing beyond the end of the stream. However, since all streams are of a minimum size, it is possible to use the stream up to that size, even if less data was originally written to it. To determine the actual size of a stream, use the IStream::Stat method.

Note If a storage element is modified by a storage object that is not in simple mode, it will not be possible to open that storage element in simple mode again.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Declared in Objbase.h.

See Also

StgCreateDocfile, StgCreateStorageEx, ISequentialStream::Read, IStorage, StgCreateDocfileOnILockBytes, StgOpenStorage, StgOpenStorageEx, StgOpenStorageOnILockBytes


STGMOVE

The STGMOVE enumeration values indicate whether a storage element is to be moved or copied. They are used in the IStorage::MoveElementTo method.

typedef enum tagSTGMOVE
{
  STGMOVE_MOVE    = 0,
  STGMOVE_COPY    = 1
} STGMOVE;

Elements

STGMOVE_MOVE
Indicates that the method should move the data from the source to the destination.
STGMOVE_COPY
Indicates that the method should copy the data from the source to the destination. A copy is the same as a move except that the source element is not removed after copying the element to the destination. Copying an element on top of itself is undefined.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Wtypes.idl.

See Also

IStorage::MoveElementTo


STGTY

The STGTY enumeration values are used in the type member of the STATSTG structure to indicate the type of the storage element. A storage element is a storage object, a stream object, or a byte-array object (LOCKBYTES).

typedef enum tagSTGTY
{
  STGTY_STORAGE      = 1,
  STGTY_STREAM       = 2,
  STGTY_LOCKBYTES    = 3,
  STGTY_PROPERTY     = 4
} STGTY;

Elements

STGTY_STORAGE
Indicates that the storage element is a storage object.
STGTY_STREAM
Indicates that the storage element is a stream object.
STGTY_LOCKBYTES
Indicates that the storage element is a byte-array object.
STGTY_PROPERTY
Indicates that the storage element is a property storage object.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream, STATSTG


STREAM_SEEK

The STREAM_SEEK enumeration values specify the origin from which to calculate the new seek-pointer location. They are used for the dworigin parameter in the IStream::Seek method. The new seek position is calculated using this value and the dlibMove parameter.

typedef enum tagSTREAM_SEEK
{
  STREAM_SEEK_SET    = 0,
  STREAM_SEEK_CUR    = 1,
  STREAM_SEEK_END    = 2
} STREAM_SEEK;

Elements

STREAM_SEEK_SET
The new seek pointer is an offset relative to the beginning of the stream. In this case, the dlibMove parameter is the new seek position relative to the beginning of the stream.
STREAM_SEEK_CUR
The new seek pointer is an offset relative to the current seek pointer location. In this case, the dlibMove parameter is the signed displacement from the current seek position.
STREAM_SEEK_END
The new seek pointer is an offset relative to the end of the stream. In this case, the dlibMove parameter is the new seek position relative to the end of the stream.

Requirements

Windows NT/2000: Requires Windows NT 3.1 or later.
Windows 95/98: Requires Windows 95 or later.
Header: Objidl.idl.

See Also

IStream::Seek


Samples

The sample applications associated with Structured Storage are as follows:

  • WriteRead Sample
  • EnumAll Sample
  • StgCreatePropSetStg Sample


WriteRead Sample

The WriteRead.cpp creates a property set, writes a property, closes and reopen the property set, and reads the property back. This sample program creates the file "WriteRead.stg" in the current directory. It creates property sets in a structure storage file, but a one-line change causes it to create NTFS file system property sets.

To build:
//      cl /GX WriteRead.cpp
//
//+============================================================================

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE

#include <stdio.h>
#include <windows.h>
#include <ole2.h>

// Implicitly link ole32.dll
#pragma comment( lib, "ole32.lib" )

// From uuidgen.exe:
const FMTID fmtid = { /* d170df2e-1117-11d2-aa01-00805ffe11b8 */
    0xd170df2e,
    0x1117,
    0x11d2,
    {0xaa, 0x01, 0x00, 0x80, 0x5f, 0xfe, 0x11, 0xb8}
  };

EXTERN_C void wmain()
{
   HRESULT hr = S_OK;
   IPropertySetStorage *pPropSetStg = NULL;
   IPropertyStorage *pPropStg = NULL;
   WCHAR *pwszError = L"";

   try
   {

        // Create a file and a property set within it.

        hr = StgCreateStorageEx( L"WriteRead.stg",
                                 STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
                                 STGFMT_STORAGE,
                                    // STGFMT_STORAGE => Structured Storage property sets
                                    // STGFMT_FILE    => NTFS file system property sets
                                 0, NULL, NULL,
                                 IID_IPropertySetStorage,
                                 reinterpret_cast<void**>(&pPropSetStg) );
        if( FAILED(hr) ) throw L"Failed StgCreateStorageEx";

        hr = pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT,
                                  STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
                                  &pPropStg );
        if( FAILED(hr) ) throw L"Failed IPropertySetStorage::Create";

        // Write a Unicode string property to the property set

        PROPVARIANT propvarWrite, propvarRead;
        PROPSPEC propspec;

        propspec.ulKind = PRSPEC_LPWSTR;
        propspec.lpwstr = L"Property Name";

        PropVariantInit( &propvarWrite );
        propvarWrite.vt = VT_LPWSTR;
        propvarWrite.pwszVal = L"Property Value";

        hr = pPropStg->WriteMultiple( 1, &propspec, &propvarWrite, PID_FIRST_USABLE );
        if( FAILED(hr) ) throw L"Failed IPropertyStorage::WriteMultiple";

        // It's not required, but give the property set a friendly name.

        PROPID propidDictionary = PID_DICTIONARY;
        WCHAR *pwszFriendlyName = L"Write/Read Properties Sample Property Set";
        hr = pPropStg->WritePropertyNames( 1, &propidDictionary, &pwszFriendlyName );
        if( FAILED(hr) ) throw L"Failed IPropertyStorage::WritePropertyNames";

        // Close and reopen everything.
        // By using the STGFMT_ANY flag in the StgOpenStorageEx call,
        // it doesn't matter if this is a Structured Storage property
        // set or an NTFS file system property set (see the StgCreateStorageEx call
        // above).

        pPropStg->Release(); pPropStg = NULL;
        pPropSetStg->Release(); pPropSetStg = NULL;

        hr = StgOpenStorageEx( L"WriteRead.stg",
                               STGM_READ|STGM_SHARE_DENY_WRITE,
                               STGFMT_ANY,
                               0, NULL, NULL,
                               IID_IPropertySetStorage,
                               reinterpret_cast<void**>(&pPropSetStg) );
        if( FAILED(hr) ) throw L"Failed StgOpenStorageEx";

        hr = pPropSetStg->Open( fmtid, STGM_READ|STGM_SHARE_EXCLUSIVE,
                                &pPropStg );
        if( FAILED(hr) ) throw L"Failed IPropertySetStorage::Open";

        // Read the property back and validate it

        PropVariantInit( &propvarRead );
        hr = pPropStg->ReadMultiple( 1, &propspec, &propvarRead );
        if( FAILED(hr) ) throw L"Failed IPropertyStorage::ReadMultiple";

        if( S_FALSE == hr )
            throw L"Property didn't exist after reopening the property set";
        else if( propvarWrite.vt != propvarRead.vt )
            throw L"Property types didn't match after reopening the property set";
        else if( 0 != wcscmp( propvarWrite.pwszVal, propvarRead.pwszVal ))
            throw L"Property values didn't match after reopening the property set";
        else
            wprintf( L"Success\n" );

   }
   catch( const WCHAR *pwszError )
   {
       wprintf( L"Error:  %s (hr=%08x)\n", pwszError, hr );
   }

   if( pPropStg ) pPropStg->Release();
   if( pPropSetStg ) pPropSetStg->Release();

}


EnumAll Sample

The EnumAll.cpp sample program dumps all the properties in all property sets of a storage file.

To Build:   cl /GX enumall.cpp
//
//+============================================================================

#define UNICODE
#define _UNICODE

#include <stdio.h>
#include <windows.h>

#pragma comment( lib, "ole32.lib" )
#pragma comment( lib, "user32.lib" )

//+----------------------------------------------------------------------------
//
//  ConvertVarTypeToString
//
//  Generate a string for a given vt. For the sake of simplicity it is assumed  that
//  the pwszType parameter points to a buffer that is at least MAX_PATH
//  in length.
//
//+----------------------------------------------------------------------------

void
ConvertVarTypeToString( VARTYPE vt, WCHAR *pwszType )
{
    switch( vt & VT_TYPEMASK )
    {
    case VT_EMPTY:
        wcscpy( pwszType, L"VT_EMPTY" );
        break;
    case VT_NULL:
        wcscpy( pwszType, L"VT_NULL" );
        break;
    case VT_I2:
        wcscpy( pwszType, L"VT_I2" );
        break;
    case VT_I4:
        wcscpy( pwszType, L"VT_I4" );
        break;
    case VT_I8:
        wcscpy( pwszType, L"VT_I8" );
        break;
    case VT_UI2:
        wcscpy( pwszType, L"VT_UI2" );
        break;
    case VT_UI4:
        wcscpy( pwszType, L"VT_UI4" );
        break;
    case VT_UI8:
        wcscpy( pwszType, L"VT_UI8" );
        break;
    case VT_R4:
        wcscpy( pwszType, L"VT_R4" );
        break;
    case VT_R8:
        wcscpy( pwszType, L"VT_R8" );
        break;
    case VT_CY:
        wcscpy( pwszType, L"VT_CY" );
        break;
    case VT_DATE:
        wcscpy( pwszType, L"VT_DATE" );
        break;
    case VT_BSTR:
        wcscpy( pwszType, L"VT_BSTR" );
        break;
    case VT_ERROR:
        wcscpy( pwszType, L"VT_ERROR" );
        break;
    case VT_BOOL:
        wcscpy( pwszType, L"VT_BOOL" );
        break;
    case VT_VARIANT:
        wcscpy( pwszType, L"VT_VARIANT" );
        break;
    case VT_DECIMAL:
        wcscpy( pwszType, L"VT_DECIMAL" );
        break;
    case VT_I1:
        wcscpy( pwszType, L"VT_I1" );
        break;
    case VT_UI1:
        wcscpy( pwszType, L"VT_UI1" );
        break;
    case VT_INT:
        wcscpy( pwszType, L"VT_INT" );
        break;
    case VT_UINT:
        wcscpy( pwszType, L"VT_UINT" );
        break;
    case VT_VOID:
        wcscpy( pwszType, L"VT_VOID" );
        break;
    case VT_SAFEARRAY:
        wcscpy( pwszType, L"VT_SAFEARRAY" );
        break;
    case VT_USERDEFINED:
        wcscpy( pwszType, L"VT_USERDEFINED" );
        break;
    case VT_LPSTR:
        wcscpy( pwszType, L"VT_LPSTR" );
        break;
    case VT_LPWSTR:
        wcscpy( pwszType, L"VT_LPWSTR" );
        break;
    case VT_RECORD:
        wcscpy( pwszType, L"VT_RECORD" );
        break;
    case VT_FILETIME:
        wcscpy( pwszType, L"VT_FILETIME" );
        break;
    case VT_BLOB:
        wcscpy( pwszType, L"VT_BLOB" );
        break;
    case VT_STREAM:
        wcscpy( pwszType, L"VT_STREAM" );
        break;
    case VT_STORAGE:
        wcscpy( pwszType, L"VT_STORAGE" );
        break;
    case VT_STREAMED_OBJECT:
        wcscpy( pwszType, L"VT_STREAMED_OBJECT" );
        break;
    case VT_STORED_OBJECT:
        wcscpy( pwszType, L"VT_BLOB_OBJECT" );
        break;
    case VT_CF:
        wcscpy( pwszType, L"VT_CF" );
        break;
    case VT_CLSID:
        wcscpy( pwszType, L"VT_CLSID" );
        break;
    default:
        wsprintf( pwszType, L"Unknown (%d)", vt & VT_TYPEMASK );
        break;
    }

    if( vt & VT_VECTOR )
        wcscat( pwszType, L" | VT_VECTOR" );
    if( vt & VT_ARRAY )
        wcscat( pwszType, L" | VT_ARRAY" );
    if( vt & VT_RESERVED )
        wcscat( pwszType, L" | VT_RESERVED" );
}

//+----------------------------------------------------------------------------
//
//  ConvertValueToString
//
//  Generate a string for the value in a given PROPVARIANT structure.
//  The most common types are supported (that is, those that can be displayed
//  with printf).  For other types, only a "..." is used.
//
//  It is assumed for simplicity that the pwszValue parameter points to
//  a buffer that is at least MAX_PATH in length.
//
//+----------------------------------------------------------------------------

void
ConvertValueTo( const PROPVARIANT &propvar, WCHAR *pwszValue )
{
    switch( propvar.vt )
    {
    case VT_EMPTY:
        wcscpy( pwszValue, L"" );
        break;
    case VT_NULL:
        wcscpy( pwszValue, L"" );
        break;
    case VT_I2:
        swprintf( pwszValue, L"%i", propvar.iVal );
        break;
    case VT_I4:
    case VT_INT:
        swprintf( pwszValue, L"%li", propvar.lVal );
        break;
    case VT_I8:
        swprintf( pwszValue, L"%I64i", propvar.hVal );
        break;
    case VT_UI2:
        swprintf( pwszValue, L"%u", propvar.uiVal );
        break;
    case VT_UI4:
    case VT_UINT:
        swprintf( pwszValue, L"%lu", propvar.ulVal );
        break;
    case VT_UI8:
        swprintf( pwszValue, L"%I64u", propvar.uhVal );
        break;
    case VT_R4:
        swprintf( pwszValue, L"%f", propvar.fltVal );
        break;
    case VT_R8:
        swprintf( pwszValue, L"%lf", propvar.dblVal );
        break;
    case VT_BSTR:
        swprintf( pwszValue, L"\"%s\"", propvar.bstrVal );
        break;
    case VT_ERROR:
        swprintf( pwszValue, L"0x%08X", propvar.scode );
        break;
    case VT_BOOL:
        swprintf( pwszValue, L"%s", VARIANT_TRUE == propvar.boolVal ? L"True" : L"False" );
        break;
    case VT_I1:
        swprintf( pwszValue, L"%i", propvar.cVal );
        break;
    case VT_UI1:
        swprintf( pwszValue, L"%u", propvar.bVal );
        break;
    case VT_VOID:
        wcscpy( pwszValue, L"" );
        break;
    case VT_LPSTR:
        swprintf( pwszValue, L"\"%hs\"", propvar.pszVal );
        break;
    case VT_LPWSTR:
        swprintf( pwszValue, L"\"%s\"", propvar.pwszVal );
        break;
    case VT_FILETIME:
        swprintf( pwszValue, L"%08x:%08x", propvar.filetime.dwHighDateTime, propvar.filetime.dwLowDateTime );
        break;
    case VT_CLSID:
        pwszValue[0] = L'\0';
        StringFromGUID2( *propvar.puuid, pwszValue, MAX_PATH );
        break;
    default:
        wcscpy( pwszValue, L"..." );
        break;
    }

}

//+----------------------------------------------------------------------------
//
//  DumpProperty
//
//  Dump the ID, name, type, and value of a property.
//
//+----------------------------------------------------------------------------

void
DumpProperty( const PROPVARIANT &propvar, const STATPROPSTG &statpropstg )
{
    WCHAR wsz[ MAX_PATH + 1 ];

    ConvertVarTypeToString( statpropstg.vt, wsz );
    wprintf( L"   ----------------------------------------------------\n"
             L"   PropID = %-5d VarType = %-23s", statpropstg.propid, wsz );

    if( NULL != statpropstg.lpwstrName )
        wprintf( L" Name = %s", statpropstg.lpwstrName );

    ConvertValueToString( propvar, wsz );
    wprintf( L"\n   Value = %s\n", wsz );

}

//+----------------------------------------------------------------------------
//
//  DumpPropertySet
//
//  Dump all the properties in a given property set.
//
//+----------------------------------------------------------------------------

void
DumpPropertySet( IPropertyStorage *pPropStg )
{
    IEnumSTATPROPSTG *penum = NULL;
    HRESULT hr = S_OK;
    STATPROPSTG statpropstg;
    PROPVARIANT propvar;
    PROPSPEC propspec;

    PropVariantInit( &propvar );

    try
    {
        // If this property set has a friendly name, display it now.

        PROPID propid = PID_DICTIONARY;
        WCHAR *pwszFriendlyName = NULL;
        hr = pPropStg->ReadPropertyNames( 1, &propid, &pwszFriendlyName );
        if( S_OK == hr )
        {
            wprintf( L" (\"%s\")\n\n", pwszFriendlyName );
            CoTaskMemFree( pwszFriendlyName );
            pwszFriendlyName = NULL;
        }
        else
            wprintf( L"\n" );

        // Get a property enumerator

        hr = pPropStg->Enum( &penum );
        if( FAILED(hr) ) throw L"Failed IPropertyStorage::Enum";

        // Get the first property in the enumeration

        memset( &statpropstg, 0, sizeof(statpropstg) );
        hr = penum->Next( 1, &statpropstg, NULL );
        while( S_OK == hr )
        {

            // Read the property out of the property set

            PropVariantInit( &propvar );
            propspec.ulKind = PRSPEC_PROPID;
            propspec.propid = statpropstg.propid;

            hr = pPropStg->ReadMultiple( 1, &propspec, &propvar );
            if( FAILED(hr) ) throw L"Failed IPropertyStorage::ReadMultiple";

            // Display the property value, type, etc.

            DumpProperty( propvar, statpropstg );

            // Free buffers that were allocated during the read and
            // by the enumerator.

            PropVariantClear( &propvar );
            CoTaskMemFree( statpropstg.lpwstrName );
            statpropstg.lpwstrName = NULL;

            // Move to the next property in the enumeration

            hr = penum->Next( 1, &statpropstg, NULL );
        }
        if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSTG::Next";
    }
    catch( LPCWSTR pwszErrorMessage )
    {
        wprintf( L"Error in DumpPropertySet: %s (hr = %08x)\n", pwszErrorMessage, hr );
    }

    if( NULL != penum )
        penum->Release();
    if( NULL != statpropstg.lpwstrName )
        CoTaskMemFree( statpropstg.lpwstrName );
    PropVariantClear( &propvar );
}

//+----------------------------------------------------------------------------
//
//  DumpPropertySetsInStorage
//
//  Dump the property sets in the top level of a given storage.
//
//+----------------------------------------------------------------------------

void
DumpPropertySetsInStorage( const WCHAR *pwszStorageName,
                           IPropertySetStorage *pPropSetStg )
{
    IEnumSTATPROPSETSTG *penum = NULL;
    HRESULT hr = S_OK;
    IPropertyStorage *pPropStg = NULL;
    STATPROPSETSTG statpropsetstg;

    try
    {
        // Get a property-set enumerator (which only enumerates the property
        // sets at this level of the storage, not its children).

        hr = pPropSetStg->Enum( &penum );
        if( FAILED(hr) ) throw L"failed IPropertySetStorage::Enum";

        // Get the first property set in the enumeration.

        memset( &statpropsetstg, 0, sizeof(statpropsetstg) );
        hr = penum->Next( 1, &statpropsetstg, NULL );
        while( S_OK == hr )
        {
            WCHAR wszFMTID[ CCH_MAX_PROPSTG_NAME + 1 ] = { L"" };

            // Open the property set

            hr = pPropSetStg->Open( statpropsetstg.fmtid,
                                    STGM_READ | STGM_SHARE_EXCLUSIVE,
                                    &pPropStg );
            if( FAILED(hr) ) throw L"failed IPropertySetStorage::Open";

            // Display the name of the property set

            StringFromGUID2( statpropsetstg.fmtid,
                             wszFMTID,
                             sizeof(wszFMTID)/sizeof(wszFMTID[0]) );
            wprintf( L"\n Property Set %s in %s:\n",
                     wszFMTID, pwszStorageName );

            // Display the properties in the property set

            DumpPropertySet( pPropStg );

            pPropStg->Release();
            pPropStg = NULL;

            // Get the FMTID of the next property set in the enumeration.

            hr = penum->Next( 1, &statpropsetstg, NULL );
        }
        if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSETSTG::Next";
    }
    catch( LPCWSTR pwszErrorMessage )
    {
        wprintf( L"Error in DumpPropertySetsInStorage: %s (hr = %08x)\n",
                 pwszErrorMessage, hr );
    }

    if( NULL != pPropStg )
        pPropStg->Release();
    if( NULL != penum )
        penum->Release();
}

//+----------------------------------------------------------------------------
//
//  DumpStorageTree
//
//  Dump all the property sets in the given storage and recursively in
//  all its children.
//
//+----------------------------------------------------------------------------

void
DumpStorageTree( const WCHAR *pwszStorageName, IStorage *pStg )
{
    IPropertySetStorage *pPropSetStg = NULL;
    IStorage *pStgChild = NULL;
    WCHAR *pwszChildStorageName = NULL;
    IEnumSTATSTG *penum = NULL;
    HRESULT hr = S_OK;
    STATSTG statstg;

    try
    {
        // Dump the property sets at this storage level

        hr = pStg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast<void**>(&pPropSetStg) );
        if( FAILED(hr) ) throw L"Failed IStorage::QueryInterface(IID_IPropertySetStorage)";

        DumpPropertySetsInStorage( pwszStorageName, pPropSetStg );

        // Get an enumerator for this storage.

        hr = pStg->EnumElements( NULL, NULL, NULL, &penum );
        if( FAILED(hr) ) throw L"failed IStorage::Enum";

        // Get the name of the first element (stream/storage) in the enumeration.

        memset( &statstg, 0, sizeof(statstg) );
        hr = penum->Next( 1, &statstg, 0 );
        while( S_OK == hr )
        {
            // Is this a storage that isn't a property set
            // (since we already displayed the property sets above)?

            if( STGTY_STORAGE == statstg.type
                &&
                L'\005' != statstg.pwcsName[0] )
            {
                // Yes, this is a normal storage.
                // Open the storage.

                hr = pStg->OpenStorage( statstg.pwcsName,
                                        NULL,
                                        STGM_READ | STGM_SHARE_EXCLUSIVE,
                                        NULL, 0,
                                        &pStgChild );
                if( FAILED(hr) ) throw L"failed IStorage::OpenStorage";

                // Compose a name of the form "Storage\ChildStorage\..."

                pwszChildStorageName = new WCHAR[ wcslen(pwszStorageName)
                                                  +
                                                  wcslen(statstg.pwcsName) + 2 ];
                if( NULL == pwszChildStorageName )
                {
                    hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
                    throw L"couldn't allocate memory";
                }

                wcscpy( pwszChildStorageName, pwszStorageName );
                wcscat( pwszChildStorageName, L"\\" );
                wcscat( pwszChildStorageName, statstg.pwcsName );

                // Dump all the property sets under this child storage.

                DumpStorageTree( pwszChildStorageName, pStgChild );

                pStgChild->Release();
                pStgChild = NULL;

                delete pwszChildStorageName;
                pwszChildStorageName = NULL;
            }

            // Move on to the next element in the enumeration of this storage.

            CoTaskMemFree( statstg.pwcsName );
            statstg.pwcsName = NULL;

            hr = penum->Next( 1, &statstg, 0 );
        }
        if( FAILED(hr) ) throw L"failed IEnumSTATSTG::Next";
    }
    catch( LPCWSTR pwszErrorMessage )
    {
        wprintf( L"Error in DumpStorageTree: %s (hr = %08x)\n",
                 pwszErrorMessage, hr );
    }

    if( NULL != pStgChild )
        pStgChild->Release();
    if( NULL != penum )
        penum->Release();
    if( NULL != pwszChildStorageName )
        delete pwszChildStorageName;

}

//+----------------------------------------------------------------------------
//
//  wmain
//
//  Dump all the property sets in a file which is a storage.
//
//+----------------------------------------------------------------------------

extern "C" void wmain( int cArgs, WCHAR *rgwszArgs[] )
{
    HRESULT hr = S_OK;
    IStorage *pStg = NULL;

    // Display usage information if necessary.

    if( 1 == cArgs
        ||
        0 == wcscmp( L"-?", rgwszArgs[1] )
        ||
        0 == wcscmp( L"/?", rgwszArgs[1] ))
    {
        printf( "\n"
                "Purpose:  Enumerate all properties in all property sets for a storage file\n"
                "Usage:    PropDump <filename>\n"
                "E.g.:     PropDump word.doc\n"
                "\n" );
        exit(0);
    }

    // Open the root storage.

    hr = StgOpenStorageEx( rgwszArgs[1],
                           STGM_READ | STGM_SHARE_DENY_WRITE,
                           STGFMT_ANY,
                           0,
                           NULL,
                           NULL,
                           IID_IStorage,
                           reinterpret_cast<void**>(&pStg) );

    // Dump all the properties in all the property sets within this
    // storage.

    if( FAILED(hr) )
    {
        wprintf( L"Error:  couldn't open file %s (hr = %08x)\n",
                 rgwszArgs[1], hr );
    }
    else
    {
        printf( "\n" );
        DumpStorageTree( rgwszArgs[1], pStg );
    }

    if( NULL != pStg )
        pStg->Release();

}


StgCreatePropSetStg Sample

The StgCreatePropSetStg.cpp sample shows how the StgCreatePropSetStg API can be used to create an IPropertySetStorage interface on top of any given IStorage interface. This API is new to Windows 2000 Professional.

To Build:   cl /GX StgCreatePropSetStg.cpp
//
//+============================================================================

#define UNICODE
#define _UNICODE
#define WIN32_LEAN_AND_MEAN

#include <stdio.h>
#include <windows.h>
#include <ole2.h>

#pragma comment( lib, "ole32.lib" )

IPropertyStorage*
CreatePropertySetInStorage( IStorage *pStg, const FMTID &fmtid )
{
    HRESULT hr = S_OK;
    IPropertySetStorage *pPropSetStg = NULL;
    IPropertyStorage *pPropStg = NULL;

    try
    {
        hr = StgCreatePropSetStg( pStg, 0 /*reserved*/, &pPropSetStg );
        if( FAILED(hr) ) throw L"Failed StgCreatePropSetStg (%08x)";

        hr = pPropSetStg->Create( fmtid, NULL,
            PROPSETFLAG_DEFAULT,
            STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
            &pPropStg );
        if( FAILED(hr) ) throw L"Failed IPropertySetStorage::Create (%08x)";

        // Success.  The caller must now call Release on both
        // pPropSetStg and pStg.

    }
    catch( const WCHAR *pwszError )
    {
        wprintf( L"Error: %s (%08x)\n", pwszError, hr );
    }

    if( NULL != pPropSetStg )
        pPropSetStg->Release();

    return( pPropStg );
}

extern "C" void wmain()
{
    HRESULT hr = S_OK;
    IStorage *pStg = NULL;
    IPropertyStorage *pPropStg = NULL;

    try
    {
        // Create an object with an IStorage interface.  It is not necessary
        // that it be a system-provided storage, such as that obtained by
        // this call.  Any object that implements IStorage will do.

        hr = StgCreateStorageEx( NULL,  // Just create a temporary storage
                                 STGM_CREATE
                                    | STGM_READWRITE
                                    | STGM_SHARE_EXCLUSIVE,
                                 STGFMT_STORAGE,
                                 0, NULL, NULL,
                                 IID_IStorage,
                                 reinterpret_cast<void**>(&pStg) );
        if( FAILED(hr) ) throw L"Failed StgCreateStorageEx";

        // Now get and use an IPropertySetStorage representing this IStorage.

        pPropStg = CreatePropertySetInStorage( pStg, FMTID_SummaryInformation );
        if( NULL == pPropStg ) throw L"Failed CreatePropertySetInStorage";

        //-----
        // Here we could call IPropertyStorage methods such as WriteMultiple and
        // ReadMultiple using the pPropStg pointer.
        //-----

        printf( "Success\n" );
    }
    catch( const WCHAR *pwszError )
    {
        wprintf( L"Error: %s (%08x)\n", pwszError, hr );
    }

    if( NULL != pPropStg )
        pPropStg->Release();
    if( NULL != pStg )
        pStg->Release();

}


Overview: COM Tutorial Samples

COM Logo COM Tutorial Samples Using Samples
Using Samples 		Lesson List
Lesson List 		First Lesson
First Lesson

Overview: COM Tutorial Programming Samples

October 22, 1998

This tutorial sample series is directed to serve advanced programmers who are using their proficiency in C++ Win32 programming to develop or modify systems-level software using COM (Component Object Model) technology. These advanced samples show how to program components at the foundation level of COM. Neither MFC nor ATL is used in the samples.

If you are developing internet Web applications with ActiveX controls and an integrated development environment like Microsoft Visual Studio, then you have the benefit of tools and abstractions that hide most of the technical detail covered in this series. The series exposes much of the infastructure that is now being increasingly hidden by MFC (Microsoft Foundation Classes) and ATL (Active Template Library). This series can help when you need to drop below the convenient abstractions in MFC or ATL to perform COM-compliant modifications. Of course, it can also help if you are working directly at this foundation level of COM programming to develop the highest performance professional applications.

These source code samples are the same as those currently found as branch \MSSDK\SAMPLES\COM\TUTSAMP of the installed Microsoft Platform SDK.

The samples branch consists of directories organized in a graduated sequence of tutorial lessons. The numbered order of lessons is the sequence we recommend that you follow in the tutorial. Each lesson illustrates a major feature of COM technology. Most lessons build on the ones that came earlier in the sequence.

Each code sample has an associated description of the sample's external operation and a narrative code tour of the internal construction (based on the specific goal of the tutorial lesson). This tutorial narrative resides in a [lesson].HTM Web page file--where [lesson] is the name of the lesson/code sample. There is one of these narrative HTML files for each tutorial code sample. All of these HTML files are located in the main directory of the tutorial samples branch. The complete tutorial is thus made up of many HTML files located in the parent directory of all the sample directories. This single parent directory is the main tutorial directory. Additional details on the internal mechanisms of each sample can be found in code comments in each source file located in the sample's directory.

The tutorial HTML files for the tutorial samples are linked together into a Web that can be viewed with a Web browser. The tutorial is aimed at revealing how to program the major features of COM and the internal behavior of the sample components. While this can serve as an inductive means to appreciate COM architectural theory, this tutorial complements other more comprehensive works that cover the full conceptual breadth of COM, ActiveX, and OLE technology. Some recommended works are:

  • The ActiveX and Object Services branch of the online documentation for the Platform SDK
  • The Component Object Model Specification (see Microsoft's Web site at: http://www.microsoft.com/com/)
  • Inside OLE 2nd Edition by Kraig Brockschmidt (Microsoft Press)
  • Understanding ActiveX and OLE by David Chappell (Microsoft Press)
  • Inside COM by Dale Rogerson (Microsoft Press)
  • Inside Distributed COM by Guy Eddon and Henry Eddon (Microsoft Press)
  • Essential COM by Don Box (Addison Wesley)
  • Professional DCOM Programming by Dr. Richard Grimes (Wrox Press)

After installing the complete sample set, you can start browsing this tutorial by executing the TUTORIAL.EXE command in the main tutorial directory. You must have a Web browser installed for TUTORIAL.EXE to work. The Web browser must have a file association with the .HTM file extension. If you are reading this Web page on the World Wide Web then you can download the complete sample set to obtain the source code for the samples that accompany the tutorial as well as copies of the Web pages that comprise the tutorial.

Click a link below:

* List of Lessons -- table of contents for the tutorial lessons.
* Using the Samples -- useful information about building the code samples, extracting individual code samples, and coding style conventions.

Top Back to page top

© 1995-1998 Microsoft Corporation


Using the COM Tutorial Samples

Building the Code Samples
Extracting the Code Samples
Coding Style Conventions

Building the Code Samples

To build any of the COM Tutorial Samples, your computer environment must be set up to build Win32 C++ applications with a properly installed 32-bit C++ compiler, linker, and resource compiler that are compatible with Microsoft Visual C++® 4.x or above, and a properly installed Platform SDK. It is usually wise to install the Platform SDK last. The Platform SDK provides .H include and .LIB library files needed for the COM functionality coded in the samples.

Because this sample series originated as an integral part of the Platform SDK, the tutorial narrative assumes an environment with the Platform SDK properly installed. However, releases of Microsoft Visual C++ after version 4.0 may also provide the .H include and .LIB library files needed for compilation. In such cases, installation of the Platform SDK may not be required to compile the samples.

To successfully run the REMCLIEN, FRESERVE, and FRECLIEN samples requires system facilities that are only available in more recent versions of the 32-bit Windows operating systems. REMCLIEN, FRESERVE, and FRECLIEN require the Windows NT version 4.0 or later. The REMCLIEN, FRESERVE, and FRECLIEN samples will build but will not run on the Windows 95 operating system until Distributed COM (DCOM) and free threaded COM are supported in that operating system. This support is available for the Windows 95 operating system in the DCOM95 add on. DCOM95 can currently be obtained by download from Microsoft's world wide Web site at: http://www.microsoft.com/com/.

Each sample directory has the necessary source files to build and run the sample. The parent sample directory has a MAKEALL.BAT file, which you can run from the command prompt to make all the code samples in the branch below. See the MAKEALL.BAT file for details on usage. If your environment is set up to build Win32 C++ applications, you can simply run MAKEALL.BAT from the directory where it resides to build all the code samples in the branch below. MAKEALL ensures the correct order to the build so that all the code sample dependencies are satisfied.

The main directory also has a makefile that builds all the tutorial code samples using options similar to those supported by MAKEALL.BAT. See this makefile for details on options. This makefile assumes that the entire code samples branch is installed as part of the Platform SDK. Currently this location has a path similar to D:\MSSDK\SAMPLES\COM\TUTSAMP, where D: represents the installation drive. If you have extracted the tutorial code sample branch (eg, directory COM and below) to another location outside the Platform SDK (or if you obtained the sample set as a separate download from the Microsoft World Wide Web site), then use MAKEALL.BAT to compile all the samples in the branch. In general, MAKEALL.BAT is the recommended way to go. A LOGMALL.BAT file is also provided. It does the same as MAKEALL.BAT except that it logs all compilation output to file ERRORLOG.TXT in the main tutorial directory.

Two batch files, REGALL.BAT and UNREGALL.BAT, are also provided in the main directory to register and unregister all COM servers in the tutorial code sample series. To register all servers, run REGALL.BAT file from the main directory. To unregister all servers, run UNREGALL.BAT in the same manner. These batch files require a prior build of the REGISTER, MARSHAL, DLLSERVE, LICSERVE, LOCSERVE, APTSERVE, FRESERVE, and CONSERVE code samples. If you perform a normal build of the code samples, the server makefiles will automatically register the servers. In this case you do not need to run the REGALL.BAT batch file.

Run the CLEANALL.BAT batch file to perform an exhaustive CLEANALL of all the COM Tutorial Samples. WARNING: This batch file deletes all Visual Studio®: project files and other temporary work files created by VC++® in the samples. All of the COM servers that are built in the tutorial code samples are unregistered from the registry. All executable EXE and DLL files are deleted. All debug symbol files are deleted. Files that are generated in a variety of build environments are also deleted.

Run 'MAKEALL CLEAN' to perform a faster but more modest cleanup of all the code samples. This cleaning operation does not attempt to be as comprehensive as that performed by CLEANALL.BAT. The .OBJ files are deleted but the output binaries are retained. The COM servers are not unregistered from the registry.

The installed Platform SDK provides a SETENV.BAT file located in the \MSSDK directory that you can run to set up your environment for building applications that use the Platform SDK.

You can also manually set up your environment by adding something like the following to your AUTOEXEC.BAT file. Under Windows 95, you can edit the AUTOEXEC.BAT file to include these commands. Under Windows NT, you can edit or add these settings to your environment variables using the System dialog that you can run from the Control Panel.

  SET INCLUDE=D:\MSSDK\INCLUDE;C:\MSDEV\INCLUDE;C:\MSDEV\ATL\INCLUDE
  SET LIB=D:\MSSDK\LIB;C:\MSDEV\LIB
  SET MSSDK=D:\MSSDK
  SET MSTOOLS=D:\MSSDK
  SET CPU=i386
  SET PATH=C:\WIN95;C:\WIN95\COMMAND;C:\DOS;D:\MSSDK\BIN;C:\MSDEV\BIN

These settings are appropriate for an Intel 80386 or better platform running Windows 95 with both Microsoft Visual C++ and the Platform SDK installed. On this system, Visual C++ version 5.0 is installed under the C:\MSDEV directory. The Platform SDK is installed under the D:\MSSDK directory. Your disk configuration may be different. The Platform SDK directories (i.e., those with D:\MSSDK in them above) are all earlier in each path sequence. This sequence assumes that command line compilations are intended to be run in the Command Prompt window and that the .H include and .LIB library files in the Platform SDK are preferred for those compilations.

The CPU environment variable is defined to control the Win32 builds, depending on the platform. Current possible values are i386, MIPS, ALPHA, and PPC. For more details, see the WIN32.MAK file provided in the Platform SDK in the \MSSDK\INCLUDE directory. All of the COM tutorial code sample makefiles include WIN32.MAK.

The makefiles for each of the code samples in this series are generic Win32 makefiles and are meant to be built from the Command Prompt window. They assume Microsoft compiler and linker tools and will probably need some modification to work with other tools. Most compiler/linker command line switches are specified by macros that are defined in the WIN32.MAK makefile include file (supplied as part of the Platform SDK). The MAKEALL.BAT file and each respective code sample makefile support the following common options for invocation from the Command Prompt window to control the nature of the build:

  NMAKE invocation   MAKEALL invocation    Effect
  -----------------  --------------------  ---------------------------------
  nmake              makeall               Compile with debug info.
  nmake nodebug=1    makeall "nodebug=1"   Compile without debug info.
  nmake profile=1    makeall "profile=1"   Compile with profiling info.
  nmake tune=1       makeall "tune=1"      With working set tuner info.
  nmake unicode=1    makeall "unicode=1"   Compile for UNICODE.
  nmake clean        makeall clean         Delete temporary binaries.
  nmake cleanall     makeall cleanall      Delete all generated files.

For the MAKEALL.BAT invocations you need the quotes as shown. The nodebug, profile, and tune options are mutually exclusive: you may use only one of them (or none) for a given compilation/link. To compile the samples to run with UNICODE strings, use the unicode=1 option. The default is to compile for the traditional ANSI string support, because you can then easily run on either Windows NT or Windows 95. Currently, you must compile without UNICODE to run these code samples on Windows 95. You can freely compile and run with or without UNICODE on Windows NT. Be careful that APPUTIL is always compiled with the same options as the other code samples you may be separately compiling. This is especially true for the unicode=1 option.

You may use an installed 32-bit C++ integrated development environment (IDE) to build the samples using the generic makefiles provided. But to do so requires that within your IDE you treat the generic makefiles as 'external' makefiles. The makefiles provided require a Microsoft NMAKE-compatible make utility.

Most C++ IDEs can recognize these makefiles as external and yet still give many of the edit-build-debug benefits of the IDE. For example, in Microsoft's Visual Studio® 97 product, you can simply use the File menu's Open Workspace choice to produce a workspace by opening an appropriately named copy (e.g., EXESKEL.MAK) of the code sample's Win32 makefile.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. This file has the DSP extension. An ALLSAMPS.DSW workspace file is also provided in the main directory so that you can compile all the samples at once from within Visual Studio.

To load the appropriate project for a sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV <MYSAMPLE>.DSP

You can also simply double-click the .DSP file in the Windows Explorer to load a sample's workspace into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations.

Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. To set the directory paths, perform the following steps (assuming Visual Studio 97).

  1. Run Microsoft Visual Studio (Visual C++).
  2. Choose "Options..." on the "Tools" menu.
  3. Choose the "Directories" tab in the "Options" dialog.
  4. In the "Show Directories For" drop-down list, select "Executable files" and enter the BIN directory path for your installed Platform SDK (for example, D:\MSSDK\BIN). Click the up arrow button to move this newly entered path so that it is the first entry in the "Directories" list.
  5. In the "Show Directories For" drop-down list select "Include files" and enter the INCLUDE directory path for your installed Platform SDK (for example, D:\MSSDK\INCLUDE). Click the up arrow button to move this newly entered path so that it is the first entry in the "Directories" list.
  6. In the "Show Directories For" drop-down list select "Library files" and enter the LIB directory path for your installed Platform SDK (for example, D:\MSSDK\LIB). Click the up arrow buttons to move this newly entered path so that it is the first entry in the "Directories" list.
  7. Click the OK button in the "Options" dialog to complete the settings.

From there you can use the editor, debugger, and project facilities to edit, compile, link, and debug. Note that in versions of Microsoft Visual C++ earlier than 5.0, the steps in Visual Studio may be a little different.

Other visual IDEs can also easily generate one of their native project makefiles, given the existing source files of a code sample. If you are using such an IDE, generating such a native project makefile can be very worthwhile because it offers a way to browse the C++ classes of the program. See your IDE documentation for details on using external makefiles or on creating a native project makefile using a set of existing source files.

Aside from the dependence on the common code in the sibling APPUTIL, INC, and LIB directories, many code samples are self-contained. You need to build APPUTIL before you build any of the other code samples. Some samples later in the sequence may work with the compiled results of earlier samples. These code sample interdependencies are as follows.

Any: | Build of any code sample needs prior build of APPUTIL.
DLLUSER: | Build or run needs prior build of DLLSKEL.
COMUSER: | Build or run needs prior build of COMOBJ.
DLLSERVE: | Build needs prior build of REGISTER.
DLLCLIEN: | Run needs prior build of DLLSERVE.
LICSERVE: | Build needs prior build of REGISTER.
LICCLIEN: | Run needs prior build of LICSERVE and DLLSERVE.
MARSHAL: | Build needs prior build of REGISTER.
LOCSERVE: | Build or run needs prior build of REGISTER and MARSHAL.
LOCCLIEN: | Run needs prior build of LOCSERVE.
APTSERVE: | Build or run needs prior build of REGISTER and MARSHAL.
APTCLIEN: | Run needs prior build of APTSERVE.
REMCLIEN: | Build or run needs prior build of REGISTER and MARSHAL on local (client) machine. Run needs prior build of REGISTER, MARSHAL, and APTSERVE on remote (server) machine.
FRESERVE: | Build needs prior build of REGISTER.
FRECLIEN: | Run needs prior build of FRESERVE.
CONSERVE: | Build needs prior build of REGISTER.
CONCLIEN: | Run needs prior build of CONSERVE.
STOSERVE: | Build needs prior build of REGISTER.
STOCLIEN: | Run needs prior build of STOSERVE.
PERSERVE: | Build needs prior build of REGISTER.
PERTEXT: | Build needs prior build of REGISTER.
PERDRAW: | Build needs prior build of REGISTER.
PERCLIEN: | Run needs prior build of PERSERVE, PERTEXT, and PERDRAW.
DCDMARSH: | Build needs prior build of REGISTER.
DCDSERVE: | Build or run needs prior build of REGISTER and DCDMARSH.
DCOMDRAW: | Build or run needs prior build of REGISTER and DCDMARSH on local (client) machine. Run needs prior build of REGISTER, DCDMARSH, and DCOMDRAW on remote (server) machine.

Top Back to page top

Extracting the Code Samples

Though the code samples are divided into a series of tutorial lessons, the appropriate sample groupings can easily be extracted from the collection. Most of the individual sample directories are meant to work in conjunction with at least one other sample directory. The component-related samples consist of a client and server pair, with the server requiring the use of the REGISTER sample utility. Here is a summary of the sample groupings and how to extract each group as a buildable unit. For each sample grouping, copy the content of the directories shown. The parent [destination] directory shown requires no content from the samples branch. However, the help menus in the running samples do assume that the appropriate tutorial .HTM help files are located in this parent [destination] directory.

For the Win32 READTUT application:

  [destination]
    APPUTIL
    INC
    LIB
    READTUT

For the Win32 EXE skeleton application:

  [destination]
    APPUTIL
    INC
    LIB
    EXESKEL

For the Win32 DLL skeleton:

  [destination]
    APPUTIL
    INC
    LIB
    DLLSKEL
    DLLUSER

For the basic COM object samples:

  [destination]
    APPUTIL
    INC
    LIB
    COMOBJ
    COMUSER

For the basic in-process DLL component client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    DLLSERVE
    DLLCLIEN

For the licensed component client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    LICSERVE
    LICCLIEN

For the standard marshaling samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    MARSHAL
    MARSHAL2

For the out-of-process local client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    MARSHAL
    LOCSERVE
    LOCCLIEN

For the apartment model client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    MARSHAL
    APTSERVE
    APTCLIEN

For the DCOM (Distributed COM) client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    MARSHAL
    APTSERVE
    REMCLIEN

For the free threading client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    FRESERVE
    FRECLIEN

For the connectable COM object client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    CONSERVE
    CONCLIEN

For the structured storage client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    STOSERVE
    STOCLIEN

For the persistent object client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    PERSERVE
    PERTEXT
    PERDRAW
    PERCLIEN

For the DCOM security client/server samples:

  [destination]
    APPUTIL
    INC
    LIB
    REGISTER
    DCDMARSH
    DCDSERVE
    DCOMDRAW

Top Back to page top

Coding Style Conventions

Coding style conventions are used in this sample series to aid clarity and consistency. The "Hungarian" notation conventions are used. These have become a common coding practice in Win32 programming. They include variable prefix notations that give to variable names a suggestion of the type of the variable. The following prefixes are common:

  a       Array
  b       BOOL (int)
  by      Unsigned Char (byte)
  c       Char
  cb      Count of bytes
  cr      Color reference value
  cx      Count of x (short)
  dw      DWORD (unsigned long)
  f       Flags (usually multiple bit values)
  fn      Function
  g_      global
  h       Handle
  i       Integer
  l       Long
  lp      Long pointer
  m_      Data member of a class
  n       Short int
  p       Pointer
  s       String
  sz      Zero terminated String
  tm      Text metric
  u       Unsigned int
  ul      Unsigned long (ULONG)
  w       WORD (unsigned short)
  x,y     x, y coordinates (short)

These are often combined, as in:

  pszMyString    A pointer to a string.
  m_pszMyString  A pointer to a string that is a data member of a class.

Other conventions are:

  CMyClass          Prefix 'C' for C++ class names.
  COMyObjectClass   Prefix 'CO' for COM object class names.
  CFMyClassFactory  Prefix 'CF' for COM class factory names.
  IMyInterface      Prefix 'I' for COM interface class names.
  CImpIMyInterface  Prefix 'CImpI' for COM interface implementation classes.

Some consistent conventions for comment header blocks are used in this sample series. They are:

File Header

/*+==========================================================================
  File:      MYFILE.EXT

  Summary:   Brief summary of the file contents and purpose.

  Classes:   Classes declared or used (in source files).

  Functions: Functions exported (in source files).

  Origin:    Indications of where content may have come from. This is not
             a change history but rather a reference to the
             editor-inheritance behind the content or other indications
             about the origin of the source.

----------------------------------------------------------------------------
  Copyright and Legal notices.
  Copyright and Legal notices.
==========================================================================+*/

Plain Comment Block

/*---------------------------------------------------------------------------
  Plain block of comment text that usually takes more than several lines.
  Plain block of comment text that usually takes more than several lines.
---------------------------------------------------------------------------*/

Class Declaration Header

/*C+C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C
  Class:    CMyClass

  Summary:  Short summary of purpose and content of CMyClass.
            Short summary of purpose and content of CMyClass.

  Methods:  MyMethodOne
              Short description of MyMethodOne.
            MyMethodTwo
              Short description of MyMethodTwo.
            CMyClass
              Constructor.
            ~CMyClass
              Destructor.
C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C-C*/

Class Method Definition Header

/*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  Method:   CMyClass::MyMethodOne

  Summary:  Short summary of purpose and content of MyMethodOne.
            Short summary of purpose and content of MyMethodOne.

  Args:     MYTYPE MyArgOne
              Short description of argument MyArgOne.
            MYTYPE MyArgTwo
              Short description of argument MyArgTwo.

  Modifies: [list of member data variables modified by this method].

  Returns:  MYRETURNTYPE
              Short description of meaning of the return type values.
M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/

Unexported or Local Function

/*F+F++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Function: MyLocalFunction

  Summary:  What MyLocalFunction is for and what it does.

  Args:     MYTYPE MyFunctionArgument1
              Description.
            MYTYPE MyFunctionArgument1
              Description.

  Returns:  MyReturnType
              Description.
------------------------------------------------------------------------F-F*/

Exported Function Definition Header

/*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  Function: MyFunction

  Summary:  What MyFunction is for and what it does.

  Args:     MYTYPE MyFunctionArgument1
              Description.
            MYTYPE MyFunctionArgument1
              Description.

  Returns:  MyReturnType
              Description.
F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/

COM Interface Declaration Header

/*I+I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I+++I
  Interface: IMyInterface

  Summary:   Short summary of what features the interface can bring to
             a COM Component.

  Methods:   MYTYPE MyMethodOne
               Description.
             MYTYPE MyMethodTwo
               Description.
I---I---I---I---I---I---I---I---I---I---I---I---I---I---I---I---I---I---I-I*/

COM Object Class Declaration Header

/*O+O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O
  ObjectClass: COMyCOMObject

  Summary:     Short summary of purpose and content of this object.

  Interfaces:  IUnknown
                 Standard interface providing COM object features.
               IMyInterfaceOne
                 Description.
               IMyInterfaceTwo
                 Description.

  Aggregation: [whether this COM Object is aggregatable or not]
O---O---O---O---O---O---O---O---O---O---O---O---O---O---O---O---O---O---O-O*/

All of these comment block conventions have consistent beginning and ending token strings. This consistency supports automatic processing of the headers in some fashion. For example, AWK scripts can be written to filter out only the function headers into a separate text file that can serve the basis for a specification document. Similarly, tools like the unsupported AUTODUCK utility (currently available on the Microsoft Developer Network Development Library CD-ROM) can be used to process the comment blocks in these headers. The following beginning and ending tokens are used:

  /*+     File Header Begin
  +*/     File Header End

  /*-     Plain comment block Header Begin
  -*/     Plain comment block Header End

  /*C     Class Header Begin
  C*/     Class Header End

  /*M     Method Header Begin
  M*/     Method Header End

  /*F     Function Header Begin
  F*/     Function Header End

  /*I     Interface Header Begin
  I*/     Interface Header End

  /*O     COM Object Class Header Begin
  O*/     COM Object Class Header End

These headers can also be used as visual cues for rapid scanning of source files. They also provide convenience for rapidly getting to some source location if you set up search strings in your editor and then 'repeat last search' to quickly locate these headers. For example, the search string "M+M" will locate the start of method headers, and "M-M" will locate the beginning of the actual method definition/implementation code.

Top Back to page top

© 1995-1998 Microsoft Corporation


Lesson List: COM Tutorial Samples

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Using Samples
Using Samples 		First Lesson
First Lesson

List of Lessons

Click a lesson link below to jump to the tutorial narrative for the associated code sample.

Directory/Sample | Lesson | Topic
APPUTIL | Lesson 0 | Win32 Basics: Win32 Application Utility Library
READTUT | Lesson 1 | Win32 Basics: Tutorial Reader and Linking to APPUTIL
EXESKEL | Lesson 2 | Win32 Basics: Win32 EXE Skeleton Application
DLLSKEL | Lesson 3 | Win32 Basics: Win32 DLL Skeleton
DLLUSER | Lesson 4 | Win32 Basics: EXE User of a DLL
COMOBJ | Lesson 5 | COM Objects: Containment and Aggregation in a DLL
COMUSER | Lesson 6 | COM Objects: Nested Aggregation in an EXE User
REGISTER | Lesson 7 | COM Components: Component Object Registration
DLLSERVE | Lesson 8 | COM Components: Component Object DLL Server
DLLCLIEN | Lesson 9 | COM Components: Client Application of DLL Server
LICSERVE | Lesson 10 | COM Components: DLL Licensed Server
LICCLIEN | Lesson 11 | COM Components: Client Application of Licensed Server
MARSHAL | Lesson 12 | COM Components: Custom Interface Standard Marshaling
MARSHAL2 | Lesson 13 | COM Components: Marshaling DLL Self-Registration
LOCSERVE | Lesson 14 | COM Components: Local Server
LOCCLIEN | Lesson 15 | COM Components: Client Application of Local Server
APTSERVE | Lesson 16 | COM Components: Local Server with Multiple Apartments
APTCLIEN | Lesson 17 | COM Components: Client of Multiple Apartment Server
REMCLIEN | Lesson 18 | COM Components: Distributed COM (DCOM) Remote Client
FRESERVE | Lesson 19 | COM Components: Free-threaded Server
FRECLIEN | Lesson 20 | COM Components: Client of Free-threaded Server
CONSERVE | Lesson 21 | COM Components: Connectable Object Server
CONCLIEN | Lesson 22 | COM Components: Client of Connectable Object Server
STOSERVE | Lesson 23 | COM Components: Structured Storage Server
STOCLIEN | Lesson 24 | COM Components: Client of Structured Storage Server
PERSERVE | Lesson 25 | COM Components: IPersistStream Persistent Object Server
PERTEXT | Lesson 26 | COM Components: IPersistStreamInit Persistent Object Server
PERDRAW | Lesson 27 | COM Components: IPersistStorage Persistent Object Server
PERCLIEN | Lesson 28 | COM Components: Client of Persistent Object Servers
DCDMARSH | Lesson 29 | COM Components: Standard Marshaling for DCOM
DCDSERVE | Lesson 30 | COM Components: DCOM Server Using Security
DCOMDRAW | Lesson 31 | COM Components: DCOM Client Using Security
INC | -- | Common include directory used by the code samples
LIB | -- | Common library directory used by the code samples
TUTSAMP | -- | Main branch directory with TUTORIAL.EXE, MAKEALL.BAT, etc.

Throughout the sample sequence a clear differentiation is maintained between client and server, with a separate lesson sample for each. Usually, each client/server pair covers an area of COM technology. Here is an overview of the technologies covered by the lessons.

Basic Win32 application programming is covered in the APPUTIL, READTUT, EXESKEL, DLLSKEL, and DLLUSER lessons. APPUTIL provides a utility framework for building Win32 applications. It also contains some tools needed for tutorial purposes. READTUT is a very simple EXE application that shows how to link to the APPUTIL static library and call utility functions in it. READTUT also shows how to invoke the COM tutorial Web page reader that is used throughout the sample series. EXESKEL shows a basic Win32 skeleton EXE application built using APPUTIL. DLLSKEL and DLLUSER simularly show a basic Win32 DLL (Dynamic Link Library) skeleton and how to access it from an EXE user application.

Basic COM object construction, custom interfaces, and techniques for coding their reuse using aggregation and containment are covered in COMOBJ and COMUSER. The implementation and use of the standard IUnknown interface is covered. The implementation and use of the custom ICar, IUtility, and ICruise interfaces is covered.

Basic COM component construction, class factories, component object registration, and techniques for housing COM objects in COM component servers are covered in REGISTER, DLLSERVE, and DLLCLIEN. The implementation and use of the standard IClassFactory interface is covered.

COM component Licensing is covered in LICSERVE and LICCLIEN. The implementation and use of the standard IClassFactory2 interface is covered.

Out-of-Process local servers and the standard marshaling of custom interfaces are covered in MARSHAL, LOCSERVE, and LOCCLIEN. Explicit self-registration in the standard marshaling DLL is covered in MARSHAL2. The use of the MIDL language to specify custom interfaces is covered. The use of the MIDL compiler to produce proxy/stub marshaling servers is also covered.

Apartment model server and client construction are covered in APTSERVE and APTCLIEN. Construction of multiple single-threaded apartments (STAs) in the same process is covered.

DCOM (Distributed COM) with custom interfaces operating between client and server across machine boundaries is covered in REMCLIEN. Specifying the remote machine name in the COSERVERINFO structure is covered. Detailed DCOM security issues are not covered.

Free-threaded COM components and their access by free-threaded clients are covered in FRESERVE and FRECLIEN. The use of several client worker threads in the multi-threaded apartment (MTA) is covered. The implementation and use of a custom IBall interface is covered.

Connectable COM object technology is covered in CONSERVE and CONCLIEN. Event source and sink construction is covered. Implementation and use of the IConnectionPointContainer, IConnectionPoint, IEnumConnectionPoints, and IEnumConnections standard interfaces are covered. The implementation and use of the custom IBall and IBallSink interfaces is covered.

Structured storage using COM's compound file technology is covered in STOSERVE and STOCLIEN. A COM-based scribble drawing application is used. Use of the IStorage and IStream standard interfaces is covered. The implementation and use of the custom IPaper and IPaperSink interfaces is covered.

Persistent COM objects are covered in PERSERVE, PERTEXT, PERDRAW, and PERCLIEN. IPersistStream is covered in the PERSERVE components. IPersistStreamInit is covered in the PERTEXT components. IPersistStorage is covered in the PERDRAW components. The PERCLIEN client functions these various persistent objects and manages storage for all of them in various substorages and streams within one structured storage compound file. The implementation and use of the custom IPageList, ITextPage, IDrawPage, IPageListSink, ITextPageSink, and IDrawPageSink interfaces is covered.

DCOM (Distributed COM) Security is covered in DCDMARSH, DCDSERVE, and DCOMDRAW. The use of CoInitializeSecurity is shown in the DCDSERVE and DCOMDRAW samples. Multiple clients accessing a shared single COM object across machine boundaries is shown in a simple network shared-drawing application. Process and activation security within NT network domains is discussed. Registry AppIDs and their LaunchPermission, AccessPermission, and RunAs named values are discussed. The use of the DCOMCNFG utility is covered.

Top Back to page top

© 1995-1998 Microsoft Corporation


APPUTIL - Win32 Application Utility Library - Lesson 0

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Lesson List
Lesson List 		Next Lesson
Next Lesson

APPUTIL - Win32 Application Utility Library

SUMMARY

The APPUTIL library provides utility classes and functions that are useful for making simple C++ Win32 Windows applications. This library is used extensively in all the COM Tutorial Samples and is provided as "Lesson 0" in the tutorial sequence. It is worth reviewing briefly before plunging into the other samples.

APPUTIL provides a minimal application framework for building Win32 applications. For example, such convenient C++ abstract base classes as CVirWindow and CVirDialog are provided to ease the creation of application windows and dialogs. Much of APPUTIL is also concerned with utility functions that add support in sample applications for tutorial behavior. For example, one significant set of functionality is a trace logging facility (see the CMsgLog and CSendLog classes) that allows the samples under study to more clearly reveal their internal behavior.

For functional descriptions and a tutorial code tour of APPUTIL, see the Code Tour section in APPUTIL.HTM. For details on setting up the programmatic usage of APPUTIL, see the Usage section in APPUTIL.HTM. To read APPUTIL.HTM, run TUTORIAL.EXE in the main tutorial directory and click the APPUTIL lesson in the table of lessons. You can also achieve the same thing by clicking the APPUTIL.HTM file after locating the main tutorial directory in the Windows Explorer.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the APPUTIL sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV APPUTIL.DSP

You can also simply double-click the APPUTIL.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

APPUTIL.LIB is meant to be statically linked to modules (.EXEs or .DLLs) that use it. You include APPUTIL.H in the module that uses features of APPUTIL.LIB. You must also include APPUTIL.LIB in the LINK command of your application's makefile. For an example of the use of APPUTIL.LIB, see the READTUT code sample.

Classes

The classes provided are: CVirWindow, CVirDialog, CAboutBox, CDelayBox, CMsgBox, CMsgLog, CSendLog, and CThreaded.

Functions

The functions provided are: WindowProc, DialogProc, AnsiToUc, UcToAnsi, DComOk, lRandom, CreateColorScalePalette, PaintWindow, SkipAnsi, FileExist, MakeFamilyPath, CmdExec, DelayBox, ErrorBox, GetErrorMsg, HrMsg, HrMsgId, ReadHelp, ReadTutorial, GoWeb, ReadSource, GetErrorMsg, and OutputDebugFmt.

There are also a series of A_ ANSII versions of COM/OLE service helper functions. The A_ functions are used in conjunction with a matching series of macros in APPUTIL.H to permit compilation of the code samples under both ANSII (default) and UNICODE. These macros and matching A_ functions are for service calls that only accept Unicode string parameters. For example, the standard StgIsStorageFile function only accepts a Unicode string. When compiling a sample for ANSII (ie, UNICODE is not defined), a macro replaces any StgIsStorageFile calls with A_StgIsStorageFile calls. A_StgIsStorageFile is implemented here in APPUTIL. A_StgIsStorageFile takes the input ANSII string that is passed and converts it into a Unicode string prior to making a call to the actual StgIsStorageFile system function.

CODE TOUR

Files         Description
APPUTIL.TXT   Short sample description.
MAKEFILE      The generic Win32 makefile for this APPUTIL library.
APPUTIL.H     The include file for the APPUTIL library. Contains the
              class declarations and function prototypes.
APPUTIL.CPP   The main implementation file for APPUTIL.
APPUTIL.DSP   Microsoft Visual Studio Project file.

An abstract base class, CVirWindow, is declared in APPUTIL.H as an aid in treating a window as a C++ object. Using the CVirWindow class, a window procedure can directly access class members by dereferencing a pointer to an object instance of this class. Through GWL_USERDATA in GetWindowLong and SetWindowLong, the WindowProc member function has access to the pointer. The global WindowProc function is still used, but this scheme allows the global function to forward most message handling to the "this" WindowProc member function of a specific object instance of CVirWindow. The global WindowProc receives the "this" pointer as the lpCreateParams member passed as part of the WM_NCCREATE message. It saves the "this" pointer in the GWL_USERDATA field of the window structure.

An abstract base class, CVirDialog, which is similar to CVirWindow, is also declared as an aid in treating a dialog box as a C++ object. Using the CVirDialog class, a dialog box procedure can directly access class members by dereferencing a pointer to an object instance of this class. Through GWL_USERDATA in GetWindowLong and SetWindowLong, the DialogProc member function has access to the "this" pointer. The global DialogProc function is still used, but this scheme allows the global function to defer most message handling to the DialogProc member function of a specific object instance of CVirDialog. The functionality is very similar to CVirWindow described above.

The CAboutBox class is declared to allow creation of a common About dialog box in applications. The class is derived from the CVirDialog abstract class and illustrates its use.

The CDelayBox class is declared to allow creation of a common dialog box in applications that have no buttons and appear on screen for a delay period. The class is derived from the CVirDialog abstract class.

The CMsgBox class is declared to provide simple message boxes to display error and notice messages. The message strings in the message box can be specified as string literals, string variables, or resource string identifiers. The Error and Note methods take string literals or string variables. The ErrorID and NoteID methods take resource string identifiers. In addition, the Notice message box methods have variants that support message string formatting in the style of the C standard library function printf. These member functions support a variable argument list, NoteFmt, and NoteFmtID.

The CMsgLog class is declared to provide a facility for logging debug trace messages to a Listbox control. This class is for code samples that use debug messages to announce internal activity in the code being studied. This message log listbox can be directed to occupy the entire client area of the parent window. An argument to the Create method determines whether the log occupies a detached child window or the entire client area as an integral child window. Message output member functions can use either string resource identifier arguments to retrieve the message strings from the application's resources, or string variables to retrieve the message strings directly. The CMsgLog::MsgFmt method is provided to allow message formatting in the style of the C standard library function wsprintf. This member function supports a variable argument list. The CMsgLog::Copy method is provided to copy the entire contents of the message log to the Windows Clipboard.

The CMsgLog logging facility is for examining the tutorial code samples. It works in parallel with the standard OutputDebugString capability. If you are compiling with NODEBUG=1, the debug output is not compiled. In this case, logging support is still available, because it is an integral part of the code sample itself. When compiling for debugging, both outputs are provided for flexibility. Most C++ debuggers have an output window that will display the debug output strings, but the logging facility works whether or not you are running the application under a debugger.

The CSendLog trace logging facility is also provided. It operates much like the CMsgLog facility, except that it is intended for an application that logs its activity to an application running in another process. This facility is useful in an out-of-process local server that logs its internal behavior to a display in a client .EXE. CSendLog uses the Win32 SendMessage function with the WM_COPYDATA message to send a block of text data from one process to another. CSendLog duplicates some of the capability of CMsgLog by allowing a local server to have its own log display. The destination of the logging can therefore be switched between the client's logging display and the local server's logging display by calling the LogToServer method.

The CThreaded class is provided as a utility base class for providing functionality in derived classes that offer mutually exclusive access among multiple threads to data in objects of the derived class. Derive your class from CThreaded to inherit these features. A typical example of a method in the derived class that exploits these features follows.

  void CServer::Unlock(void)
  {
    if (OwnThis())
    {
      m_cLocks -= 1;
      if (m_cLocks < 0)
        m_cLocks = 0;
      if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
        PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);

      UnOwnThis();
    }
    return;
  }

This Unlock method implements numerous thread-safe accesses and changes to the guarded m_cLocks member variable. CServer was derived public from CThreaded. Two CThreaded methods are of value here: OwnThis and UnOwnThis are virtual functions in CThreaded that have default defintions that enforce mutually exclusive access to data in objects of the derived class (like CServer here). You use bracketed pairs of these OwnThis and UnOwnThis methods, as above, to code the protection. OwnThis blocks the currently executing thread until the currently owning thread executes the UnOwnThis method. OwnThis returns true if ownership is granted. See the CThreaded method definitions in APPUTIL.CPP for more details.

Some general utility functions and macros provided by APPUTIL are briefly described below.

COM often deals with parameters that are pointers to pointers (void**). To capture this as a type, a PPVOID typedef is provided.

The DELETE_POINTER macro deletes the object pointed to by a pointer and sets the pointer to NULL.

The RELEASE_INTERFACE macro releases a pointer to an interface and sets the pointer to NULL. Use this carefully. If you have to perform a sequence of releases on the same interface don't use this macro because it NULLs the interface pointer and thus prevents any subsequent releases. Use it to perform the last release on an interface to a COM object.

Two utility functions are provided for string conversion between ANSII and Unicode strings. AnsiToUc converts an ANSII 'multi-byte' string to a Unicode 'wide character' string. UcToAnsi converts a Unicode string to an ANSII string.

The DComOk function detects whether the DCOM (Distributed COM) capability is installed on the system. It checks to ensure that the CoInitializeEx function is exported from the OLE32.DLL system library. CoInitializeEx was initially supplied with the DCOM functionality in Windows NT 4.0 and its presence is thus a simple way to confirm that DCOM is present. Simularly, DComOk will return FALSE on Windows 95 if the DCOM95 add-on is not installed.

lRandom is a simple DWORD pseudo-random number generator.

Two utility functions are provided for creating color scale palettes and for painting windows with such colors: CreateColorScalePalette and PaintWindow. Lifted from the GDIDEMO sample in the Win32 samples of the Platform SDK. They are used in these tutorial samples to add color background to dialogs like the standard Aboutbox dialog.

The SkipAnsi function scans an input ANSI string and either skips white characters or non-white characters. The string pointer after the skip operation is returned.

The FileExist utility function indicates whether a specified file exists.

The MakeFamilyPath utility function uses GetModuleFileName to dynamically determine the path to the module currently executing and construct a family variant of that path with the proper file name extension for a particular purpose. Various such extensions (for example, .HLP, .HTM, .TXT, and .LIC) are defined in APPUTIL.H for use with this MakeFamilyPath function. If the EXE program executing MakeFamilyPath is D:\MYPATH\MYAPP.EXE then MakeFamilyPath can be used to construct such filenames as D:\MYPATH\MYAPP.HTM.

The GetExeName function gets the executable file name of the currently executing module. The caller must allocate a string big enough for long file names (typically MAX_PATH characters long). This function returns file names like: MyApp.EXE and MyDynamicLinkLibrary.DLL.

The DelayBox function uses a specified dialog template from the calling application's resources and shows a buttonless dialog message box that remains on screen for about 3 seconds. You can set the delay period at compile time using DELAYBOX_DURATION in APPUTIL.H.

The ErrorBox function displays an error message dialog box. It displays an error message string in the dialog that is specified in the call by a resource ID.

The HrMsg function is used to display an Error Message box. The function takes a standard HRESULT result code, looks it up in the system error string tables, and shows the hex error code with the retrieved system error message string. HrMsg accepts an additional user message string that is displayed in the Message Box. The HrMsgId function does the same as the HrMsg function except that the user message string is specified as a resource ID rather than a string pointer.

The ReadTutorial function is for use by tutorial code sample applications that study themselves. This function uses the shell to launch the Web browser to read an HTML Web page that contains a tutorial covering the sample. If no HTM file (in a sibling or parent directory) is specified, this function assumes a family-named <sample>.HTM tutorial HTML file that is located next to the main .EXE file of the code sample. Family-named means that if the EXE program calling this function is MYAPP.EXE then the associated family-named HTML file is constructed as MYAPP.HTM. The function accepts a pointer to an optional HTML FileName string. If this pointer is NULL, then the family-named file is used for the HTML file and is assumed to be located next to the main .EXE file of the program executing this function. Non-NULL values are of the form MySiblingDirectory\MyHTMLFile.HTM or of the form MyHTMLFileInTheParentDirectory.HTM. Special non-NULL values beginning with '.' (eg. .HTM or .ASP) specify that the family-named file is assumed to be located in the parent directory. In this case, the specified file extension is used.

The GoWeb function is used to launch the Web browser on a specified HTML file via either a URL or a full file path/name. It accepts a pointer to the target specification string that must be ANSI. If this pointer is NULL then GoWeb simply calls ReadTutorial function (above). If the target starts with '//' then the target is assumed to be a www URL trailer of the form: //www.microsoft.com. If the target is any other string, then it is assumed to be a full HTML path/file name.

The ReadSource utility function is provided to display the Open common dialog box so the user can select any of the source files for the current code sample and start a reader on that file. The Windows Notepad (NOTEPAD.EXE) is the default reader. You can specify a different reader by modifying EDITOR_FILE_STR in APPUTIL.H

The GetErrorMsg function accepts a standard Win32 error code (i.e., an HRESULT SCODE) and retrieves the human readable system error message string that corresponds to that error code.

The OutputDebugFmt utility function is provided to support formatted (printf-style) variable argument output to the debugger. This function wraps the standard OutputDebugString function.

A set of debug output macros is provided to obtain source file and line number information for the debug output. These macros use the function OutputDebugFmt to support variable argument lists in the format of the debug output display string. The debug output macros are:

  Macro Description
  ODS   Output debug string literal.
        Example:  ODS("String Literal").
  ODFn  Output formatted debug string using printf-style format string
        and a variable argument list to correspond to the format.
        Examples: ODF1("Integer=%i", iInteger);
                  ODF2("Integer=%i String=%s \r\n", iInteger, szString);

A set of debug trace logging macros is provided to obtain source file and line number information for the debug output while also logging these messages to a built-in CMsgLog message logging facility in the application. These macros also output the same messages to the debugger using the standard OutputDebugString call. These are convenience macros to perform both the message logging that the application requires and to output the same messages to the debugger, if the application was compiled for debugging and you are running it under a debugger aware of such output. Like the ODFn macros, these macros form a series. For the string literal arguments, do not use the TEXT macro--it is provided inside the macro.

  Macro     Description
  LOG       Log a string literal message.
            Example: LOG("String Literal");
  LOGFn     Log a formatted message using printf-style format string
            and a variable argument list to correspond to the format.
            Examples: LOGF1("Integer=%i", iInteger);
                      LOGF2("Integer=%i String=%s \r\n", iInteger, szString);
  LOGID     Log a message string retrieved from the module's resources.
            Example:  LOGID(ID_OF_MY_STRING_IN_THE_RESOURCES);
  LOGERROR  Check an error code and if it is an error then fetch the matching
            error string message from the system tables and log the error.
            Example: LOGERROR("SysApiThatWasCalled:", ErrorCodeReturned);

The CKHR macro is provided as a convenient way to check standard COM HRESULT return codes. If the return code is an error then this macro uses the HrMsg function to display a message box with the system error description associated with that error code.

Top Back to page top

© 1995-1998 Microsoft Corporation


READTUT - Tutorial Reader and Linking to APPUTIL - Lesson 1

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

READTUT - Tutorial Reader and Linking to APPUTIL

SUMMARY

The READTUT sample builds a simple READTUT.EXE application that is used as the main utility for reading the online tutorial for the COM Tutorial Samples. The TUTORIAL.EXE utility, found in the main tutorial directory, is produced in this sample. To do this, a copy of READTUT.EXE is simply renamed to TUTORIAL.EXE and placed in the main tutorial directory where it provides the main tutorial entry for the entire lesson series.

This lesson illustrates:

  • a very simple Win32 EXE application that links to the APPUTIL library and uses utility functions in it. APPUTIL is linked to and used in this manner throughout the tutorial sample series.
  • how the main TUTORIAL.EXE tutorial reader application is made.
  • the technique used by each tutorial sample to read the tutorial page for that sample from within the running sample.

For functional descriptions and a tutorial code tour of READTUT, see the Code Tour section in READTUT.HTM. For details on the external user operation of READTUT, see both the Usage and Operation sections in READTUT.HTM. To read READTUT.HTM, run TUTORIAL.EXE in the main tutorial directory and click the READTUT lesson in the table of lessons. You can also achieve the same thing by clicking the READTUT.HTM file after locating the main tutorial directory in the Windows Explorer.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the READTUT sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV READTUT.DSP

You can also simply double-click the READTUT.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

READTUT.EXE is an application that you can run directly from Windows or from the Command Prompt. READTUT can be run in one of the following forms.

    READTUT
      Launches the Web browser to read file READTUT.HTM in the same
      directory as the executing READTUT.EXE. If READTUT.EXE is renamed to
      TUTORIAL.EXE then file TUTORIAL.HTM will be read in the same manner.

    READTUT MyHTMLFile.HTM
      Launches the Web browser to read file MyHTMLFile.HTM in the main
      tutorial directory. This directory is assumed to be the parent of
      the directory where READTUT.EXE is executing.

    READTUT MySiblingDir\MyHTMLFile.HTM
      Launches the Web browser to read MyHTMLFile.HTM in the MySiblingDir
      directory that is a sibling to the directory where READTUT.EXE is
      executing.

    READTUT //www.Adventure-Works.com/
      Launches the Web browser with URL internet address
      http://www.Adventure-Works.com/.

Run the Sample

READTUT.EXE must be compiled before you can run it. For more details on building the samples, see Building the Code Samples.

If you have already built the READTUT sample, you can simply run READTUT.EXE in a command window. A small tutorial home page, READTUT.HTM, is provided in the READTUT sample directory. This test page is viewed by READTUT.EXE when it is run.

OPERATION

The READTUT application operates transparently except when an error occurs. If an error occurs, a modal error dialog box is displayed with one of the following errors:

  READTUT.EXE: COM initialization failed.
  READTUT.EXE: Can't find HTM file.
  READTUT.EXE: Can't run browser.

CODE TOUR

Files         Description
READTUT.TXT   Short description of sample.
READTUT.HTM   Short sample HTML home page for testing READTUT.EXE.
MAKEFILE      The generic makefile for building the code sample.
READTUT.H     The include file for the READTUT application. Contains
              string literals and Resource identifiers.
READTUT.CPP   The main implementation file for READTUT.EXE. Has WinMain.
READTUT.RC    The application resource definition file.
READTUT.ICO   The application icon resource.
READTUT.DSP   Microsoft Visual Studio Project file.

The READTUT.EXE utility in this lesson is used to launch the system Web browser on a specified (or asssumed) HTML Web page file. READTUT is so simple that no main window or message loop is required. The only user interface that might be encountered is a set of message dialog boxes to notify the user of progress or error conditions.

The main content of the program is in READTUT.CPP. The following fragment contains the declaration files included.

  /*-------------------------------------------------------------------------
    We include WINDOWS.H for all Win32 applications.
    We include OLE2.H because we will be calling the COM/OLE Libraries.
    We include APPUTIL.H because we will be building this application using
      the utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
    We include READTUT.H because it has resource definitions specific to
      this tutorial application.
  -------------------------------------------------------------------------*/
  #include <windows.h>
  #include <ole2.h>
  #include <apputil.h>
  #include "readtut.h"

WINDOWS.H is included because this is a Win32 Windows application. OLE2.H is included because calls will be made to such COM/OLE functions as CoInitialize and CoUninitialize. APPUTIL.H is located in the common sibling include directory, INC. It is included by almost all samples in this series as a very thin Win32 application framework. APPUTIL also contains many utility functions that are used to help samples expose their inner behavior for tutorial purposes. See APPUTIL.HTM for details. READTUT.H is included for resource IDs and string literals that are unique to READTUT.

All the work of READTUT is done in the WinMain function in READTUT.CPP.

  extern "C" int PASCAL WinMain(
                          HINSTANCE hInstance,
                          HINSTANCE hPrevInstance,
                          LPSTR pszCmdLine,
                          int nCmdShow)
  {
    int   iFail = FALSE;
    LPSTR psz;
    char  ch;

    if (SUCCEEDED(CoInitialize(NULL)))
    {
      // Show a message box saying that loading of tutorial is underway.
      DelayBox(hInstance, MAKEINTRESOURCE(IDD_LOADING_MSG), NULL);

      /*--------------------------------------------------------------------
        Walk down the command line looking for an HTML target. The first
        non-whitespace character after whitespace is assumed to be the
        beginning of the HTML target specification. This can be a fully
        qualified internet URL or a path/filename. This is all done in ANSI
        because pszCmdLine is in ANSI. Any command line switches are
        skipped and ignored.
      --------------------------------------------------------------------*/
      psz = pszCmdLine;

      while (ch = *psz)
      {
        BOOL bStop = FALSE;

        switch (ch)
        {
          case '\t':
          case '\n':
          case '\r':
          case ' ':
            // Skip any white space.
            psz = SkipAnsi(psz, TRUE);
            continue;

          case '/':
            // Handle '/' switches here. If '//' then assume it's a URL.
            ch = *(++psz);
            if ('/' == ch)
            {
              --psz;
              bStop = TRUE;
              break;
            }
            else
            {
              // If any '/' switches found skip to next white space.
              psz = SkipAnsi(psz, FALSE);
              continue;
            }

          case '-':
            // If any '-' switches found skip to next white space.
            psz = SkipAnsi(psz, FALSE);
            continue;

          default:
            bStop = TRUE;
            break;
        }

        if (bStop)
          break;

        psz++;
      }

      if (0 == ch)
      {
        /*------------------------------------------------------------------
          If the command line has no HTML target specified then assume a
          file with name [thismodulename].HTM in the same directory as this
          executing EXE. In this case, the assumed file is READTUT.HTM. Call
          the APPUTIL ReadTutorial utility function to Browse this HTML
          file. If READTUT.EXE is renamed to TUTORIAL.EXE then the assumed
          file to browse is TUTORIAL.HTM.
        ------------------------------------------------------------------*/
        ReadTutorial(hInstance, NULL, NULL);
      }
      else
      {
        // If the command line has an HTML target specified then use it
        // to call the APPUTIL GoWeb utility function to Browse to the
        // specified HTML file on the world wide Web.
        GoWeb(hInstance, psz);
      }

      // We're exiting this app so shut down the COM Library.
      CoUninitialize();
    }
    else
    {
      // Show error message box if we can't initialize the COM libraries.
      MessageBox(NULL,
                 TEXT(NOCOM_ERROR_STR),
                 TEXT(ERROR_TITLE_STR),
                 MB_OK | MB_ICONEXCLAMATION);
      iFail = TRUE;
    }

    return iFail;
  }

Because this application will likely make calls to COM services, the program calls COM's CoInitialize function to initialize the COM service libraries. This CoInitialize call must be paired with a matching CoUninitialize call that is made above near the end of WinMain.

The program displays a simple message box for several seconds to notify the user that the Web browser is being launched. The DelayBox utility function in APPUTIL is called to do this. Notice that a dialog resource from the executing READTUT module is used for the buttonless dialog that appears. That dialog definition is found in READTUT.RC and is identified by the IDD_LOADING_MSG resource ID. That ID is #defined in include file READTUT.H. The Win32 MAKEINTRESOURCE macro is used to convert an ID number into a resource name string which the DelayBox function requires to specify a dialog template.

The program then scans its own command line to determine the kind of HTML target that the Web browser will be directed to browse.

With the psz string pointer variable assigned to point to the command line input argument string, the above while loop in WinMain uses the SkipAnsi function to skip white space and any command line switches. The SkipAnsi function is a utility function defined in the APPUTIL library. SkipAnsi can either skip over whitespace characters or skip over non-whitespace characters. This is determined by the value of TRUE or FALSE for the second parameter. Note that SkipAnsi only accepts ANSI character string pointers. This is because the pszCmdLine command line string is available to WinMain only in ANSI regardless of whether this program is compiled for Unicode. See APPUTIL.HTM for more details on SkipAnsi.

The program then calls either the APPUTIL ReadTutorial or GoWeb functions based on whether an HTML target was specified on the command line. When ch == 0 above then no target was specified.

The ReadTutorial function launches the Web browser to read a specified .HTM file. ReadTutorial is in the APPUTIL library. ReadTutorial calls another APPUTIL function, ReadHelp, to perform the actual launch of the Web browser. Here is ReadHelp from APPUTIL.CPP in the sibling APPUTIL directory.

  void ReadHelp(
         HWND hWndOwner,
         LPTSTR pszHelpFile)
  {
    int iRes;

    if (NULL != pszHelpFile)
    {
      // First check if the .HTM help file is there at all.
      if (FileExist(pszHelpFile))
      {
        // Use shell to invoke web browser on the HTML help file.
        iRes = (int) ShellExecute(
                       hWndOwner,
                       TEXT("open"),
                       pszHelpFile,
                       NULL,
                       NULL,
                       SW_SHOWNORMAL);
        if (iRes <= 32)
        {
          // If unable to browse then put up an error box.
          HrMsg(hWndOwner, TEXT(NOBROWSE_ERROR_STR), E_FAIL);
        }
      }
      else
      {
        // If the .HTM file doesn't exist the put up an error box.
        iRes = MessageBox(
                 hWndOwner,
                 TEXT(NOHTM_ERROR_STR),
                 TEXT(ERROR_TITLE_STR),
                 MB_OK | MB_ICONEXCLAMATION);
      }
    }

    return;
  }

Using a call to the Win32 ShellExecute function, ReadHelp uses the operating system Shell to launch the current system Web browser on a specified HTML file. This action relies on a current file type association that establishes an installed Web browser as the agent that handles .HTM files. It assumes that a Web browser is installed and that the file association was set up during the install process.

Usually the READTUT.EXE program will have no target specified on the command line. In such a case, APPUTIL's ReadTutorial function is called with a NULL for the HTML target parameter. As a result, ReadTutorial determines the path and file name of the EXE in which it is executed and uses that information to construct the path and name of the target HTML file. Thus, if READTUT.EXE resides in directory D:\MSSDK\SAMPLES\COM\TUTSAMP\READTUT it will launch the Web browser to view the file D:\MSSDK\SAMPLES\COM\TUTSAMP\READTUT\READTUT.HTM. Such a small HTML page is provided as file READTUT.HTM in the READTUT sample directory. This initial HTML page is the entry to a potentialy large tutorial.

As another example, if READTUT.EXE is copied to directory D:\MSSDK\SAMPLES\COM\TUTSAMP, renamed to TUTORIAL.EXE, and executed, then TUTORIAL.EXE will launch the Web browser to browse file D:\MSSDK\SAMPLES\COM\TUTSAMP\TUTORIAL.HTM. This is what was actually done to provide TUTORIAL.EXE in the main tutorial directory of this sample series. TUTORIAL.EXE is just a renamed copy of READTUT.EXE.

The program calls GoWeb when an HTML target was specified on the READTUT command line. The GoWeb function accepts a pointer to an ANSI string that specifies the target HTML file. If the target is of the form //www.Adventure-Works.com/, then GoWeb detects this and launches the Web browser using a complete world wide Web internet URL address of the form http://www.Adventure-Works.com. If the specified target string does not have a leading '//' then a full path and file name for the local file system is assumed and GoWeb launches the Web browser using that full specification.

Top Back to page top

© 1995-1998 Microsoft Corporation


EXESKEL - Win32 EXE Skeleton Application - Lesson 2

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

EXESKEL - Win32 EXE Skeleton Application

SUMMARY

The EXESKEL sample introduces the basic application skeleton that can be used as a point of departure for more complex Win32 applications. It is used as a base for the COM Tutorial series of code samples. Of particular interest in this code sample is the support for initializing and uninitializing the COM Libraries. The general use of APPUTIL to construct this application is also worthy of study.

For functional descriptions and a tutorial code tour of EXESKEL, see the Code Tour section in EXESKEL.HTM. For details on the external user operation of EXESKEL, see both the Usage and Operation sections in EXESKEL.HTM. To read EXESKEL.HTM, run TUTORIAL.EXE in the main tutorial directory and click the EXESKEL lesson in the table of lessons. You can also achieve the same thing by clicking the EXESKEL.HTM file after locating the main tutorial directory in the Windows Explorer.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the EXESKEL sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV EXESKEL.DSP

You can also simply double-click the EXESKEL.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

EXESKEL is an .EXE file that you can execute directly from Windows or from the command prompt. No command line arguments are recognized by EXESKEL.

OPERATION

Menu Selection: File/Exit
Quits EXESKEL.

Menu Selection: Help/EXESKEL Tutorial
Opens the EXESKEL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Starts the Windows Notepad and displays a selected EXESKEL source file. This command illustrates how to program the use of the File Open common dialog box.

Menu Selection: Help/About EXESKEL
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

CODE TOUR

Files          Description
EXESKEL.TXT    Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
EXESKEL.H      The include file for the EXESKEL application. Contains
               class declarations, function prototypes, and resource
               identifiers.
EXESKEL.CPP    The main implementation file for EXESKEL.EXE. Has WinMain
               and CMainWindow implementation.
EXESKEL.RC     The application resource definition file.
EXESKEL.ICO    The application icon resource.
EXESKEL.DSP    Microsoft Visual Studio Project file.

EXESKEL is meant to be a generic point of departure for building Win32 C++ applications that support COM/OLE. It is the basic skeleton that is built upon in subsequent code samples in this tutorial series. You can study the code comments to learn more about this C++ application skeleton.

EXESKEL.CPP defines the WinMain entry function for the entire application, which contains the message loop. EXESKEL makes use of many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the source code located in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

EXESKEL.H exploits APPUTIL's CVirWindow abstract base class (see APPUTIL.H) to derive and implement a CMainWindow class. CMainWindow encapsulates the main window functionality into a convenient C++ object. It handles initialization of new instances of the main window and the message dispatching of the main window procedure. This main window procedure is a method of CMainWindow.

In the context of a COM Tutorial series of code samples, the goal of EXESKEL is to show the initial and final code you need so your application can properly use the COM libraries and implement COM interfaces.

Before the COM libraries can be used, they must be initialized. Since the COM Libraries allocate resources, you must uninitialize the libraries just before exiting the application to release any resources allocated by the libraries and to unload the COM DLL libraries themselves.

For EXESKEL, the code for initializing and uninitializing the COM libraries is found entirely in the WinMain function, located in EXESKEL.CPP. The following code fragment found at the very start of WinMain illustrates how to check for initialization of the COM libraries and to exit gracefully if the libraries cannot be initialized properly.

  // Call to initialize the COM Library. Use the COM SUCCEEDED macro
  // to detect success.
  if (SUCCEEDED(CoInitialize(NULL)))
  {
    // If we succeeded in initializing the COM Library we proceed to
    //   initialize and run the application.
    ...
    ...
  }
  else
  {
    // We failed to Initialize the COM Library. Put up error message box
    // saying that COM Library couldn't be initialized.  Parent window
    // is desktop (ie, NULL). Exit the failed application (ie, by
    // returning FALSE to WinMain).
    ErrorBox(hInstance, NULL, IDS_COMINITFAILED);
  }

For Win32 applications, the pvReserved parameter to the CoInitialize call must always be passed as NULL. The parameter isn't used in 32-bit COM, and CoInitialize will return E_INVALIDARG if you pass a non-NULL parameter. The COM SUCCEEDED macro is used to check the CoInitialize return code for success. The hPrevInstance parameter of WinMain is not used because under Win32, it is always NULL. Other techniques must be used if you want to allow only one instance of the application to run. In this case, multiple instances of EXESKEL are permitted. If COM could not be initialized then APPUTIL's ErrorBox function is used to display an error message box.

Once the checks and COM initialization are done, the application continues. Just before it exits, it must call CoUninitialize. Such exits could occur in many places in the program. The final normal program exit usually occurs after the application's message loop is terminated. The following code fragment shows this CoUninitialize call. It's a simple call that takes no parameters.

  // We're exiting this app (either normally or by init failure) so shut
  // down the COM Library.
  CoUninitialize();

CoInitialize could be called more than once in an application. The first successful call will return NOERROR; subsequent successful calls will return S_FALSE. Each successful nested call to CoInitialize must be matched with a corresponding call to CoUninitialize. CoUninitialize frees resources the COM libraries have allocated and unloads the libraries themselves.

The EXESKEL application calls CoInitialize and CoUninitialize because it uses only COM services in the COM library (currently part of OLE32.DLL). If the application will be exploiting OLE technology--such as OLE automation, compound documents, in-place activation, linking, embedding, and drag-and-drop--the OleInitialize call must be used instead. The OleInitialize function loads the OLE libraries, including the COM library. Thus OleInitialize calls CoInitialize internally, and OleUninitialize calls CoUninitialize internally.

There is stub code in the DoMenu method of EXESKEL.CPP for opening the application's help file. This code is essentially a placeholder. The EXESKEL.CPP code assembles the proper help file name, as in the following fragment from CMainWindow::InitInstance:

  // Build a path to where the help file should be (it should be in
  // the same directory as the .EXE but with the .HTM extension).
  MakeFamilyPath(hInstance, m_szHelpFile, TEXT(HELP_FILE_EXT));

MakeFamilyPath is a utility function in the APPUTIL.LIB library that uses GetModuleFileName to dynamically determine the path to the executing module and then construct a proper "family" variant of that path with the proper file name extension for a particular purpose. In this case, HELP_FILE_EXT (.HTM) is used. Various such extensions are defined in APPUTIL.H for use with the MakeFamilyPath function.

If support for the case IDM_HELP_CONTENTS: menu selection is provided in the EXESKEL.RC file, the following code attempts to find and run WinHelp on that EXESKEL.HLP file (from the DoMenu method in EXESKEL.CPP).

  ...
  case IDM_HELP_CONTENTS:
    // We have some stubbed support here for bringing up the online
    // Help for this application.
    ReadHelp(m_hWnd, m_szHelpFile);
    break;
  ...

EXESKEL does not use an accompanying help file since such information is available in the tutorial files. Thus, the EXESKEL.RC file has the Help Contents menu item commented out.

We saw how this sample series uses an online HTML-based tutorial in the previous READTUT sample. Here is EXESKEL's menu code that invokes such HTML-based tutorial help (again from DoMenu in EXESKEL.CPP).

  ...
  case IDM_HELP_TUTORIAL:
    // Call the APPUTIL utility function, ReadTutorial, to Browse the HTML
    // tutorial narrative file associated with this tutorial code sample.
    ReadTutorial(m_hInst, m_hWnd, TEXT(HTML_FILE_EXT));
    break;
  ...

ReadTutorial is called with a non-NULL HTML target specification. In this case, ReadTutorial detects that a .HTM file extension is specified and launches the Web browser on file EXESKEL.HTM which is assumed to be located in the parent directory of the EXESKEL sample directory. ReadTutorial determines the path and name of the module that is executing it and constructs the path and name for an EXESKEL.HTM file in the parent directory.

At the very start of WinMain, the UnicodeOk function call is made to determine if the application was compiled with UNICODE defined and is running on a platform where Win32 string functions are supported for Unicode.

The UnicodeOk function is defined and used only within the EXESKEL.CPP module.

  BOOL UnicodeOk(void)
  {
    BOOL bOk = TRUE;
    TCHAR szUserName[MAX_STRING_LENGTH];
    DWORD dwSize = MAX_STRING_LENGTH;

    if (!GetUserName(szUserName, &dwSize))
      bOk = ERROR_CALL_NOT_IMPLEMENTED == GetLastError() ? FALSE : TRUE;

    return bOk;
  }

This function uses a representative Unicode-supported Win32 API function call (GetUserName). This call reduces to GetUserNameW when UNICODE is defined and reduces to GetUserNameA when UNICODE is not defined. If a call to GetLastError returns ERROR_CALL_NOT_IMPLEMENTED, the implication is that the platform won't handle other Unicode calls very well either, and UnicodeOK returns FALSE.

Here's the code at the start of WinMain that uses the check:

  // If we were compiled for UNICODE and the platform seems OK with this,
  // then proceed.  Otherwise, we error, and exit the application.
  if (UnicodeOk())
  {
    ...
    ...
    ...
  }
  else
  {
    // If we were compiled for UNICODE but the platform has problems with
    // this, then indicate an error and exit the application immediately.
    CHAR szMsg[MAX_STRING_LENGTH];

    if (LoadStringA(
          hInstance,
          IDS_UNICODEFAIL,
          szMsg,
          MAX_STRING_LENGTH))
    {
      MessageBoxA(
        NULL,
        szMsg,
        ERROR_TITLE_STR,
        MB_OK | MB_ICONEXCLAMATION);
    }
  }

Notice that the xxxA (ie, ANSI) versions of the LoadString and MessageBox Win32 API calls are used explicitly. This is because the macro versions of these calls (LoadString and MessageBox), when compiled with UNICODE defined, will expand to LoadStringW and MessageBoxW. If the platform does not support these xxxW calls (the ones sensitive to Unicode), they will fail with GetLastError() returning ERROR_CALL_NOT_IMPLEMENTED. Many Win32 platforms support Unicode, and more will do so over time. The Unicode (xxxW) versions of the Win32 API are fully implemented under Windows NT.

Top Back to page top

© 1995-1998 Microsoft Corporation


DLLSKEL - Win32 DLL Skeleton - Lesson 3

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DLLSKEL - Win32 DLL Skeleton

SUMMARY

The DLLSKEL sample introduces the basic DLL skeleton that can be used as a point of departure for more complex Win32 DLLs (dynamic link libraries). It is used as a base for other COM Tutorial code samples. In this series of COM Tutorial code samples, DLLSKEL works with the DLLUSER code sample to illustrate how DLLSKEL's function services are called by an EXE consumer.

For functional descriptions and a tutorial code tour of DLLSKEL, see the Code Tour section in DLLSKEL.HTM. For details on setting up the programmatic usage of DLLSKEL, see the Usage section in DLLSKEL.HTM. To read DLLSKEL.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DLLSKEL lesson in the table of lessons. You can also achieve the same thing by clicking the DLLSKEL.HTM file after locating the main tutorial directory in the Windows Explorer. See also DLLUSER.HTM in the main tutorial directory for more details on the DLLUSER application and how it works with DLLSKEL.DLL. You must build DLLSKEL.DLL before building DLLUSER. After producing DLLSKEL.DLL and DLLUSER.EXE, the makefile for DLLSKEL copies the necessary DLLSKEL.H, DLLSKEL.LIB, and DLLSKEL.DLL files to the appropriate sibling directories.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DLLSKEL sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DLLSKEL.DSP

You can also simply double-click the DLLSKEL.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

DLLSKEL is a DLL that you can access from applications by either performing an explicit LoadLibrary call or implicitly loading the DLL by linking to its associated .LIB file. In either case, you need to include DLLSKEL.H to declare the functions that are defined as exported in the DLLSKEL DLL. In the case of this Tutorial lesson, a representative DLLUSER.EXE application is provided to illustrate the programmatic use of DLLSKEL.DLL. DLLUSER is built in the DLLUSER lesson (in sibling directory DLLUSER). See below for more details.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, DLLUSER.EXE is the client executable to run for this sample.

CODE TOUR

Files        Description
DLLSKEL.TXT  Short sample description.
MAKEFILE     The generic makefile for building the DLLSKEL.DLL
             code sample of this tutorial lesson.
DLLSKEL.H    The include file for declaring as imported or defining as
             exported the service functions in DLLSKEL.DLL. Meant for
             eventual use by outside users of the DLL.
DLLSKEL.CPP  The main implementation file for DLLSKEL.DLL. Has DllMain
             and the two exported service functions.
DLLSKELI.H   The include file for the internal class declarations and
             the identifier definitions for resources stored inside the
             DLLSKEL.DLL.
DLLSKEL.RC   The DLL resource definition file.
DLLSKEL.ICO  The icon resource.
DLLSKEL.DSP  Microsoft Visual Studio Project file.

In the context of this tutorial, the goal of DLLSKEL is to illustrate a C++ DLL skeleton that can serve as a point of departure for making more sophisticated DLLs. The resulting DLL is meant to work in conjuntion with DLLUSER.EXE to illustrate how to link to and call services in Win32 DLLs. It is the basic DLL framework for subsequent code samples in this tutorial. Study the code comments to learn more about this Win32 C++ DLL skeleton.

DLLSKEL makes use of many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the source code located in the sibling APPUTIL directory APPUTIL.HTM in the main tutorial directory.

In DLLSKEL.CPP, two exported functions are defined to export representative calls to outside consumers: DllHelloBox and DllAboutBox.

After incrementing a shared global counter variable, DllHelloBox uses APPUTIL's CMsgBox facility to show a simple information message box. This box says hello and shows the DLL's user instance count and a total count of the number of times this Hello function has been called by any user. The following DllHelloBox code from DLLSKEL.CPP shows one method to protect the incrementation of a global counting variable in a multitasking environment. The use of the Win32 InterlockedIncrement function enforces mutual exclusion for the incrementing operation when multiple processes attempt to increment a shared variable.

  STDENTRY_(BOOL) DllHelloBox(
                    HWND hWnd)
  {
    int iHelloCount;
    CDllShared* pShared = (CDllShared*) g_pvShared;

    // Increment the cummulative global count of all Hellos.
    InterlockedIncrement((LONG*) &pShared->iHelloCount);
    iHelloCount = pShared->iHelloCount;

    // Now show the user a -Notice- message box and load the display strings
    // out of this DLL's resources.  Use the format string to show the
    // user instance count of this loaded DLL and the shared hello count.
    g_pDll->pMsgBox->Init(g_pDll->hDllInst, hWnd);
    g_pDll->pMsgBox->NoteFmtID(
              IDS_HELLOCOUNTFMT,
              g_pDll->iUserCount,
              iHelloCount);

    return TRUE;
  }

The global hello count variable is accessed in a shared memory location that is part of a section of file-mapped memory. A g_pvShared global variable is used to store a pointer to the file-mappped shared memory. This g_pvShared variable is a static global variable within the DLL. It is newly created and assigned for each user process that attaches to the DLL. In the above fragment, the address of the iHelloCount counter within a shared data object (ie, a within CDllShared object) is passed to the InterlockedIncrement function. This use of a CDllShared object and the appropriate (CDllShared*) casting shows how to apply the object as a template over the file-mapped shared memory image pointed to by g_pvShared. See the discussion of DllMain below for more details on how the shared memory object is set up as a file mapping.

The DllAboutBox exported function creates an object of the CAboutBox class, as implemented in the APPUTIL utility library, to load a dialog box from the DLL's resources and then display an About box.

DLLSKEL.CPP defines the DllMain entry function for the entire DLL. To construct a Win32 DLL, you must provide the DllMain entry function and handle the following messages sent to the DLL via that function: DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH.

The following code fragment from DLLSKEL.CPP shows how the DllMain function is defined and how the messages are handled. In this sample, no specific actions are taken during thread attach and detach.

  BOOL WINAPI DllMain(
                HINSTANCE hDllInst,
                DWORD fdwReason,
                LPVOID lpvReserved)
  {
    BOOL bResult = TRUE;

    // Dispatch this call based on the reason it was called.
    switch (fdwReason)
    {
      case DLL_PROCESS_ATTACH:
        // The DLL is being loaded for the first time by a given process.
        // Perform per-process initialization here. If the initialization
        // is successful, return TRUE; if unsuccessful, return FALSE.
        bResult = FALSE;
        if (UnicodeOk())
        {
          // Instantiate a DLL global data encapsulator class.
          g_pDll = new CDllData;
          if (NULL != g_pDll)
          {
            // Remember the DLL Instance handle.
            g_pDll->hDllInst = hDllInst;
            // Create a MsgBox object.
            g_pDll->pMsgBox = new CMsgBox;
            if (NULL != g_pDll->pMsgBox)
            {
              BOOL fFirst;
              int iSharedSize = sizeof(CDllShared);

              // Create a named file mapping object.
              g_hMapObj = CreateFileMapping(
                            (HANDLE) 0xFFFFFFFF,  // Use paging file
                            NULL,                 // No security attributes
                            PAGE_READWRITE,       // Read/Write access
                            0,                    // Mem Size: high 32 bits
                            iSharedSize,          // Mem Size: low 32 bits
                            DLLSKELSHARED_STR);   // Name of map object
              if (NULL != g_hMapObj)
              {
                // Determine if this is the first create of the file mapping.
                fFirst = (ERROR_ALREADY_EXISTS != GetLastError());

                // Now get a pointer to the file-mapped shared memory.
                g_pvShared = MapViewOfFile(
                               g_hMapObj,        // File Map obj to view
                               FILE_MAP_WRITE,   // Read/Write access
                               0,                // high: map from beginning
                               0,                // low:
                               0);               // default: map entire file
                if (NULL != g_pvShared)
                {
                  CDllShared* pShared = (CDllShared*) g_pvShared;

                  if (fFirst)
                  {
                    // If this is the first attaching process, init the
                    // shared memory.
                    memset(g_pvShared, 0, iSharedSize);
                    pShared->iUserCount = 1;
                  }
                  else
                  {
                    // Increment the cummulative global count of all
                    // attached processes (ie, the count of DLL users).
                    InterlockedIncrement((LONG*) &pShared->iUserCount);
                  }
                  // Save a local instance copy of this user instance
                  // count. Each user process has its own g_pDll instance
                  // data and can thus remember it's user instance count.
                  g_pDll->iUserCount = pShared->iUserCount;
                  bResult = TRUE;
                }
              }
            }
          }
        }
        break;

      case DLL_PROCESS_DETACH:
        // The DLL is being unloaded by a given process. Do any
        // per-process clean up here, such as undoing what was done in
        // DLL_PROCESS_ATTACH. The return value is ignored.
        // Unmap any shared memory from the process's address space.
        UnmapViewOfFile(g_pvShared);
        // Close the process's handle to the file-mapping object.
        CloseHandle(g_hMapObj);
        if (NULL != g_pDll)
        {
          // Delete the message box and global DLL instance data.
          DELETE_POINTER(g_pDll->pMsgBox);
          DELETE_POINTER(g_pDll);
        }
        break;

      case DLL_THREAD_ATTACH:
        // A thread is being created in a process that has already loaded
        // this DLL. Perform any per-thread initialization here. The
        // return value is ignored.
        break;

      case DLL_THREAD_DETACH:
        // A thread is exiting cleanly in a process that has already
        // loaded this DLL. Perform any per-thread clean up here. The
        // return value is ignored.
        break;

      default:
        break;
    }

    return (bResult);
  }

When the DLL is attached to a process, DllMain is called with DLL_PROCESS_ATTACH. During DLL_PROCESS_ATTACH above the UnicodeOk function is called to determine if the DLL will run on the platform when compiled for Unicode strings. If the platform doesn't support Unicode and yet the DLL is compiled for Unicode, the DllMain function fails, and the DLL is unloaded.

The global instance data object is created. This object is pointed to by global variable, g_pDLL. This CDllData object encapsulates instance data that is instantiated separately for each process that attaches. In this sample, this object contains the DLL's instance handle, a place to save the count of the user instance, and a pointer to the Message Box object. The Message Box object is created during DLL_PROCESS_ATTACH. After its creation, the pointer to it is assigned.

The remainder of the work during DLL_PROCESS_ATTACH sets up the file-mapped memory section for a shared CDllShared object. First CreateFileMapping is called to assign a global handle to the mapping object, g_hMapObj. The current operating system virtual memory paging file is used for the file. No security attributes are needed. Read/Write access is specified. A size equal to the size of the shared object is specified. The mapping object is given a name so that it can be explicitly shared by instances of this DLL. The DLLSKELSHARED_STR macro is used to specify the name string "DLLSkelShared" as ANSI or Unicode depending on whether DLLSKEL is being compiled for Unicode or not. This macro is defined immediately before DllMain as follows.

  #if defined(UNICODE)
  #define DLLSKELSHARED_STR L"DLLSkelShared"
  #else
  #define DLLSKELSHARED_STR "DLLSkelShared"
  #endif

The mapping object is then used to map a memory view of the file. This action obtains a pointer to the shared memory and assigns it to global variable g_pvShared. If this is the first creation (rather than a re-open) of the mapping object, then the shared memory area is initialized to zero and the user instance counter is intitialized to 1. If this is not the initial creation of the shared object, then the user instance counter is incremented to keep a cumulative count of the attaching processes. Since this is globally shared memory, the Win32 InterlockedIncrement function is used to ensure mutual exclusion during the increment operation. Once this user counter is incremented the new value is saved in a non-shared variable, g_pDll->iUserCount, that is part of the instance data that is specific to the attaching process.

When the DLL is detached from the process DllMain is called with DLL_PROCESS_DETACH. During DLL_PROCESS_DETACH the mapped memory view is unmapped and the mapping object is closed. The CMsgBox and CDllData objects are then deleted. To perform the delete, the DELETE_POINTER macro is used to delete the memory object pointed to by the pointer. Here is the macro definition from APPUTIL.H:

  #define DELETE_POINTER(p)\
  {\
    if (NULL != p)\
    {\
      delete p;\
      p = NULL;\
    }\
  }

If the original pointer value was not NULL, the macro deletes the memory object that was pointed to. By then immediately setting the pointer to NULL, the macro prevents other threads from using it.

To build the DLL, you provide specific DLL-related instructions when you compile the modules of the DLL and when you link the modules with the Link command. The following fragment from the DLLSKEL makefile (file MAKEFILE) shows the compilation rule for the DLLSKEL.CPP module.

  # Compilation/Dependency rules for the .DLL source files.
  $(TDIR)\$(DLL).obj: $(DLL).cpp $(DLL).h $(DLL)r.h
    $(cc) $(cvarsdll) $(cflags) $(cdebug) -Fo$@ $(DLL).cpp

Note the use of the 'cvarsdll' macro when compiling module components of a DLL. This is in contrast to the 'cvars' macro normally used in linking EXE applications. Both of these macros are defined in the makefile include file WIN32.MAK. For more details, see WIN32.MAK in the \MSSDK\INCLUDE directory of the Platform SDK. All of the COM Tutorial code sample makefiles include WIN32.MAK.

If there are to be resources in the DLL (as there are in the DLLSKEL DLL), the appropriate .RC file is RC-compiled in the normal manner as follows.

  # Compile the DLL resources.
  $(TDIR)\$(DLL).res: $(DLL).rc $(DLL).ico $(DLL)r.h
  rc $(RCFLAGS) -r -fo$@ $(DLL).rc

To link the modules and the resources together to build the DLL binary, you provide a Link command like the following from the DLLSKEL makefile:

  # Link the object and resource binaries into the target DLL binary.
  # Build the import LIB file so apps can link to and use this DLL.
  $(DLL).dll: $(DLLOBJS) $(TDIR)\$(DLL).res
  $(LINK) @<<
  $(LINKFLAGS) $(dlllflags)
  -base:0x1C000000
  -out:$@
  -map:$(TDIR)\$*.map
  -implib:$*.lib
  $(DLLOBJS)
  $(TDIR)\$*.res
  $(olelibsdll) $(APPLIBS)
  <<

The 'implib:$*.lib' directive to the Linker instructs it to produce the DLLSKEL.LIB import library file. This .LIB file is used in turn when the DLL is linked with any application that calls the DLL.

Note also that this DLL makes calls to another library, APPUTIL, by statically linking to it. The APPLIBS macro reduces to APPUTIL.LIB. In this case, the compiled content of APPUTIL is contained entirely in APPUTIL.LIB.

APPUTIL itself could have been a DLL. It is a judgement call as to whether you build one library type or another. Usually, if you are exporting C++ class definitions across the library boundary, the exported names are decorated in a manner proprietary to the compiler vendor. This approach restricts the use of such a DLL to applications that are also compiled with the same compiler. If the exported classes are small, it is often more convenient to put them into a static .LIB (as is the case with APPUTIL) than to put them into a .DLL.

Because the DLLSKEL DLL has resources and because it is desirable to provide outside users of the DLL with an .H file of the same name, a separate DLLSKELI.H is used for internal class declarations and resource identifier definitions, while the DLLSKEL.H file is intended solely for the DLL's function import declarations and export definitions.

For the consumer application (in this case DLLUSER.EXE) to link to and properly call the exported functions in the DLL, it must import them. The following fragment from DLLSKEL.H shows how to use the same convenient 'STDENTRY' macros to serve both the consumer application's import declaration and the provider DLL's export definition needs.

  #if !defined(_DLLEXPORT_)

  // If _DLLEXPORT_ is not defined then the default is to import.
  #if defined(__cplusplus)
  #define DLLENTRY extern "C" __declspec(dllimport)
  #else
  #define DLLENTRY extern __declspec(dllimport)
  #endif
  #define STDENTRY DLLENTRY HRESULT WINAPI
  #define STDENTRY_(type) DLLENTRY type WINAPI

  // Here is the list of service APIs offered by the DLL (using the
  // appropriate entry API declaration macros just #defined above).
  STDENTRY_(BOOL) DllHelloBox (HWND);
  STDENTRY_(BOOL) DllAboutBox (HWND);

  #else  // _DLLEXPORT_

  // Else if _DLLEXPORT_ is defined then we've been told to export.
  #if defined(__cplusplus)
  #define DLLENTRY extern "C" __declspec(dllexport)
  #else
  #define DLLENTRY __declspec(dllexport)
  #endif
  #define STDENTRY DLLENTRY HRESULT WINAPI
  #define STDENTRY_(type) DLLENTRY type WINAPI

  #endif // _DLLEXPORT_

The DLLENTRY macro allows you more freedom to specify return types and calling conventions. The STDENTRY assumes the COM/OLE standard HRESULT return type and the WINAPI calling conventions (usually defined as __stdcall). The STDENTRY_ macro makes this same assumption but does allow you to specify a return type.

The '__declspec(dllimport)' and '__declspec(dllexport)' directives are one way to declare DLL imports and to define DLL exports in Win32 programming. Another way is to Link the executable using a module definition (.DEF) file that contains import and export statements. This technique was commonly used in Win16 programming but is often not needed in Win32 programming. Note that the __declspec specifications are Microsoft-specific techniques in C++ programs. DLL exports can also be designated using the export switch on the Linker command line.

Note the use of extern "C" when compiling under C++ (__cplusplus). This tells the compiler, during compilation of both the DLL and the calling code, that the default C++ function name decoration will not be used. This is usually preferred in generic DLLs, whose function calls might be called by applications that could be compiled with a variety of C++ compiler tools, including straight C compilers.

The default behavior of DLLSKEL.H is to serve calling applications that import the functions in the DLL. Such applications simply include DLLSKEL.H during compilation of such a calling module. When the DLL itself is compiled, however, this file is included as in the following fragment from DLLSKEL.CPP.

  #define _DLLEXPORT_
  #include "dllskel.h"

When _DLLEXPORT_ is thus defined, the behavior of DLLSKEL.H is to serve the DLL itself in defining its exported functions. The following fragment from DLLSKEL.CPP shows such a function definiton.

  STDENTRY_(BOOL) DllAboutBox(HWND hWnd);
  {
    ...
  }

When the DllAboutBox function is defined in DLLSKEL.CPP, the STDENTRY_ macro expands to yield the following:

  extern "C" __declspec(dllexport) BOOL WINAPI DllAboutBox(HWND hWnd)
  {
    ...
  }

Likewise, when the DllAboutBox function is imported in DLLSKEL.H, the declaration is expanded to the following import function prototype.

  extern "C" __declspec(dllimport) BOOL WINAPI DllAboutBox(HWND hWnd);

The WINAPI macro expands to various things, depending on the build environment, but generally stipulates the calling convention (usually __stdcall). See WIN32.MAK and the standard Windows include file WINDEF.H for more details on the WINAPI macro.

Top Back to page top

© 1995-1998 Microsoft Corporation


DLLUSER - EXE User of a DLL - Lesson 4

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DLLUSER - EXE User of a DLL

SUMMARY

The DLLUSER sample introduces the basic skeleton for an EXE that implicitly loads a Win32 DLL and makes calls to it. In this case, DLLUSER works with the DLLSKEL.DLL dynamic link library from the DLLSKEL lesson. You must build DLLSKEL before you try to build DLLUSER.EXE, because the DLLSKEL makefile copies the necessary DLLSKEL.H, DLLSKEL.LIB, and DLLSKEL.DLL files it produces into the DLLUSER sibling directory.

For functional descriptions and a tutorial code tour of DLLUSER, see the Code Tour section in DLLUSER.HTM. For details on the external user operation of DLLUSER, see both the Usage and Operation sections in DLLUSER.HTM. To read DLLUSER.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DLLUSER lesson in the table of lessons. You can also achieve the same thing by clicking the DLLUSER.HTM file after locating the main tutorial directory in the Windows Explorer. See also the DLLSKEL.HTM file in the main tutorial directory for more details on how DLLSKEL works and exposes its services to DLLUSER.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DLLUSER sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DLLUSER.DSP

You can also simply double-click the DLLUSER.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

This DLLUSER.EXE application is provided to interact with DLLSKEL.DLL. DLLUSER recognizes no command line arguments. See the Operation section below for more details.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, DLLUSER.EXE is the client executable to run for this sample.

OPERATION

The DLLUSER.EXE application provides the main user interface for this lesson. It exercises the associated, but independent, DLLSKEL.DLL. Here is a summary of operation from the standpoint of DLLUSER.EXE as a controller of DLLSKEL.DLL:

Menu Selection: File/Exit
Quits DLLUSER.

Menu Selection: Test/Call DLLSKEL.DLL
Calls the DllHelloBox function of DLLSKEL.DLL, which displays a message box with the following format: DLLSKEL instance [i] hello. Shared count = [c], where [i] is the DLLUSER process instance count and [c] is the shared count of the number of times the DllHelloBox function has been called from any DLLUSER.

Menu Selection: Test/About DLLSKEL.DLL
Calls the DllAboutBox function of DLLSKEL.DLL, which displays the About dialog box for the DLL itself, as opposed to the About dialog box for the controlling DLLUSER application.

Menu Selection: Help/DLLUSER Tutorial
Opens the DLLUSER.HTM tutorial file in the Web browser.

Menu Selection: Help/DLLSKEL Tutorial
Opens the DLLSKEL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Opens one of this sample's source files. After you select a file name, the Windows Notepad utility displays the selected source file. This command illustrates how to program the use of the File Open common dialog box.

Menu Selection: Help/About DLLUSER
Displays the About dialog box for this application, a standard part of this series of code samples. The command illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

CODE TOUR

Files          Description
DLLUSER.TXT    Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
DLLUSER.H      The include file for the DLLUSER.EXE application. Contains
               class declarations, function prototypes, and resource
               identifiers.
DLLUSER.CPP    The main implementation file for DLLUSER.EXE. Has WinMain
               and CMainWindow implementation.
DLLUSER.RC     The DLLUSER.EXE application resource definition file.
DLLUSER.ICO    The icon resource for DLLUSER.EXE.
DLLSKEL.H      The include file for declaring as imported the service
               functions in DLLSKEL.DLL. This file is copied to the
               sibling ..\INC directory during the build of DLLSKEL.
DLLSKEL.LIB    The library file for use in linking to the function
               calls used in DLLSKEL.DLL. This file is copied to the
               sibling ..\LIB directory during the build of DLLSKEL.
DLLSKEL.DLL    The binary DLLSKEL dynamic link library. This file is copied
               to this DLLUSER directory during the build of DLLSKEL.
DLLSKEL.DSP    Microsoft Visual Studio Project file.

This DLLUSER code sample offers another basic skeleton that is used in subsequent code samples in this tutorial. You can also use it as a source skeleton in your own programming. DLLUSER is based largely on the previous EXESKEL code sample. You can study the code comments in DLLUSER to learn more about this C++ skeleton.

In the context of an COM Tutorial series of code samples, the goal of DLLUSER is to show the use of services in the separate DLLSKEL DLL. The DLLUSER application also illustrates how to link a Win32 C++ EXE application to a separate Win32 DLL and call the exported services.

DLLUSER.CPP defines the WinMain entry function for the entire application, which contains the message loop. Like EXESKEL, DLLUSER.CPP makes use of many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the source code located in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

DLLUSER.H exploits APPUTIL's CVirWindow abstract base class (see APPUTIL.H) to derive and implement a CMainWindow class. CMainWindow encapsulates the main window functionality into a convenient C++ object. It handles initialization of new instances of the main window and the message dispatching of the main window procedure. This main window procedure is a method of CMainWindow.

For the DLLUSER application to make use of the exported functions in DLLSKEL, the DLLSKEL.H file must be included. The default behavior of DLLSKEL.H is to serve calling applications that import the functions residing in the DLL.

For example, the following prototype appers in DLLSKEL.H.

  STDENTRY_(BOOL) DllAboutBox(HWND hWnd);

When the DllAboutBox function is imported, this prototype declaration is expanded as follows.

  extern "C" __declspec(dllimport) BOOL WINAPI DllAboutBox(HWND hWnd);

The WINAPI macro expands to various things, depending on the build environment, but generally stipulates the calling convention (for example, __stdcall). See WIN32.MAK and the standard Windows include file WINDEF.H for more details on the WINAPI macro.

The two sample calls in DLLUSER are shown in the following fragment from the menu handling in DLLUSER.CPP.

    ...
    case IDM_TEST_DLLHELLO:
      // Call the DLLSKEL DLL to say hello from it.
      ::DllHelloBox(m_hWnd);
      break;

    case IDM_TEST_DLLABOUT:
      // Call the DLLSKEL DLL to show the DLL's About Box.
      ::DllAboutBox(m_hWnd);
      break;
    ...

Both calls pass the window handle of the main DLLUSER window as a parent window of the dialog boxes that DLLSKEL.DLL will put on the screen.

Top Back to page top

© 1995-1998 Microsoft Corporation


COMOBJ - Containment and Aggregation in a DLL - Lesson 5

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

COMOBJ - Containment and Aggregation in a DLL

SUMMARY

This sample introduces COM objects.

An object is a software construct that encapsulates or packages some data and some public methods that operate on that data. The data is often said to be "hidden" while the methods are exposed as the chief means to access the data. At compile time, programming languages provide varying degrees of syntactic support to express objects. For example, the C++ language offers a Class syntactic construct to express an abstract type (or class) of object. However, this C++ Class construct does allow for public access to an object's data. An object class is an abstraction that refers to an open-ended set of potential objects of the same specific kind. Objects are real things that occupy memory with their data and code instantiated in live binary form. The 'object' term typically refers to the runtime behavior of the this real software thing.

A COM object is a kind of object that originated with Microsoft's Component Object Model (COM). COM objects completely hide their data and expose their methods through a construct called an interface. A COM interface is a grouping of related methods that is uniquely identified for all programs and all time (by an Interface ID). Interfaces are used to encapsulate COM object feature sets. The most fundamental feature set in COM gives COM objects their nature as COM objects. The IUnknown interface exposes this feature set in several methods (AddRef, Release, and QueryInterface) that determine the common behavior governing all COM object lifetimes and how interfaces on COM objects are properly acquired.

Outside users of a COM object can only use the object by acquiring one of its interfaces. This acquisition of an interface is achieved by obtaining a pointer to the COM object's implementation of the interface. COM objects "know" about the interfaces they expose to clients and can provide pointer references to the interface implementations. Likewise, COM objects know about how many interface references they have handed out to clients and can thus control their own lifetime. When no references remain, the object normally removes itself from memory and ceases to exist.

The COMOBJ DLL offers several car-related COM object classes. Because COMOBJ.DLL supports no object handlers, class factories, full in-process servers, or marshaling, it is not a full-blown COM Server. Rather, it is a primitive precursor to a COM in-process server.

This DLL exposes the following COM objects: COCar, COUtilityCar, and COCruiseCar. Appropriate create functions are exported from this DLL: CreateCar, CreateUtilityCar, and CreateCruiseCar.

In this tutorial, COMOBJ works with the COMUSER code sample to show how an EXE client (COMUSER.EXE) calls COM object creation services and manipulates the COM objects that are created.

For functional descriptions and a tutorial code tour of COMOBJ, see the Code Tour section in COMOBJ.HTM. For details on setting up the programmatic usage of COMOBJ, see the Usage section in COMOBJ.HTM. To read COMOBJ.HTM, run TUTORIAL.EXE in the main tutorial directory and click the COMOBJ lesson in the table of lessons. You can also achieve the same thing by clicking the COMOBJ.HTM file after locating the main tutorial directory in the Windows Explorer. See also COMUSER.HTM in the main tutorial directory for more details on the COMUSER client application and how it works with COMOBJ.DLL itself. You must build COMOBJ.DLL before building COMUSER. The makefile for COMOBJ copies the necessary COMOBJ.H, COMOBJ.LIB, and COMOBJ.DLL files to the appropriate sibling directories once the files are built. The .H goes to \INC, the .LIB goes to \LIB, and the .DLL goes to \COMUSER.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the COMOBJ sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV COMOBJ.DSP

You can also simply double-click the COMOBJ.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

COMOBJ is a DLL that you can access from .EXE modules either by performing an explicit LoadLibrary call or implicitly loading the DLL by linking to its associated import library (.LIB) file. In either case, you need to include COMOBJ.H to declare the functions that are defined as exported in the COMOBJ DLL. In this lesson, a representative COMUSER.EXE application is provided to illustrate the programmatic use of COMOBJ.DLL. COMUSER is built in the COMUSER lesson (in sibling directory COMUSER). See below for more details.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, COMUSER.EXE is the client executable to run for this sample.

CODE TOUR

Files       Description
COMOBJ.TXT  Short sample description.
MAKEFILE    The generic makefile for building the COMOBJ.DLL code sample.
COMOBJ.H    The include file for declaring as imported or defining as
            exported the service functions in COMOBJ.DLL.
COMOBJ.CPP  The main implementation file for COMOBJ.DLL. Has DllMain
            and the COM Object creation functions.
CAR.H       The include file for the COCar COM object class.
CAR.CPP     The implementation file for the COCar COM object class.
UTILCAR.H   The include file for the COUtilityCar COM object class.
UTILCAR.CPP The implementation file for the COUtilityCar COM object class.
CRUCAR.H    The include file for the COCruiseCar COM object class.
CRUCAR.CPP  The implementation file for the COCruiseCar COM object class.
COMOBJI.H   The include file for the internal class declarations and
            resource identifier definitions for resources stored inside
            the COMOBJ.DLL.
COMOBJ.RC   The DLL resource definition file.
COMOBJ.ICO  The icon resource.
COMOBJ.DSP  Microsoft Visual Studio Project file.

This code sample is based on the code in DLLSKEL. See the DLLSKEL code tour in the sibling DLLSKEL directory for more detail on the DLL skeleton.

COMOBJ makes use of many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the source code in the sibling APPUTIL directory and tutorial file APPUTIL.HTM in the main tutorial directory.

In the context of this tutorial, the goal of COMOBJ is to use a C++ DLL framework to illustrate various techniques for constructing COM objects. The DLL acts as a primitive server for several simple COM objects. Later in this series, COM objects are shown housed as COM component objects in COM servers. COM components are COM object types that have associated class factories, are housed in COM servers, and are registered in the registry.

The COM objects that are used in both the COMUSER and COMOBJ code samples represent sport utility vehicles. We invent some basic feature sets that are available for modeling such car objects. These feature sets are implemented as custom interfaces in COM objects. Custom interfaces are created by application programmers and are declared in the application's header files. In contrast to custom interfaces, standard interfaces (e.g., IUnknown) are provided by COM and are pre-declared in COM's system header files. In this sample, three custom COM interfaces are created to roughly model some feature sets on real cars: ICar, IUtility, and ICruise.

The most basic interface, ICar, has the following methods: Shift, Clutch, Speed, and Steer. Through this interface, car objects can be commanded to:

  1. Shift. Shift the 5-speed manual transmission (1 - 5 forward; 6 reverse).
  2. Clutch. Engage the engine to, or disengage it from, the transmission.
  3. Speed. Accelerate or decelerate the car to a specified speed in miles per hour.
  4. Steer. Steer the car by turning the steering wheel to point the car at a specified angle.

The IUtility Interface can add off-road and 4-wheel drive utility systems to our car objects: Offroad and Winch. Through this interface, these car objects can:

  1. Offroad. Set the transfer case gear to the specified gear (0 = 2H or regular 2-wheel drive; 1 = 4H or 4-wheel drive high speed; 2 = neutral; and 3 = 4L or 4-wheel drive low speed).
  2. Winch. Turn on/off the front-mounted winch.

The ICruise interface can add automatic cruise control methods to our car objects: Engage, Adjust. Through this interface these car objects can:

  1. Engage. Turn the cruise control system on or off.
  2. Adjust. Adjust the cruising speed up or down by 3 mph.

Using these interfaces, COMOBJ exposes three COM object classes: COCar, COUtilityCar, and COCruiseCar. COCar exposes the basic ICar interface. COUtilityCar exposes the ICar and IUtility interfaces. COCruiseCar exposes the ICar and ICruise interfaces. So COCars have basic Car behavior (ICar). UtilityCars have basic Car behavior (ICar) with offroad utility systems (IUtility). COCruiseCars have basic Car behavior (ICar) with a cruise control system (ICruise).

COCar is a COM object class that exposes a native implementation of the ICar interface. The multiple interfaces of COCar are implemented using the nested class technique.

COUtilityCar is a COM object class that exposes a native implementation of the IUtility interface and reuses the COCar class and its ICar interface by containment. The multiple interfaces of COUtilityCar are implemented using the nested class technique.

COCruiseCar is a COM object class that exposes a native implementation of the ICruise interface and reuses the COCar class and its ICar interface by aggregation. The multiple interfaces of COCruiseCar are implemented using the nested class technique.

COM objects created from these three classes can be used by client software only as in-process COM objects. No object handler or marshaling is provided, and simple creation functions are supplied instead of fully functioning class factories. The idea here is to keep it simple to illustrate COM object interface augmentation and the COM object reuse techniques of containment and aggregation.

COMOBJ.H is an important file not only for compiling COMOBJ.DLL, but also for outside users of the DLL. As with DLLSKEL.H in the DLLSKEL code sample, this include file serves double duty by defining certain functions as exported and also declaring those functions as imported, depending on how COMOBJ.H is included.

COMOBJ.H uses the #if !defined(RC_INCLUDE) block to exclude the contents of this file if someone includes it in an .RC file. RC_INCLUDE is defined on the RC compiler invocation command lines. See RCFLAGS in the makefiles. In general, COMOBJ.H is written to support either C or C++ programs.

In ICARS.H in the sibling \INC directory, the car-related interfaces are declared. CARGUIDS.H is in the same common directory and defines the GUIDs for the interfaces. CARGUIDS.H also contains the CLSIDs for the class factories of the main COM object classes. Class Factories are not used in this code sample but will be studied in future lessons. So these two files are common include files used by the current code sample as well as future ones.

If your project consists of a suite of executables that all manage a common set of COM objects and interfaces (as this code sample series does), it is a good practice to factor out those common interface and GUID definitions into a common include directory. Such factoring can help you ensure that there are no conflicts in the interface or GUID definitions between executables in the application suite.

First the interfaces. From ICARS.H, here is a representative declaration for ICar.

  DECLARE_INTERFACE_(ICar, IUnknown)
  {
    // IUnknown methods.
    STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE;
    STDMETHOD_(ULONG,AddRef)  (THIS) PURE;
    STDMETHOD_(ULONG,Release) (THIS) PURE;

    // ICar methods.
    STDMETHOD(Shift)   (THIS_ short) PURE;
    STDMETHOD(Clutch)  (THIS_ short) PURE;
    STDMETHOD(Speed)   (THIS_ short) PURE;
    STDMETHOD(Steer)   (THIS_ short) PURE;
  };

The DECLAREINTERFACE_ macro is used to publicly derive the ICar interface class from the existing COM IUnknown interface abstract base class. The STDMETHOD macro declares the method with the standard HRESULT error return code. The parameter is the method name. The STDMETHOD_ macro is used when the method returns something other than HRESULT. For example, in AddRef above, a ULONG return type is specified. THIS is used when the method accepts the void parameter. THIS_ is used when parameter types are accepted and those types are specified in parameter order. The PURE macro (in C++ it reduces to '=0') designates these methods as pure virtual functions, as all methods in C++ interface declarations must be. In C, this macro reduces to nothing. With these macros, you should be able to use the services of COMOBJ.DLL from either C or C++ programs.

A standard part of every interface is an IUnknown implementation, which includes the three methods QueryInterface, AddRef, and Release. All COM interfaces are derived from IUnknown. Additional declarations are necessary for the methods that are unique to each interface (for example, the Shift, Clutch, Speed, and Steer methods of ICar).

For this interface to be used in a way fully consistent with COM interfaces, it needs a globally unique identifier, or GUID, as in CARGUIDS.H.

  DEFINE_GUID(IID_ICar,
    0x0002da00, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

With this definition, the GUID of the interface can be referred to by the name IID_ICar.

This GUID is part of a Microsoft pre-allocated sequence in which the first value 0x0002DAxx has values of xx in the range 00 to FF. Do not use the GUIDs in this sequence or any GUIDs in this code sample series elsewhere in other applications. You can obtain your own unique GUIDs by running the UUIDGEN utility supplied with the Platform SDK. The following command in the command prompt window will produce 10 such GUIDs in file GUIDS.C.

  UUIDGEN -s -n10 -oGUIDS.C

This utility uses random number generation techniques to ensure absolute GUID uniqueness. It produces the GUIDs as a C structure:

  INTERFACENAME = { /* b124eec0-e7a9-11ce-9a5f-444553540000 */
      0x3c0869c0,
      0xd721,
      0x11ce,
      {0x9a, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
    };

This structure is easy to edit into the format required by the DEFINE_GUID macro as shown above.

COMOBJ.CPP provides the main exported creation functions for the DLL. It also has the DllMain function that you saw in DLLSKEL. Because COMOBJ.CPP brings together the functionality of the DLL, it includes the following #include statements:

  #include <windows.h>
  #include <ole2.h>
  #include <initguid.h>
  #include <apputil.h>
  #include <icars.h>
  #include <carguids.h>
  #include "comobji.h"
  #define _DLLEXPORT_
  #include "comobj.h"
  #include "car.h"
  #include "utilcar.h"
  #include "crucar.h"

We include OLE2.H because we will need to call the COM/OLE library functions. Of special note is INITGUID.H. This file must be included only once in each executable, just after the other COM/OLE header files are included. What this ensures is that any GUIDs defined in this application will be instantiated as named data in the data segment of this executable DLL. If you do not include this file, you will get unresolved external references to the IID_IMyInterface GUIDs during linking. We include ICARS.H for the common car-related interface specifications. We include CARGUIDS.H for the interface GUID definitions.

As with DLLSKEL, we confine internal resource identifier macros and utility classes to a separate file, COMOBJI.H. We define the _DLLEXPORT_ macro to force export definition of the creation functions in this file. We next include COMOBJ.H for the STDENTRY macro definitions, as well as the main interfaces already discussed. We include CAR.H, UTILCAR.H, and CRUCAR.H for the COM object class declarations of the three main objects exposed by this DLL: COCar, COUtilityCar, and COCruiseCar.

There is a global variable worthy of note:

  // Here is a pointer for use by the global Debug Message logging macros.
  // Set by the ComObjInitMsgLog function call from an outside EXE user.
  CMsgLog* g_pMsgLog = NULL;

The trace message log facility is a simple one that is meant to be global and nonintrusive. One central set of LOGxx macros all assume the same-named global variable (ie, g_pMsgLog) regardless of the executable module. Because the MsgLog window lives in a host EXE which has a message loop, we define this global variable in the DLL. It is later assigned by a call from the EXE to the ComObjInitMsgLog function which is also defined in COMOBJ.

As a primitive server providing the three COM objects, this DLL exports three creation functions: CreateCar, CreateUtilityCar, and CreateCruiseCar. Explicit calls to delete the objects are not needed, because COM object lifetimes are controlled by their own internal reference counts. The creation functions provide a requested interface pointer on the new object. After creation, it is only through interfaces that the objects are accessible. The Car, UtilityCar, and CruiseCar objects are all exposed as COM objects that are aggregatable outside this DLL via the pUnkOuter parameter to their creation functions. Here is a representative CreateCruiseCar function:

  STDENTRY CreateCruiseCar(
             IUnknown* pUnkOuter,
             REFIID riid,
             PPVOID ppv)
  {
    HRESULT hr;
    COCruiseCar* pCob;

    LOGF1("D: CreateCruiseCar. pUnkOuter=0x%X.",pUnkOuter);

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state the IUnknown interface MUST also concomitantly
    // be requested. If it is not so requested (riid != IID_IUnknown), then
    // an error must be returned indicating that no aggregate creation of
    // the COCruiseCar COM Object can be performed.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COCruiseCar COM Object.
      pCob = new COCruiseCar(pUnkOuter);
      if (NULL != pCob)
      {
        // If we have succeeded in instantiating the COCruiseCar object,
        // we initialize it to set up any subordinate objects (ie, via
        // containment or aggregation).
        hr = pCob->Init();
        if (SUCCEEDED(hr))
        {
          // We QueryInterface this new COM Object not only to deposit the
          // requested interface pointer into the caller's pointer variable,
          // but to also automatically bump the Reference Count on the new
          // COM Object after handing out this reference to it.
          hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
      LOGF1("D: CreateCruiseCar Succeeded. *ppv=0x%X.",*ppv);

    return hr;
  }

Using the REFIID parameter, this function attempts to return the requested interface pointer after the COCruiseCar COM object is created. The QueryInterface method of the new object's IUnknown interface is used to get the interface pointer. The pUnkOuter parameter determines whether the object is being aggregated into another object. The CoCruiseCar's Init method is called to compartmentalize any creation of subordinate objects, which applies here because a subordinate COCar object is created using aggregation in this Init method.

COCar COM Object

The COCar COM object class is declared in CAR.H.

  class COCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COCar(IUnknown* pUnkOuter);
      ~COCar(void);

      // IUnknown members. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpICar : public ICar
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICar(COCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICar(void);

          // IUnknown members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICar members.
          STDMETHODIMP Shift(short nGear);
          STDMETHODIMP Clutch(short nEngaged);
          STDMETHODIMP Speed(short nMph);
          STDMETHODIMP Steer(short nAngle);

        private:
          // Data private to this interface implementation of ICar.
          ULONG        m_cRefI;       // Interface Ref Count (for debugging)
          COCar*       m_pBackObj;    // Parent Object back pointer
          IUnknown*    m_pUnkOuter;   // Outer unknown for Delegation
      };

      // Make the otherwise private and nested ICar interface implementation
      // a friend to COM object instantiations of this selfsame COCar
      // COM object class.
      friend CImpICar;

      // Private data of COCar COM objects.

      // Nested ICar implementation instantiation.  This ICar interface is
      // instantiated inside this COCar object as a native interface.
      CImpICar         m_ImpICar;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*        m_pUnkOuter;
  };

The ICar interface is implemented as a nested class. The nested CImpICar is declared in the private section of the main COCar object class. An instance of the ICar interface is created in the private section with this code.

  // Nested ICar implementation instantiation. This ICar interface is
  // instantiated inside this COCar object as a native interface.
  CImpICar        m_ImpICar;

Because the ICar interface implementation is nested in the COCar object class, no explicit creation and deletion operations are needed. Its lifetime is tied to the lifetime of the parent COCar object, so it is automatically constructed and destroyed as the parent object is. To ensure the CImpICar methods can access the private data of the COCar class, CImpICar is made a friend ot COCar.

The constructor function of CImpICar contains two important parameters. The pBackObj pointer is used by CImpICar methods to access private data in COCar. The pUnkOuter parameter signals aggregation if it is non-NULL, in which case it is stored within the CImpICar object and used for later delegation of calls to its own interfaces' IUnknown methods. More on this later, when we tour CAR.CPP.

The m_pUnkOuter variable is used when this object is aggregated inside another COM object. The value then is a pointer to the outermost controlling IUnknown, which is passed to the COCar constructor. COCar supports both containment and aggregation.

CAR.CPP defines the methods of COCar. The constructor uses an interesting member initializer to pass the pBackObj and pUnkOuter parameters mentioned above to the CImpICar constructor. This inner class's constructor is thereby executed prior to the main COCar constructor.

  COCar::COCar(IUnknown* pUnkOuter) : m_ImpICar(this, pUnkOuter)
  {
    // Zero the COM object's reference count.
    m_cRefs = 0;

    // No AddRef necessary if non-NULL, as we're nested.
    m_pUnkOuter = pUnkOuter;

    LOGF1("D: COCar Constructor. m_pUnkOuter=0x%X.", m_pUnkOuter);

    return;
  }

Here is that inner CImpICar::CImpICar constructor.

  COCar::CImpICar::CImpICar(
    COCar* pBackObj,
    IUnknown* pUnkOuter)
  {
    // Init the Interface Ref Count (used for debugging only).
    m_cRefI = 0;

    // Init the Back Object Pointer to point to the parent object.
    m_pBackObj = pBackObj;

    // Init the CImpICar interface's delegating Unknown pointer. We use
    // the Back Object pointer for IUnknown delegation here if we are not
    // being aggregated. If we are being aggregated, we use the supplied
    // pUnkOuter for IUnknown delegation. In either case, the pointer
    // assignment requires no AddRef because the CImpICar lifetime is
    // quaranteed by the lifetime of the parent object in which
    // CImpICar is nested.
    if (NULL == pUnkOuter)
    {
      m_pUnkOuter = pBackObj;
      LOG("D: COCar::CImpICar Constructor. Non-Aggregating.");
    }
    else
    {
      m_pUnkOuter = pUnkOuter;
      LOG("D: COCar::CImpICar Constructor. Aggregating.");
    }

    return;
  }

Note the logic used to assign the delegating m_pUnkOuter pointer. If the COCar object is not being aggregated (parameter pUnkOUter == NULL), m_pUnkOuter is assigned the COCar object's 'this' pointer (passed in the pBackObj parameter of the member initializer pointed out above). In this case, the ICar interface's IUnknown methods will all be delegated to the COCar object. If COCar is being aggregated (pUnkOuter != NULL), m_pUnkOuter is assigned a pointer to the outermost controlling IUnknown (passed in the pUnkOuter parameter of the member initializer pointed out above). The pUnkOuter parameter is then passed to the constructor of COCar. If pUnkOuter is non-NULL, it is passed to any subordinate objects that are created. It makes the code for the CImpICar IUnknown methods simple as well--they all blindly delegate through the interface's assigned m_pUnkOuter pointer. Here, for example, is CImpICar::QueryInterface.

  STDMETHODIMP COCar::CImpICar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    LOG("D: COCar::CImpICar::QueryInterface. Delegating.");

    // Delegate this call to the outer object's QueryInterface.
    return m_pUnkOuter->QueryInterface(riid, ppv);
  }

When the COCar object is aggregated, calls to this method are delegated to the outermost controlling IUnknown. At the outermost aggregating object, it appears that this ICar interface is on the outer object rather than on the inner COCar object.

Since this COCar COM object does not reuse any COM objects, its IUnknown methods are easy. Here's QueryInterface:

  STDMETHODIMP COCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("D: COCar::QueryInterface. 'this' pIUnknown returned.");
    }
    else if (IID_ICar == riid)
    {
      *ppv = &m_ImpICar;
      LOG("D: COCar::QueryInterface. pICar returned.");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to the interface so obey the COM rules
      // and AddRef the reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }

    return (hr);
  }

For IID_IUnknown, the 'this' pointer is used, because a pointer to the object itself is a pointer to the object's IUnknown. For IID_ICar, QueryInterface passes the address of the nested instance of CImpICar (*ppv = &m_ImpICar). After passing an interface pointer, QueryInterface calls AddRef to maintain the object's reference count.

The "if (IID_Unknown == riid)" comparisons take advantage of an overloaded C++ == operator. Such overloaded operators are provided in the COM/OLE headers for types GUID, CLSID, and IID. IIDs are actually the 128-bit (16 byte) integer entities that you saw above. In C++, these entities can simply be referred to using references (as with IID_Unknown above).

The actual ICar methods (like CImpICar::Shift) in this code sample are empty LOG announcements that the methods have been called. This is where the skeleton ends and real application code would be added.

COUtilityCar COM Object

UTILCAR.H declares the COUtilityCar COM object class.

  class COUtilityCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COUtilityCar(IUnknown* pUnkOuter);
      ~COUtilityCar(void);

      // A general public method for initializing this newly created
      // COUtilityCar object. Creates any subordinate arrays, structures,
      // or objects. Not exposed as a method in an interface.
      HRESULT Init(void);

      // IUnknown members. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // We get this ICar interface pointer via containment reuse of the
      // ICar interface in an instantiated COCar.
      ICar*           m_pICar;

    private:
      // We show nested interface class implementations here.

      // We implement the basic ICar interface in this COUtilityCar
      // COM object class.
      class CImpICar : public ICar
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICar(COUtilityCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICar(void);

          // IUnknown members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICar members.
          STDMETHODIMP Shift(short nGear);
          STDMETHODIMP Clutch(short nEngaged);
          STDMETHODIMP Speed(short nMph);
          STDMETHODIMP Steer(short nAngle);

        private:
          // Data private to this interface implementation of ICar
          ULONG         m_cRefI;      // Interface Ref Count (for debugging)
          COUtilityCar* m_pBackObj;   // Parent Object back pointer
          IUnknown*     m_pUnkOuter;  // Outer unknown for Delegation
      };

      // We implement the IUtility interface (ofcourse) in this COUtilityCar
      // COM object class. This is the interface that we are using as an
      // augmentation to the existing COCar COM object class.
      class CImpIUtility : public IUtility
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIUtility(COUtilityCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpIUtility(void);

          // IUnknown members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IUtility members.
          STDMETHODIMP Offroad(short nGear);
          STDMETHODIMP Winch(short nRpm);

        private:
          // Data private to this interface implementation of IUtility
          ULONG         m_cRefI;      // Interface Ref Count (for debugging)
          COUtilityCar* m_pBackObj;   // Parent Object back pointer
          IUnknown*     m_pUnkOuter;  // Outer unknown for Delegation
      };

      // Make the otherwise private and nested ICar interface implementation
      // a friend to COM object instantiations of this selfsame COUtilityCar
      // COM object class.
      friend CImpICar;
      friend CImpIUtility;

      // Private data of COUtilityCar COM objects.

      // Nested ICar implementation instantiation.
      CImpICar        m_ImpICar;

      // Nested IUtility implementation instantiation.
      CImpIUtility    m_ImpIUtility;

      // Main Object reference count.
      ULONG           m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown       *m_pUnkOuter;
  };

COM objects of the COUtilityCar class augment COCar COM objects (which expose ICar interface features) with a native implementation of IUtility interface features (Offroad and Winch). A COUtilityCar COM object class reuses the COCar COM object class by containment. The multiple interfaces on this COM object class are implemented as nested classes.

To supply both the ICar and IUtility interfaces, we implement them in this COM object. The IUtility interface is a native implementation in the COUtilityCar COM object. The COUtilityCar object contains a delegating implementation of the ICar interface, called CImpICar in the sample code. The whole purpose of CImpICar is to delegate ICar calls to the real ICar implementation, which is located in a contained COCar object.

For this implementation, the COUtilityCar object's constructor creates a COCar object and holds a pointer to the ICar interface of the COCar object. This pointer is used to delegate calls to ICar methods (implemented in the COUtilityCar object) to the corresponding method implemented in the contained object's ICar interface. We declare the delegation pointer like this:

  // We get this ICar interface pointer via containment reuse of the
  // ICar interface in an instantiated COCar.
  ICar*           m_pICar;

This delegating pointer is assigned when the COUtilityCar is initialized by the Init call in the CreateUtilityCar function in COMOBJ.CPP.

  // Instantiate a COUtilityCar COM Object.
  pCob = new COUtilityCar(pUnkOuter);
  if (NULL != pCob)
  {
    // If we have succeeded in instantiating the COUtilityCar object
    // we initialize it to set up any subordinate objects (ie, via
    // containment or aggregation).
    hr = pCob->Init();
    if (SUCCEEDED(hr))
    {
      // We QueryInterface this new COM Object not only to deposit the
      // requested interface pointer into the caller's pointer variable,
      // but to also automatically bump the Reference Count on the new
      // COM Object after handing out this reference to it.
      hr = pCob->QueryInterface(riid, (PPVOID)ppv);
    }
  }
  else
    hr = E_OUTOFMEMORY;

After creating a COUtilityCar COM object, the object's Init method is called to create and initialize any inner objects. In this case, an inner COCar COM object must be created. Here is the Init method (from UTILCAR.CPP):

  HRESULT COUtilityCar::Init(void)
  {
    HRESULT hr;

    LOG("D: COUtilityCar::Init.");

    // Create an instance of the COCar object and ask for its ICar
    // interface directly. We pass NULL for the pUnkOuter because we
    // are not aggregating. It is here that we are reusing the COCar
    // COM Object through the Containment technique. We cache the
    // pointer to the ICar interface for later delegation use. We
    // don't need to AddRef it here because CreateCar does that for us.
    hr = CreateCar(NULL, IID_ICar, (PPVOID) &m_pICar);

    return (hr);
  }

The call to the CreateCar function creates a COCar COM object. COUtilityCar objects reuse COCar objects by containment, not aggregation, so the pUnkOuter parameter is NULL. Since the COCar COM object is not aggregated, CreateCar can explicitly ask it for its IID_ICar interface. The output parameter passes the address of the m_pICar delegation pointer (&m_pICar).

In UTILCAR.CPP, the IUnknown methods of CImpIUtility all delegate as was done for the ICar implementation in COCar (through the interface's m_pUnkOuter). But the COUtilityCar object contains a COCar object, so the delegating ICar implementation delegates calls (through m_pICar) to COCar's ICar implementation. Here is an example, ICar::Speed.

  STDMETHODIMP COUtilityCar::CImpICar::Speed(
                 short nMph)
  {
    LOGF1("D: COUtilityCar::CImpICar::Speed. Delegating. nMph=%i.",nMph);

    m_pBackObj->m_pICar->Speed(nMph);

    return NOERROR;
  }

We need the m_pBackObj pointer to access the COUtilityCar object's m_pICar pointer because this method is inside the delegating CImpICar class. Even though this CImpICar class is a delegating interface implementation for the one in the COCar object, we must still delegate calls to the IUnknown of CImpICar using the interface's m_pUnkOuter. Doing so helps make the COUtilityCar object itself an aggregatable COM object; just because it contains a COCar object (to expose the ICar interface) doesn't mean it cannot in turn be either contained or aggregated by some other COM object. As we did for COCar, we code COUtilityCar to support either containment or aggregation--a good general practice for all COM objects since it's easy once you understand what's going on.

Now we can look at the COUtilityCar controlling IUnknown's QueryInterface method:

  STDMETHODIMP COUtilityCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("D: COUtilityCar::QueryInterface. 'this' pIUnknown returned");
    }
    else if (IID_ICar == riid)
    {
      *ppv = &m_ImpICar;
      LOG("D: COUtilityCar::QueryInterface. pICar returned");
    }
    else if (IID_IUtility == riid)
    {
      *ppv = &m_ImpIUtility;
      LOG("D: COUtilityCar::QueryInterface. pIUtility returned");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to the interface so obey the COM rules
      // and AddRef the reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }

    return (hr);
  }

The IID_ICar case looks deceptively simple. QueryInterface just gives back the address of the delegating ICar interface implementation (&m_ImpICar). The IID_IUtility case is indeed simple, since we have implemented IUtility as a native interface on this COUtilityCar object. QueryInterface gives back the address of that IUtility interface (&m_ImpIUtility).

The last point to make about COUtilityCar concerns its constructor. As before, we use member initializers for the constructors of the nested interface implementation classes. These are required because we are using nested interface classes.

  COUtilityCar::COUtilityCar(IUnknown* pUnkOuter) :
    m_ImpICar(this, pUnkOuter),
    m_ImpIUtility(this, pUnkOuter)
  {
    ...
  }

COCruiseCar COM Object

CRUCAR.H declares the COCruiseCar COM object class.

  class COCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COCruiseCar(IUnknown* pUnkOuter);
      ~COCruiseCar(void);

      // A general public method for initializing this newly created
      // COCruiseCar object. Creates any subordinate arrays, structures,
      // or objects. Not exposed as a method in an interface.
      HRESULT Init(void);

      // IUnknown members. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // We implement the ICruise interface (ofcourse) in this COCruiseCar
      // COM object class.  This is the interface that we are using as an
      // augmentation to the existing COCar COM object class.
      class CImpICruise : public ICruise
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICruise(COCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICruise(void);

          // IUnknown members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICruise members.
          STDMETHODIMP Engage(BOOL bOnOff);
          STDMETHODIMP Adjust(BOOL bUpDown);

        private:
          // Data private to this interface implementation of ICruise
          ULONG         m_cRefI;      // Interface Ref Count (for debugging)
          COCruiseCar*  m_pBackObj;   // Parent Object back pointer
          IUnknown*     m_pUnkOuter;  // Outer unknown for Delegation
      };

      // Make the otherwise private and nested ICar interface implementation
      // a friend to COM object instantiations of this selfsame COCruiseCar
      // COM object class.
      friend CImpICruise;

      // Private data of COCruiseCar COM objects.

      // Nested ICruise implementation instantiation. This ICruise interface
      // is implemented inside this COCruiseCar object as a native interface.
      CImpICruise     m_ImpICruise;

      // Main Object reference count.
      ULONG           m_cRefs;

      // Outer unknown (aggregation & delegation). Used when this COCruiseCar
      // object is being aggregated. Otherwise, it is used for delegation
      // if this object is reused via containment.
      IUnknown*       m_pUnkOuter;

      // We need to save the IUnknown interface pointer on the COCar
      // object that we aggregate. We use this when we need to delegate
      // IUnknown calls to this aggregated inner object.
      IUnknown*       m_pUnkCar;
  };

With the COCruiseCar COM object class, once more the native interface, ICruise, is implemented as a nested interface class. COCruiseCar exposes the ICar interface through an aggregated COCar object. This is similar to the way COUtilityCar exposes its own native IUtility interface and also exposes the ICar interface, but COUtilityCar objects reuse the ICar interface through containment, not aggregation, of a COCar object. In the COMUSER code sample, we will build a COUtilityCruiseCar COM object that aggregates this COCruiseCar object. We'll then have a COM object that aggregates a COM object that aggregates a COM object.

CImpICruise itself has a private data member, m_pUnkOuter, for supporting aggregation and delegation. This pointer is used to delegate calls to IUnknown methods of the ICruise interface. If the COCruiseCar object is not aggregated, calls are delegated to its IUnknown. If the COCruiseCar object is aggregated, calls are delegated to the outermost controlling IUnknown. The m_pBackObj is for use in any of the ICruise methods to reference members of the COCruiseCar object.

As in previous interface implementations, the CImpICruise embedded class needs to be made a friend class to COCruiseCar so that CImpICruise's methods can access the private data in COCruiseCar.

COCruiseCar is itself constructed by aggregating the COCar COM object with this object's native ICruise interface implementation. That is what the m_pUnkCar pointer in the COCruiseCar object's private data is for. It caches a pointer to the aggregated inner COCar object. This pointer is used to delegate certain IUnknown calls to the inner object's IUnknown interface. More on this later.

When COCruiseCar is aggregated in an outer composite COM object, it has an m_pUnkOuter delegation pointer in its private data area. If it is not aggregated, this pointer merely serves as a record of the NULL pUnkOuter when a COCruiseCar object is created.

We will skip the COCruiseCar constructor function, because it is similar to that of other COM functions we have already studied.

The CreateCruiseCar function discussed earlier in this tour calls the COCruiseCar::Init method. Here is COCruiseCar::Init as it is implemented in CRUCAR.CPP:

  HRESULT COCruiseCar::Init(void)
  {
    HRESULT hr;
    // Set up the right pIUnknown for delegation. If we are being
    // aggregated, then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    LOG("D: COCruiseCar::Init.");

    // We create an instance of the COCar object and do this via the
    // Aggregation reuse technique. Note we pass pUnkOuter as the
    // Aggregation pointer. It is the 'this' pointer to this present
    // COCruiseCar object if we are not being aggregated; otherwise it is
    // the pointer to the outermost object's controlling IUnknown.
    // Following the rules of Aggregation we ask CreateCar to give us an
    // IID_IUnknown interface. We cache this pointer to the IUnknown of
    // the new COCar COM object for later use in delegating IUnknown calls.
    hr = CreateCar(pUnkOuter, IID_IUnknown, (PPVOID)&m_pUnkCar);

    return (hr);
  }

The CreateCar function creates and aggregates a COCar object and asks that the controlling IUnknown of the new COCar object be placed in the m_pUnkCar pointer discussed above.

This Init functionality could be attempted in the constructor function, but aggregation requests in the constructor can cause problems that are difficult to debug. Depending on the order of events, such creation calls often cause QueryInterface calls that are delegated to the outermost aggregating object. At that point, the source of any errors encountered can be difficult to detect. Placing aggregation requests in a separate Init method can make it easier to avoid problems, or to detect them if they arise. It is also convenient to make the Init method a member of the COCruiseCar class (rather than putting it in the CreateCruiseCar function) because the method has access to the object's private data and to the 'this' pointer. For example, in Init's conditional assignment of pUnkOuter above, we use both m_pUnkOuter and the 'this' pointer.

  IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

This conditional assignment gives pUnkOuter the right value for the CreateCar aggregation creation call. It's part of what is needed to make the COCruiseCar COM object an aggregating object that is at the same time aggregatable itself.

If m_pUnkOuter is NULL, this COCruiseCar object itself is not being aggregated. If it is not being aggregated, the inner COCar object still needs to be created as an aggregated object. In this case, COCruiseCar's 'this' controlling IUnknown pointer is passed in as the outermost controlling IUnknown. By doing so, any aggregated inner objects are informed that this COCruiseCar object is the outermost controlling IUnknown.

If m_pUnkOuter is not NULL, the COCruiseCar object is itself being aggregated, and it passes to any aggregated inner objects the pointer it receives in m_pUnkOuter as the outermost controlling IUnknown.

We mentioned earlier that m_pUnkCar was used to delegate certain IUnknown calls to the aggregated inner object. One place to see this delegation in action is in COCruiseCar::QueryInterface.

  STDMETHODIMP COCruiseCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("D: COCruiseCar::QueryInterface. 'this' pIUnknown returned.");
    }
    else if (IID_ICruise == riid)
    {
      // This ICruise interface is implemented in this COCruiseCar object
      // and might be called a native interface of COCruiseCar.
      *ppv = &m_ImpICruise;
      LOG("D: COCruiseCar::QueryInterface. pICruise returned.");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to an interface, so obey the COM rules
      // and AddRef its reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }
    else if (IID_ICar == riid)
    {
      LOG("D: COCruiseCar::QueryInterface. ICar delegating.");
      // We didn't implement the ICar interface in this COCruiseCar object.
      // The aggregated inner object (COCar) is contributing the ICar
      // interface to this present composite or aggregating COCruiseCar
      // object. So, to satisfy a QI request for the ICar interface,
      // we delegate the QueryInterface to the inner object's controlling
      // IUnknown.
      hr = m_pUnkCar->QueryInterface(riid, ppv);
    }

    return (hr);
  }

The IID_IUnknown and IID_ICruise cases are familiar. If QueryInterface passes those pointers by request, it must call AddRef on their interface. The IID_ICruise interface is exposed as a native interface and is implemented in the COCruiseCar class, so it is easy to pass a pointer to it. For the IID_ICar interface, however, the request must be passed to the aggregated object that owns it. The cached object pointer m_pUnkCar is used to delegate the QueryInterface call to the inner COCar object, which will delegate the call to its outermost controlling IUnknown, the present COCruiseCar object.

For information on the COCruiseCar::Release method, see the code comments below.

  STDMETHODIMP_(ULONG) COCruiseCar::Release(void)
  {
    ULONG ulCount = --m_cRefs;

    LOGF1("D: COCruiseCar::Release. New cRefs=%i.", m_cRefs);

    if (0 == m_cRefs)
    {
      // We artificially bump the main ref count. This fulfills one of
      // the rules of aggregated objects and ensures that an indirect
      // recursive call to this release won't occur because of other
      // delegating releases that might happen in our own destructor.
      m_cRefs++;
      delete this;
    }

    return ulCount;
  }

The COCruiseCar::CImpICruise::CImpICruise constructor uses the following logic to assign its own IUnknown delegation pointer.

  if (NULL == pUnkOuter)
  {
    m_pUnkOuter = pBackObj;
    LOG("D: COCruiseCar::CImpICruise Constructor. Non-Aggregating.");
  }
  else
  {
    m_pUnkOuter = pUnkOuter;
    LOG("D: COCruiseCar::CImpICruise Constructor. Aggregating.");
  }

If the COCruiseCar object is aggregated, the ICruise interface's m_pUnkOuter delegation pointer is assigned the pointer to the outermost controlling IUnknown (constructor parameter pUnkOuter). If the COCruiseCar object is not aggregated, any IUnknown calls received by the ICruise interface are delegated to the IUnknown of the COCruiseCar object using the value of pBackObj. The pBackObj pointer is passed to the CImpICruise constructor as the COCruiseCar object's 'this' pointer. This parameter to the interface implementation constructor was achieved through the use of member initializers in COCruiseCar's constructor. For more details on this special use of member initializers, see the discussion of the COCruiseCar::Init method earlier in this text file.

The CImpICruise interface's IUnknown methods are simple outward delegations using the value of m_pUnkOuter assigned. This assignment of the m_pUnkOuter IUnknown pointer in the CImpICruise constructor requires no call to AddRef, because the CImpICruise lifetime is guaranteed by the lifetime of the parent object in which CImpICruise is nested. This convenience is one of the benefits of nested interface class implementations.

Top Back to page top

© 1995-1998 Microsoft Corporation


COMUSER - Nested Aggregation in EXE User - Lesson 6

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

COMUSER - Nested Aggregation in EXE User

SUMMARY

The COMUSER sample presents a series of COM objects, several of which were introduced in the COMOBJ code sample. These objects represent various types of cars, and they expose the following interfaces: ICar, IUtility, and ICruise. COMUSER works in conjunction with the separate COMOBJ.DLL, which creates COCar, COUtilityCar, and COCruiseCar COM objects.

COMUSER.EXE creates its own COUtilityCruiseCar COM object, which is constructed by reusing the COCruiseCar COM object by aggregation and augmenting it with the IUtility interface. Because the COCruiseCar COM object class is also an aggregate--that is, it reuses an inner COCar object by aggregation--COMUSER illustrates nested aggregation of COM objects.

For functional descriptions and a tutorial code tour of COMUSER, see the Code Tour section in COMUSER.HTM. For details on the external user operation of COMUSER, see both the Usage and Operation sections in COMUSER.HTM. To read COMUSER.HTM, run TUTORIAL.EXE in the main tutorial directory and click the COMUSER lesson in the table of lessons. You can also achieve the same thing by clicking the COMUSER.HTM file after locating the main tutorial directory in the Windows Explorer. See also COMOBJ.HTM in the main tutorial directory for more details on how COMOBJ works and exposes its services to COMUSER. You must build COMOBJ.DLL before building COMUSER. The makefile for COMOBJ copies the necessary COMOBJ.H, COMOBJ.LIB, and COMOBJ.DLL files to the appropriate sibling directories once the files are built.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the COMUSER sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV COMUSER.DSP

You can also simply double-click the COMUSER.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

COMUSER is a simple application that you can execute directly from Windows in the normal manner or from the command prompt. No command line parameters are recognized by COMUSER.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, COMUSER.EXE is the client executable to run for this sample.

OPERATION

The COMUSER.EXE application provides the main user interface for this lesson. It exercises the associated, but independent, COMOBJ.DLL. Here is a summary of operation from the standpoint of COMUSER.EXE as a controller of COMOBJ.DLL.

The COM objects that are used in the COMUSER and COMOBJ code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

COMUSER.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in COMOBJ.DLL. For details, see the COMOBJ lesson. COCruiseCar is constructed using aggregation and is also implemented in COMOBJ.DLL. COUtilityCruiseCar is constructed using aggregation and is implemented in COMUSER.EXE. COUtilityCruiseCar aggregates COCruiseCar, so nested aggregation is thereby illustrated in this lesson.

COMUSER.EXE presents a menu for each of these four main COM objects. Each menu has items that call the methods of the various available interfaces. The code samples (both COMUSER and COMOBJ) have trace message log statements throughout. When you exercise the objects from COMUSER.EXE, the main COMUSER window will display a log of internal activity in these COM objects.

Menu Selection: File/Exit
Exits COMUSER.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/COMUSER Tutorial
Opens the COMUSER.HTM tutorial file in the Web browser.

Menu Selection: Help/COMOBJ Tutorial
Opens the COMOBJ.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About COMUSER
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

Menu Selection: Help/About COMOBJ
Displays the About dialog box for COMOBJ.DLL, which is used by this application. In this series of code samples, partner DLLs like COMOBJ are given their own About dialog box in the native resources of the DLL. This menu item calls the DLL function that displays this dialog box.

CODE TOUR

Files          Description
COMUSER.TXT    Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
COMUSER.H      The include file for the COMUSER application. Contains
               class declarations, function prototypes, and resource
               identifiers.
COMUSER.CPP    The main implementation file for COMUSER.EXE. Has WinMain
               and CMainWindow implementation. Also has the main menu
               dispatching.
COMUSER.RC     The application resource definition file.
COMUSER.ICO    The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
COMUSER.DSP    Microsoft Visual Studio Project file.

This code sample is based on the code in DLLUSER, which in turn is based on the code in EXESKEL. See the code tours in those samples for more details on using the application skeleton and the DLL.

COMUSER uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling \APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

COMUSER illustrates various kinds of COM objects in various settings. The object created and used in COMUSER is of class COUtilityCruiseCar. It is constructed by aggregation of the COCruiseCar COM object, which is implemented in COMOBJ.DLL. See the COMOBJ code tour for more information about the COCar, COUtilityCar, and COCruiseCar objects.

In addition to showing how COUtilityCruiseCar is constructed, this lesson demonstrates how the COMUSER.EXE, as a client of the COM objects implemented in both COMUSER and COMOBJ.DLL, creates, exercises, and releases those objects.

We start in COMUSER.CPP. The following include statements are used to bring in include files:

     #include <windows.h>
     #include <ole2.h>
     #include <initguid.h>
     #include <commdlg.h>
     #include <apputil.h>
     #include <icars.h>
     #include <carguids.h>
     #include <comobj.h>
     #include "comuser.h"
     #include "utcrucar.h"

Of special note is INITGUID.H. This file must be included only once in each executable. It ensures that any GUIDs defined in this application will be instantiated as named data in the data segment of the EXE. If you forget to include this file, you will get unresolved external references to the IID_IMyInterface GUIDs during linking.

The ICARS.H and CARGUIDS.H files, located in the sibling \INC directory, are included to get the car-related interface abstract base class declarations and their associated GUID definitions.

The COMOBJ lesson introduced the MsgBox and MsgLog facilities, and their use is continued here. The LOG* macro calls in the source code are trace statements that log and display appropriate conditions at run time.

After the message log is created, a global MsgLog pointer is assigned. Such global variables are usually avoided in object-oriented programming, but they conveniently support the centrally defined logging macros used throughout this code sample series. A definition of this same-named global variable, g_pMsgLog, is also needed inside such subordinate DLLs as COMOBJ.DLL for the logging macros to work there. Because of this, the actual pMsgLog pointer for this current application's message log must be passed to the DLL. The ComObjInitMsgLog call below passes this needed pointer to the DLL so that its logged internal behavior is displayed in COMUSER.EXE's logging display.

  // Create the Message Log ListBox as a child window in the Main Window.
  if (m_pMsgLog->Create(m_hInst, m_hWnd, TRUE))
  {
    // Assign the global MsgLog pointer.
    g_pMsgLog = m_pMsgLog;
    // Use macro to log messages.
    LOGID(IDS_START_MESSAGE_LOG);
    // Since we're exploiting the COMOBJ DLL and we want it to use
    // this same Message Log facility, we pass it a pointer to our
    // particular instance's CMsgLog object.
    ComObjInitMsgLog(g_pMsgLog);
  }

In the CMainWindow::DoMenu method, we see how the menu choices are dispatched. We'll take a typical example from the COM object implemented in this application. The following fragment shows the Create command from the UtilityCruiseCar menu.

    case IDM_UCRU_CREATE:
      LOG("E: === UtilityCruiseCar Menu: Create.");
      if (NULL == m_pUtilityCruiseCar)
      {
        hr = CreateUtilityCruiseCar(
               NULL,
               IID_IUnknown,
               (PPVOID)&m_pUtilityCruiseCar);
        if (SUCCEEDED(hr))
        {
          ::CheckMenuItem(
              hMenu,
              IDM_CAR_CREATE,
              MF_BYCOMMAND | MF_CHECKED);
        }
        else
          LOG("C: ???? Car creation failed.");
      }
      else
        LOG("E: ???? UtilityCruiseCar already exists.");
      break;

The CreateUtilityCruiseCar function is called. This function is defined in the UTCRUCAR.CPP file, part of this application. NULL is passed for the first argument because at this top level of usage we are not aggregating the new COUtilityCruiseCar object as part of some outer object. Once the object is created, the output argument, m_pUtilityCruiseCar, will write a pointer to the IUnknown interface of the new object to m_pUtilityCruiseCar. We cache this object pointer for subsequent use of the COM object. Following the rules of COM, the reference count on this pointer is incremented with a call to AddRef from the CreateUtilityCruiseCar function, and it will need to be decremented with a call to Release when it is no longer needed. A checkmark is displayed beside the UtilityCruiseCar/Create menu item to indicate that there is currently an instance of this object.

The following fragment handles the matching release of the COUtilityCruiseCar COM object.

    case IDM_UCRU_RELEASE:
      LOG("E: === UtilityCruiseCar Menu: Release.");
      if (NULL != m_pUtilityCruiseCar)
      {
        RELEASE_INTERFACE(m_pUtilityCruiseCar);
        ::CheckMenuItem(
            hMenu,
            IDM_CAR_CREATE,
            MF_BYCOMMAND | MF_UNCHECKED);
      }
      else
        LOG("E: ???? No UtilityCruiseCar to Release.");
      break;

The UtilityCruiseCar/Create menu item is unchecked to show that the object no longer exists. The RELEASE_INTERFACE macro used above is defined in APPUTIL.H:

      #define RELEASE_INTERFACE(p)\
      {\
        IUnknown* pTmp = (IUnknown*)p;\
        p = NULL;\
        if (NULL != pTmp)\
          pTmp->Release();\
      }

If the m_pUtilityCruiseCar object pointer is not NULL, a COUtilityCruiseCar object currently exists, and RELEASE_INTERFACE calls the interface's Release method. The pTmp pointer is used and the input interface pointer p is set to NULL prior to release to provide more robust behavior in multi-threaded programs.

A pointer like m_pUtilityCruiseCar is commonly referred to as a pointer to the object. Technically, it is a pointer to the IUnknown interface of that object. Once we have this pointer, we can access the object's other interfaces and make the object do things by calling their methods. Here is an example:

  case IDM_UCRU_WINCH:
    LOG("E: === UtilityCruiseCar Menu: IUtility::Winch");
    if (GetInterface(m_pUtilityCruiseCar, IID_IUtility, (PPVOID)&pIUtility))
    {
      LOG("E: --Calling pIUtility->Winch");
      pIUtility->Winch(30);
      LOG("E: --Releasing pIUtility");
      pIUtility->Release();
    }
    break;

This fragment calls the Winch method of the IUtility interface on the COUtilityCruiseCar object. The GetInterface call, defined in COMUSER.CPP, is a simple wrapper for IUnknown::QueryInterface. It is used to reduce code clutter by isolating some of the logging activity associated with the QueryInterface call in this tutorial. QueryInterface is called to obtain the COUtilityCruiseCar object's IUtility interface. The pointer to IUtility is used to call the Winch method. When the IUtility interface is no longer needed, Release is called on the pIUtility pointer.

We now look at the UtilityCruiseCar COM object itself (in UTCRUCAR.H and UTCRUCAR.CPP). The UtilityCruiseCar COM object class is declared in UTCRUCAR.H.

  class COUtilityCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COUtilityCruiseCar(IUnknown* pUnkOuter);
      ~COUtilityCruiseCar(void);

      // A general method for initializing a newly created UtilityCruiseCar.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // We implement the IUtility interface (ofcourse) in this
      // COUtilityCruiseCar COM object class. This is the native
      // interface that we are using as an augmentation to the
      // existing COCruiseCar COM object class.
      class CImpIUtility : public IUtility
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIUtility(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpIUtility(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IUtility methods.
          STDMETHODIMP Offroad(short nGear);
          STDMETHODIMP Winch(short nRpm);

        private:
          // Data private to this interface implementation of IUtility.
          ULONG               m_cRefI;     // Interface Ref Count (debug).
          COUtilityCruiseCar* m_pBackObj;  // Parent Object back pointer.
          IUnknown*           m_pUnkOuter; // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested IUtility interface
      // implementation a friend to COM object instantiations of this
      // selfsame COUtilityCruiseCar COM object class.
      friend CImpIUtility;

      // Private data of COUtilityCruiseCar COM objects.

      // Nested IUtility implementation instantiation. This IUtility
      // interface is instantiated inside this COUtilityCruiseCar object
      // as a native interface.
      CImpIUtility     m_ImpIUtility;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation delegation). Used when this
      // COUtilityCruiseCar object is itself being aggregated.
      IUnknown*        m_pUnkOuter;

      // We need to save the IUnknown interface pointer on the COCruiseCar
      // object that we aggregate. We use this when we need to delegate
      // IUnknown calls to this aggregated inner object.
      IUnknown*        m_pUnkCruiseCar;
  };

The IUtility interface is implemented in this COM object as a nested class. The embedded CImpIUtility is declared in the private section of the COUtilityCruiseCar object class. An instance of CImpIUtility is also created:

     // Nested IUtility implementation instantiation. This IUtility
     // interface is instantiated inside this COUtilityCruiseCar object
     // as a native interface.
     CImpIUtility     m_ImpIUtility;

As we'll see, COUtilityCruiseCar is itself constructed by aggregating the COCruiseCar object with this IUtility implementation. That is what the m_pUnkCruiseCar pointer, also in the COUtilityCruiseCar private area, is for. It caches a pointer to the aggregated COCruiseCar object. The pointer is used to delegate certain IUnknown calls to the inner object's IUnknown interface.

For the case when a COUtilityCruiseCar object is itself aggregated by an outer COM object, it has an m_pUnkOuter delegation pointer in its private data area. The value of this pointer is set when the COUtilityCruiseCar object is created. Because COMUSER does not aggregate this object, it sets the pointer to NULL.

CImpIUtility itself also has a private data member, m_pUnkOuter, for supporting aggregation and delegation. This pointer is used to delegate calls to the IUtility interface's IUnknown to either the COUtilityCruiseCar object (when it is not aggregated) or to the controlling IUnknown (when the COUtilityCruiseCar object is aggregated). The m_pBackObj pointer is for convenient use inside any of the IUtility method functions to reference members of the COUtilityCruiseCar object.

As an embedded class, CImpIUtility needs to be made a friend class to COUtilityCruiseCar so that CImpIUtility's methods can access the private data in COUtilityCruiseCar.

The method definitions for the COUtilityCruiseCar class are in UTCRUCAR.CPP. As we observed in the COMOBJ code sample, the COUtilityCruiseCar constructor uses a member initializer, m_ImpIUtility(this, pUnkOuter), to pass the 'this' and pUnkOuter arguments to the CImpIUtility constructor, which is executed just prior to the main body of the COUtilityCruiseCar constructor. Both of the argument values are available when the member initializer is executed. In these code samples, the successful use of the nested classes technique for interface implementations depends on the use of such member initializers. Here is the constructor:

  COUtilityCruiseCar::COUtilityCruiseCar(IUnknown* pUnkOuter)
    : m_ImpIUtility(this, pUnkOuter)
  {
    // Zero the COM object's reference count.
    m_cRefs = 0;

    // No AddRef necessary if non-NULL, as this COM object's lifetime
    // is totally coupled with the controlling Outer object's lifetime.
    m_pUnkOuter = pUnkOuter;

    // Zero the pointer to the aggregated COCruiseCar object's IUnknown
    // interface (for delegation of IUnknown calls to it).
    m_pUnkCruiseCar = NULL;

    LOGF1("E: COUtilityCruiseCar Constructor. m_pUnkOuter=0x%X.", m_pUnkOuter);

    return;
  }

The COUtilityCruiseCar::Init method is almost identical to the COCruiseCar::Init that we studied in the COMOBJ code sample.

The CreateCruiseCar function is implemented in COMOBJ.DLL. CreateCruiseCar calls QueryInterface which assigns its output m_pUnkCruiseCar object pointer. This is a pointer to the aggregated COCruiseCar object's IUnknown. We cache this pointer in the COUtilityCruiseCar object for future use in delegating IUnknown calls to COCruiseCar. An example of such delegation is in COUtilityCruiseCar object's IUnknown definition of QueryInterface.

  STDMETHODIMP COUtilityCruiseCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("E: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.");
    }
    else if (IID_IUtility == riid)
    {
      // This IUtility interface is implemented in this COUtilityCruiseCar
      // object and might be called a native interface of
      // COUtilityCruiseCar.
      *ppv = &m_ImpIUtility;
      LOG("E: COUtilityCruiseCar::QueryInterface. pIUtility returned.");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to an interface so obey the COM rules
      // and AddRef its reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }
    else if (IID_ICar == riid)
    {
      LOG("E: COUtilityCruiseCar::QueryInterface. ICar delegating.");
      // We didn't implement the ICar interface in this COUtilityCruiseCar
      // object. The aggregated inner object (CruiseCar) is contributing
      // the ICar interface to this present composite or aggregating
      // UtilityCruiseCar object. So, to satisfy a QI request for the
      // ICar interface, we delegate the QueryInterface to the inner
      // object's IUnknown.
      hr = m_pUnkCruiseCar->QueryInterface(riid, ppv);
    }
    else if (IID_ICruise == riid)
    {
      LOG("E: COUtilityCruiseCar::QueryInterface. ICruise delegating.");
      // We didn't implement the ICruise interface in this
      // COUtilityCruiseCar object. The aggregated inner object
      // (CruiseCar) is contributing the ICruise interface to this
      // present composite or aggregating UtilityCruiseCar object.
      // As above, we delegate this QI to the aggregated object's IUnknown.
      hr = m_pUnkCruiseCar->QueryInterface(riid, ppv);
    }

    return (hr);
  }

The IID_IUnknown and IID_IUtility cases are familiar, and if we pass one of these interface pointers by request, we increment the interface's reference count with a call to its AddRef method. The IID_IUtility interface is exposed as a native interface on this COM object, so it's easy to pass a pointer to it. For the IID_ICar and IID_ICruise interfaces, however, we need to pass the request to the aggregated inner object where they are implemented. We use the cached m_pUnkCruiseCar object pointer to delegate the QueryInterface call to aggregated objects that are providing implementations of those interfaces to the COUtilityCruiseCar COM object.

Prior to deleting COUtilityCruiseCar, the COUtilityCruiseCar::Release method artificially increments the object's reference count, as was discussed for the COCruiseCar object in the COMOBJ code sample.

The COUtilityCruiseCar::CImpIUtility::CImpIUtility constructor uses the same logic to assign its own IUnknown delegation pointers as that discussed for the COCruiseCar object in the COMOBJ code sample.

The COUtilityCruiseCar::CImpIUtility::Offroad method has been set up to show something a little different. Here we show the aggregation rules when an outer object needs to call methods of interfaces to the inner aggregated objects. The Offroad method stops the car (sets its speed to 0) in order to shift the transfer case to offroad 4-wheel drive Low gear (nGear = 3). In the COUtilityCruiseCar object's implementation of IUtility::Offroad, we need to call the ICar interface's Speed method if the Offroad request is for 4-wheel drive low speed. But this ICar interface is implemented in the inner COCruiseCar object. We must call QueryInterface to get a pointer to it and then carefully manage the reference count on the outer object, for reasons we will discuss in a moment.

  STDMETHODIMP COUtilityCruiseCar::CImpIUtility::Offroad(
                 short nGear)
  {
    HRESULT hr;
    ICar* pICar = NULL;

    // In our fantasy Sport-Utility Car world we may need to stop the car
    // before switching to 4-Wheel drive low (nGear == 3 for 4L). Let's
    // assume so because it's a convenient excuse to show this aggregating
    // COUtilityCruiseCar outer object using one of the interfaces (ICar)
    // of its aggregated COCruiseCar inner object. COMUSER gracefully
    // cooperates in this by invoking this Offroad method from its
    // UtilityCruiseCar menu with nGear == 3.
    if (3 == nGear)
    {
      hr = m_pBackObj->m_pUnkCruiseCar->QueryInterface(
                                          IID_ICar,
                                          (PPVOID)&pICar);
      if (SUCCEEDED(hr))
      {
        m_pUnkOuter->Release();
        pICar->Speed(0);
        m_pUnkOuter->AddRef();
        pICar->Release();
      }
    }

    LOGF1("E: COUtilityCruiseCar::CImpIUtility::Offroad. Called. nGear=%i.",nGear);

    return NOERROR;
  }

The interface's m_pBackObj pointer provides a way to access the cached pointer to the aggregated COCruiseCar object, m_pUnkCruiseCar. Because the QueryInterface method of the inner object is delegated as part of an aggregated object, it will increment the reference count of the outermost aggregating object by calling the AddRef method of the controlling IUnknown. This outermost aggregating object may not be the COUtilityCruiseCar object. Here's the problem. A aggregate object is using an interface on itself from inside itself, but the call to AddRef is delegated to the outermost aggregating object and thus falsely records an outstanding external reference to it. As a result, the lifetime of that outer aggregating object is no longer fully controlled by releases of actual external references held to it.

To put the reference count in a better state for the duration that the outer object holds a reference to an aggregated inner object's interface, Release is explicitly called on the outer object after the QueryInterface call (to offset the internal delegated AddRef call). The acquired inner interface is now used and could be held for a lengthy period. To properly release it, AddRef is called explicitly on the outer object, followed by a call to Release on the acquired inner object's interface pointer. This sequence is necessary to avoid recursive or reentrant destruction during the process. This practice is recommended under Win32, especially if the inner interface pointer is held for some time and if the inner object is in a different process (or even on a different machine) from that of the outer object.

At the end of UTCRUCAR.CPP is the CreateUtilityCruiseCar function, which creates an instance of the COUtilityCruiseCar COM object. We saw a call to this function above in the code for the UtilityCruiseCar menu's Create item.

  HRESULT CreateUtilityCruiseCar(
            IUnknown* pUnkOuter,
            REFIID riid,
            PPVOID ppv)
  {
    HRESULT hr;
    COUtilityCruiseCar* pCob;

    LOGF1("E: CreateUtilityCruiseCar. pUnkOuter=0x%X.",pUnkOuter);

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state the IUnknown interface MUST also be concomitantly
    // requested. If it is not so requested (riid != IID_IUnknown), then
    // an error must be returned indicating that no aggregate creation of
    // the COUtilityCruiseCar COM Object can be performed using anything
    // other than a controlling IUnknown interface.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COUtilityCruiseCar COM Object.
      pCob = new COUtilityCruiseCar(pUnkOuter);
      if (NULL != pCob)
      {
        // If we have succeeded in instantiating the COUtilityCruiseCar
        // object, we initialize it to offer its interfaces.
        hr = pCob->Init();
        if (SUCCEEDED(hr))
        {
          // We QueryInterface this new COM Object not only to deposit the
          // main interface pointer to the caller's pointer variable, but to
          // also automatically bump the Reference Count on the new COM
          // Object after handing out this *ppv external reference to it.
          hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
      LOGF1("E: CreateUtilityCruiseCar Succeeded. *ppv=0x%X.",*ppv);

    return hr;
  }

The arguments to this function permit the object to be created as part of an even grander aggregation. If the first pUnkOuter parameter is non-NULL, the COUtilityCruiseCar object is to be aggregated, and all the subsequent logic will support this aggregation. For another such example, see the code for the COCruiseCar object in the COMOBJ directory. An instance of the COM object class is first created (memory is allocated for it, and its constructor is executed). The object's Init() member function is then called to create any subordinate objects and interfaces. In this case, the COCruiseCar COM object is created and aggregated into the new COUtilityCruiseCar object. If this step succeeds, QueryInterface is called to obtain the IUnknown of the outer object. On the way, this writes that object pointer at the location specified by the ppv output argument. On the way, this also calls the AddRef method to increment the reference count on the new object, because a pointer to it was passed to an outside consumer--in this case, COMUSER.EXE.

You can experiment with the COMUSER menus to watch the internal activity of these COM objects. Here we will tour some representative trace logs. This one is for the menu selection UtilityCruiseCar\Create:

  E: === UtilityCruiseCar Menu: Create.
  E: CreateUtilityCruiseCar. pUnkOuter=0x0.
  E: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  E: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  E: COUtilityCruiseCar::Init.
  D: CreateCruiseCar. pUnkOuter=0x770694.
  D: COCruiseCar::CImpICruise Constructor. Aggregating.
  D: COCruiseCar Constructor. m_pUnkOuter=0x770694.
  D: COCruiseCar::Init.
  D: CreateCar. pUnkOuter=0x770694.
  D: COCar::CImpICar Constructor. Aggregating.
  D: COCar Constructor. m_pUnkOuter=0x770694.
  D: COCar::QueryInterface. 'this' pIUnknown returned.
  D: COCar::AddRef. New cRefs=1.
  D: CreateCar Succeeded. *ppv=0x660698.
  D: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  D: COCruiseCar::AddRef. New cRefs=1.
  D: CreateCruiseCar Succeeded. *ppv=0x660674.
  E: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  E: COUtilityCruiseCar::AddRef. New cRefs=1.
  E: CreateUtilityCruiseCar Succeeded. *ppv=0x770694.

The 'E:' or 'D:' at the beginning of each line indicates, respectively, that execution was in the .EXE or in the .DLL.

First, COMUSER calls CreateUtilityCruiseCar (passing a NULL pUnkOuter, because it is simply creating a COUtilityCruiseCar object without aggregating one). Then a COUtilityCruiseCar object is created, causing first the constructor for the IUtility implementation and then that for the COUtilityCruiseCar object to be executed. COUtilityCruiseCar::Init is then called, and it calls the CreateCruiseCar function in COMOBJ.DLL to create an aggregated COCruiseCar object. This object is to be aggregated, so a non-NULL pUnkOuter is passed. When the COCruiseCar COM object is created, the constructors for CImpICruise and then COCruiseCar are called. The CImpICruise interface constructor is passed the non-NULL pUnkOuter. This ICruise interface implementation will later use this pointer to delegate its IUnknown calls to the controlling IUnknown. Next, CreateCruiseCar calls the COCruiseCar object's Init method to create any subordinate COM objects.

Because a COCruiseCar object obtains its ICar features from an aggregated COCar COM object, CreateCar is called within COMOBJ.DLL. As before, we see the constructors for CImpICar and COCar and that they are passed a non-NULL pUnkOuter, indicating aggregation. The same pUnkOuter value is passed to these nested aggregated objects. This is how all of them can delegate directly to the controlling IUnknown. As we back out of the call tree, each create function uses a call to QueryInterface to pass a pointer to an appropriate IUnknown and to increment (via calls to AddRef) the appropriate reference count. Each object then ends up with a reference count of 1.

Now that we have created a UtilityCruiseCar, here's the trace log for menu selection UtilityCruiseCar\ICar::Shift:

  E: === UtilityCruiseCar Menu: ICar::Shift
  E: --Obtaining Interface Pointer.
  E: COUtilityCruiseCar::QueryInterface. ICar delegating.
  D: COCruiseCar::QueryInterface. ICar delegating.
  D: COCar::QueryInterface. pICar returned.
  D: COCar::CImpICar::Addref. Delegating. New cI=1.
  E: COUtilityCruiseCar::AddRef. New cRefs=2.
  E: Interface obtained. *ppv=0x6606A4
  E: --Calling pICar->Shift
  D: COCar::CImpICar::Shift. Called. nGear=1.
  E: --Releasing pICar
  D: COCar::CImpICar::Release. Delegating. New cI=0.
  E: COUtilityCruiseCar::Release. New cRefs=1.

First, an ICar interface pointer on the COUtilityCruiseCar object must be obtained by calling QueryInterface. This call delegates to the next inner object that has the interface being asked for--in this case, the COCruiseCar object. This COM object's QueryInterface in turn does the same thing: it delegates the request to the ICar interface that is implemented in the COCar COM object. The pICar pointer is finally obtained from the COCar object's QueryInterface. The AddRef call from QueryInterface is delegated directly to the controlling IUnknown (in this case, COUtilityCruiseCar::AddRef). This call increments the COUtilityCruiseCar object's reference count to 2. An outside agent is now holding a reference to the ICar interface on this aggregate object. That same outside agent (COMUSER.EXE) has also been holding a reference to the object's IUnknown, which explains the reference count of 2. The ICar interface pointer is used to directly call the implementation of the Shift method in the COCar object. The pointer to the ICar interface is then released, which causes a delegation to the controlling IUnknown's Release method, upon which the object's reference count is decremented to a 1.

Here's the trace log for menu item UtilityCruiseCar\Release:

  E: === UtilityCruiseCar Menu: Release.
  E: COUtilityCruiseCar::Release. New cRefs=0.
  E: COUtilityCruiseCar::Destructor.
  D: COCruiseCar::Release. New cRefs=0.
  D: COCruiseCar::Destructor.
  D: COCar::Release. New cRefs=0.
  D: COCar::Destructor.
  D: COCar::CImpICar Destructor.
  D: COCruiseCar::CImpICruise Destructor.
  E: COUtilityCruiseCar::CImpIUtility Destructor.

This menu item causes a cascade of releases and object destruction. The initial call to Release decrements the outstanding reference count of 1 to 0, causing the entire COUtilityCruiseCar COM object to be deleted. The object's destructor is called, which has a call to Release the aggregated COCruiseCar COM object. That object's reference count is decremented from 1 to 0, causing the COCruiseCar object to be deleted. As above, its destructor releases the aggregated COCar object, decrementing its reference count to 0 and causing its destructor to run. As we wind back out of all these destructors, we see the automatic destructors of the ICar, ICruise,and IUtility 'ImpI' interface implementations. These required no explicit deletions, because the interfaces are implemented as nested classes. This is one of the benefits of the nested classes technique.

Top Back to page top

© 1995-1998 Microsoft Corporation


REGISTER - Component Object Registration - Lesson 7

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

REGISTER - Component Object Registration

SUMMARY

The REGISTER sample builds a simple REGISTER.EXE utility that is used in subsequent code samples to invoke DLL- or EXE-housed COM servers and command them to register or unregister their components in the system registry.

For functional descriptions and a tutorial code tour of REGISTER, see the Code Tour section in REGISTER.HTM. For details on the external user operation of REGISTER, see both the Usage and Operation sections in REGISTER.HTM. To read REGISTER.HTM, run TUTORIAL.EXE in the main tutorial directory and click the REGISTER lesson in the table of lessons. You can also achieve the same thing by clicking the REGISTER.HTM file after locating the main tutorial directory in the Windows Explorer.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the REGISTER sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV REGISTER.DSP

You can also simply double-click the REGISTER.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

REGISTER.EXE is an application that you can run directly from Windows or from the Command Prompt. REGISTER is usually run from the Command Prompt in one of the following forms:

    register myserver.dll       - To register the DLL server.
    register /u myserver.dll    - To unregister the DLL server.
    register /e myserver.exe    - To register the EXE server.
    register /u /e myserver.exe - To unregister the EXE server.

OPERATION

The REGISTER application operates transparently except when an error occurs. If an error occurs, a modal error dialog box is displayed with one of the following errors:

  REGISTER.EXE: COM initialization failed
  REGISTER.EXE: DLL Unregister failed
  REGISTER.EXE: DLL Registration failed
  REGISTER.EXE: DLL Load Library failed
  REGISTER.EXE: WinExec of EXE server failed

This utility is designed to be used by installation programs that must register the COM components housed inside COM servers (either EXE or DLL). In this code sample series, REGISTER is invoked by the makefiles of each server code sample to register the components offered by the server. For example, the DLLSERVE code sample makefile supports NMAKE REGISTER and NMAKE UNREGISTER options to register and unregister its components.

CODE TOUR

Files          Description
REGISTER.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample.
REGISTER.H     The include file for the REGISTER application. Contains
               string literals and Resource identifiers.
REGISTER.CPP   The main implementation file for REGISTER.EXE. Has WinMain
               and CMainWindow implementation.
REGISTER.RC    The application resource definition file.
REGISTER.ICO   The application icon resource.
REGISTER.DSP   Microsoft Visual Studio Project file.

REGISTER is a simple utility that is used in subsequent samples to register COM servers. Before a COM component can be used by a client, its server must be loaded and running. Because the client doesn't load the servers directly, the client doesn't need specific information about the location of the components. All the client needs are the CLSIDs and Interface GUIDs (IIDs) of the components. The client presents to COM a CLSID for a component and an interface IID for the services sought. COM takes care of finding and loading the appropriate server, creating a COM object instance of the desired component, and providing the client with the requested interface to the object.

COM can perform these tasks only if the components have been previously registered. There are currently two principal ways to register a component. In the first method, you provide a .REG file that contains the registry entries for your components. The eventual user of the components can then use a utility like the Registry Editor (REGEDIT.EXE in Windows 95, REGEDT32.EXE in Windows NT) to process the .REG file and register the components. A disadvantage of this method is that the .REG file must contain specific paths to the server executables, so the .REG file may have to be edited when the components are installed.

The second method solves this problem by having the components register themselves. Self-registration is thus built into the component's server and is accessed through a standard protocol. With this method, the entries for the path to the server are dynamically determined by the server itself; no editing of .REG files is required. Though it takes a little more work to implement the server housing, self-registration is recommended and is used throughout this code sample series.

The specifics of self-registration will be covered when first encountered in the series: in the DLLSERVE lesson for DLL in-process servers, and in the LOCSERVE lesson for EXE local servers.

The REGISTER.EXE utility in this lesson is used to load a specified server and invoke its self-registration facilities. This application is so simple that no main window or message loop is required. The only user interface that might be needed is a set of message dialog boxes to notify the user of error conditions.

The main content of the program is in REGISTER.CPP. Almost all the work is done in the WinMain function. The program scans its own command line options to determine the kind of COM server to load and whether the server should be registered or unregistered. The following while loop does this:

  ...
  while (ch = *psz)
  {
    BOOL    bStop = FALSE;

    switch (ch)
    {
      case '\t':
      case '\n':
      case '\r':
      case ' ':
        // Skip any white space.
        psz = SkipAnsiString(psz, TRUE);
        continue;

      case '/':
      case '-':
        // Check what option this is, then skip to next whitespace.
        ch = *(++psz);
        if ('u' == ch)
          bUnreg = TRUE;
        if ('e' == ch)
          bEXE = TRUE;
        psz = SkipAnsiString(psz, FALSE);
        continue;

      default:
        bStop = TRUE;
        break;
    }

    if (bStop)
      break;

    psz++;
  }
  ...

With psz pointing to the command line input, the loop scans for '/u' or '/e' to determine respectively whether unregistration or an EXE server is specified. The default is to register a DLL server. SkipAnsiString is a utility function defined locally in this module that can either skip over whitespace characters or skip to the next whitespace character. This entire application is compiled for the ANSI character set and uses the appropriate versions of Win32 API calls. This is simply because the pszCmdLine command line is available to WinMain only in ANSI. The makefile ignores any request to compile this program for Unicode.

As the while loop scans the command line, the bUnreg and bEXE Boolean flags are assigned. They are then used to determine the execution of the remaining code in WinMain. For the default case of a DLL server (bEXE == FALSE), the following code runs:

  // Because we load the DLL server into our own (ie, REGISTER.EXE)
  // process space, call to initialize the COM Library. Use the
  // SUCCEEDED macro to detect success. If fails, then exit
  // application with error message.
  if (SUCCEEDED(CoInitialize(NULL)))
  {
    HINSTANCE hMod;

    // Load the Server DLL into our process space.
    hMod = LoadLibraryA(psz);

    if (NULL != hMod)
    {
      HRESULT (STDAPICALLTYPE *pfn)(void);

      if (bUnreg)
      {
        (FARPROC&)pfn = GetProcAddress(hMod, "DllUnregisterServer");

        if (NULL != pfn)
          iFail = FAILED((*pfn)());

        if (iFail)
          ErrorBoxID(hInstance, IDS_DLLUNREG_FAIL);
        else
        {
          // Show a timed message box indicating registration success.
          DelayBox(hInstance, MAKEINTRESOURCE(IDD_SUCCESS_MSG), NULL);
        }
      }
      else
      {
        (FARPROC&)pfn = GetProcAddress(hMod, "DllRegisterServer");

        if (NULL != pfn)
          iFail = FAILED((*pfn)());
        if (iFail)
          ErrorBoxID(hInstance, IDS_DLLREG_FAIL);
        else
        {
          // Show a timed message box indicating registration success.
          DelayBox(hInstance, MAKEINTRESOURCE(IDD_SUCCESS_MSG), NULL);
        }
      }
      CoFreeLibrary(hMod);
    }
    else
      ErrorBoxID(hInstance, IDS_LOADLIB_FAIL);

    // We're exiting this app so shut down the COM Library.
    CoUninitialize();
  }
  else
    ErrorBoxID(hInstance, IDS_OLEINITFAILED);

Because the DLL server will probably make calls to COM services, the program calls the CoInitialize function to initialize the COM service libraries. After the /u and /e switches are found or not, the code treats the remainder of the command line pointed to by psz as the path to the DLL server. The server is then explicity loaded with a LoadLibraryA call. If /u was specified, GetProcAddress is called to obtain the address of the DllUnregisterServer function. If the function is found, it is called to actually get the server to unregister its components. If /u is not specified, the DllRegisterServer function is found and called. The loaded server DLL is freed after use, and the COM library is uninitialized.

For the case of an EXE server (bEXE == TRUE) the following code runs.

  if (bEXE)
  {
    UINT uError;
    CHAR szEXE[MAX_PATH];

    wsprintfA(
      szEXE,
      "%s %s",
      psz,
      bUnreg ? "/UNREGSERVER" : "/REGSERVER");

    // We WinExec the .EXE server passing /unregserver or /regserver.
    uError = WinExec(szEXE, SW_HIDE);
    if (uError < 32)
    {
      ErrorBoxID(hInstance, IDS_EXERUN_FAIL);
      iFail = FALSE;
    }
    else
    {
      // Show a timed message box indicating registration success.
      DelayBox(hInstance, MAKEINTRESOURCE(IDD_SUCCESS_MSG), NULL);
    }
  }

The EXE server is started by a call to WinExec with the appropriate command line option, /UNREGSERVER or /REGSERVER. The server is run with SW_HIDE because only an internal service of the server is being requested, and no user interface is required.

For both EXE and DLL servers, APPUTIL's DelayBox function is used to show a success message box for a few seconds if the registration is successful.

One final note about self-registration. For tutorial purposes, this lesson shows the internal operation of a self-registering utility for both EXE and DLL COM servers. A similar utility, REGSVR32.EXE, is provided as part of the Platform SDK in the \MSSDK\BIN directory. REGSVR32 can be used to register in-process COM server DLLs and marshaling server DLLs. The Microsoft Visual C++® product (version 4.x and above) also includes this utility.

Top Back to page top

© 1995-1998 Microsoft Corporation


DLLSERVE - Component Object DLL Server - Lesson 8

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DLLSERVE - Component Object DLL Server

SUMMARY

The COMOBJ sample introduced COM objects. This DLLSERVE sample introduces COM components (also called simply component objects). A component object is a reusable software unit that encapsulates or packages the manufacturing of a specific class of COM object. A COM object class specifies an open-ended set of behaviorally identical COM objects that is uniquely identified for all programs and all time (by a Class ID).

Component objects are housed in a COM Server. The server executable is registered (or published) in a system registry to act as the creation agent for COM object instances of the COM Component. The server contains one or more Class Factories used for the creation of COM objects. Class factories are themselves COM objects that expose the IClassfactory[2] interface. However, as an integral part of the server housing, class factories are typically not full-fledged component objects.

Component objects are the building blocks in COM and ActiveX programming. Component objects are combined to make some portion of an application. The running behavior of the applicaion is often determined by, and evidenced in, the COM objects that were instantiated (i.e., that were manufactured in the class factories of the various component objects that were combined in the application).

The DLLSERVE sample begins with the car-related COM Objects of the previous COMOBJ lesson and refashions them into component objects. To do so requires little change to the COM objects themselves (COCar, COUtilityCar, and COCruiseCar). But to envelop them as components, this lesson introduces new facilities to house them in an in-process DLL COM server. These facilities include class factories for each component and a CarSample utility component that allows clients to see logged behavior in the server itself.

The DLL server provides the following components: Car, UtilityCar, CruiseCar, and CarSample.

In the series of COM tutorial code samples, DLLSERVE works with the DLLCLIEN code sample to illustrate DLLSERVE's COM server facilities for creating components that can be used by an EXE client and the subsequent manipulation of those components by DLLCLIEN.EXE.

For functional descriptions and a tutorial code tour of DLLSERVE, see the Code Tour section in DLLSERVE.HTM. For details on setting up the programmatic usage of DLLSERVE, see the Usage section in DLLSERVE.HTM. To read DLLSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DLLSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the DLLSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also DLLCLIEN.HTM in the main tutorial directory for more details on the DLLCLIEN client application and how it works with DLLSERVE.DLL.

You must build DLLSERVE.DLL before building or running DLLCLIEN. DLLSERVE's makefile automatically registers DLLSERVE's components in the registry. These components must be registered before DLLSERVE is available to outside COM clients as a server for those components. This registration is done using the REGISTER.EXE utility built in the previous REGISTER lesson. To build or run DLLSERVE, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DLLSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DLLSERVE.DSP

You can also simply double-click the DLLSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

DLLSERVE is a DLL that is meant to be used primarily as a COM server. Though it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call (usually from COM's CoGetClassObject function). Servers like DLLSERVE are registered in the registry. To use DLLSERVE in a COM client program, a client does not need to include DLLSERVE.H or link to DLLSERVE.LIB. A COM client of DLLSERVE obtains access solely through its components' CLSIDs and COM services. For DLLSERVE, those CLSIDs are CLSID_DllCar, CLSID_DllUtilityCar, CLSID_DllCruiseCar, and CLSID_DllCarSample. The DLLCLIEN lesson shows how this is done.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the DLLSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the DLLSERVE directory.

  ..\register\register.exe dllserve.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of DLLSERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, DLLCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
DLLSERVE.TXT  Short sample description.
MAKEFILE      The generic makefile for building the DLLSERVE.DLL
              code sample of this tutorial lesson.
DLLSERVE.H    The include file for declaring as imported or defining as
              exported the service functions in DLLSERVE.DLL.
DLLSERVE.CPP  The main implementation file for DLLSERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
DLLSERVE.DEF  The module definition file. Exports server housing functions.
DLLSERVE.RC   The DLL resource definition file for the executable.
DLLSERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object. Also has
              resource identifiers for resources stored inside DLLSERVE.DLL
              and other external declarations that are used internally
              within the modules of DLLSERVE.DLL.
SERVER.CPP    The implementation file for the server control object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CAR.H         The include file for the COCar COM object class.
CAR.CPP       The implementation file for the COCar COM object class.
UTILCAR.H     The include file for the COUtililtyCar COM object class.
UTILCAR.CPP   The implementation file for the COUtilityCar COM object class.
CRUCAR.H      The include file for the COCruiseCar COM object class.
CRUCAR.CPP    The implementation file for the COCruiseCar COM object class.
SAMPLE.H      The include file for the COCarSample COM object class.
SAMPLE.CPP    The implementation file for the COCarSample COM object class.
DLLSERVE.DSP  Microsoft Visual Studio Project file.

This code sample is based on the code in the COMOBJ lesson. The same COM objects studied in COMOBJ and COMUSER are encountered here again in DLLSERVE: COCar, COUtilityCar, and COCruiseCar. Some minor changes have been made to house these objects in a COM server. More on this below.

DLLSERVE is a server that manages several component types. This need not be the case. A COM server might need to manage only one component type. If you're modeling such a single component server on DLLSERVE, it is easy to remove the code for the other components.

DLLSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling \APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

The main elements of the DLLSERVE and DLLCLIEN code samples are the mechanisms required to render COM objects into components by housing them in a DLL (in-process) server. Subsequent lessons will perform this same kind of evolution of COM objects by housing them as licensed in-process components (LICSERVE and LICCLIEN) and by housing them as components in an EXE (out-of-process) server (LOCSERVE and LOCCLIEN).

All of these COM servers are self-registering. The previous lesson in this series presented a small REGISTER.EXE utility that DLLSERVE will use for self-registration. We'll look first at the support for this facility that is provided in DLLSERVE. We will then see how the class factories are built. We'll tour the mechanism for control of the server as objects are created and deleted. We'll look at a new component (CarSample) that integrates the logging faciities of the server and the client to provide more instructive code samples. Finally, we'll look at the changes made to the COM object classes of the components themselves. This last section won't be long, because these COM object classes are almost the same as those presented in COMOBJ and COMUSER.

We start in DLLSERVE.H. Two exported functions, DllRegisterServer and DllUnregisterServer, are implemented to manage registration of the components. Both functions are declared for import in the manner seen in the DLLSKEL and COMOBJ lessons. The STDENTRY macro from DLLSERVE.H is used when applications want to build a .LIB file from DLLSERVE.DLL and then link to DLLSERVE.LIB in order to import these two functions. However, the macro is not used to define them as exports, as was done in earlier code samples. In this code sample, the desired functions are specified as exported in the a DLLSERVE.DEF module definition file. This .DEF file is submitted to the Linker in the makefile's Link command line as follows.

  $(DLL).dll: $(DLLOBJS) $(TDIR)\$(DLL).res
      $(LINK)  @<<
      $(LINKFLAGS) $(dlllflags)
      -out:$@
      -base:0x1C000000
      -def:$*.def
      -implib:$*.lib
      -map:$(TDIR)\$*.map
      $(DLLOBJS)
      $(TDIR)\$*.res
      $(olelibsdll) $(APPLIBS)
  <<

The Linker's -def:$*.def command line switch is used above. This reduces to -def:DLLSERVE.DEF when the makefile is processed. Here is the content of DLLSERVE.DEF.

  LIBRARY     DLLSERVE

  DESCRIPTION 'DLLSERVE: COM Tutorial Sample. Copyright Microsoft Corp., 1997'

  EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE
Export definition is done differently in this code sample because using the STDENTRY macro for the export definitions (as was done in the COMOBJ sample) may yield compile-time errors (linkage conflict). These errors happen because functions like DllGetClassObject are already declared in the COM/OLE header files, and these prototypes do not currently include linkage instructions in their signature. Thus, for COM servers, the DllClassObject, DllCanUnloadNow, DllRegisterServer, and DllUnregisterServer functions are considered exports that are intended for internal, private use only by COM. The linker is told explicitly--in the export statements of the DLLSERVE.DEF file--to mark those functions as PRIVATE exports. This also excludes those functions from any import libraries that may be produced during Linking.

The above export approach applies especially to the DllGetClassObject and DllCanUnloadNow functions, which all COM component servers must implement. More on them later.

First, look at DllRegisterServer (in DLLSERVE.CPP). The following fragment shows what it does.

  STDAPI DllRegisterServer(void)
  {
    HRESULT  hr = NOERROR;
    TCHAR    szID[GUID_SIZE+1];
    TCHAR    szCLSID[GUID_SIZE+32];
    TCHAR    szModulePath[MAX_PATH];

    // Obtain the path to this module's executable file for later use.
    GetModuleFileName(
      g_pServer->m_hDllInst,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    /*-------------------------------------------------------------------
      Create registry entries for the DllCar Component.
    -------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(CLSID_DllCar, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);

    // Create ProgID keys.
    SetRegKeyValue(
      TEXT("DllCar1.0"),
      NULL,
      TEXT("DllCar Component - DLLSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("DllCar1.0"),
      TEXT("CLSID"),
      szID);

    // Create VersionIndependentProgID keys.
    SetRegKeyValue(
      TEXT("DllCar"),
      NULL,
      TEXT("DllCar Component - DLLSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("DllCar"),
      TEXT("CurVer"),
      TEXT("DllCar1.0"));
    SetRegKeyValue(
      TEXT("DllCar"),
      TEXT("CLSID"),
      szID);

    // Create entries under CLSID.
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("DllCar Component - DLLSERVE Code Sample"));
    SetRegKeyValue(
      szCLSID,
      TEXT("ProgID"),
      TEXT("DllCar1.0"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("DllCar"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("InprocServer32"),
      szModulePath);

    ...
    ...
    [Similar for UtilityCar, CruiseCar, and CarSample.]
    ...
    ...

    return hr;
  }

Notice the specific CLSIDs, such as CLSID_DllCar, used for these component types. A CLSID is important in distinguishing a component from the in-process COM objects that were introduced in the previous code samples. As we shall see, other work must be done to fully flesh them out as components.

These CLSIDs must be unique, hence the DllCar, DllUtilityCar, and DllCruiseCar in their names. Even though these components are similar to the COCar, COUtilityCar, and COCruiseCar COM objects, different CLSIDs must be created for them when they reside in other servers. All the car-related CLSIDs, GUIDs, and interfaces are declared in CARGUIDS.H and ICARS.H, in sibling directory \INC. These include files are common to all the code samples in this tutorial series.

The CLSIDs are like tickets to the servers. You present a CLSID to COM to get components from the servers that house them. Though different servers may create COCar COM objects, the client's access to these objects is first through their component type CLSID, which gets to their server, which in turn can create them. Subsequent use of the COM objects by the client is solely through COM interfaces on the objects themselves.

The internal avenue from the CLSID of a component type to its server is provided by appropriate entries in the system registry. One way to get those entries into the registry is to have the server that manages a component type put them there. That is what the DllRegisterServer function does for each component type.

The StringFromGUID2 service is called to convert the GUID corresponding to CLSID_DllCar into a string that can be stored in the registry. Most of the work is in constructing the proper strings to write into the registry. The CLSIDs themselves are stored as registry keys under the CLSID subkey, which is in turn under HKEY_CLASSES_ROOT. All of these entries are stored in the registry under the HKEY_CLASSES_ROOT key. You can look at these entries by running the Registry Editor utility that is provided as part of the operating system (REGEDIT.EXE under Windows 95, and REGEDT32.EXE under Windows NT).

Note that this sample assumes that all registration is under the main registry key of HKEY_CLASSES_ROOT. The same content that is in the HKEY_CLASSES_ROOT branch can also be accessed in the HKEY_LOCAL_MACHINE\SOFTWARE\Classes branch. These two branches of the Registry can be used interchangeably.

Aside from the CLSID itself (which ties a component type's CLSID to its COM server), other registry entries are written for VersionIndependentProgID and ProgID. For the HKEY_CLASSES_ROOT\CLSID entry, because this server is in a DLL, the path to the server is the value of an 'InprocServer32' subkey. This value will probably be different on each system, so the function GetModuleFileName is called to dynamically determine where the executing server is located. This approach is more convenient than using .REG files, but it does require running a small utility such as REGISTER.EXE or SELFREG.EXE. It also requires that a runnable copy of the server be present, which may not always be the case.

To make the code example easier to read and maintain, these keys, subkeys, and values are written to the registry within DllRegisterServer using calls to the SetRegKeyValue local function.

The entries for each server's component types are removed from the registry within DllUnregisterServer using calls to the RegDeleteKey COM function. These are the same entries that DllRegisterServer had previously written to the registry. Here is DllUnregisterServer (in DLLSERVE.CPP).

  STDAPI DllUnregisterServer(void)
  {
    HRESULT  hr = NOERROR;
    TCHAR    szID[GUID_SIZE+1];
    TCHAR    szCLSID[GUID_SIZE+32];
    TCHAR    szTemp[MAX_PATH+GUID_SIZE];

    /*-----------------------------------------------------------------------
    Delete registry entries for the DllCar Component.
    -----------------------------------------------------------------------*/
    //Create some base key strings.
    StringFromGUID2(CLSID_DllCar, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);

    RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CTS.DllCar\\CurVer"));
    RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CTS.DllCar\\CLSID"));
    RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CTS.DllCar"));

    RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CTS.DllCar.1\\CLSID"));
    RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CTS.DllCar.1"));

    wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("ProgID"));
    RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);

    wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("VersionIndependentProgID"));
    RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);

    wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("NotInsertable"));
    RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);

    wsprintf(szTemp, TEXT("%s\\%s"), szCLSID, TEXT("InprocServer32"));
    RegDeleteKey(HKEY_CLASSES_ROOT, szTemp);

    RegDeleteKey(HKEY_CLASSES_ROOT, szCLSID);

    ...
    ...
    [Similar for DllUtilityCar, DllCruiseCar, and DllCarSample.]
    ...
    ...

    return hr;
  }

We turn next to class factories. To be a COM server is to expose the class factories for the component types the server supports. These class factories create instances of the component types supported. In the process, much like the CreatexxxCar functions of the COMOBJ sample, they pass the client a requested interface pointer to the new COM object. Part of the elegance of COM is in how it is built using itself. Class factories can illustrate this point. The class factories in this code sample are themselves COM objects that implement the standard COM IClassFactory interface. IClassFactory is called a standard interface because it is provided by COM and is pre-declared in the system header files.

Outside the COM server, COM is made aware of the class factories through a DllGetClassObject function that the server must implement and export. We'll see in the DLLCLIEN lesson how the client presents a CLSID to COM to get a class factory, then uses it to create the object, and then uses the object through its interfaces.

Here's the whole of DllGetClassObject (again from DLLSERVE.CPP).

  STDAPI DllGetClassObject(
           REFCLSID rclsid,
           REFIID riid,
           PPVOID ppv)
  {
    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
    IUnknown* pCob = NULL;

    if (CLSID_DllCar == rclsid)
    {
      LOG("S: DllGetClassObject: Requesting CFCar.");
      hr = E_OUTOFMEMORY;
      pCob = new CFCar(NULL, g_pServer);
    }
    else if (CLSID_DllUtilityCar == rclsid)
    {
      LOG("S: DllGetClassObject: Requesting CFUtilityCar.");
      hr = E_OUTOFMEMORY;
      pCob = new CFUtilityCar(NULL, g_pServer);
    }
    else if (CLSID_DllCruiseCar == rclsid)
    {
      LOG("S: DllGetClassObject: Requesting CFCruiseCar.");
      hr = E_OUTOFMEMORY;
      pCob = new CFCruiseCar(NULL, g_pServer);
    }
    else if (CLSID_DllCarSample == rclsid)
    {
      LOG("S: DllGetClassObject: Requesting CFCarSample.");
      hr = E_OUTOFMEMORY;
      pCob = new CFCarSample(NULL, g_pServer);
    }

    if (NULL != pCob)
    {
      g_pServer->ObjectsUp();
      hr = pCob->QueryInterface(riid, ppv);
      if (FAILED(hr))
      {
        g_pServer->ObjectsDown();
        DELETE_POINTER(pCob);
      }
    }

    return hr;
  }

For a given CLSID, the function creates the appropriate new CFCarXXX class factory COM object. A pointer to the requested interface on the new class factory COM object (usually IClassFactory) is then obtained by a call to QueryInterface. To create an object, the CreateInstance method of the obtained IClassFactory interface is then called by the client. This DllGetClassObject is not called directly by the client, but by COM on behalf of clients, usually through such COM services as CoGetClassObject or CoCreateInstance. More on this in the DLLCLIEN lesson.

The CFCarXXX class factory object obtained is also a component in its own right. When a class factory object is created or destroyed, it maintains its server's object count by calling the ObjectsUp and ObjectsDown functions of the appropriate server control object. There will be more on these calls later when we look at the server control object itself and at the actual car-related application components to see how they are related to the server as components.

All the CFxxx class factories are declared in FACTORY.H and implemented in FACTORY.CPP. Throughout most of the rest of this tour of the class factories, we'll use the Car component's server code as a representative example. Unless stated otherwise, this server code applies by direct analogy to the other class factories. From FACTORY.H, here is the declaration for CFCar.

  class CFCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      CFCar(IUnknown* pUnkOuter, CServer* pServer);
      ~CFCar(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // We implement the IClassFactory interface (ofcourse) in this class
      // factory COM object class.
      class CImpIClassFactory : public IClassFactory
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIClassFactory(
            CFCar* pBackObj,
            IUnknown* pUnkOuter,
            CServer* pServer);
          ~CImpIClassFactory(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IClassFactory methods.
          STDMETHODIMP         CreateInstance(IUnknown*, REFIID, PPVOID);
          STDMETHODIMP         LockServer(BOOL);

        private:
          // Data private to this interface implementation of IClassFactory.
          ULONG         m_cRefI;       // Interface Ref Count (debugging).
          CFCar*        m_pBackObj;    // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;   // Outer unknown for Delegation.
          CServer*      m_pServer;     // Server's control object.
      };

      // Make the otherwise private and nested IClassFactory interface
      // implementation a friend to COM object instantiations of this
      // selfsame CFCar COM object class.
      friend CImpIClassFactory;

      // Private data of CFCar COM objects.

      // Nested IClassFactory implementation instantiation.
      CImpIClassFactory m_ImpIClassFactory;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation). Used when this
      // CFCar object is being aggregated. Otherwise it is used
      // for delegation if this object is reused via containment.
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;
  };

This code should look familiar, since there were a lot of COM objects very much like this in the COMOBJ and COMUSER code samples. Here again we see the pattern of nested classes used to implement the multiple interfaces on this COM object, in this case IUnknown and IClassFactory. We see prototypes for the two IClassFactory methods, CreateInstance and LockServer. We won't repeat the discussion of COM object construction that was covered in COMOBJ. Return to that lesson if you don't recognize the COM object pattern of this code.

We will focus here on the code that constructs this class factory so that, when CreateInstance is called, the class factory creates its COM objects and gives those objects access to their server. To consolidate and encapsulate this internal access to the server housing, a CServer server control object is used. The server control object is a C++ object, not a COM object. In this scheme, only one server control object is implemented for the entire server, regardless of how many component types the server manages.

For now we'll examine how a pointer to the server control object is passed into the labyrinth of COM objects, their interface implementations, and the components that are actually manufactured. First notice the CFCar constructor.

  CFCar(IUnknown* pUnkOuter, CServer* pServer);

Here a pointer to the server control object is passed to the class factory. Think about where this pointer came from in the first place. One clue is in the call to the class factory constructor (see above in function DllGetClassObject). Here's the line for CFCar:

  pCob = new CFCar(NULL, g_pServer);

The value is passed as global variable g_pServer. This value in turn is assigned during execution of the DLL_PROCESS_ATTACH case in DllMain, where the server control object is created. (DllMain is in DLLSERVE.CPP.)

  g_pServer = new CServer;

This pointer value to the server control object must also be passed to the CFCar's IClassFactory implementation through its constructor.

  CImpIClassFactory(
    CFCar* pBackObj,
    IUnknown* pUnkOuter,
    CServer* pServer);

As with the pBackObj and pUnkOuter pointers, the pServer pointer is passed so the methods inside the protected scope of the interface implementation can access the outside server control object. Both the CFCar COM object and its implementation of IClassFactory have m_pServer pointer members to store this value when it is passed by the constructors. The constructor method implementations have appropriate assignments, such as the following from the CFCar constructor (in FACTORY.CPP).

  // Init the pointer to the server control object.
  m_pServer = pServer;

The class factory, in turn, passes the pointer to any new COM objects that it creates. To see this, you have to switch over to the IClassFactory interface implementation in FACTORY.CPP. For example, from the CFCar implementation of IClassFactory, here is method CFCar::CImpIClassFactory::CreatInstance:

  STDMETHODIMP CFCar::CImpIClassFactory::CreateInstance(
                 IUnknown* pUnkOuter,
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_FAIL;
    COCar* pCob = NULL;

    // NULL the output pointer.
    *ppv = NULL;

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state that the IUnknown interface MUST also be
    // requested concomitantly. If it is not so requested
    // (riid != IID_IUnknown), then an error must be returned indicating
    // that no aggregate creation of the CFCar COM Object can be
    // performed.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COCar COM Object.
      pCob = new COCar(pUnkOuter, m_pServer);
      if (NULL != pCob)
      {
        // We initially created the new COM object so tell the server
        // to increment its global server object count to help ensure
        // that the server remains loaded until this partial creation
        // of a component is completed.
        m_pServer->ObjectsUp();

        // We QueryInterface this new COM Object not only to deposit the
        // main interface pointer to the caller's pointer variable, but to
        // also automatically bump the Reference Count on the new COM
        // Object after handing out this reference to it.
        hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        if (FAILED(hr))
        {
          m_pServer->ObjectsDown();
          delete pCob;
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    return hr;
  }

The CreateInstance code above should remind you of the CreateCar function in COMOBJ. For example, like CreateCar, the CreateInstance function calls QueryInterface to provide the caller with the requested interface output pointer (value *ppv) after successfully creating its COM object.

In terms of m_pServer, the important line is:

  // Instantiate a COCar COM Object.
  pCob = new COCar(pUnkOuter, m_pServer);

The interface implementation's m_pServer data member is passed to the new COCar COM object. What will the new COCar COM object do with it? The primary thing that the newly manufactured COCar object needs to communicate to the server is when the COCar COM object has been deleted. This happens when a client of the object releases the last interface pointer held on the object. The server needs to be informed when one of its objects is deleted so that the server can unload itself when it is no longer needed by any clients. This is part of the control responsibility encapsulated in the CServer server control object. The new COCar object will use m_pServer to notify the server control object when the COCar object is being deleted. More on this below.

CreateInstance (above) uses m_pServer in other ways, as well, such as the following.

  // We initially created the new COM object so tell the server
  // to increment its global server object count to help ensure
  // that the server remains loaded until this partial creation
  // of a component is completed.
  m_pServer->ObjectsUp();

The server control object is responsible for maintaining the server's object count, among other things. In a way, the server control object governs the lifetime of the server as a whole. The ObjectsUp method increments the server's object count, and the ObjectsDown method decrements the count. After the class factory successfully manufactures a new COM object of the requested component type, it calls ObjectsUp in the server control object to increment the server's object count.

The server control object is an outgrowth of the CDllData object that was used to encapsulate DLL data in the DLLSKEL and COMOBJ code samples. Now it takes on greater importance. CServer is declared in SERVER.H and implemented in SERVER.CPP. Here is the CServer declaration from SERVER.H:

  class CServer
  {
    public:
      CServer(void);
      ~CServer(void);

      void Lock(void);
      void Unlock(void);
      void ObjectsUp(void);
      void ObjectsDown(void);

      // A place to store the handle to loaded instance of this DLL module.
      HINSTANCE m_hDllInst;

      // A place to store a client's parent window.
      HINSTANCE m_hWndParent;

      // A Pointer to a Message Box object.
      CMsgBox* m_pMsgBox;

      // Global DLL Server living Object count.
      LONG m_cObjects;

      // Global DLL Server Client Lock count.
      LONG m_cLocks;
  };

The ObjectsUp and ObjectsDown methods take care of incrementing and decrementing the server's object count. For example, here is the ObjectsDown method implementation.

  void CServer::ObjectsDown(void)
  {
    InterlockedDecrement((PLONG) &g_pServer->m_cObjects);

    LOGF1("S: CServer::ObjectsDown, new cObjects=%i.", g_pServer->m_cObjects);

    return;
  }

ObjectsUp and ObjectsDown call the Win32 functions InterlockedIncrement and InterlockedDecrement, respectively. Because the server exists and has clients in a multitasking environment, the server's object count must be accessed in ways that guarantee mutual exclusion among contending tasks (or threads within the same client process). These Win32 functions guarantee this mutual exclusion for the increment and decrement operations. The Lock and Unlock methods use the same functions to perform increment and decrement operations on a global server lock count. These lock methods are called by the IClassFactory::LockServer method implementation when a client needs to lock the server in a loaded state, regardless of whether the server has a zero object count.

This server is unloaded through the DllCanUnloadNow function that all COM servers must implement and export. Returning to file DLLSERVE.CPP, here is DllCanUnloadNow.

  STDAPI DllCanUnloadNow(void)
  {
    HRESULT hr;

    LOGF1("S: DllCanUnloadNow. cObjects=%i.", g_pServer->m_cObjects);

    // We return S_OK if there are no longer any living objects AND
    // there are no outstanding client locks on this server.
    hr = (0L==g_pServer->m_cObjects && 0L==g_pServer->m_cLocks) ? S_OK : S_FALSE;

    return hr;
  }

This function is occasionally called by COM, usually when the client calls CoFreeUnusedLibraries, to ask the server if COM can unload it. The server gives the OK on return from this function if both the lock count and the object count are zero. Though encapsulated in the CServer object, these counts are global to the attached process associated with a client instance and its use of the server. It is not necessary to place these count variables in a shared section to make them global to all client processes, because DllCanUnloadNow can be called only in the context of the CoInitialize/CoUninitialize matching pair of a given client process. The system maintains process-global usage counts for DLLs, and there is no need for COM servers to manage this.

As was pointed out earlier, CFCar is a COM object in its own right. But unlike the other applications objects managed by the server, it is also part of what makes the server a server. In the CFCar::Release method implementation, notice that m_pServer->ObjectsDown is called when the client releases its pointer to the class factory.

In the COMOBJ and COMUSER samples, we used a direct DLL call to connect the DLL to the EXE user of the DLL so that the sample's trace logging could be integrated in one display. But with DLLSERVE, the client accesses services in the DLL server solely through interfaces. To provide a clean model of client/server separation, we have no direct (DLL-exported) call that the client can make to pass a pointer to its logging facility into the server.

There is always power in such indirection. By isolating the client from direct linkage to the server, the way is open for the servers to reside on other machines. By making a component, we use an abstraction agency between the client and the component in the server. This agency is a cooperative effort between COM and the server.

Now consider another component that is also part of the infastructure of this server. In this server code sample, we use a new custom interface (ISample) and implement it in a CarSample component. Though its purpose is administratively mundane, it is representative of situations we all face in developing real applications.

ISample is declared in ICARS.H, in the sibling \INC directory, and has two methods: Init and AboutBox. Init is the significant one for associating the client's logging display with the logging being done in the server. This method yields an integrated display in the client that shows the internal sequential behavior in both server and client. This logging scheme was used to good effect in the prior lessons. Those lessons also had separate About dialog boxes in the resources of each DLL. This may seem odd in one sense, but the dialog box doesn't have to be an AboutBox as such: DLLs might often need to supply dialog boxes to an application. A banking application, for example, might call on a component in a DLL to display a dialog box containing a loan application form. The AboutBox is just an excuse to put a dialog box in the DLL. The ISample implementation in the CarSample component provides an interface method through which the client can invoke the dialog box in the server.

The COCarSample COM object is declared in SAMPLE.H and implemented in the SAMPLE.CPP file. Once again, the object's multiple interfaces, IUnknown and a native implementation of ISample, are nested classes.

The other COM objects provided by DLLSERVE are COCar, COUtilityCar, and COCruiseCar. They retain their COM object construction characteristics. COCar (in CAR.H and CAR.CPP) has nested implementations of IUnknown with a native ICar interface. COUtilityCar (in UTILCAR.H and UTILCAR.CPP) has nested implementations of IUnknown with two other interfaces, ICar and IUtility, which are reused by containment. COCruiseCar (in CRUCAR.H and CRUCAR.CPP) has nested implementations of IUnknown with two other interfaces, ICar and ICruise, which are reused by aggregation. We'll tour COCruiseCar as a representative example.

The COCruiseCar COM object class declaration in CRUCAR.H is a little different from the declaration in the COMOBJ code sample. These differences associate the COM object with the server housing and help make the COM object a properly constructed instance of the CLSID_DllCruiseCar class of components. Here is the COCruiseCar COM object class declaration:

  class COCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COCruiseCar(IUnknown* pUnkOuter, CServer* pServer);
      ~COCruiseCar(void);

      // A general method for initializing a newly created COCruiseCar.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // We implement the ICruise interface (ofcourse) in this COCruiseCar
      // COM object class. This is the interface that we are using as an
      // augmentation to the existing COCar COM object class.
      class CImpICruise : public ICruise
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICruise(COCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICruise(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICruise methods.
          STDMETHODIMP Engage(BOOL bOnOff);
          STDMETHODIMP Adjust(BOOL bUpDown);

        private:
          // Data private to this interface implementation of ICruise.
          ULONG         m_cRefI;       // Interface Ref Count (debugging).
          COCruiseCar*  m_pBackObj;    // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;   // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested ICar interface implementation
      // a friend to COM object instantiations of this selfsame COCruiseCar
      // COM object class.
      friend CImpICruise;

      // Private data of COCruiseCar COM objects.

      // Nested ICruise implementation instantiation.  This ICruise
      // interface is implemented inside this COCruiseCar object as a native
      // interface.
      CImpICruise     m_ImpICruise;

      // Main Object reference count.
      ULONG           m_cRefs;

      // Outer unknown (aggregation & delegation). Used when this
      // COCruiseCar object is being aggregated. Otherwise it is used for
      // delegation if this object is reused via containment.
      IUnknown*       m_pUnkOuter;

      // We need to save the IUnknown interface pointer on the COCar
      // object that we aggregate. We use this when we need to delegate
      // IUnknown calls to this aggregated inner object.
      IUnknown*       m_pUnkCar;

      // Pointer to this component server's control object.
      CServer*        m_pServer;
  };

The COM object has to be able to inform the server when the object is being deleted. It does so by storing a pointer to the server's control object, declared as CServer* m_pServer above. This value is passed to the constructor and assigned in the constructor implementation. Earlier in this tour, we saw how this value was passed to the class factory. When the class factory for COCruiseCar creates a new COCruiseCar COM object, it passes a copy of this m_pServer pointer to the constructor of the new COCruiseCar object.

There is no need, as there was in the interface implementations of the class factory COM objects, to pass this m_pServer value to COCruiseCar's other interface implementations (ImpICar and ImpICruise). They simply don't need it. The pointer is needed only in the COCruiseCar object's IUnknown, where the object's lifetime is managed. The Release method of the COCruiseCar object's IUnknown uses m_pServer as follows:

  STDMETHODIMP_(ULONG) COCruiseCar::Release(void)
  {
    ULONG ulCount = --m_cRefs;

    LOGF1("S: COCruiseCar::Release. New Count=%i.", m_cRefs);

    if (0 == m_cRefs)
    {
      // We've reached a zero reference count for this COM object.
      // So we tell the server housing to decrement its global object
      // count so that the server will be unloaded if appropriate.
      if (NULL != m_pServer)
        m_pServer->ObjectsDown();

      // We artificially bump the main ref count. This fulfills one of
      // the rules of aggregated objects and ensures that an indirect
      // recursive call to this release won't occur because of other
      // delegating releases that might happen in our own destructor.
      m_cRefs++;
      delete this;
    }

    return ulCount;
  }

When the reference count of this COM object goes to zero, m_pServer->ObjectsDown is called to inform the server. If there are no more objects for the server (m_cObjects == 0) and no clients have outstanding locks on the server as a result of calls to IClassFactory::LockServer, the next call to DllCanUnloadNow will cause COM to unload the server.

Another thing to notice in this lesson is the COCruiseCar::Init method. In COMOBJ we saw that there was an Init method to create any subordinate COM objects. In the class factory for COCruiseCar components (CFCruiseCar's CreateInstance method implementation in FACTORY.CPP), this Init method is called just after a new COCruiseCar object is created.

    ...
    ...
    // Instantiate a COCruiseCar COM Object.
    pCob = new COCruiseCar(pUnkOuter, m_pServer);
    if (NULL != pCob)
    {
      // We initially created the new COM object so tell the server
      // to increment its global server object count to help ensure
      // that the server remains loaded until this partial creation
      // of a component is completed.
      m_pServer->ObjectsUp();

      // If we have succeeded in instantiating the COM object we
      // initialize it to set up any subordinate objects.
      hr = pCob->Init();
      if (SUCCEEDED(hr))
      {
        // We QueryInterface this new COM Object not only to deposit the
        // main interface pointer to the caller's pointer variable, but to
        // also automatically bump the Reference Count on the new COM
        // Object after handing out this reference to it.
        hr = pCob->QueryInterface(riid, (PPVOID)ppv);
      }

      if (FAILED(hr))
      {
        m_pServer->ObjectsDown();
        delete pCob;
      }
    }
    else
      hr = E_OUTOFMEMORY;
    ...
    ...

The ObjectsUp call is made before the call to Init. This is to keep the server loaded (by incrementing its object count) while the composite object creation is completed. Init could take some time, and we wouldn't want another client releasing the last interface on a server-managed object, causing COM to prematurely unload the server.

This code guards against something that is extremely unlikely in this code sample because the class factory component itself resides in the same server. In order to call CreateInstance, the client must be holding an interface pointer to the class factory. Since the class factory is itself a COM object, the server's object count will be nonzero, at least until the client releases this pointer to the class factory. There is nothing wrong, however, with writing foolproof code, and in other application coding contexts it is at least conceivable that a class factory might reside in a different server from the components it creates.

Back in CRUCAR.CPP, here is the COCruiseCar::Init method:

  HRESULT COCruiseCar::Init(void)
  {
    HRESULT hr;

    LOG("S: COCruiseCar::Init.");

    // Set up the right pIUnknown for delegation. If we are being
    // aggregated, then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    // We create an instance of the COCar object and do this via the
    // Aggregation reuse technique. Note we pass pUnkOuter as the
    // Aggregation pointer. It is the 'this' pointer to this present
    // CruiseCar object if we are not being aggregated; otherwise it is the
    // pointer to the outermost object's controlling IUnknown. Following
    // the rules of Aggregation, we must ask for an IID_IUnknown interface.
    // We cache the requested pointer to the IUnknown of the new COCar COM
    // object for later use in delegating IUnknown calls.
    hr = CoCreateInstance(
           CLSID_DllCar,
           pUnkOuter,
           CLSCTX_INPROC_SERVER,
           IID_IUnknown,
           (PPVOID)&m_pUnkCar);

    return (hr);
  }

We don't just use a "new COCar" statement to create the subordinate COCar COM object. COCar is a component in its own right, and rather than assuming any special knowledge about its availability inside this server, we use the general power of COM to find the right server and create the object. Our code is not hard-wired to require that all CLSID_DllCar components be managed by the same server. We are going to make a new COCruiseCar COM Object and aggregate a new COCar object to do it. We don't care where the new COCar is or where it comes from; it might even be on another machine. We only want COM to give back a pointer to the object's IUnknown.

CoCreateInstance is a COM service that accepts a CLSID for a component type and creates a single instance of it. CoCreateInstance encapsulates the following behavior:

  1. Locates and loads the appropriate server.
  2. Obtains a pointer to the appropriate class factory.
  3. Requests that the class factory create an instance of the component.
  4. Releases the class factory interface pointer.
  5. Returns a pointer to the requested interface in the location specified (in the sample code above, by the &m_pUnkCar parameter).

This COM API "helper" function is useful when you need a single instance of a given component type.

Top Back to page top

© 1995-1998 Microsoft Corporation


DLLCLIEN - Client Application of DLL Server - Lesson 9

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DLLCLIEN - Client Application of DLL Server

SUMMARY

The DLLCLIEN sample presents a series of COM objects, several of which were introduced in the DLLSERVE code sample. These objects represent various types of cars, and they expose the following interfaces: ICar, IUtility, and ICruise. DLLCLIEN works in conjunction with the separate DLLSERVE.DLL, which creates COCar, COUtilityCar, and COCruiseCar COM objects.

DLLCLIEN.EXE creates its own COUtilityCruiseCar COM object, which is constructed by reusing the COCruiseCar COM object by aggregation and augmenting it with the IUtility interface. Because the COCruiseCar COM object class is also an aggregate--that is, it reuses an inner COCar object by aggregation--DLLCLIEN illustrates nested aggregation of COM objects.

For functional descriptions and a tutorial code tour of DLLCLIEN, see the Code Tour section in DLLCLIEN.HTM. For details on the external user operation of DLLCLIEN, see both the Usage and Operation sections in DLLCLIEN.HTM. To read DLLCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DLLCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the DLLCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also DLLSERVE.HTM in the main tutorial directory for more details on how DLLSERVE works and exposes its services to DLLCLIEN. You must build the DLLSERVE DLL before building DLLCLIEN. The makefile for DLLSERVE automatically registers that server in the system registry, so you must build DLLSERVE before attempting to run DLLCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DLLCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DLLCLIEN.DSP

You can also simply double-click the DLLCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

DLLCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by DLLCLIEN.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, DLLCLIEN.EXE is the client executable to run for this sample.

OPERATION

The DLLCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, DLLSERVE.DLL. Here is a summary of operation from the standpoint of DLLCLIEN.EXE as a COM client of the DLLSERVE.DLL COM server.

The DLLCLIEN and DLLSERVE samples are directly analogous to the COMUSER and COMOBJ samples. The same COM objects are used and the same menu system exercises those objects. The main difference, as was discussed in the DLLSERVE lesson, is that these COM objects are now components that are housed in a COM server and accessed by a COM client. The DLLCLIEN.EXE application is the client.

The COM objects that are used in the DLLCLIEN and DLLSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

DLLCLIEN.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in DLLSERVE.DLL. For details, see the DLLSERVE lesson. COCruiseCar is constructed using aggregation and is also implemented in DLLSERVE.DLL. COUtilityCruiseCar is constructed using aggregation and is implemented in DLLCLIEN.EXE. COUtilityCruiseCar aggregates COCruiseCar, to illustrate nested aggregation in this lesson.

DLLCLIEN.EXE presents a menu for each of these four main COM objects. Each menu has commands that call the methods of the various available interfaces. The code samples (both DLLCLIEN and DLLSERVE) have trace message log statements throughout. When you exercise the objects from DLLCLIEN.EXE, the main DLLCLIEN window will display a log of internal activity in these COM objects.

Menu Selection: File/Exit
Exits DLLCLIEN.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/DLLCLIEN Tutorial
Opens the DLLCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/DLLSERVE Tutorial
Opens the DLLSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/REGISTER Tutorial
Opens the REGISTER.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this sample or another one in the Windows Notepad.

Menu Selection: Help/About DLLCLIEN
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

Menu Selection: Help/About DLLSERVE
Displays the About dialog box for DLLSERVE.DLL, which is used by this application. In this series of code samples, partner DLLs like DLLSERVE are given their own About dialog box in the native resources of the DLL. This menu item calls the DLL function that displays this dialog box.

CODE TOUR

Files          Description
DLLCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
DLLCLIEN.H     The include file for the DLLCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
DLLCLIEN.CPP   The main implementation file for DLLCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
DLLCLIEN.RC    The application resource definition file.
DLLCLIEN.ICO   The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
DLLCLIEN.DSP   Microsoft Visual Studio Project file.

This code sample is based on the code in COMUSER, which is based on the code in DLLUSER, which in turn is based on the code in EXESKEL. See the code tours in those samples for more details on using the application skeleton and the DLL.

DLLCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling \APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

DLLCLIEN illustrates various kinds of COM objects in various settings. The object created and used in COMUSER is of class COUtilityCruiseCar. It is constructed by aggregation of the COCruiseCar COM object, which is implemented in DLLSERVE.DLL. See the DLLSERVE code tour for more information about the COCar, COUtilityCar, and COCruiseCar objects.

In addition to showing how COUtilityCruiseCar is constructed, this lesson demonstrates how DLLCLIEN.EXE, as a client of the COM objects implemented in both DLLCLIEN and DLLSERVE.DLL, creates, exercises, and releases those objects.

We start in DLLCLIEN.CPP. The following include statements are used to bring in the include files:

     #include <windows.h>
     #include <ole2.h>
     #include <initguid.h>
     #include <commdlg.h>
     #include <apputil.h>
     #include <icars.h>
     #include <carguids.h>
     #include "dllclien.h"
     #include "utcrucar.h"

Here, as in COMUSER, INITGUID.H is included. This file must be included only once in each executable. It ensures that any GUIDs defined in this application will be instantiated as named data in the data segment of the EXE.

We include ICARS.H, located in the sibling \INC directory, to get the car-related interface abstract base class declarations. We include CARGUIDS.H, also located in the sibling \INC directory, for the associated GUID definitions and the CLSIDs for the car-related component types (CLSID_DllCar, CLSID_DllUtilityCar, CLSID_DllCruiseCar, CLSID_DllUtilityCruiseCar, and CLSID_DllCarSample).

Earlier lessons introduced the MsgBox and MsgLog facilities, and their use is continued here. The LOG* macro calls in the source code are trace statements that log and display appropriate conditions at run time.

The following fragment from CMainWindow::InitInstance in file DLLCLIEN.CPP shows something different from what was done before.

  ...
  ...
  // Now ask COM for the ISample interface to the server's
  // CarSample component. This effectively loads the Server DLL.
  LOG("C: Start DLLSERVE Server Trace Logging.");
  hr = CoCreateInstance(
         CLSID_DllCarSample,
         NULL,
         CLSCTX_INPROC_SERVER,
         IID_ISample,
         (PPVOID)&m_pCarSample);
  if (SUCCEEDED(hr))
  {
    hr = m_pCarSample->Init(m_hWnd, (PVOID)g_pMsgLog);
    // Signal if we succeeded in initializing the app.
    bOk = SUCCEEDED(hr);
    if (!bOk)
    {
      RELEASE_INTERFACE(m_pCarSample);
      // We ask COM to unload any unused COM Servers.
      CoFreeUnusedLibraries();
    }
  }
  else
    m_pMsgBox->ErrorID(IDS_NOSERVER);
  ...
  ...

After the message log is created, we build a bridge to the server so that the server's trace logging macros will be displayed in the client's log display. In COMUSER, we simply called COMOBJ.DLL's exported function (ComObjInitMsgLog) to pass a pointer to the client's message log facility. Things are different now that we have a stronger separation between client and server. DLLCLIEN is not implicitly linked to the DLLSERVE.DLL server. Instead, our only access to components in the server is through CLSIDs and interface GUIDs. Using COM's CoCreateInstance API function, we ask for an ISample interface pointer to a new COCarSample component.

This component was introduced in the DLLSERVE lesson. After COM creates one of these utility components, we call the component's ISample::Init method to pass a handle to the DLLCLIEN main window and a pointer to the DLLCLIEN logging facility. That's how we connect the server's logging facility with that of the client. One side effect of this strategy is that the entire server is loaded early during execution of this application's CMainWindow::InitInstance method. The server's object count will thus be set to 1 to account for the COCarSample component that is created.

As a result, one of last tasks upon exiting this client is to release the COCarSample component. Because this is the last component controlled by the server, releasing it decrements the server's object count to 0, which causes COM to unload the entire server when CoFreeUnusedLibraries is called. However, a 0 object count and resulting unload of the server may not occur because other client processes may be using objects managed by the server.

In the CMainWindow::DoMenu method, we see how the menu choices are dispatched. We'll take a typical example from the COM object implemented in this application. The following fragment shows the Create command from the UtilityCruiseCar menu.

    case IDM_UCRU_CREATE:
      LOG("C: === UtilityCruiseCar Menu: Create.");
      if (NULL == m_pUtilityCruiseCar)
      {
        // Call a create function to create an instance.
        hr = CreateUtilityCruiseCar(
               NULL,
               IID_IUnknown,
               (PPVOID)&m_pUtilityCruiseCar);
        if (SUCCEEDED(hr))
        {
          ::CheckMenuItem(
              hMenu,
              IDM_UCRU_CREATE,
              MF_BYCOMMAND | MF_CHECKED);
        }
        else
        {
          LOGERROR("C: ???? UtilityCruiseCar creation",hr);
        }
      }
      else
        LOG("C: ???? UtilityCruiseCar already exists.");
      break;

The CreateUtilityCruiseCar function is called. This function is defined in UTCRUCAR.CPP, part of this application. NULL is passed for the first parameter because at this top level of usage we are not aggregating the new COUtilityCruiseCar object as part of some outer object. Once the object is created, the output parameter, m_pUtilityCruiseCar, will contain a pointer to the IUnknown of the new object. We cache this object pointer for subsequent use of the COM object.

The following fragment handles the matching release of the COUtilityCruiseCar COM object.

    case IDM_UCRU_RELEASE:
      LOG("C: === UtilityCruiseCar Menu: Release.");
      if (NULL != m_pUtilityCruiseCar)
      {
        RELEASE_INTERFACE(m_pUtilityCruiseCar);
        // We ask COM to unload any unused COM Servers.
        CoFreeUnusedLibraries();
        ::CheckMenuItem(
            hMenu,
            IDM_UCRU_CREATE,
            MF_BYCOMMAND | MF_UNCHECKED);
      }
      else
        LOG("C: ???? No UtilityCruiseCar to Release.");
      break;

The CoFreeUnusedLibraries API function is called to unload the server, if appropriate. This function usually causes COM to call the in-process server's DllCanUnloadNow function. If the release caused the server's object count to reach 0, COM will unload the server DLL. As mentioned above, that won't happen in this code sample because of our use of the CarSample component. The server's object count will usually revert to 1 after the release of the COUtilityCruiseCar object.

In DLLSERVE, CoCreateInstance was used to create single instances of the server's component types. Sometimes it is necessary to create multiple instances of the same component in quick succession (for example, when creating an array of such components). This is done more efficiently by directly using the component's class factory. Here's the CreateUtilityCruiseCar function from UTCRUCAR.CPP.

  HRESULT CreateUtilityCruiseCar(
            IUnknown* pUnkOuter,
            REFIID riid,
            PPVOID ppv)
  {
    HRESULT hr;
    COUtilityCruiseCar* pCob;

    LOGF1("C: CreateUtilityCruiseCar. pUnkOuter=0x%X.",pUnkOuter);

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state the IUnknown interface MUST also be concomitantly
    // requested. If it is not so requested ( riid != IID_IUnknown) then
    // an error must be returned, indicating that no aggregate creation of
    // the COUtilityCruiseCar COM Object can be performed using anything
    // other than a controlling IUnknown interface.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COUtilityCruiseCar COM Object.
      pCob = new COUtilityCruiseCar(pUnkOuter);
      if (NULL != pCob)
      {
        // If we have succeeded in instantiating the COUtilityCruiseCar
        // object we initialize it to offer its interfaces.
        hr = pCob->Init();
        if (SUCCEEDED(hr))
        {
          // We QueryInterface this new COM Object not only to deposit the
          // main interface pointer to the caller's pointer variable, but to
          // also automatically bump the Reference Count on the new COM
          // Object after handing out this *ppv reference to it.
          hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
      LOGF1("C: CreateUtilityCruiseCar Succeeded. *ppv=0x%X.",*ppv);

    return hr;
  }

Here is the Init method, also from UTCRUCAR.CPP, that is executed when a new COUtilityCruiseCar object is created.

  HRESULT COUtilityCruiseCar::Init(void)
  {
    HRESULT hr;
    IClassFactory* pICFCruiseCar;

    // Set up the right pIUnknown for delegation. If we are being
    // aggregated, then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    LOG("C: COUtilityCruiseCar::Init.");

    // Get a class factory for DllCruiseCar and issue IClassFactory's
    // CreateInstance method to manufacture a COCruiseCar COM object.
    hr = CoGetClassObject(
           CLSID_DllCruiseCar,
           CLSCTX_INPROC_SERVER,
           NULL,
           IID_IClassFactory,
           (PPVOID)&pICFCruiseCar);
    if (SUCCEEDED(hr))
    {
      hr = pICFCruiseCar->CreateInstance(
                            pUnkOuter,
                            IID_IUnknown,
                            (PPVOID)&m_pUnkCruiseCar);
      pICFCruiseCar->Release();
    }

    return (hr);
  }

After we obtain a class factory for the COCruiseCar component type, we call the CreateInstance method to create an instance of the COCruiseCar object, which we reuse by aggregation. We pass pUnkOuter as the aggregation pointer. It is the 'this' pointer to the present COUtilityCruiseCar object if the object is not aggregated; otherwise, it is the pointer to the controlling IUnknown. Following the rules of aggregation, we ask for an IID_IUnknown interface. We cache this pointer to the IUnknown of the new COCruiseCar COM object for later use in delegating IUnknown calls. We then release the class factory after we are done with it.

The COM CoCreateInstance helper function encapsulates this use of the class factory. When creating only one instance, CoCreateInstance is adequate. We show direct use of the class factory here for purposes of illustration. If we need to create many COCruiseCar COM objects at the same time, we would use the class factory, which is much more efficient than a series of CoCreateInstance calls that must obtain and release the class factory to create each object.

These are the only significant differences between the COUtilityCruiseCar COM object in this lesson and the one in the previous COMUSER lesson. The object class defined in UTCRUCAR.H implements the native IUtility interface as a nested class. As before, the object is constructed by aggregating the native IUtility interface with an existing COCruiseCar COM object.

For symmetry, we'll list and tour a trace log of the same sequence of menu actions that we studied in the COMUSER lesson. Here is the trace log for the Create command from the UtilityCruiseCar menu. Trace lines preceded with 'C:' are from the DLLCLIEN.EXE client. Lines preceeded with 'S:' are from the DLLSERVE.DLL server.

  C: === UtilityCruiseCar Menu: Create.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init.
  S: DllGetClassObject: Requesting CFCruiseCar.
  S: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=2.
  S: CFCruiseCar::QueryInterface. pIClassFactory returned.
  S: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCruiseCar::AddRef. New cRefs=1.
  S: CFCruiseCar::CImpIClassFactory::CreatInstance. pUnkOuter=0x77069C.
  S: COCruiseCar::CImpICruise Constructor. Aggregating.
  S: COCruiseCar Constructor. m_pUnkOuter=0x77069C.
  S: CServer::ObjectsUp. New cObjects=3.
  S: COCruiseCar::Init.
  S: DllGetClassObject: Requesting CFCar.
  S: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=4.
  S: CFCar::QueryInterface. pIClassFactory returned.
  S: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreatInstance. pUnkOuter=0x77069C.
  S: COCar::CImpICar Constructor. Aggregating.
  S: COCar Constructor. m_pUnkOuter=0x77069C.
  S: CServer::ObjectsUp. New cObjects=5.
  S: COCar::QueryInterface. 'this' pIUnknown returned.
  S: COCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreatInstance Succeeded. *ppv=0x660760.
  S: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=4.
  S: CFCar::Destructor.
  S: CFCar::CImpIClassFactory Destructor.
  S: COCruiseCar::Init (New Aggregation of COCar) Succeeded.
  S: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  S: COCruiseCar::AddRef. New cRefs=1.
  S: CFCruiseCar::CImpIClassFactory::CreatInstance Succeeded. *ppv=0x660710.
  S: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCruiseCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=3.
  S: CFCruiseCar::Destructor.
  S: CFCruiseCar::CImpIClassFactory Destructor.
  C: COUtilityCruiseCar::Init Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x77069C.

There's quite a lot going on here, so let's break this log down into more manageable chunks. First, choose the menu item in the client.

  C: === UtilityCruiseCar Menu: Create.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init.

The CreateUtilityCruiseCar function is called with pUnkOuter passed as NULL, because we are not creating the COUtilityCruiseCar object for aggregation in another COM object. The IUnknown of the new COUtilityCruiseCar COM object will be the controlling IUnknown for the inner COCruiseCar object. Within CreateUtilityCruiseCar, a new COUtilityCruiseCar object is created, and the constructors are called for both the nested native implementation of the IUtility interface and the COUtilityCruiseCar object itself. Again, within the create function, the Init method of the new object is called.

  S: DllGetClassObject: Requesting CFCruiseCar.
  S: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=2.
  S: CFCruiseCar::QueryInterface. pIClassFactory returned.
  S: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCruiseCar::AddRef. New cRefs=1.
  S: CFCruiseCar::CImpIClassFactory::CreatInstance. pUnkOuter=0x77069C.
  S: COCruiseCar::CImpICruise Constructor. Aggregating.
  S: COCruiseCar Constructor. m_pUnkOuter=0x77069C.
  S: CServer::ObjectsUp. New cObjects=3.
  S: COCruiseCar::Init.

The COUtilityCruiseCar::Init method calls CoGetClassObject to request the class factory (CFCruiseCar) for COCruiseCar components. COM in turn calls the server's DllGetClassObject function, which is executed by the server (hence the 'S:' in the trace). DllGetClassObject provides the appropriate class factory and creates a new CFCruiseCar COM object. The constructors for the CFCruiseCar object and for the IClassFactory interface are called. Since this CFCruiseCar object is a server-housed component, the server control object (CServer) increments the global server object count to 2--one for the pre-existing CarSample utility component and one for the new class factory object.

Still in DllGetClassObject, the server calls the appropriate QueryInterface to obtain the requested IClassFactory interface. QueryInterface makes the mandatory call to AddRef on this pointer, and this call is delegated to the AddRef in the CFCruiseCar object's IUnknown. Using the pointer to IClassFactory (still inside COUtilityCruiseCar::Init), CreateInstance is called to request a new COCruiseCar COM component. The value of pUnkOuter is non-NULL, signifying that the new object will be aggregated. This pUnkOuter points to the controlling IUnknown, which is that of the existing COUtilityCruiseCar COM object.

CreateInstance (in the server) creates the new COCruiseCar object, so the constructors for both the nested ICruise interface implementation and the object itself are executed. The constructors indicate that the object is being created for aggregation. Since COCruiseCar is a component, the server's object count is incremented to 3. The CreateInstance method then calls the COCruiseCar::Init method on the new object to create any inner COM objects.

  S: DllGetClassObject: Requesting CFCar.
  S: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=4.
  S: CFCar::QueryInterface. pIClassFactory returned.
  S: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreatInstance. pUnkOuter=0x77069C.
  S: COCar::CImpICar Constructor. Aggregating.
  S: COCar Constructor. m_pUnkOuter=0x77069C.
  S: CServer::ObjectsUp. New cObjects=5.
  S: COCar::QueryInterface. 'this' pIUnknown returned.
  S: COCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreatInstance Succeeded. *ppv=0x660760.
  S: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=4.
  S: CFCar::Destructor.
  S: CFCar::CImpIClassFactory Destructor.
  S: COCruiseCar::Init (New Aggregation of COCar) Succeeded.

There is indeed an inner COCar object to be aggregated as part of the COCruiseCar COM object. The COCruiseCar::Init method calls CoCreateInstance to create an instance of the COCar component. COM issues the request for the class factory, and as before, the server's DllGetClassObject function is called. The CFCar class factory is created, and its constructors are executed. The server is notified about this new CFCar object, and server's object count is incremented to 4. QueryInterface and AddRef are called on the returned IClassFactory interface pointer. COM calls CreateInstance and passes the pUnkOuter that COCruiseCar::Init passed to CoCreateInstance. Because this pointer is not NULL, the new COCar COM object is being created for aggregation. The pUnkOuter value is the same one that was passed in the CFCruiseCar::CreateInstance call above (0x77069C). It's the controlling IUnknown, the IUnknown of the COUtilityCruiseCar object that was created in the beginning.

The CFCar class factory's CreateInstance method creates a new COCar COM object. The constructors for the object and its interface implementation are executed. The COCar object is created for aggregation. The server's object count is incremented to 5. QueryInterface and AddRef are called on the returned IUnknown interface.

We now have objects nested at two levels of aggregation. CFCar's CreateInstance method returns success. COM's CoCreateInstance then releases the IClassFactory pointer. This release is delegated to the Release method of the CFCar object's IUnknown, which decrements its reference count to 0. The CFCar object is deleted, and the server's object count is decremented to 4. The destructors of the CFCar object and its nested IClassFactory interface implementation are executed. We continue to wind back up the call stack. The COCruiseCar::Init method returns success in creating an aggregated COCar object as part of the COCruiseCar object.

  S: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  S: COCruiseCar::AddRef. New cRefs=1.
  S: CFCruiseCar::CImpIClassFactory::CreatInstance Succeeded. *ppv=0x660710.
  S: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCruiseCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=3.
  S: CFCruiseCar::Destructor.
  S: CFCruiseCar::CImpIClassFactory Destructor.
  C: COUtilityCruiseCar::Init Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x77069C.

Because COCruiseCar::Init succeeded, CFCruiseCar's CreateInstance (which we have now returned to) calls QueryInterface on the new COCruiseCar object to pass the caller the requested IUnknown interface. As a result, AddRef is called. CFCruiseCar's CreateInstance then returns, indicating success to the caller (COUtilityCruiseCar::Init), which then explicitly releases the pointer to CFCruiseCar's IClassFactory interface. The release causes the CFCruiseCar object to be deleted. A call to the server control object's ObjectsDown method decrements the server's object count to 3. The destructors of the CFCruiseCar object and its IClassFactory interface implementation are executed.

Control now returns to the client CreateUtilityCruiseCar function, where all this started, and COUtilityCruiseCar::Init has returned successfully. Since a new COUtilityCruiseCar object has been created, QueryInterface is called to obtain its IUnknown in order to return to the caller of CreateUtilityCruiseCar the pointer to the controlling IUnknown of this three-level nested aggregate. CreateUtilityCruiseCar finally returns with success.

Now that we have created a COUtilityCruiseCar object, here's the trace log for the ICar::Shift command on the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: COUtilityCruiseCar::QueryInterface. ICar delegating.
  S: COCruiseCar::QueryInterface. ICar delegating.
  S: COCar::QueryInterface. pICar returned.
  S: COCar::CImpICar::Addref. Delegating. New cI=1.
  C: COUtilityCruiseCar::AddRef. New cRefs=2.
  C: Interface obtained. *ppv=0x66076C
  C: --Calling pICar->Shift
  S: COCar::CImpICar::Shift. nGear=1.
  C: --Releasing pICar
  S: COCar::CImpICar::Release. Delegating. New cI=0.
  C: COUtilityCruiseCar::Release. New cRefs=1.

First, an ICar interface pointer on the UtilityCruiseCar object must be obtained using QueryInterface. This call is delegated to the next inner object that has the interface being asked for--in this case, the COUtilityCruiseCar::QueryInterface. This COM object's QueryInterface in turn does the same thing. Its ICar interface is provided by the COCar COM object. The interface pointer pICar is finally returned by the COCar object's QueryInterface. The AddRef call made by this QueryInterface is delegated directly to the controlling IUnknown (in this case, COUtilityCruiseCar::AddRef), and the COUtilityCruiseCar object's reference count is incremented to 2.

An outside agent now holds a reference to the ICar interface on this aggregate object. That same outside agent (DLLCLIEN.EXE) has also been holding a reference to the object's IUnknown, which explains the reference count of 2. The ICar interface pointer is used to directly call the implementation of the Shift method in the COCar object. The pointer to this ICar is then released, which causes a delegation to the Release method of the controlling IUnknown, where the outermost object's reference count is decremented to 1.

Here's the trace log for the Release command on the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: Release.
  C: COUtilityCruiseCar::Release. New cRefs=0.
  C: COUtilityCruiseCar::Destructor.
  S: COCruiseCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=2.
  S: COCruiseCar::Destructor.
  S: COCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=1.
  S: COCar::Destructor.
  S: COCar::CImpICar Destructor.
  S: COCruiseCar::CImpICruise Destructor.
  C: COUtilityCruiseCar::CImpIUtility Destructor.
  S: DllCanUnloadNow. cObjects=1, cLocks=0.

This command causes a cascade of releases and object destruction. The initial call to Release decrements the reference count of 1 to 0, causing the entire COUtilityCruiseCar COM object to be deleted. The object's destructor is called, which calls the Release method of the aggregated COCruiseCar COM object, in DLLSERVE.DLL. The COCruiseCar::Release method calls the server control object's ObjectsDown method to decrement the server's object count to 2. The COCruiseCar object's reference count is also decremented from 1 to 0, causing the object to be deleted. As above, its destructor releases the aggregated COCar object, decrementing its reference count to 0 and causing its destructor to run. Again, the server's object count is decremented, this time to 1. As we wind back out of all these destructors, the destructors of the ICar, ICruise, and IUtility ImpI interface implementations are executed. These require no explicit deletions, because the interfaces are implemented as nested classes. This is one of the benefits of this technique.

Finally, the client calls the COM CoFreeUnusedLibraries function, causing COM in turn to ask the server (via the DllCanUnloadNow call) if it can unload the server. It can't in this case (cObjects != 0), because the CarSample component is still in use. It's logging the internal trace announcements and won't be released until the client application is exited.

As a final experiment, you may want to try running several instances of DLLCLIEN and watching the server's object counts as you create server components from each client instance.

Top Back to page top

© 1995-1998 Microsoft Corporation


LICSERVE - DLL Licensed Server - Lesson 10

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

LICSERVE - DLL Licensed Server

SUMMARY

The LICSERVE sample introduces licensed components. LICSERVE modifies the COCruiseCar component of the DLLSERVE code sample and houses it as a licensed LicCruiseCar component in the LICSERVE.DLL COM server. This in-process server provides the components LicCruiseCar and LicCarSample. LicCarSample is the utility component that allows clients access to this server's logged behavior.

LICSERVE provides class factories for each of these components. The class factory for the LicCruiseCar component implements IClassFactory2, instead of the usual IClassFactory, to provide the licensing mechanism for this component. The LicCarSample component is not licensed and thus implements IClassFactory.

In the series of COM tutorial code samples, LICSERVE works with the LICCLIEN code sample to illustrate LICSERVE's COM server facilities for creating licensed components and supporting the subsequent manipulation of those components by the LICCLIEN.EXE client.

For functional descriptions and a tutorial code tour of LICSERVE, see the Code Tour section in LICSERVE.HTM. For details on setting up the programmatic usage of LICSERVE, see the Usage section in LICSERVE.HTM. To read LICSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the LICSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the LICSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also LICCLIEN.HTM in the main tutorial directory for more details on the LICCLIEN client application and how it works with LICSERVE.EXE itself. You must build LICSERVE.DLL before building or running LICCLIEN. LICSERVE's makefile automatically registers LICSERVE's components in the system registry. These components must be registered before LICSERVE is available to outside COM clients as a server for those components. This registration is done using the REGISTER.EXE utility built in the earlier REGISTER lesson. To build or run LICSERVE, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the LICSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV LICSERVE.DSP

You can also simply double-click the LICSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

LICSERVE is a DLL that is meant to be used primarily as a licensed COM server. Though it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from COM's CoGetClassObject function. Servers like LICSERVE are registered in the registry. To use LICSERVE in a COM client program, a client does not need to include LICSERVE.H or link to LICSERVE.LIB. A COM client of LICSERVE obtains access solely through its components' CLSIDs and COM services. For LICSERVE, those CLSIDs are CLSID_LicCruiseCar and CLSID_LicCarSample. The LICCLIEN sample shows how this is done with the LicCruiseCar component that is licensed in the LICSERVE server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the LICSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the LICSERVE directory.

  ..\register\register.exe licserve.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of LICSERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, LICCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files        Description
LICSERVE.TXT Short sample description.
MAKEFILE     The generic makefile for building the LICSERVE.DLL
             code sample of this tutorial lesson.
LICSERVE.H   The include file for declaring as imported or defining as
             exported the service functions in LICSERVE.DLL.
LICSERVE.CPP The main implementation file for LICSERVE.DLL. Has DllMain
             and the COM server functions (for example, DllGetClassObject).
LICSERVE.DEF The module definition file. Exports server housing functions.
LICSERVE.RC  The DLL resource definition file for the executable.
LICSERVE.LIC The license file for this licensed COM server.
LICSERVE.ICO The icon resource for the executable.
SERVER.H     The include file for the server control C++ object. Also has
             resource identifiers for resources stored in LICSERVE.DLL and
             other extern declarations that are used internally within the
             modules of LICSERVE.DLL.
SERVER.CPP   The implementation file for the Server Control object.
FACTORY.H    The include file for the server's class factory COM objects.
FACTORY.CPP  The implementation file for the server's class factories.
CRUCAR.H     The include file for the COLicCruiseCar COM object class.
CRUCAR.CPP   The implementation file for the COLicCruiseCar COM object
             class.
SAMPLE.H     The include file for the COLicCarSample COM object class.
SAMPLE.CPP   The implementation file for the COLicCarSample COM object
             class.
LICSERVE.DSP Microsoft Visual Studio Project file.

This code sample is based on the code in the DLLSERVE lesson. This lesson begins with the COCruiseCar component studied in DLLSERVE lesson and modifies it into a licensed component, COLicCruiseCar, which is housed in the LICSERVE server. The bulk of this work is done in the class factory, where the IClassFactory2 interface is implemented instead of the usual IClassFactory interface. COLicCruiseCar is an aggregatable COM object that also aggregates the COCar COM object provided by the in-process server DLLSERVE. The LICSERVE in-process server uses this unlicensed COM object from another in-process server to provide the licensed COLicCruiseCar component to outside clients. The next lesson, LICCLIEN, illustrates just such a client.

LICSERVE also provides the necessary utility COM object, COLicCarSample, to permit trace logging of behavior inside LICSERVE components to appear in the client's trace logging display.

The main objective of the LICSERVE and LICCLIEN code samples is to show the mechanisms required to license and verify licensing of a component (LicCruiseCar).

LICSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

Like the DLLSERVE server, LICSERVE is self-registering. See the DLLSERVE lesson for more details on this process. For LICSERVE, the code for self-registration is in the DllRegisterServer and DllUnregisterServer functions in LICSERVE.CPP. Two new components are registered, CLSID_LicCruiseCar and CLSID_LicCarSample. These CLSIDs are defined in the global CARGUIDS.H in the \INC sibling directory.

In this lesson we'll concentrate on what it takes to license the COLicCruiseCar component. Other aspects of this code sample are very much the same as the DLLSERVE code sample.

We start the tour in LICSERVE.CPP. There is nothing significantly different with the DllGetClassObject and DllCanUnloadNow functions that were studied in DLLSERVE. However, there is something significantly new in the DLL_PROCESS_ATTACH case of the DllMain function. Here is DllMain:

  BOOL WINAPI DllMain(
                HINSTANCE hDllInst,
                DWORD fdwReason,
                LPVOID lpvReserved)
  {
    BOOL bResult = TRUE;

    // Dispatch this main call based on the reason it was called.
    switch (fdwReason)
    {
      case DLL_PROCESS_ATTACH:
        // The DLL is being loaded for the first time by a given process.
        // Perform per-process initialization here. If the initialization
        // is successful, return TRUE; if unsuccessful, return FALSE.
        bResult = FALSE;
        if (UnicodeOk())
        {
          // Instantiate the CServer utility class.
          g_pServer = new CServer;
          if (NULL != g_pServer)
          {
            // Remember the DLL Instance handle.
            g_pServer->m_hDllInst = hDllInst;

            // Create a MsgBox object.
            g_pServer->m_pMsgBox = new CMsgBox;
            if (NULL != g_pServer->m_pMsgBox)
            {
              // Check for valid Machine License.
              g_pServer->CheckLicense();
              bResult = TRUE;
            }
          }
        }
        break;

      case DLL_PROCESS_DETACH:
        ...
        break;

      case DLL_THREAD_ATTACH:
        ...
        break;

      case DLL_THREAD_DETACH:
        ...
        break;

      default:
        break;
    }

    return (bResult);
  }

The significant difference here is the g_pServer->CheckLicense call. CheckLicense is a new licensing-related method of the server control object (CServer). Here is what the CServer class declaration in SERVER.H looks like with added support for license management.

  class CServer
  {
    public:
      CServer(void);
      ~CServer(void);

      void Lock(void);
      void Unlock(void);
      void ObjectsUp(void);
      void ObjectsDown(void);
      BOOL CheckLicense(void);

      // A place to store the handle to loaded instance of this DLL module.
      HINSTANCE m_hDllInst;

      // A place to store a client's parent window.
      HINSTANCE m_hWndParent;

      // A Pointer to a Message Box object.
      CMsgBox* m_pMsgBox;

      // Global DLL Server living Object count.
      LONG m_cObjects;

      // Global DLL Server Client Lock count.
      LONG m_cLocks;

      // The Machine License key string for the LICSERVE server.
      CHAR m_szLicenseKey[MAX_LICENSEKEY];

      // Length of the global g_szLicenseKey string.
      UINT m_cLicenseLen;

      // Machine license verified.
      BOOL m_bLicensed;
  };

The CheckLicense method returns TRUE if the license was verified and FALSE if not. An ASCII m_szLicenseKey string is defined to contain a unique license key string that is hard-coded for this server. This key string provides what is called the machine license for the server. In this scheme, the entire server is licensed for use on the machine through this one key. For those components housed within the server that you want to license, you implement their class factories using IClassFactory2. We'll look at this shortly.

The key string is initialized in the CServer::CServer constructor (in SERVER.CPP) as follows:

  CServer::CServer(void)
  {
    // Init the Server License Key string.
    lstrcpyA(
      m_szLicenseKey,
      "LICSERVE 1.0 - Component Server. Copyright (C) 1995-1998 Microsoft Corp.");

    // Zero the Object and Lock counts for this attached process.
    m_cObjects = 0;
    m_cLocks = 0;

    return;
  }

We force the use of the ASCII lstrcpyA function because the key string is specified in CServer as a CHAR ASCII string and not as a more general TCHAR, which would compile to either Unicode or ANSI. This reliance on ASCII is merely a convenience, since the license file (where the matching license key string should be found, in this case LICSERVE.LIC) is also provided by us--the builders of this server--as part of the server. We control the format of the license file. To read an ASCII text file and match two ASCII text strings is easy. However, we could make the license key a Unicode or even an encrypted binary sequence if we were so inclined.

The internal license key used in this code sample is chosen to match the first text line of an outside ASCII text file. Here is that entire LICSERVE.LIC text file.

  LICSERVE 1.0 - Component Server. Copyright (C) 1995-1998 Microsoft Corp.

  Warning: This product is licensed to you pursuant to the terms of the
  license agreement included with the original software, and is protected
  by copyright law and international treaties. Unauthorized reproduction
  or distribution may result in severe civil and criminal penalties, and
  will be prosecuted to the maximum extent possible under the law.

If this file's copyright line is tampered with in any way, or if the file is absent, the server's class factories won't create any instances of their respective components. The presence of this file constitutes a machine license for the LICSERVE comonents. We'll explore this idea in more detail later.

CServer also has two other new data members, m_cLicenseLen and m_bLicensed. They are for later use by the various license-related methods in the server. CServer::CheckLicense assigns both of these values. Here is CheckLicense (in SERVER.CPP):

  BOOL CServer::CheckLicense(void)
  {
    TCHAR  szPath[MAX_PATH];
    CHAR   szLicKeyRead[MAX_LICENSEKEY];
    UINT   cbRead;
    ULONG  cbWasRead;
    UINT   cb;
    HANDLE hFile;

    // Assume license not verified to start.
    m_bLicensed = FALSE;

    // Assume the license key string is ASCII and count its length.
    m_cLicenseLen = cb = lstrlenA(m_szLicenseKey);

    // Get the module path. Then parse and replace DLL extension with LIC.
    MakeFamilyPath(m_hDllInst, szPath, TEXT(LICENSE_FILE_EXT));

    // Open the xx.LIC file and read in and check the license key for
    // a match.
    hFile = CreateFile(
              szPath,
              GENERIC_READ,
              FILE_SHARE_READ,
              NULL,
              OPEN_EXISTING,
              FILE_ATTRIBUTE_NORMAL,
              NULL);

    if (INVALID_HANDLE_VALUE != hFile)
    {
      cbRead = cb * sizeof(CHAR);
      ReadFile(hFile, szLicKeyRead, cbRead, &cbWasRead, NULL);
      CloseHandle(hFile);
      if (cbRead == cbWasRead)
        if (0 == memcmp(m_szLicenseKey, szLicKeyRead, cb))
          m_bLicensed = TRUE;
    }

    LOGF1("P: CServer::CheckLicense. bLicensed=%i.", m_bLicensed);

    return m_bLicensed;
  }

CheckLicense first calculates the length of the internally held license key string and assigns it to m_bLicenseLen for later use. It then calls APPUTIL's MakeFamilyPath function to construct the path name to the LICSERVE.LIC license file. It calls the CreateFile function to open this license file for reading. If successful, it calls the ReadFile function to read a series of bytes from the front of the file. The number of bytes read is equal to the length of the internally stored license key string (the length assigned above to m_cLicenseLen). This program logic assumes that a copy of the internally held license key is stored at the beginning of the LICSERVE.LIC file. The call to memcmp, a C standard library function, compares the internal license key with the one read from the .LIC file.

If the two license key strings match, m_bLicensed is set to TRUE. If they do not, m_bLicensed is FALSE. The m_bLicensed Boolean variable specifies if the server module has verified the presence of the machine license file and is used by licensing functionality in the server (for example, the class factories). We will see how this variable is used later in this code tour.

Class factories are where the core of the licensing mechanism resides. For those components we want to protect with a license in this server, we implement IClassFactory2 instead of IClassFactory in their class factory. In this server we are protecting only one component, COLicCruiseCar. Here is the class factory declaration for the class factory CFLicCruiseCar in FACTORY.H:

  class CFLicCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      CFLicCruiseCar(IUnknown* pUnkOuter, CServer* pServer);
      ~CFLicCruiseCar(void);

      // A general method for initializing a newly created CFLicCruiseCar.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // A private unconditional create for LicCruiseCar.
      STDMETHODIMP CreateLicCruiseCar(IUnknown*, REFIID, PPVOID);

      // We declare nested class interface implementations here.

      // We implement the IClassFactory2 interface on this LicCruiseCar
      // Class Factory to license these COM objects.
      class CImpIClassFactory : public IClassFactory2
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIClassFactory(
            CFLicCruiseCar* pBackObj,
            IUnknown* pUnkOuter,
            CServer* pServer);
          ~CImpIClassFactory(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IClassFactory methods.
          STDMETHODIMP         CreateInstance(IUnknown*, REFIID, PPVOID);
          STDMETHODIMP         LockServer(BOOL);

          // IClassFactory2 methods.
          STDMETHODIMP         GetLicInfo(LPLICINFO);
          STDMETHODIMP         RequestLicKey(DWORD, BSTR*);
          STDMETHODIMP         CreateInstanceLic(
                                 IUnknown*,
                                 IUnknown*,
                                 REFIID,
                                 BSTR,
                                 PPVOID);

        private:
          // Data private to this interface implementation of IClassFactory.
          ULONG            m_cRefI;       // Interface Ref Count (debug).
          CFLicCruiseCar*  m_pBackObj;    // Parent Object back pointer.
          IUnknown*        m_pUnkOuter;   // Outer unknown for Delegation.
          CServer*         m_pServer;     // Server Control Object.
      };

      // Make the otherwise private and nested IClassFactory interface
      // implementation a friend to COM object instantiations of this
      // selfsame CFLicCruiseCar COM object class.
      friend CImpIClassFactory;

      // Private data of CFLicCruiseCar COM objects.

      // Nested IClassFactory implementation instantiation.
      CImpIClassFactory m_ImpIClassFactory;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation). Used when this
      // CFLicCruiseCar object is being aggregated. Otherwise it is used
      // for delegation if this object is reused via containment.
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;
  };

A CreateLicCruiseCar method is provided as an internal unconditional create function. This method creates a new COLicCruiseCar object regardless of any licensing conditions. It is conditionally called from two different license-aware creation methods, CreateInstance and CreateInstanceLic.

The IClassFactory interface is implemented as the nested class CImpIClassFactory, a pattern that should be familiar by now. As noted above, the CImpIClassFactory implementation is derived from IClassFactory2. Thus it must implement three methods (GetLicInfo, RequestLicKey, and CreateInstanceLic) in addition to the usual IClassFactory methods CreateInstance and LockServer. The rest of this CFLicCruiseCar class factory declaration is essentially the same as that presented in the DLLSERVE code sample's CFCruiseCar class factory. The CFCruiseCar::QueryInterface does one thing worthy of comment.

  STDMETHODIMP CFLicCruiseCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("P: CFLicCruiseCar::QueryInterface. 'this' pIUnknown returned.");
    }
    else if (IID_IClassFactory == riid)
    {
      *ppv = &m_ImpIClassFactory;
      LOG("P: CFLicCruiseCar::QueryInterface. pIClassFactory returned.");
    }
    else if (IID_IClassFactory2 == riid)
    {
      *ppv = &m_ImpIClassFactory;
      LOG("P: CFLicCruiseCar::QueryInterface. pIClassFactory2 returned.");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to the interface so obey the COM rules
      // and AddRef the reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }

    return (hr);
  }

Since IClassFactory2 is derived from abstract base class IClassFactory, if we implement IClassFactory2 we must also implement IClassFactory methods. If QueryInterface requests either the IClassFactory or IClassFactory2 interface, we can safely return the same pointer, the one to the IClassFactory2 interface implementation.

We next turn to the implementations of the licensing-aware class factory methods. Here is the IClassFactory CreateInstance method in FACTORY.CPP.

  STDMETHODIMP CFLicCruiseCar::CImpIClassFactory::CreateInstance(
                 IUnknown* pUnkOuter,
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = CLASS_E_NOTLICENSED;

    if (g_pServer->m_bLicensed)
      hr = m_pBackObj->CreateLicCruiseCar(pUnkOuter, riid, ppv);
    else
    {
      LOG("P: CImpIClassFactory::CreateInstance. No Machine License.");
      g_pServer->m_pMsgBox->ErrorID(IDS_NOLICENSE);
    }

    return hr;
  }

The unconditional CreateLicCruiseCar function is called only if CheckLicense previously set m_bLicensed to TRUE when the server was first initialized for a given process. If the machine license was not verified, CheckLicense displays an error message indicating to the user that the client application is attempting to run with a component that is not licensed for use on this machine.

GetLicInfo is one of the methods required by IClassFactory2:

  STDMETHODIMP CFLicCruiseCar::CImpIClassFactory::GetLicInfo(
                 LPLICINFO pLicInfo)
  {
    HRESULT hr = NOERROR;

    LOG("P: CFLicCruiseCar::CImpIClassFactory::GetLicInfo.");

    if (NULL != pLicInfo)
    {
      pLicInfo->cbLicInfo = sizeof(LICINFO);

      // Inform whether RequestLicKey will work.
      pLicInfo->fRuntimeKeyAvail = g_pServer->m_bLicensed;

      // Inform whether the standard CreateInstance will work.
      pLicInfo->fLicVerified = g_pServer->m_bLicensed;
    }
    else
      hr = E_POINTER;

    return hr;
  }

This method writes appropriate values to the LICINFO structure whose address is passed in the pLicInfo parameter. The two important values in this structure are fRuntimeKeyAvail and fLicVerified. The fRuntimeKeyAvail member is set to the value that was returned by CheckLicense. This flag indicates in advance whether the RequestLicKey method can be attempted. If the server found no machine license, it will not (and should not) provide the internal license key to any caller of the RequestLicKey method. The fLicVerified member indicates whether the server verified that there is at least a machine license for the server.

Here is the IClassFactory2 RequestLicKey method (again in file FACTORY.CPP).

  STDMETHODIMP CFLicCruiseCar::CImpIClassFactory::RequestLicKey(
                 DWORD dwReserved,
                 BSTR* pbstrKey)
  {
    HRESULT hr = CLASS_E_NOTLICENSED;
    OLECHAR szLicKey[MAX_LICENSEKEY];

    LOG("P: CFLicCruiseCar::CImpIClassFactory::RequestLicKey.");

    // Only return the license key if the server machine license was
    // verified.
    if (g_pServer->m_bLicensed)
    {
      // Convert our ANSI license string to Wide Char and then alloc as
      // a BSTR.
      mbstowcs(szLicKey, g_pServer->m_szLicenseKey,
      g_pServer->m_cLicenseLen); *pbstrKey = SysAllocString(szLicKey);
      hr = (NULL != *pbstrKey) ? NOERROR : E_OUTOFMEMORY;
    }

    return hr;
  }

RequestLicKey returns the license key in the form of a BSTR. In 32-bit COM, these basic strings are like zero-terminated Unicode strings, except that they are preceded by a DWORD value indicating the length of the string. BSTR variables are used heavily in OLE Automation and have come to us from Microsoft Visual Basic®. COM/OLE provides a set of API functions to manipulate these BSTRs, such as the SysAllocString call above. There is also a matching SysFreeString function that must be called to free these allocated BSTRs.

The RequestLicKey method is used to request a run-time license key from the server. The idea is that during development, the developer (or a development tool) can request from the server a copy of its internal license key. This request can succeed only if there is already a machine license with the server. Once obtained, the license key copy can be stored persistently for use by a client application that uses and ships with the server. Later, the client application can use this persistently stored license key to create a licensed component by calling the CreateInstanceLic method, which accepts the key as an argument.

The client application can succeed at this even if the machine license file does not accompany the server. In this situation, the normal call to IClassFactory::CreateInstance would fail, but a call to IClassFactory2::CreateInstanceLic would succeed.

Here is the IClassFactory2 CreateInstanceLic method in FACTORY.CPP.

  STDMETHODIMP CFLicCruiseCar::CImpIClassFactory::CreateInstanceLic(
                 IUnknown* pUnkOuter,
                 IUnknown* pUnkReserved,
                 REFIID riid,
                 BSTR bstrKey,
                 PPVOID ppvCob)
  {
    HRESULT hr;
    BOOL    bMatch = FALSE;
    BSTR    bstrTemp;
    UINT    cch = g_pServer->m_cLicenseLen;
    OLECHAR szLicKey[MAX_LICENSEKEY];

    *ppvCob = NULL;

    // Convert our ANSI license string to Wide Char and then alloc as BSTR.
    mbstowcs(szLicKey, g_pServer->m_szLicenseKey, cch);
    bstrTemp = SysAllocString(szLicKey);
    if (NULL != bstrTemp)
    {
      if (NULL != bstrKey)
        bMatch = (0 == memcmp(bstrTemp, bstrKey, cch * sizeof(OLECHAR)));

      SysFreeString(bstrTemp);

      if (bMatch)
        hr = m_pBackObj->CreateLicCruiseCar(pUnkOuter, riid, ppvCob);
      else
      {
        LOG("P: CImpIClassFactory::CreateInstanceLic. No Runtime License.");
        g_pServer->m_pMsgBox->ErrorID(IDS_NORUNLICENSE);
        hr = CLASS_E_NOTLICENSED;
      }
    }
    else
      hr = E_OUTOFMEMORY;

    return hr;
  }

The license key is passed as a BSTR, so we convert the internal license key string to a BSTR and then compare the two. If there is a match, we have a run-time license and can create an instance of the COLicCruiseCar and pass back the requested interface on it. If there is not a match, an error message is displayed to inform the user that the component is not licensed for run-time use.

What we've seen so far is that when the server DLL is attached to a process, it checks for the presence of the machine .LIC license file and tries to match its internal key to the key in the license file. If a match is found, the CFLicCruiseCar class factory will permit a normal call to IClassFactory::CreateInstance. Otherwise, it won't. In this tutorial sample, if the machine license is found, the client can also call IClassFactory2::RequestLicKey to obtain a copy of the internal license key. It can store this run-time license key to later create an instance by calling the IClassFactory2::CreateInstanceLic method. In actual applications the client would not call RequestLicKey. This call would be used during development only to obtain the run-time license key for the component in the server.

Run-time licensing is intended for component servers that are purchased in binary form by a developer and resold with the developer's client application. During development, the developer uses some tool that acts as a temporary client of the licensed component and calls IClassFactory2::RequestLicKey to obtain the run-time license key. This call would succeed because a machine license for the component would be present as part of the development kit purchased by the developer for the component. The developer would then use the run-time license key value in compiling the retail client application.

In a typical licensing scheme, the run-time key value is compiled into the client executable and used at run-time in calls to IClassFactory2::CreatInstanceLic. Then, when the retail client application is later sold by the developer in a package with the accompanying server component, the machine license file is not included in the package. In this case, only the client with the proper built-in, run-time license key will work with the resold component. This protects the component vendor from unauthorized copies of the component working with any client application that can call the component's IClassFactory::CreateInstance. Since the machine license file is absent, IClassFactory::CreateInstance will fail, returning error CLASS_E_NOTLICENSED.

Before leaving the class factories in FACTORY.CPP, we should note that an implementation of a CFLicCarSample factory is provided in a way very similar to what was studied in the DLLSERVE lesson. As before, this CarSample utility component is used to display trace logging of this server's behavior in the client's logging display and to provide an About dialog box from the server.

Top Back to page top

© 1995-1998 Microsoft Corporation


LICCLIEN - Client App of Licensed Server - Lesson 11

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

LICCLIEN - Client Application of Licensed Server

SUMMARY

The LICCLIEN sample examines how a client application uses licensed components provided by a COM server. LICCLIEN works with the COM servers of two previous samples, LICSERVE and DLLSERVE. It manipulates a set of components similar to those presented in DLLCLIEN: COCar, COUtilityCar, COLicCruiseCar, and COUtilityCruiseCar. In this lesson, the COCar and COUtilityCar components are obtained, as they were in DLLCLIEN, from the DLLSERVE server. This sample, however, uses a new licensed version of the COCruiseCar component that was presented in DLLSERVE and DLLCLIEN. This licensed component, COLicCruiseCar, is housed in a different server, LICSERVE.

Like DLLCLIEN, LICCLIEN.EXE creates its own COUtilityCruiseCar COM object, which is constructed by reusing the licensed COLicCruiseCar COM object by aggregation and augmenting it with a native IUtility interface. Because the COLicCruiseCar COM object class is a licensed aggregatable component, LICCLIEN illustrates nested aggregation involving a licensed component.

For functional descriptions and a tutorial code tour of LICCLIEN, see the Code Tour section in LICCLIEN.HTM. For details on the external user operation of LICCLIEN, see both the Usage and Operation sections in LICCLIEN.HTM. To read LICCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the LICCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the LICCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also LICSERVE.HTM in the main tutorial directory for more details on how LICSERVE works and exposes its services to LICCLIEN. You must build the LICSERVE DLL before building LICCLIEN. The makefile for LICSERVE automatically registers that server in the system registry, so you must build LICSERVE before attempting to run LICCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the LICCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV LICCLIEN.DSP

You can also simply double-click the LICCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

LICCLIEN.EXE is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by LICCLIEN.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, LICCLIEN.EXE is the client executable to run for this sample.

OPERATION

The LICCLIEN.EXE application provides the user interface for this lesson. It exercises the components in the associated, but independent, LICSERVE.DLL and DLLSERVE.DLL servers. Here is a summary of operation from the standpoint of LICCLIEN.EXE as a COM client of the licensed component COLicCruiseCar in the LICSERVE.DLL COM server.

The COM objects that are used in the LICCLIEN and LICSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

The COCar and COUtilityCar components are constructed and housed in servers in the manner presented in the DLLCLIEN and DLLSERVE lessons. A COLicCruiseCar component is constructed using aggregation and is implemented as a licensed component in LICSERVE.DLL. COUtilityCruiseCar is constructed using aggregation and is implemented in LICCLIEN.EXE, which is built in this lesson. COUtilityCruiseCar reuses LicCruiseCar by aggregation to illustrate nested aggregation of a licensed component.

The menu system of LICCLIEN provides functionality similar to that of DLLCLIEN. The Car and UtilityCar menu operations are the same as those presented in DLLCLIEN and will not be detailed here. We will tour the internal operation of the LicCruiseCar and the UtilityCruiseCar menus. To simulate behavior when a component is unlicensed, we will also examine some changes made to the Log menu.

LICCLIEN.EXE provides menus for creating, releasing, and invoking methods for four components: COCar, COUtilityCar, COLicCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COLicCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COLicCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

LICCLIEN.EXE presents a menu for each of these four main components. Each menu has commands that call the methods of the various available interfaces. The code samples (both LICCLIEN and LICSERVE) have trace message log statements throughout. When you exercise the objects from LICCLIEN.EXE, the main LICCLIEN window will display a log of internal activity in these components and their servers.

Menu Selection: File/Exit
Exits LICCLIEN.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: LicCruiseCar/Create
Creates the COLicCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: LicCruiseCar/Release
Releases the COLicCruiseCar COM object.

Menu Selection: LicCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COLicCruiseCar object.

Menu Selection: LicCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COLicCruiseCar object.

Menu Selection: LicCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COLicCruiseCar object.

Menu Selection: LicCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COLicCruiseCar object.

Menu Selection: LicCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COLicCruiseCar object.

Menu Selection: LicCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COLicCruiseCar object.

Menu Selection: UtilityCruiseCar/Get License Key
Calls the IClassFactory2::RequestLicKey method to obtain the internal license key string for the COLicCruiseCar component and stores the key obtained as a run-time license key for later use by the client. A checkmark beside the menu item indicates that the key has already been obtained.

Menu Selection: UtilityCruiseCar/Clear License Key
Clears the run-time license key string obtained for the COLicCruiseCar component.

Menu Selection: UtilityCruiseCar/Create with Key
Creates the COUtilityCruiseCar COM object using the run-time license key obtained previously for the aggregated LicCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Lic Server
Engages or disengages trace logging from the LICSERVE server. A checkmark beside the menu item indicates that logging from the LICSERVE server is engaged. If logging is active, the server is loaded, because the COLicCarSample component must be created to support logging.

Menu Selection: Loc/Dll Server
Engages or disengages trace logging from the DLLSERVE server. A checkmark beside the menu item indicates that logging from the DLLSERVE server is engaged. If logging is active, the server is loaded, because the DllCarSample component must be created to support logging.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/LICCLIEN Tutorial
Opens the LICCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/LICSERVE Tutorial
Opens the LICSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/DLLSERVE Tutorial
Opens the DLLSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About LICCLIEN
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

Menu Selection: Help/About LICSERVE
Displays the About dialog box for LICSERVE.DLL, which is used by this application. In this series of code samples, partner DLLs like LICSERVE are given their own About dialog box in the native resources of the DLL. This menu item calls the DLL function that displays this dialog box.

Menu Selection: Help/About DLLSERVE
Displays the About dialog box for DLLSERVE.DLL, which is used by this application. In this series of code samples, partner DLLs like DLLSERVE are given their own About dialog box in the native resources of the DLL. This menu item calls the DLL function that displays this dialog box.

CODE TOUR

Files          Description
LICCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample application
               of this tutorial lesson.
LICCLIEN.H     The include file for the LICCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
LICCLIEN.CPP   The main implementation file for LICCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
LICCLIEN.RC    The application resource definition file.
LICCLIEN.ICO   The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
LICCLIEN.DSP   Microsoft Visual Studio Project file.

This code sample is based on the code in DLLCLIEN, which is based on the code in COMUSER, which is based on the code in DLLUSER, which in turn is based on the code in EXESKEL. See the code tours in those samples for more details on using the application skeleton and the DLLs.

LICCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

LICCLIEN illustrates what must be done in the client application to use a licensed component from a COM server. We will use LICCLIEN to demonstrate the use of licensed and unlicensed components, and we will examine trace logs of internal activity in various situations.

We start in LICCLIEN.CPP. The following files are included, one of which is new for this code sample.

  #include <windows.h>
  #include <ole2.h>
  #include <initguid.h>
  #include <olectl.h>
  #include <commdlg.h>
  #include <apputil.h>
  #include <icars.h>
  #include <carguids.h>
  #include "licclien.h"
  #include "utcrucar.h"

Because this module is going to implement IClassFactory2, we must make appropriate declarations by including OLECTL.H. This file should be included after OLE2.H and INITGUID.H. OLECTL.H was not part of early releases of the Platform SDK, but instead was part of the Control Development Kit (CDK). In recent releases, OLECTL.H is part of the Platform SDK.

As in the previous DLLCLIEN code sample, we include ICARS.H, located in the sibling \INC directory, to get the car-related interface abstract base class declarations. We include CARGUIDS.H, also located in the sibling \INC directory, for the associated GUID definitions and the CLSIDs for the car-related component types (CLSID_DllCar, CLSID_DllUtilityCar, CLSID_LicCruiseCar, CLSID_LicCarSample, and CLSID_DllCarSample).

The LICCLIEN application's InitInstance method has some interesting points.

  BOOL CMainWindow::InitInstance(
         HINSTANCE hInstance,
         int nCmdShow)
  {
    BOOL bOk = FALSE;
    HWND hWnd;

    // Create the Message Box and Message Log objects.
    m_pMsgBox = new CMsgBox;
    m_pMsgLog = new CMsgLog;

    if (NULL != m_pMsgBox && NULL != m_pMsgLog)
    {
      // Note, the Create method sets the m_hWnd member so we don't
      // need to set it explicitly here first.

      // Here is the create of this window. Size the window reasonably.
      // Create sets both m_hInst and m_hWnd.
      hWnd = Create(
               TEXT(MAIN_WINDOW_CLASS_NAME_STR),
               TEXT(MAIN_WINDOW_TITLE_STR),
               WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
                 | WS_MAXIMIZEBOX | WS_THICKFRAME,
               CW_USEDEFAULT,
               CW_USEDEFAULT,
               ::GetSystemMetrics(SM_CXSCREEN)*3/5,
               ::GetSystemMetrics(SM_CYSCREEN)*3/5,
               NULL,
               NULL,
               hInstance);
      if (hWnd)
      {
        // Ensure the new window is shown on screen and its content is
        // painted.
        ::ShowWindow(m_hWnd, nCmdShow);
        ::UpdateWindow(m_hWnd);

        // Build a path to where the help file should be (it should be in
        // the same directory as the .EXE but with the .HLP extension.
        MakeFamilyPath(hInstance, m_szHelpFile, TEXT(HELP_FILE_EXT));

        // Init the Message Box object.
        if (m_pMsgBox->Init(m_hInst, m_hWnd))
        {
          // Create the Trace Message Log ListBox as a child window that
          //   fits the client area of the Main Window (the TRUE 3rd
          //   argument specifies such an inside child).
          // If you want the Trace Message Log in a separate (but owned)
          //   window, then pass a FALSE instead for the 3rd argument.
          if (m_pMsgLog->Create(m_hInst, m_hWnd, TRUE))
          {
            HRESULT hr;

            // Assign the global MsgLog pointer.
            g_pMsgLog = m_pMsgLog;
            // Use macro to log an initial start messsage.
            LOGID(IDS_START_MESSAGE_LOG);

            LOG("C: Start LICSERVE Server Trace Logging.");
            // Now ask COM for the ISample interface to the server's
            // LicCarSample component.  This effectively loads the
            // LICSERVE DLL.
            hr = CoCreateInstance(
                   CLSID_LicCarSample,
                   NULL,
                   CLSCTX_INPROC_SERVER,
                   IID_ISample,
                   (PPVOID)&m_pLicCarSample);
            if (SUCCEEDED(hr))
            {
              hr = m_pLicCarSample->Init(m_hWnd, g_pMsgLog);
              if (SUCCEEDED(hr))
              {
                HMENU hMenu  = ::GetMenu(m_hWnd);

                ::CheckMenuItem(
                    hMenu,
                    IDM_LOG_LICSERVER,
                    MF_BYCOMMAND | MF_CHECKED);

                LOG("C: Start DLLSERVE Server Trace Logging.");
                // Also set up logging from the subordinate DLLSERVE server
                // to the client as well.
                hr = CoCreateInstance(
                       CLSID_DllCarSample,
                       NULL,
                       CLSCTX_INPROC_SERVER,
                       IID_ISample,
                       (PPVOID)&m_pDllCarSample);
                if (SUCCEEDED(hr))
                {
                  hr = m_pDllCarSample->Init(m_hWnd, g_pMsgLog);
                  bOk = SUCCEEDED(hr);
                  if (bOk)
                  {
                    ::CheckMenuItem(
                        hMenu,
                        IDM_LOG_DLLSERVER,
                        MF_BYCOMMAND | MF_CHECKED);
                  }
                  else
                  {
                     RELEASE_INTERFACE(m_pDllCarSample);
                     // We ask COM to unload any unused COM Servers.
                     CoFreeUnusedLibraries();
                  }
                }
                else
                  m_pMsgBox->ErrorID(IDS_NODLLSERVER);
              }
              else
              {
                RELEASE_INTERFACE(m_pLicCarSample);
                // We ask COM to unload any unused COM Servers.
                CoFreeUnusedLibraries();
              }
            }
            else
              m_pMsgBox->ErrorID(IDS_NOLICSERVER);
          }
        }
      }
    }

    if (!bOk)
    {
      DELETE_POINTER(m_pMsgBox);
      DELETE_POINTER(m_pMsgLog);
    }

    return (bOk);
  }

After the CMainWindow is created and the MsgBox facility is initialized, trace logging is set up for two servers using their respective CarSample components, LicCarSample and DllCarSample. We store pointers to the ISample interfaces on these components in the corresponding CMainWindow member variables, m_pLicCarSample and m_pDllCarSample. The ISample::Init methods are then called to set up logging from those servers to this client's logging display. The g_pMsgLog value is passed in the calls to Init. By creating these CarSample components as COM object instances, the LICSERVE and DLLSERVE servers are loaded early during execution of the InitInstance method. This is similar to the behavior of DLLCLIEN; however, in this lesson we'll have reason to force these servers to be unloaded. More on this later, when we look at the new selections in the Log menu.

The CMainWindow::DoMenu method in LICCLIEN.CPP contains the code for the Car and UtilityCar menus. This code is identical to the corresponding menu code in DLLCLIEN and in fact operates identically, because it exercises components in DLLSERVE. The LicCruiseCar menu code takes the same form as the code for the CruiseCar menu in DLLCLIEN, but when CoCreateInstance is called, the class factory for the LicCruiseCar component in the LICSERVE server is invoked.

  ...
  ...
  // Call COM service to create an instance.
  hr = CoCreateInstance(
         CLSID_LicCruiseCar,
         NULL,
         CLSCTX_INPROC_SERVER,
         IID_IUnknown,
         (PPVOID)&m_pLicCruiseCar);
  if (SUCCEEDED(hr))
  {
    ::CheckMenuItem(
        hMenu,
        IDM_CCAR_CREATE,
        MF_BYCOMMAND | MF_CHECKED);
  }
  else
  {
    LOG("C: ???? LicCruiseCar creation failed.");
    if (CLASS_E_NOTLICENSED == hr)
      m_pMsgBox->ErrorID(IDS_NOLICENSE);
  }
  ...
  ...

Though this code looks the same in LICCLIEN as it did in DLLCLIEN, the server code that it executes is different. The LicCruiseCar class factory in LICSERVE implements IClassFactory2, not IClassFactory. The CoCreateInstance function will therefore call IClassFactory2::CreateInstance to check for a valid machine license. If the LICSERVE server did not detect a valid machine license on initialization, the CoCreateInstance call in LICCLIEN would fail, returning CLASS_E_NOTLICENSED, and an error message would be displayed to indicate that the requested component is not licensed.

The UtilityCruiseCar menu has several new menu items: Get License Key, Clear License Key, and Create with Key. The license key being used here controls creation of the COLicCruiseCar component and not the COUtilityCruiseCar object itself. The COUtilityCruiseCar COM object is created in this client as an object that aggregates the COLicCruiseCar component provided by the LICSERVE server. We will look at each of these menu items in turn. Here is Get License Key:

  case IDM_UCRU_GETLICKEY:
    LOG("C: === UtilityCruiseCar Menu: Get License Key.");
    // Get a class factory for LicCruiseCar and issue IClassFactory's
    // CreateInstance method to manufacture a COLicCruiseCar COM object.
    hr = CoGetClassObject(
           CLSID_LicCruiseCar,
           CLSCTX_INPROC_SERVER,
           NULL,
           IID_IClassFactory2,
           (PPVOID)&pICF2LicCruiseCar);
    if (SUCCEEDED(hr))
    {
      LOG("C: LicCruiseCar's IClassFactory2 obtained.");
      hr = pICF2LicCruiseCar->RequestLicKey(0, &m_bstrLicKey);
      pICF2LicCruiseCar->Release();
    }

    if (SUCCEEDED(hr))
    {
      LOG("C: Obtained LicCruiseCar's Runtime License Key.");
      ::CheckMenuItem(
          hMenu,
          IDM_UCRU_GETLICKEY,
          MF_BYCOMMAND | MF_CHECKED);
    }
    else
      LOG("C: Failed to Obtain LicCruiseCar's Runtime License Key.");
    break;

The CoGetClassObject COM API function obtains the IClassFactory2 interface on the class factory for the COLicCruiseCar component. This class factory resides in the LICSERVE server. Upon obtaining the interface, we call the IClassFactory2::RequestLicKey method to obtain the license key. We cache this license key string for later use by the IClassFactory2::CreateInstanceLic method.

The Clear License Key on the UtilityCruiseCar menu simply frees the string that was obtained above.

  case IDM_UCRU_CLEARLICKEY:
    LOG("C: === UtilityCruiseCar Menu: Clear License Key.");
    SysFreeString(m_bstrLicKey);
    m_bstrLicKey = NULL;
    ::CheckMenuItem(
        hMenu,
        IDM_UCRU_GETLICKEY,
        MF_BYCOMMAND | MF_UNCHECKED);
    break;

The Create with Key menu item passes the license key string to the CreateUtilityCruiseCar function. If creation fails because the string does not match the internal license key string (the function returns CLASS_E_NOTLICENSED), an error message is displayed, indicating that an attempt was made to use a component (in this case the LicCruiseCar component) that does not have a run-time license.

  case IDM_UCRU_CREATE:
    LOG("C: === UtilityCruiseCar Menu: Create with Key.");
    if (NULL == m_pUtilityCruiseCar)
    {
      // Call a create function to create an instance.
      hr = CreateUtilityCruiseCar(
             NULL,
             IID_IUnknown,
             m_bstrLicKey,
             (PPVOID)&m_pUtilityCruiseCar);
      if (SUCCEEDED(hr))
      {
        ::CheckMenuItem(
            hMenu,
            IDM_UCRU_CREATE,
            MF_BYCOMMAND | MF_CHECKED);
      }
      else
      {
        LOG("C: ???? UtilityCruiseCar creation failed.");
        if (CLASS_E_NOTLICENSED == hr)
          m_pMsgBox->ErrorID(IDS_NORUNLICENSE);
      }
    }
    else
      LOG("C: ???? UtilityCruiseCar already exists.");
    break;

Here is the CreateUtilityCruiseCar function from UTCRUCAR.CPP.

  HRESULT CreateUtilityCruiseCar(
            IUnknown* pUnkOuter,
            REFIID riid,
            BSTR bstrLicKey,
            PPVOID ppv)
  {
    HRESULT hr;
    COUtilityCruiseCar* pCob;

    LOGF1("C: CreateUtilityCruiseCar. pUnkOuter=0x%X.",pUnkOuter);

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state the IUnknown interface MUST also be concomitantly
    // requested. If it is not so requested ( riid != IID_IUnknown), then
    // an error must be returned indicating that no aggregate creation of
    // the COUtilityCruiseCar COM Object can be performed using anything
    // other than a controlling IUnknown interface.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COUtilityCruiseCar COM Object.
      pCob = new COUtilityCruiseCar(pUnkOuter);
      if (NULL != pCob)
      {
        // If we have succeeded in instantiating the COUtilityCruiseCar
        // object, we initialize it to offer its interfaces.
        hr = pCob->Init(bstrLicKey);
        if (SUCCEEDED(hr))
        {
          // We QueryInterface this new COM Object not only to deposit the
          // main interface pointer to the caller's pointer variable, but to
          // also automatically bump the Reference Count on the new COM
          // Object after handing out this *ppv reference to it.
          hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
    {
      LOGF1("C: CreateUtilityCruiseCar Succeeded. *ppv=0x%X.",*ppv);
    }
    else
    {
      LOG("C: CreateUtilityCruiseCar Failed.");
    }

    return hr;
  }

This function accepts the run-time license key string (bstrLicKey) and passes it to the Init method of the newly created COUtilityCruiseCar COM object. The Init method is usually called when inner COM objects are reused to create the COM object. Here is the COUtilityCruiseCar::Init method from UTCRUCAR.CPP:

  HRESULT COUtilityCruiseCar::Init(
            BSTR bstrLicKey)
  {
    HRESULT hr;
    IClassFactory2* pICF2LicCruiseCar;

    // Set up the right pIUnknown for delegation. If we are being
    // aggregated, then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    LOG("C: COUtilityCruiseCar::Init.");

    // Get a class factory for LicCruiseCar and issue IClassFactory's
    // CreateInstance method to manufacture a COLicCruiseCar COM object.
    hr = CoGetClassObject(
           CLSID_LicCruiseCar,
           CLSCTX_INPROC_SERVER,
           NULL,
           IID_IClassFactory2,
           (PPVOID)&pICF2LicCruiseCar);
    if (SUCCEEDED(hr))
    {
      LOG("C: COUtilityCruiseCar::Init IClassFactory2 obtained.");
      hr = pICF2LicCruiseCar->CreateInstanceLic(
                                pUnkOuter,
                                NULL,
                                IID_IUnknown,
                                bstrLicKey,
                                (PPVOID)&m_pUnkCruiseCar);
      pICF2LicCruiseCar->Release();

      if (SUCCEEDED(hr))
      {
        LOG("C: COUtilityCruiseCar::Init Created using License Key.");
      }
      else
      {
        LOG("C: COUtilityCruiseCar::Init Unable to Create LicCruiseCar.");
      }
    }

    return (hr);
  }

The COUtilityCruiseCar object is made by creating and aggregating a LicCruiseCar component. Why aggregation? Because the CreateUtilityCruiseCar function that was called in the code for the Create with Key command passed a NULL for the first pUnkOuter argument. That NULL pointer was eventually passed to this Init method, where the logic assigns to pUnkOuter the 'this' pointer of the COUtilityCruiseCar object itself. This pUnkOuter is then passed to the CreateInstanceLic method. The CoGetClassObject COM API function is called to obtain the IClassFactory2 interface of the class factory for CLSID_LicCruiseCar. The CreateInstanceLic method of this interface is then called to create the aggregated COLicCruiseCar COM object. At this point, we don't really need to know that the LICSERVE server provides this COLicCruiseCar object.

We are now ready to tour some trace logs of internal behavior. This lesson demonstrates two kinds of licensing behavior. The first creates a machine-licensed component by calling the IClassFactory::CreateInstance method. We saw in the code above that the class factory for COLicCruiseCar implements IClassFactory2, whose CreateInstance method can create a component only if the server has found a valid machine license.

The second acquires a run-time license and later uses it to create a COUtilityCruiseCar component. We'll look at both licensing scenarios internally in the following trace logs.

Here is the trace for the Create command on the LicCruiseCar menu:

  C: === LicCruiseCar Menu: Create.
  P: DllGetClassObject: Requesting CFLicCruiseCar.
  P: CFLicCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  P: CFLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=2.
  P: CFLicCruiseCar::QueryInterface. pIClassFactory returned.
  P: CFLicCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  P: CFLicCruiseCar::AddRef. New cRefs=1.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  P: CFLicCruiseCar::CreatLicCruiseCar. pUnkOuter=0x0.
  P: COLicCruiseCar::CImpICruise Constructor. Non-Aggregating.
  P: COLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=3.
  P: COLicCruiseCar::Init.
  S: DllGetClassObject: Requesting CFCar.
  S: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=2.
  S: CFCar::QueryInterface. pIClassFactory returned.
  S: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x66091C.
  S: COCar::CImpICar Constructor. Aggregating.
  S: COCar Constructor. m_pUnkOuter=0x66091C.
  S: CServer::ObjectsUp. New cObjects=3.
  S: COCar::QueryInterface. 'this' pIUnknown returned.
  S: COCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x66096C.
  S: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=2.
  S: CFCar::Destructor.
  S: CFCar::CImpIClassFactory Destructor.
  P: COLicCruiseCar::Init (New Aggregation of COCar) Succeeded.
  P: COLicCruiseCar::QueryInterface. 'this' pIUnknown returned.
  P: COLicCruiseCar::AddRef. New cRefs=1.
  P: CFLicCruiseCar::CreateLicCruiseCar Succeeded. *ppv=0x66091C.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance Success. *ppv=0x66091C.
  P: CFLicCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  P: CFLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=2.
  P: CFLicCruiseCar::Destructor.
  P: CFLicCruiseCar::CImpIClassFactory Destructor.

First, notice the new 'P:' indicator. Because we're using nested aggregated components from two different servers, we use a different trace log code for each server: 'S:' for behavior inside DLLSERVE and 'P:' for behavior inside the protected LICSERVE. As in previous trace logs, we retain the 'C:' for behavior in the Client.

We'll break this trace sequence into pieces for easier reading:

  C: === LicCruiseCar Menu: Create.
  P: DllGetClassObject: Requesting CFLicCruiseCar.
  P: CFLicCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  P: CFLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=2.
  P: CFLicCruiseCar::QueryInterface. pIClassFactory returned.
  P: CFLicCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  P: CFLicCruiseCar::AddRef. New cRefs=1.

When the client calls CoGetClassObject, the COM implementation locates the server for the class factory and asks the server (using the server's DllGetClassObject function) for the IClassFactory interface to the class factory. In this case, we see the LICSERVE log event indicating that the CFLicCruiseCar factory is requested. This means that LICSERVE is loaded. The constructors of both the new CFLicCruiseCar COM object and its nested implementation of the IClassFactory interface are executed. The CServe server control object for LICSERVE increments its object count to 2. This counts the LicCarSample object that supports logging and the new CFLicCruiseCar COM object. As the CFLicCruiseCar COM object is created, we see the QueryInterface call on the requested output pointer (pIClassFactory) and the resultant call to AddRef on that pointer.

  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  P: CFLicCruiseCar::CreateLicCruiseCar. pUnkOuter=0x0.
  P: COLicCruiseCar::CImpICruise Constructor. Non-Aggregating.
  P: COLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=3.

Next, COM calls CreateInstance on the IClassFactory interface. This results in an unconditional call to CreateLicCruiseCar. We saw in the LICSERVE lesson that the class factory for COLicCruiseCar took licensing into account, and calls to its CreateInstance method would succeed only if the machine license was verified. In this example, creation is successful, because a valid machine license was found when LICSERVE was initially loaded.

The CreateLicCruiseCar method proceeds, and the constructors for the COLicCruiseCar COM object are executed. The LICSERVE server increments its object count to 3.

  P: COLicCruiseCar::Init.
  S: DllGetClassObject: Requesting CFCar.
  S: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=2.
  S: CFCar::QueryInterface. pIClassFactory returned.
  S: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCar::AddRef. New cRefs=1.

Because COLicCruiseCar aggregates a COCar object, the COLicCruiseCar::Init method must call CoCreateInstance to create this inner component. The server managing COCar components is DLLSERVE, so 'S:' appears at the beginning of these log entries. DllGetClassObject is called, and the CFCar class factory COM object is created. For this server, the object count is only 2: counting the DllCarSample object and the new class factory object. QueryInterface and AddRef are called on the returned IClassFactory interface.

  S: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x66091C.
  S: COCar::CImpICar Constructor. Aggregating.
  S: COCar Constructor. m_pUnkOuter=0x66091C.
  S: CServer::ObjectsUp. New cObjects=3.
  S: COCar::QueryInterface. 'this' pIUnknown returned.
  S: COCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x66096C.
  S: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=2.
  S: CFCar::Destructor.
  S: CFCar::CImpIClassFactory Destructor.

COM uses the obtained IClassFactory interface to call the CreateInstance method. The COCar COM object is created in DLLSERVE: the constructors are executed, the server's object count is incremented to 3, and QueryInterface and AddRef are called on the new COM object. When CreateInstance returns, COM releases the IClassFactory interface. The reference count of CFCar is decremented to 0, the server's object count is decremented to 2, and the CFCar object's destructors are executed as the class factory is destroyed. The final DLLSERVE object count is 2, one for the new COCar object and one for the DllCarSample utility object.

  P: COLicCruiseCar::Init (New Aggregation of COCar) Succeeded.
  P: COLicCruiseCar::QueryInterface. 'this' pIUnknown returned.
  P: COLicCruiseCar::AddRef. New cRefs=1.
  P: CFLicCruiseCar::CreateLicCruiseCar Succeeded. *ppv=0x66091C.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance Success. *ppv=0x66091C.
  P: CFLicCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  P: CFLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=2.
  P: CFLicCruiseCar::Destructor.
  P: CFLicCruiseCar::CImpIClassFactory Destructor.

Execution returns to the COLicCruiseCar::Init method, and the new COCar object is successfully aggregated. Creation of the COLicCruiseCar object continues with calls to QueryInterface and AddRef on the object's IUnknown. The COLicCruiseCar has been successfully created. The IClassFactory interface on CFLicCruiseCar is released, decrementing its reference count to 0. As a result, the CFLicCruiseCar class factory is destroyed, and the object count of the LICSERVE server is decremented to 2: one for the new COLicCruiseCar object and one for the COLicCarSample utility object.

At this point, the LICCLIEN client has successfully requested that a COLicCruiseCar object be created, and it has obtained a pointer to that object's IUnknown interface.

The matching release of this IUnknown interface is straightforward. Here's the log after the user chooses the Release command from LICCLIEN's LicCruiseCar menu:

  C: === LicCruiseCar Menu: Release.
  P: COLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=1.
  P: COLicCruiseCar::Destructor.
  S: COCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=1.
  S: COCar::Destructor.
  S: COCar::CImpICar Destructor.
  P: COLicCruiseCar::CImpICruise Destructor.
  S: DllCanUnloadNow. cObjects=1, cLocks=0.
  P: DllCanUnloadNow. cObjects=1, cLocks=0.

In LICSERVE, the COLicCruiseCar object's IUnknown reference is decremented to 0, causing the object to be deleted. The server decrements its object count to 1. The destructor of COLicCruiseCar is executed, and the IUnknown of the COCar object is released, decrementing its reference count to 0. The object count of the DLLSERVE server is now decremented to 1, and the destructors for the COCar COM object are executed. Control returns to the LICSERVE server, and the destructor of the COLicCruiseCar object is called. After all this, CoFreeUnusedLibraries is called, and this function calls each server's DllCanUnloadNow function. The servers won't be unloaded, because each has an object count of 1: for the CarSample utility component. The next topic of internal study is run-time licensing. To simulate run-time licensing, menu items in both the UtilityCruiseCar menu and the Log menu of LICCLIEN will be used. The simulation sequence is as follows:

  1. Start LICCLIEN.EXE.
  2. From the UtilityCruiseCar menu, choose Get License Key. This causes the client to acquire the server's license key and to save this value (simulating a persistent saving of it).
  3. In the Command Prompt window, rename the LICSERVE server's machine license file. For example, rename LICSERVE.LIC to L.LIC. This action effectively hides the license file.
  4. From the Log menu, choose Lic Server. Repeat this step to achieve an unload and then reload of the LICSERVE server.. If the LICSERVE server has only the LicCarSample object, which supports logging activity, the Lic Server command loads or unloads the LICSERVE server. When the server is reloaded, CheckLicense will attempt to verify the machine license and detect that the license file is missing. This simulates the typical situation in a software package that includes the server components. The machine license would normally not be included in the package.
  5. From the LicCruiseCar menu, choose Create. The command will fail, because the machine license file is missing. This simulates an attempte to use components in an unlicensed copy of the server.
  6. From the UtilityCruiseCar menu, choose Create Using Key. This command will succeed in creating a COUtilityCruiseCar component, which internally creates a LicCruiseCar licensed component. Instead of calling CreateInstance, Create Using Key calls IClassFactory2::CreateInstanceLic, which uses the stored copy of the run-time license key. This simulates the normal run-time licensing behavior of the client in a package with a resold copy of the server.

Here is the trace log for step 2, the Get License Key command:

  C: === UtilityCruiseCar Menu: Get License Key.
  P: DllGetClassObject: Requesting CFLicCruiseCar.
  P: CFLicCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  P: CFLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=2.
  P: CFLicCruiseCar::QueryInterface. pIClassFactory2 returned.
  P: CFLicCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  P: CFLicCruiseCar::AddRef. New cRefs=1.
  C: LicCruiseCar's IClassFactory2 obtained.
  P: CFLicCruiseCar::CImpIClassFactory::RequestLicKey.
  P: CFLicCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  P: CFLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=1.
  P: CFLicCruiseCar::Destructor.
  P: CFLicCruiseCar::CImpIClassFactory Destructor.
  C: Obtained LicCruiseCar's Runtime License Key.

The client calls the CoGetClassObject COM API function to obtain an IClassFactory2 interface on the class factory for LicCruiseCar components. The factory is created, and the IClassFactory2::RequestLicKey method is called. The license key is obtained, the interface is released, and the CFLicCruiseCar class factory is destroyed normally.

We now perform step 3 and hide the machine license file, LICSERVE.LIC. We then restart the LICSERVE server using the Lic Server command. Here is the log of that sequence (step 4 above):

  C: Stop LICSERVE Server Trace Logging.
  P: COLicCarSample::CImpISample::Release. Delegating. New cI=0.
  P: COLicCarSample::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=0.
  P: COLicCarSample::Destructor.
  P: COLicCarSample::CImpISample Destructor.
  P: DllCanUnloadNow. cObjects=0, cLocks=0.
  S: DllCanUnloadNow. cObjects=1, cLocks=0.

  C: Start LICSERVE Server Trace Logging.
  P: --- LICSERVE.DLL now logging to Client ---

The ISample interface on the COLicCarSample object is released. Its reference count goes to 0, and the object is destroyed. The server decrements its object count to 0. When CoFreeUnusedLibraries is called after this release, COM calls the server to see if it should be unloaded. As the log shows, DllCanUnloadNow is called for each server. For the LICSERVE server, the object count is 0, so COM unloads the server DLL. We select this menu item again to re-engage LICSERVE server logging, which reloads the server. At this point, LICSERVE detects that the license file is missing.

We can confirm this by attempting to create a COLicCruiseCar component using the Create command on the LicCruiseCar menu. Here's what the log looks like with this attempt (step 5 above):

  C: === LicCruiseCar Menu: Create.
  P: DllGetClassObject: Requesting CFLicCruiseCar.
  P: CFLicCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  P: CFLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=2.
  P: CFLicCruiseCar::QueryInterface. pIClassFactory returned.
  P: CFLicCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  P: CFLicCruiseCar::AddRef. New cRefs=1.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance. No Machine License.
  P: CFLicCruiseCar::CImpIClassFactory::CreateInstance Failed.
  P: CFLicCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  P: CFLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=1.
  P: CFLicCruiseCar::Destructor.
  P: CFLicCruiseCar::CImpIClassFactory Destructor.
  C: ???? LicCruiseCar creation failed.

Compare this with the trace log studied earlier for this same menu selection. The call to CoCreateInstance causes COM to obtain an IClassFactory interface on an appropriate class factory (CFLicCruiseCar) in the LICSERVE server. The CreateInstance method is attempted as before. But now we see the cascade of failure as the absence of a machine license is discovered. When the call stack unwinds back to the client, an error message is displayed.

For the final confirmation of run-time licensing, we perform step 6 and choose the Create with Key command from the UtilityCruiseCar menu. Here is the trace log, with some blank lines indicating sections for comment:

  C: === UtilityCruiseCar Menu: Create with Key.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init.
  P: DllGetClassObject: Requesting CFLicCruiseCar.
  P: CFLicCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  P: CFLicCruiseCar Constructor. m_pUnkOuter=0x0.
  P: CServer::ObjectsUp. New cObjects=2.
  P: CFLicCruiseCar::QueryInterface. pIClassFactory2 returned.
  P: CFLicCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  P: CFLicCruiseCar::AddRef. New cRefs=1.
  C: COUtilityCruiseCar::Init IClassFactory2 obtained.

  P: CFLicCruiseCar::CImpIClassFactory::CreateInstanceLic.
  P: CFLicCruiseCar::CreateLicCruiseCar. pUnkOuter=0x7706EC.
  P: COLicCruiseCar::CImpICruise Constructor. Aggregating.
  P: COLicCruiseCar Constructor. m_pUnkOuter=0x7706EC.
  P: CServer::ObjectsUp. New cObjects=3.

  P: COLicCruiseCar::Init.
  S: DllGetClassObject: Requesting CFCar.
  S: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  S: CFCar Constructor. m_pUnkOuter=0x0.
  S: CServer::ObjectsUp. New cObjects=2.
  S: CFCar::QueryInterface. pIClassFactory returned.
  S: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  S: CFCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x7706EC.
  S: COCar::CImpICar Constructor. Aggregating.
  S: COCar Constructor. m_pUnkOuter=0x7706EC.
  S: CServer::ObjectsUp. New cObjects=3.
  S: COCar::QueryInterface. 'this' pIUnknown returned.
  S: COCar::AddRef. New cRefs=1.
  S: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x661230.
  S: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  S: CFCar::Release. New cRefs=0.
  S: CServer::ObjectsDown. New cObjects=2.
  S: CFCar::Destructor.
  S: CFCar::CImpIClassFactory Destructor.

  P: COLicCruiseCar::Init (New Aggregation of COCar) Succeeded.
  P: COLicCruiseCar::QueryInterface. 'this' pIUnknown returned.
  P: COLicCruiseCar::AddRef. New cRefs=1.
  P: CFLicCruiseCar::CreateLicCruiseCar Succeeded. *ppv=0x6611E0.
  P: CFLicCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  P: CFLicCruiseCar::Release. New cRefs=0.
  P: CServer::ObjectsDown. New cObjects=2.
  P: CFLicCruiseCar::Destructor.
  P: CFLicCruiseCar::CImpIClassFactory Destructor.
  C: COUtilityCruiseCar::Init Created LicCruiseCar using License Key.
  C: COUtilityCruiseCar::Init Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x7706EC.

LICCLIEN calls the CreateUtilityCruiseCar function. This function creates a new COUtilityCruiseCar COM object and calls the object's Init method to create any inner objects. In this case, the important one is the licensed COLicCruiseCar object that will be aggregated as part of the COUtilityCruiseCar object. As before, the class factory is obtained. In this case, however, the IClassFactory2 interface is explicitly requested, because to create an instance of COLicCruiseCar with a run-time license, the IClassFactory2::CreateInstanceLic method must be called.

This method is called by the client, and the saved license key string is passed as an argument. The resultant creation of a new COLicCruiseCar succeeds because the license key string is a match. If it was not, we would see a cascade of failure, and the object would not be created.

As part of this COLicCruiseCar object, an inner COCar object must be created and aggregated. The COLicCruiseCar::Init method gets the class factory (CFCar in server DLLSERVE) and creates a COCar COM object. This sequence is the same as in other creations of COLicCruiseCar components.

The COLicCruiseCar is created, and the class factory is released. LICSERVE ends up with an object count of 2, one for the new COLicCruiseCar object and one for the LicCarSample utility component. Control returns to the client, where the trace log shows the final success of the CreateUtilityCruiseCar function.

Top Back to page top

© 1995-1998 Microsoft Corporation


MARSHAL - Standard Marshaling of Custom Interfaces - Lesson 12

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

MARSHAL - Custom Interface Standard Marshaling

SUMMARY

The MARSHAL sample anticipates the interaction of a client and out-of-process server in the LOCCLIEN and LOCSERVE samples. For a client like LOCCLIEN to use the COM objects in an out-of-process server like LOCSERVE requires marshaling the car-related interfaces used in the previous COM samples of this series. The MARSHAL sample creates a proxy/stub DLL that provides standard marshaling for the ICar, IUtility, and ICruise custom interfaces.

The Microsoft® Interface Definition Language (MIDL) compiler is used to compile the interface specifications (expressed using the MIDL language in the MICARS.IDL file). MIDL.EXE is a utility provided as part of the Microsoft Platform SDK. The MIDL compilation of MICARS.IDL generates additional source files: MICARS.H, MICARS_I.C, MICARS_P.C, and DLLDATA.C.

In the series of COM Tutorial code samples, MARSHAL works with the LOCSERVE and LOCCLIEN samples to illustrate a COM client using the interfaces on COM objects in an out-of-process local server. In this sample series, only standard marshaling is used for the custom ICar, IUtility, and ICruise interfaces. Later samples in the series which use these interfaces across apartment, process, or machine boundaries (for example, APTSERVE, APTCLIEN, and REMCLIEN) also rely on MARSHAL.DLL for standard marshaling of these interfaces.

The MARSHAL sample demonstrates a simple and efficient way to create a marshaling DLL for custom interfaces. Default definitions are generated for the DllMain, DllGetClassObject, DllCanUnloadNow, GetProxyDllInfo, DllRegisterServer, and DllUnregisterServer functions. With this technique, all that is needed is a makefile, a .DEF file, and an input .IDL file specifying the interfaces. However, there may be occasions when you want more control over the content of the marshaling DLL. For example, you may want to perform some action within DllMain during DLL_PROCESS_ATTACH, you may want to code explicit control over the registration and unregistration of the marshaling server, or you may want to add standard module version information to the DLL resources. For coverage of these areas, see the subsequent MARSHAL2 sample. MARSHAL2 produces a marshaling DLL that marshals the same custom interfaces as MARSHAL. When registered, MARSHAL2.DLL is functionally equivalent to MARSHAL.DLL and replaces it. The makefile for the MARSHAL2.DLL does not automatically register the server to achieve this replacement. You must enable this in MARSHAL2's makefile. For the MARSHAL2 lesson, see MARSHAL2.HTM.

For functional descriptions and a tutorial code tour of MARSHAL, see the Code Tour section in MARSHAL.HTM. For details on setting up the programmatic usage of MARSHAL.DLL, see the Usage section in MARSHAL.HTM. To read MARSHAL.HTM, run TUTORIAL.EXE in the main tutorial directory and click the MARSHAL lesson in the table of lessons. You can also do the same thing by double-clicking the MARSHAL.HTM file after locating the main tutorial directory in Windows Explorer. For more details on the LOCCLIEN client and the LOCSERVE server and how MARSHAL.DLL supports their operation, see LOCSERVE.HTM and LOCCLIEN.HTM in the main tutorial directory. Because those code samples rely on MARSHAL.DLL, you must build MARSHAL DLL before building or running LOCCLIEN and LOCSERVE. MARSHAL's makefile automatically registers MARSHAL's proxy and stub interface marshalers in the system registry. This registration must be done before these interfaces can be used by COM clients or servers in LOCSERVE, LOCCLIEN, and later samples of the series.

MARSHAL's self-registration is done using the REGISTER.EXE utility built in the REGISTER sample. To build or run MARSHAL, you must build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the MARSHAL sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV MARSHAL.DSP

You can also simply double-click the MARSHAL.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

MARSHAL.DLL is built solely as a marshaling DLL for the specified interfaces. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used on behalf of a COM client that is using the interfaces across apartment, process, or machine boundaries. In these cases COM automatically loads this DLL as needed. Before COM can find and load the MARSHAL DLL to support marshaling of its supported interfaces, the MARSHAL server must be registered in the registry as the marshaling server for those interfaces. MARSHAL is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the MARSHAL directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the MARSHAL directory.

  ..\register\register.exe marshal.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of MARSHAL.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Microsoft® Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

CODE TOUR

Files        Description
MARSHAL.TXT  Short description of the sample.
MAKEFILE     The generic makefile for building the MARSHAL.DLL
             code sample.
MARSHAL.DEF  The module definition file. Exports server housing functions.
MICARS.IDL   The MIDL interface specifications for ICar, IUtility,
             and ICruise.
MICARS.H     Generated by compiling MICARS.IDL. The interface include
             file for the specified interfaces.
MICARS_I.C   Generated by compiling MICARS.IDL. The data definitions
             of the GUIDs for the marshaled interfaces.
MICARS_P.C   Generated by compiling MICARS.IDL. The actual proxy and
             stub functions for the interface methods.
DLLDATA.C    Generated by compiling MICARS.IDL. DLL data routines for
             the proxies and default definitions for the DllMain,
             DllRegisterServer, and DllUnregisterServer functions.
MARSHAL.DSP  Microsoft Visual Studio Project file.

This code sample introduces standard marshaling of custom COM interfaces. Before custom interfaces in a COM server can be used at run-time by a client across apartment, process, or machine boundaries, they must be marshaled. Method calls must be translated from the client's address space to that of the server, and arguments must be packaged in the client and converted in the server's environment. The return value from the method call must also be packaged in the server and converted in the client for use in the client's environment. A proxy on the client side works with a stub on the server side to achieve this packaging and unpackaging.

This sample builds a proxy/stub marshaling DLL to serve three interfaces that will be used in subsequent samples in this series. The LOCCLIEN and LOCSERVE samples will be the first that require this marshaling. The two EXE applications built in those samples, LOCSERVE.EXE and LOCCLIEN.EXE, are a client and out-of-process local server that manipulate the car-related components introduced in earlier lessons. The MARSHAL.DLL server built in this sample provies standard marshaling support for the ICar, IUtility, and ICruise custom interfaces.

MARSHAL.DLL provides this support by implementing interface proxy and stub functions for each method of each interface. The internal details of these proxy/stub pairs, while worthy of study, are beyond the scope of this lesson. Standard marshaling largely hides the details, so you can take a black-box approach. For more information, see the COM documentation in the Platform SDK.

MARSHAL.DLL registers itself as the provider of standard marshaling for the interfaces specified in MICARS.IDL. Each interface is registered using to the GUID specified for it (for example, IID_ICar for the ICar interface). The makefile for MARSHAL invokes the REGISTER.EXE utility to register the interfaces. Later, when a client calls COM to create an instance of a component, the call specifies an execution context (for example, CLSCTX_LOCAL_SERVER). This context indicates whether marshaling is required for the interfaces on the created component, and if so, whether the out-of-process server is on the local machine. COM thus loads MARSHAL.DLL as needed, locating it by consulting the registry entries for the marshaled interfaces.

The MARSHAL.DLL developed in this sample is very similar to the in-process COM servers produced in previous samples. It is a COM server that offers standard marshaling services for the interfaces it is registered to handle. MARSHAL.DLL is produced by compiling some source files that are automatically generated by the MIDL compiler when it compiles MICARS.IDL, which specifies the custom interfaces.

The sequence of build operations in the makefile is different from that seen in such previous COM server code samples, such as the DLLSERVE in-process server. This is because most of the source code for MARSHAL.DLL is automatically generated by the MIDL compiler. The dependencies in the makefile guarantee that these generated source files (in this case, MICARS.H, MICARS_I.C, MICARS_P.C, and DLLDATA.C) are created before any attempt is made to compile and link them. Here is the MIDL compilation from the makefile:

  # Generate the proxy/stub source from the .IDL file.
  micars.h micars_p.c micars_i.c dlldata.c: micars.idl
    midl /ms_ext /app_config /c_ext micars.idl

The generated source files all depend on MICARS.IDL. If any of them are absent, all will be regenerated by MIDL. If MICARS.IDL has a more recent time stamp than the C files, all the files will be regenerated.

MICARS.IDL is thus the principal source for this entire executable. The following are the interface definitions from MICARS.IDL:

  [uuid(0002da00-0000-0000-c000-000000000046),
      object
  ]
  interface ICar : IUnknown
  {
    import "unknwn.idl";

    HRESULT Shift([in] short nGear);
    HRESULT Clutch([in] short nEngaged);
    HRESULT Speed([in] short nMph);
    HRESULT Steer([in] short nAngle);
  }

  [uuid(0002da01-0000-0000-c000-000000000046),
      object
  ]
  interface IUtility : IUnknown
  {
    import "unknwn.idl";

    HRESULT Offroad([in] short nGear);
    HRESULT Winch([in] short nRpm);
  }

  [uuid(0002da02-0000-0000-c000-000000000046),
      object
  ]
  interface ICruise : IUnknown
  {
    import "unknwn.idl";

    HRESULT Engage([in] BOOL bOnOff);
    HRESULT Adjust([in] BOOL bUpDown);
  }

MIDL's specialized interface syntax is similar to that of C++. Because each interface is derived from IUnknown, a common .IDL file specifying it (UNKNWN.IDL, provided as part of the Platform SDK) is imported. The [in] directional attributes direct how the method arguments are marshaled. Other directional attributes in MIDL are [out] and [in,out]. Complete details on MIDL are beyond the scope of this lesson's introduction to standard marshaling. For more information on MIDL, see the Platform SDK.

Note that in previous code samples in this series, where marshaling was not involved, car-related interfaces like ICar could have used the int parameter type without problems. In this and the following lessons that involve marshaling, these parameter types are short (as in the ICar Shift method above). In the past this use of short paramters in marshaled interfaces was necessary when compiling 16-bit applications. Consistent with this, earlier versions of MIDL would generate compiler errors and not produce marshaling support for parameter types such as int whose final machine representation may be hardware-dependent. However, for Win32 applications, this restriction is no longer enforced by MIDL and the int parameter type can be used in interfaces that require marshaling.

MICARS.H is generated by the MIDL compiler when it compiles MICARS.IDL. (MICARS.H looks very similar to the ICARS.H interface declaration file from previous samples in this series.) MICARS.H is used as an include file when compiling the clients and servers that require the interface marshaling it supports. For example, LOCCLIEN.CPP includes this file for its declarations of the ICar, IUtility, and ICruise interfaces. Because the later samples like LOCSERVE and LOCCLIEN rely on MARSHAL.DLL, the MARSHAL makefile copies the generated MICARS.H file to the sibling \INC directory.

The C files (MICARS_I.C, MICARS_P.C, and DLLDATA.C) created by the MIDL compiler are later compiled and linked to produce MARSHAL.DLL. Here are the rules for these compilations from the makefile.

  # Compilation/Dependency rules for the .DLL source files.
  #
  $(TDIR)\micars_i.obj: micars.h
    $(cc) $(cvarsdll) $(cflags) $(CDBG) -Fo$@ micars_i.c

  $(TDIR)\micars_p.obj: micars.h
    $(cc) $(cvarsdll) $(cflags) $(CDBG) -Fo$@ micars_p.c

  # Note the compiler switch that defines REGISTER_PROXY_DLL.
  # This turns on conditional compilaiton of default definitions for the
  # DllMain, DllRegisterServer, and DllUnregisterServer functions. This
  # means that these functions do not need to be explicitly defined
  # in a separate source module.
  $(TDIR)\dlldata.obj: micars.h
    $(cc) $(cvarsdll) $(cflags) $(CDBG) -DREGISTER_PROXY_DLL -Fo$@ dlldata.c

The automatically generated C files are the only files that need be compiled. DLLDATA.C has some special features that greatly ease the task of producing the DLL. These special features are controlled in the rpcproxy.h file that is included by DLLDATA.C in the the following line:

  #include <rpcproxy.h>

RPCPROXY.H supports some important conditional compilation that is controlled by defining the appropriate macros prior to compilation. The most important of these is the REGISTER_PROXY_DLL macro. By defining this, you can enable default definitions for the DllMain, DllRegisterServer, and DllUnregisterServer functions. By relying on these default function definitions you don't need to explicitly define them as you normally would for an in-process COM server.

Since MICARS.IDL specified the interfaces completely, enough information is available when DLLDATA.C is compiled to automatically generate the code within DllRegisterServer and DllUnregisterServer to register and unregister the interfaces. In contrast to the registration process in previous server samples, it is interfaces, rather than components, that are now being registered and unregistered. From the standpoint of this sample, you can regard this process as a black box. By enabling the default functions, MARSHAL.DLL will contain appropriate code to fully register and unregister the interfaces. MARSHAL.DLL will also have a default DllMain definition. For more details on writing explicit functions for DllMain, DllRegisterServer, and DllUnregisterServer, see the MARSHAL2 sample.

Whether or not you define the REGISTER_PROXY_DLL macro, the MIDL-generated C files will have default definitions for the other required functions: DllGetClassObject, DllCanUnloadNow, and GetProxyDllInfo. The GetProxyDllInfo function is used internally by COM. The DllGetClassObject and DllCanUnloadNow functions should be familiar as standard fixtures for COM in-process servers. Thus, MARSHAL.DLL is actually an in-process server with a class factory as well as special COM components that support the operation of standard marshaling for those interfaces originally specified in MICARS.IDL. These default functions are defined for you but you must still export them from the DLL during the final link of the object modules. The following is the final link of MARSHAL.DLL from the makefile:

  # Link the object and resource binaries into the target DLL binary.
  # Build the import LIB file so apps can link to and use this DLL.
  $(DLL).dll: $(DLLOBJS)
      $(LINK)  @<<
      $(LINKFLAGS) $(dlllflags)
      -out:$@
      -base:0x1C000000
      -def:$*.def
      -implib:$*.lib
      -map:$(TDIR)\$*.map
      $(DLLOBJS)
      $(olelibsdll) $(APPLIBS)
  <<

The Linker's -def:$*.def command line switch is used above. This reduces to -def:MARSHAL.DEF when the makefile is processed. Thus, the link relies on MARSHAL.DEF for the necessary export specifications. Here is MARSHAL.DEF:

  LIBRARY     MARSHAL

  DESCRIPTION 'MARSHAL: COM Tutorial Sample. Copyright Microsoft Corp., 1997'

  EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE
    GetProxyDllInfo PRIVATE

The requied exports for the default functions in this server DLL are marked for internal, private use only by COM.

After you build (and thus register) this code sample, you can view the registered entries in the Registry Editor (REGEDT32.EXE for Windows NT®, REGEDIT.EXE for Windows® 95). Its entries for the ICar interface look like this.

  HKEY_CLASSES_ROOT
     \CLSID
        \{0002DA00-0000-0000-C000-000000000046} = "PSFactoryBuffer"
           \InprocServer32 = "D:\TUTSAMP\MARSHAL\MARSHAL.DLL"
            ThreadingModel = "Both"

  HKEY_CLASSES_ROOT
     \Interface
         \{0002DA00-0000-0000-C000-000000000046} = "ICar"
           \NumMethods = "7"
           \ProxyStubClsid32 = "{0002DA00-0000-0000-C000-000000000046}"

Under HKEY_CLASSES_ROOT\CLSID, this marshaling DLL appears with the in-process COM server entry, InprocServer32. COM treats this standard marshaling server as an in-process server. Note that this server is registered to support both the Apartment and Free threading models. For more information on the significance of this designation see the FRESERVE sample.

The default registration code uses the GUID of the first interface encountered (for example, IID_ICar) as the CLSID for registering the entire proxy/stub server. COM later uses this CLSID to locate and load MARSHAL.DLL for the marshaling of any of the interfaces it is registered to handle (that is, ICar, IUtility, ICruise). When an application makes an interface method call that crosses apartment, process, or machine boundaries, COM uses the interface's IID (that is, a GUID) registry entry to locate the CLSID registry entry for the MARSHAL.DLL server. It then uses this CLSID to load the server (if it isn't already loaded) so the interface call can then be marshaled using the appropriate proxy/stub pair in the server.

In the preceding registry entries the CLSID used for the server is the GUID of the first interface encountered (in this case, IID_ICar). This double use of the GUID is permitted because the interface IID will not be confused with the server's CLSID--the same GUID can be safely used for completely different things. The default behavior of using the IID of the first interface encountered as the CLSID of the proxy/stub server is adequate for most programming.

However, there are situations that may warrant explicit control over the default DLLDATA.C code that is generated. For example, instead of relying on the default use of the first interface IID as the marshaling server's CLSID, you can explicitly specify a CLSID. When REGISTER_PROXY_DLL is defined, RPCPROXY.H provides for additional conditional compilation control with the following macros: PROXY_CLSID=<clsid symbol>, PROXY_CLSID_IS=<explicit value of guid>, and ENTRY_PREFIX=<function name prefix>.

The PROXY_CLSID= macro explicitly specifies a CLSID for the marshaling server. The following is an example of how this macro is defined in a linker command-line switch:

  -DREGISTER_PROXY_DLL -DPROXY_CLSID=CLSID_CarMarshal

This macro directly uses the CLSID definition symbol that you may have already defined elsewhere in an include file using the DEFINE_GUID macro. It assumes that you have declared and instantiated CLSID_CarMarshal elsewhere in this DLL. For more information, see the discussion of INITGUID.H in DLLSERVE.HTM.

The PROXY_CLSID_IS macro can be used instead of PROXY_CLSID to define the explicit value of the CLSID in the standard binary HEX form of a GUID. The following example illustrates this:

  -DPROXY_CLSID_IS={0x0002da03,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}

The ENTRY_PREFIX macro is used to generate the marshaling server code in a way that combines the proxies with other COM objects in the same in-process server. In many cases the interfaces that are marshaled in the marshaling server are implemented in COM objects that are housed in different servers. A separate marshaling server is appropriate for these situations. However, there are cases when the interfaces exposed by the COM objects are uniquely associated with those objects. In these situations it can be efficient to combine the marshaling server with the in-process server that houses the application COM objects. Thus, a COM server--such as the previous DLLSERVE sample server--that would normally house application COM objects can also be used as the server housing for the marshaling proxy. The ENTRY_PREFIX provides a convenient way to create this combined server.

By specifying the ENTRY_PREFIX macro, each of the normal DLL entry points supplied in DLLDATA.C (via RPCPROXY.H) is renamed by adding the specified entry prefix. Thus, the DllGetClassObject generated for the proxy becomes <prefix>DllGetClassObject, and so on. The enveloping COM server would implement and expose the normal DllGetClassObject function. The main functions should call their renamed counterpart functions (that is, <prefix>DllMain, <prefix>DllGetClassObject, <prefix>DllCanUnloadNow, <prefix>DllRegisterServer, and <prefix>DllUnregisterServer) at the end of the server's main implementation of these functions.

For example, if ENTRY_PREFIX=Prxy during the compile of DLLDATA.C, then the server's main implementation of DllGetClassObject would look like the following (based on code in DLLSERVE.CPP):

  STDAPI DllGetClassObject(
           REFCLSID rclsid,
           REFIID riid,
           PPVOID ppv)
  {
    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
    IUnknown* pCob = NULL;
    BOOL bProxyObject = TRUE;

    if (CLSID_DllCar == rclsid)
    {
      hr = E_OUTOFMEMORY;
      pCob = new CFCar(NULL, g_pServer);
      bProxyObject = FALSE;
    }
    else if (CLSID_DllUtilityCar == rclsid)
    {
      hr = E_OUTOFMEMORY;
      pCob = new CFUtilityCar(NULL, g_pServer);
      bProxyObject = FALSE;
    }

    if (NULL != pCob)
    {
      g_pServer->ObjectsUp();
      hr = pCob->QueryInterface(riid, ppv);
      if (FAILED(hr))
      {
        g_pServer->ObjectsDown();
        DELETE_POINTER(pCob);
      }
    }

    if (bProxyObject)
      hr = PrxyDllGetClassObject(rclsid, riid, ppv);

    return hr;
  }

You do not need to code any explicit linkage of the proxy object lifetimes to the combined server lifetime. The proxy objects will maintain an independent object count. Either object count will cause the DLL to stay loaded. When the server's object count reaches zero, DllCanUnloadNow should chain to PrxyDllCanUnloadNow in a manner similar to that shown above for DllGetClassObject.

There are other special macros supported in RPCPROXY.H. See the comments at the front of that file for more details. RPCPROXY.H is located in the \MSSDK\INCLUDE directory of the installed Microsoft Platform SDK.

Top Back to page top

© 1995-1998 Microsoft Corporation


MARSHAL2 - Marshaling DLL Self-Registration - Lesson 13

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

MARSHAL2 - Marshaling DLL Self-Registration

SUMMARY

The MARSHAL2 sample shows an alternate and more advanced technique for building a proxy/stub marshaling DLL for the ICar, IUtility, and ICruise custom interfaces. The previous MARSHAL sample illustrated the system default method of producing the marshaling DLL. That method controlled the conditional compilation of the DLLDATA.C file to generate default definitions for the DllMain, DllRegisterServer, and DllUnregisterServer functions in the DLL. MARSHAL2 does not generate these default functions. Instead, they coded explicitly. Thus, this lesson has more detail on the content of these functions.

Although the default definitions of these functions are adequate for most programming, there may be occasions when you want more control over the content of the marshaling DLL. For example, you might want to perform some action within DllMain during DLL_PROCESS_ATTACH, you might want to code explicit control over the registration and unregistration of the marshaling server, or you might want to add standard module version information to the DLL resources. This sample covers these areas.

As in the MARSHAL sample, the Microsoft® Interface Definition Language compiler (MIDL.EXE) is used to compile the interface definitions (expressed using the MIDL language in the MICARS.IDL file). MIDL.EXE is a utility provided as part of the Microsoft Platform Software Development Kit (SDK). The MIDL compilation of MICARS.IDL generates additional source files: MICARS.H, MICARS_I.C, MICARS_P.C, and DLLDATA.C.

In the series of COM Tutorial code samples, MARSHAL2 is an alternate marshaling DLL that can replace MARSHAL.DLL produced in the MARSHAL sample and can work with later samples in the series to provide standard marshaling for the custom ICar, IUtility, and ICruise interfaces. To achieve this, just register MARSHAL2.DLL. This will override any previous registration of MARSHAL.DLL. Since both of these DLLs provide proxy/stub marshaling for the same interfaces, the one registered most recently will replace the other if it is registered.

For functional descriptions and a tutorial code tour of MARSHAL2, see the Code Tour section in MARSHAL2.HTM. For details on setting up the programmatic usage of MARSHAL2, see the Usage section in MARSHAL2.HTM. To read MARSHAL2.HTM, run TUTORIAL.EXE in the main tutorial directory and click the MARSHAL2 lesson in the table of lessons. You can also do the same thing by double-clicking the MARSHAL2.HTM file after locating the main tutorial directory in Windows Explorer. For a simpler way to provide the same standard marshaling functionality that MARHSAL2 provides, see MARSHAL.HTM.

The subsequent samples in the series that require marshaling of the ICar, IUtility, and ICruise interfaces normally rely on the registration of MARSHAL.DLL as the marshaling DLL. MARSHAL.DLL is produced in the previous MARSHAL sample. MARSHAL2's makefile does not automatically register MARSHAL2.DLL as the marshaling DLL for those car-related interfaces. However, this registration can be enabled in MARSHAL2's makefile by uncommenting the appropriate lines. Registering MARSHAL2 will replace any prior registration of MARSHAL.DLL. Although, these two marshaling DLLs are functionally equivalent, there are subtle differences in how they perform their self-registration. For more details on how MARSHAL2.DLL performs self-registration see below.

If enabled in the makefile, MARSHAL2's self-registration is done using the REGISTER.EXE utility built in the REGISTER sample. To build or run MARSHAL2, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the MARSHAL2 sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV MARSHAL2.DSP

You can also simply double-click the MARSHAL2.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

MARSHAL2 is a DLL that is built solely as a marshaling DLL for the specified interfaces. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used on behalf of a COM client that functions the server across apartment, process, or machine boundaries. COM loads this DLL automatically in these situations. Before COM can find and load MARSHAL2.DLL to support marshaling of its supported interfaces, the MARSHAL2 server must be registered in the registry as the marshaling server for those interfaces. MARSHAL2 is a self-registering in-process server. If enabled (see above), the makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the MARSHAL2 directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the MARSHAL2 directory.

  ..\register\register.exe marshal2.dll

These registration commands require a prior build of both the REGISTER sample and the MARSHAL2 sample.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Microsoft Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

CODE TOUR

Files        Description
MARSHAL2.TXT Short description of the sample.
MAKEFILE     The generic makefile for building the MARSHAL2.DLL
             code sample.
MARSHAL2.CPP The main implementation file for MARSHAL2.DLL. Has DllMain
             and the self-registration functions.
MARSHAL2.DEF The module definition file. Exports server housing functions.
MARSHAL2.RC  The DLL resource definition file for the executable.
MARSHAL2.ICO The icon resource for the executable.
MICARS.IDL   The MIDL interface definitions for ICar, IUtility,
             and ICruise.
MICARS.H     Generated by compiling MICARS.IDL. The interface include
             file for the defined interfaces.
MICARS_I.C   Generated by compiling MICARS.IDL. The data definitions
             of the GUIDs for the marshaled interfaces.
MICARS_P.C   Generated by compiling MICARS.IDL. The actual proxy and
             stub functions for the interface methods.
DLLDATA.C    Generated by compiling MICARS.IDL. DLL data routines for
             the proxies.
MARSHAL2.DSP Microsoft Visual Studio Project file.

This code sample covers explicit definitions of the DllMain, DllRegisterServer, and DllUnregisterServer functions in a standard marshaling server which duplicates the functionality of the MARSHAL sample by providing standard marshaling for the same ICar, IUtility, and ICruise custom interfaces. For more details on the general topic of standard marshaling, see the MARSHAL sample. This sample focuses primarily on coding an explicit DLL framework for the marshaling server.

MARSHAL2 uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

MARSHAL2.DLL is very similar to the in-process COM servers produced in earlier samples and is a COM server that offers standard marshaling services for the interfaces it is registered to handle. You create MARSHAL2 DLL by compiling some source files that are automatically generated by the MIDL compiler when it processes the primary source for this sample, MICARS.IDL. This file defines the custom interfaces.

The MARSHAL2 DLL is coded in much the same way as the DLLSERVE server from an earlier lesson. A good place to start looking at the code is the DLL framework code in MARSHAL2.CPP. The following files are included by MARSHAL2.CPP:

  #include <windows.h>
  #include <ole2.h>
  #include <apputil.h>
  #include "micars.h"

The MIDL compiler generates MICARS.H when it compiles MICARS.IDL. MICARS.H looks very similar to the ICARS.H interface declaration file from previous samples in this series. MICARS.H is used as an include file when compiling the clients and servers that require the interface marshaling it supports. It is included by MARSHAL2.CPP when MARSHAL2.DLL is compiled by the C++ compiler. In the LOCCLIEN sample, LOCCLIEN.CPP also includes this file for its declarations of the ICar, IUtility, and ICruise interfaces. Because the later samples like LOCSERVE and LOCCLIEN rely on MARSHAL.DLL, the MARSHAL2 makefile copies the generated MICARS.H file to the common INC directory.

The rest of MARSHAL2.CPP should be familiar. The DllMain function and the self-registration functions DllRegisterServer and DllUnregisterServer were presented in earlier lessons. The DllMain coded explicitly in this sample allows you to honor DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH.

Server registration and unregistration is different now, because interfaces, rather than components, are being registered and unregistered. The following is the DllRegisterServer function from MARSHAL2.CPP:

  STDAPI DllRegisterServer(void)
  {
    HRESULT  hr = NOERROR;
    TCHAR    szID[GUID_SIZE+1];
    TCHAR    szIFace[GUID_SIZE+32];
    TCHAR    szCLSID[GUID_SIZE+32];
    TCHAR    szModulePath[MAX_PATH];

    // Obtain the path to this module's executable file for later use.
    GetModuleFileName(
      g_hDllInst,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    /*---------------------------------------------------------------------
      Create registry entries for the ICar Interface.
    ---------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(IID_ICar, szID, GUID_SIZE);
    lstrcpy(szIFace, TEXT("Interface\\"));
    lstrcat(szIFace, szID);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);

    // Create the HKEY_CLASSES_ROOT\Interface entries.
    SetRegKeyValue(
      szIFace,
      NULL,
      TEXT("ICar"));
    SetRegKeyValue(
      szIFace,
      TEXT("ProxyStubClsid32"),
      szID);
    SetRegKeyValue(
      szIFace,
      TEXT("NumMethods"),
      TEXT("7"));

    // Create the HKEY_CLASSES_ROOT\CLSID entries.
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("ICar Proxy/Stub Factory"));
    SetRegKeyValue(
      szCLSID,
      TEXT("InprocServer32"),
      szModulePath);

    ...
    ...
      // Similar code for the IUtility and ICruise interfaces.
    ...
    ...

    return hr;
  }

The ICar interface registration appears under two main registry keys: HKEY_CLASSES_ROOT\Interface\[guid] and HKEY_CLASSES_ROOT\CLSID\[guid]. Note that this sample assumes that all registration is under the main registry key of HKEY_CLASSES_ROOT. The content of the HKEY_CLASSES_ROOT branch is replicated under HKEY_LOCAL_MACHINE\SOFTWARE\Classes. These two branches of the Registry can be used interchangeably.

The CLSIDs used above in DllRegisterServer are the same as the GUIDS defined for the interfaces. In this case, they are defined in MICARS.IDL, and the MIDL compiler places them in the generated MICARS.H file. The reference above to IID_ICar is thus given meaning by MICARS.H which is an include file at the beginning of MARSHAL2.CPP. The double use of the interface GUIDs as CLSIDs is permitted because the interface IID will not be confused with the server's CLSID--the same GUID can be safely used for completely different things.

The registration code above differs from the default registration code used in the previous MARSHAL sample, which used the first interface GUID (IID_ICar) as the CLSID. In that sample, the ICar, IUtility, and ICruise proxy/stub marshaling components were all registered under that single CLSID. However, in DllRegisterServer above, each interface marshaling component is registered with a different CLSID. These CLSIDs are IID_ICar, IID_IUtility, and IID_ICruise. Thus, in this sample there is a separate CLSID entry for each marshaled interface. This variation is possible here because of the explicit control over the coding of DllRegisterServer and DllUnregisterServer. Of course, your explicit coding of these functions could just as well mimic the single CLSID technique used in the default functions of the MARSHAL sample.

The value of the NumMethods entry is a count that includes both the IUnknown methods and the methods unique to the interface. For the ICar interface entry above, the count is 7, including three methods for IUnknown (QueryInterface, AddRef, Release) and four for ICar (Shift, Clutch, Speed, Steer).

After you enable registration in the makefile (see above) and build this code sample, you can view the registered entries using the Registry Editor (REGEDT32.EXE for Windows NT, REGEDIT.EXE for Windows 95). MARSHAL2's registry entries for the ICar interface look like the following:

  HKEY_CLASSES_ROOT
     \CLSID
        \{0002DA00-0000-0000-C000-000000000046} = "ICar Proxy/Stub Factory"
           \InprocServer32 = "D:\TUTSAMP\MARSHAL2\MARSHAL2.DLL"

  HKEY_CLASSES_ROOT
     \Interface
         \{0002DA00-0000-0000-C000-000000000046} = "ICar"
           \ProxyStubClsid32 = "{0002DA00-0000-0000-C000-000000000046}"
           \NumMethods = "7"

Under HKEY_CLASSES_ROOT\CLSID, this marshaling DLL appears with the in-process COM server entry, InprocServer32. COM treats this standard marshaling server as an in-process server.

As in earlier lessons, the DllUnregisterServer function in MARSHAL2.CPP simply removes the registry entries created by the DllRegisterServer function. For example, see DllUnregisterServer in MARSHAL2.CPP.

One final note about the self-registration of MARSHAL2.DLL. As in other self-registering COM servers in this tutorial series, for instructional purposes we have written our own support for registration and unregistration. You, however, can forgo writing a main module file like MARSHAL2.CPP and a main module resource file like MARSHAL2.RC, and instead easily obtain a default implementation for DllMain, DllRegisterServer, and DllUnregisterServer by conditionally compiling your code. You do this by setting a compilation switch, -DREGISTER_PROXY_DLL, when you compile DLLDATA.C. For more details see the MARSHAL sample.

You may have noticed that MARSHAL2.CPP lacks the familiar COM in-process server functions DllGetClassObject and DllCanUnloadNow. As in the MARSHAL standard marshaling sample, explicit definitions of these functions are not needed because compiling DLLDATA.C generates default implementations for you. Those implementations, however, must still be exported from this DLL. Standard marshaling DLLs require the DllGetClassObject, DllCanUnloadNow, and GetProxyDllInfo exports. You need only export these three functions; you don't need to implement them within this standard marshaling DLL. Of course, you must also export the DllRegisterServer and DllUnregisterServer functions if you have defined them in the DLL.

From the makefile, the following linker switches that direct the Link of MARSHAL2 DLL:

  # Link the object and resource binaries into the target DLL binary.
  # Build the import LIB file so apps can link to and use this DLL.
  $(DLL).dll: $(DLLOBJS) $(TDIR)\$(DLL).res
      $(LINK)  @<<
      $(LINKFLAGS) $(dlllflags)
      -out:$@
      -base:0x1C000000
      -def:$*.def
      -implib:$*.lib
      -map:$(TDIR)\$*.map
      $(DLLOBJS)
      $(TDIR)\$*.res
      $(olelibsdll) $(APPLIBS)
  <<

As used in the MARSHAL sample, the Link of MARSHAL2.DLL uses a -def command line switch to specify a MARSHAL2.DEF file to use for the export of the necessary marshaling server functions. The following is MARSHAL2.DEF:

  LIBRARY     MARSHAL2

  DESCRIPTION 'MARSHAL2: COM Tutorial Sample. Copyright Microsoft Corp., 1997'

  EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE
    GetProxyDllInfo PRIVATE

The requied exports for the default functions in this server DLL are marked for internal, private use only by COM.

A .RES resource file is linked into the final exeutable. Unlike MARSHAL.DLL and like earlier in-process servers in this series, this marshaling server DLL contains such resources as standard version information. For more details, see MARSHAL2.RC.

Top Back to page top

© 1995-1998 Microsoft Corporation


LOCSERVE - Local Out-of-Process Server - Lesson 14

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

LOCSERVE - Local Server

SUMMARY

The LOCSERVE sample begins with the car-related COM Objects of the previous DLLSERVE sample and rehouses them in an out-of-process local server, LOCSERVE.EXE. To do so requires little change to the COM objects themselves (COCar, COUtilityCar, and COCruiseCar). This sample introduces the new facilities to house them in a out-of-process COM server, including class factories for each component.

This out-of-process server provides the following components: LocCar, LocUtilityCar, and LocCruiseCar.

In the series of COM tutorial code samples, LOCSERVE works with the LOCCLIEN code sample to illustrate LOCSERVE's out-of-process local server facilities for creating components that can be used by an EXE client and the subsequent manipulation of those components by LOCCLIEN.EXE.

For functional descriptions and a tutorial code tour of LOCSERVE, see the Code Tour section in LOCSERVE.HTM. For details on setting up the programmatic usage of LOCSERVE, see the Usage section in LOCSERVE.HTM. To read LOCSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the LOCSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the LOCSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also LOCCLIEN.HTM in the main tutorial directory for more details on the LOCCLIEN client application and how it works with LOCSERVE.EXE itself. You must build LOCSERVE.EXE before building or running LOCCLIEN. LOCSERVE's makefile automatically registers LOCSERVE's components in the registry. These components must be registered before LOCSERVE is available to outside COM clients as a server for those components. This registration is done using the REGISTER.EXE utility built in the earlier REGISTER lesson. To build or run LOCSERVE, you should build the REGISTER code sample first.

As an out-of-process local server, LOCSERVE relies on standard marshaling for clients to use its interfaces across process boundaries. Such standard marshaling for the interfaces used in LOCSERVE's COM objects is provided in the MARSHAL.DLL server built in the previous lesson. To build or run LOCSERVE, you should build the MARSHAL code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the LOCSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV LOCSERVE.DSP

You can also simply double-click the LOCSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

LOCSERVE is an application that is meant to be used as an out-of-process COM server. Out-of-process servers like LOCSERVE are registered in the system registry, and LOCSERVE has built-in support for registering its components. It accepts the following command line switches to register and unregister:

  -RegServer or /RegServer to register
  -UnregServer or /UnregServer to unregister

String matches on these switches are case-insensitive. As an out-of-process server, LOCSERVE also recognizes the standard -Embedding or /Embedding switch, which directs it to run as such a server. In this sample, that means LOCSERVE will run hidden. If you attempt to run LOCSERVE as a stand-alone application, it will exit with an error. You can manually direct LOCSERVE to run visible by starting it with an explicit -Embedding switch on its command line prior to running the LOCCLIEN client.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the LOCSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the LOCSERVE directory.

  ..\register\register.exe locserve.exe

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of LOCSERVE.EXE.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, LOCCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files        Description
LOCSERVE.TXT Short sample description.
MAKEFILE     The generic makefile for building the LOCSERVE.EXE
             code sample of this tutorial lesson.
LOCSERVE.H   The include file for the LOCSERVE application. Contains
             class declarations, function prototypes, and resource
             identifiers.
LOCSERVE.CPP The main implementation file for LOCSERVE.EXE. Has WinMain
             and CMainWindow implementation, as well as the main menu
             dispatching.
LOCSERVE.RC  The resource definition file for the executable.
LOCSERVE.ICO The icon resource for the executable.
SERVER.H     The include file for the server control C++ object. Also
             used for LOCSERVE externs.
SERVER.CPP   The implementation file for the server control object.
             Manages object counts and creation of class factories.
FACTORY.H    The include file for the server's class factory COM objects.
FACTORY.CPP  The implementation file for the server's class factories.
CAR.H        The include file for the COCar COM object class.
CAR.CPP      The implementation file for the COCar COM object class.
UTILCAR.H    The include file for the COUtililtyCar COM object class.
UTILCAR.CPP  The implementation file for the COUtilityCar COM object class.
CRUCAR.H     The include file for the COCruiseCar COM object class.
CRUCAR.CPP   The implementation file for the COCruiseCar COM object class.
LOCSERVE.DSP Microsoft Visual Studio Project file.

With this LOCSERVE code sample, we cross process boundaries for the first time when the client manipulates components in the server. With DLLSERVE, we saw a server that was registered as in-process and was loaded by COM on behalf of clients and unloaded by COM when no longer needed by any clients. We saw the in-process server housing in DLLSERVE.DLL and how that housing implemented and exposed its class factories.

With LOCSERVE, these server housing schemes are different. As a separate EXE application with its own message loop, the server itself must take on more responsibility for its lifetime and must expose its class factories to COM differently.

Though not essential to the COM nature of this out-of-process local server, the logging facility that we used previously to enhance the tutorial value of these code samples needs significant changes to accommodate cross-process trace logging from the server to the client.

In a general sense, LOCSERVE is an EXE version of DLLSERVE. As COM servers, they both offer the same components: COCar, COUtilityCar, and COCruiseCar. They both also provide appropriate class factories for those components: CFCar, CFUtilityCar, and CFCruiseCar.

The main point of this LOCSERVE code sample is to illustrate the mechanisms required to render its COM objects into components by housing them in an out-of-process server.

Like its predecessors, LOCSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

Also like its server predecessors, LOCSERVE is self-registering, and its makefile uses REGISTER.EXE (built in the REGISTER lesson) to register LOCSERVE.

The major topics covered in this code tour are:

  • self-registration of local servers
  • local server housing
  • class factories in local servers
  • mechanisms that control local server lifetime
  • COM object classes in local servers
  • logging internal server-side behavior to a client trace log

Local Server Self-registration

We start in LOCSERVE.CPP with the following registration-related code in the WinMain function:

  ...
  ...
  // Check command line for switches to register or unregister
  // this server's managed components. iRun will be set to 2
  // to signal an immediate and quiet exit of this application
  // if such registration or unregistration is requested.
  if (0 == lstrcmpiA(lpCmdLine, "-RegServer")
      || 0 == lstrcmpiA(lpCmdLine, "/RegServer"))
  {
    if (pWin->RegisterServer())
      iRun = 2;
  }
  else if (0 == lstrcmpiA(lpCmdLine, "-UnregServer")
           || 0 == lstrcmpiA(lpCmdLine, "/UnregServer"))
  {
    if (pWin->UnregisterServer())
      iRun = 2;
  }
  ...
  ...

WinMain has been toured in previous lessons. The logic above shows how an out-of-process server can honor self-registration requests by recognizing certain standard switches on its command line. The above code runs after CMainWindow has been created, but before InitInstance is called and the main message loop is entered. The code checks the command line (using pointer lpCmdLine, passed with the call to WinMain) for the appropriate switches. The explicit ANSI variants of lstrcmpi are used, because the command line is available only in ANSI, even if this application is compiled for Unicode.

The string comparisons are case-insensitive. If switch -RegServer was specified on the command line, then CMainWindow::RegisterServer is called. Similarly, if switch -UnregServer was specified, CMainWindow::UnregisterServer is called. In the REGISTER code sample, we saw how these switches might have been specified when REGISTER started LOCSERVE.EXE with a call to WinExec. The switches can also be specified manually if LOCSERVE is started in the Command Prompt window. After the appropriate registration or unregistration function is called, an iRun variable is set to 2 to indicate that LOCSERVE will be exited immediately without entering its message loop.

Here is a portion of RegisterServer that registers the LocCar component:

  BOOL CMainWindow::RegisterServer(void)
  {
    BOOL  bOk = TRUE;
    TCHAR szID[GUID_SIZE+1];
    TCHAR szAID[GUID_SIZE+1];
    TCHAR szCLSID[GUID_SIZE+32];
    TCHAR szAPPID[GUID_SIZE+32];
    TCHAR szModulePath[MAX_PATH];
    TCHAR szExeName[MAX_PATH];

    // Be safe with null strings in these stack-allocated strings.
    szID[0] = 0;
    szAID[0] = 0;
    szCLSID[0] = 0;
    szAPPID[0] = 0;
    szModulePath[0] = 0;
    szExeName[0] = 0;

    // Obtain the path to this module's executable file for later use.
    // GetModuleFileName is an APPUTIL convenience function.
    GetModuleFileName(
      g_pServer->m_hInstServer,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    // Obtain the file name of this module's executable for later use.
    // GetExeName is an APPUTIL convenience function.
    GetExeName(
      g_pServer->m_hInstServer,
      szExeName);

    /*---------------------------------------------------------------------
      Create registry entries for the LocCar Component.
    ---------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(CLSID_LocCar, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);
    // Use the ClassID of this first component as the AppID
    // for all of the server's components.
    lstrcpy(szAPPID, TEXT("AppID\\"));
    lstrcat(szAPPID, szID);
    lstrcpy(szAID, szID);

    // Create ProgID keys.
    SetRegKeyValue(
      TEXT("LocCar1.0"),
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("LocCar1.0"),
      TEXT("CLSID"),
      szID);

    // Create VersionIndependentProgID keys.
    SetRegKeyValue(
      TEXT("LocCar"),
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("LocCar"),
      TEXT("CurVer"),
      TEXT("LocCar1.0"));
    SetRegKeyValue(
      TEXT("LocCar"),
      TEXT("CLSID"),
      szID);

    // Create entries under CLSID.
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      szCLSID,
      TEXT("ProgID"),
      TEXT("LocCar1.0"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("LocCar"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("LocalServer32"),
      szModulePath);
    AddRegNamedValue(
      szCLSID,
      NULL,
      TEXT("AppID"),
      szAID);

    /*-----------------------------------------------------------------------
      Create registry entries for the LocUtilityCar Component.
    -----------------------------------------------------------------------*/
      ...
      ... Similar to above code for LocCar.
      ...

    /*-----------------------------------------------------------------------
      Create registry entries for the LocCruiseCar Component.
    -----------------------------------------------------------------------*/
      ...
      ... Similar to above code for LocCar.
      ...

    /*-----------------------------------------------------------------------
      Create Server AppID entries.
    -----------------------------------------------------------------------*/
    SetRegKeyValue(
      szAPPID,
      NULL,
      TEXT("CTS.LOCSERVE(Cars).1"));
    SetRegKeyValue(
      TEXT("AppID"),
      szExeName,
      NULL);
    AddRegNamedValue(
      TEXT("AppID"),
      szExeName,
      TEXT("AppID"),
      szAID);

    return bOk;
  }

This registration code is very similar to that used in DLLSERVE for its DllCar component. The CLSID_LocCar (defined in CARGUIDS.H) is used for this component. CARGUIDS.H is found in the sibling INC directory. The main difference is the 'LocalServer32' entry. After such registration code is run (for example, as this server is built), you can run the Registry Editor (REGEDT32.EXE in Windows NT, REGEDIT.EXE in Windows 95) and peruse the registered entries. The registry entries look like this for the LocCar component:

  HKEY_CLASSES_ROOT
    \CLSID
       \{0002DA0A-0000-0000-C000-000000000046} = "LocCar Component - LOCSERVE Code Sample"
           [Named Values]
              AppID = "{0002DA0A-0000-0000-C000-000000000046}"
           \ProgID = "LocCar1.0"
           \VersionIndependentProgID = "LocCar"
           \NotInsertable
           \LocalServer32 = "D:\TUTSAMP\LOCSERVE\LOCSERVE.EXE"
    ...
    ...
    \LocCar = "LocCar Component - LOCSERVE Code Sample"
       \CurVer = "LocCar1.0"
       \CLSID = "{0002DA0A-0000-0000-C000-000000000046}"
    ...
    ...
    \LocCar1.0 ="LocCar Component - LOCSERVE Code Sample"
       \CLSID = "{0002DA0A-0000-0000-C000-000000000046}"
    ...
    ...
    \AppID
       \{0002DA0A-0000-0000-C000-000000000046} = "CTS.LOCSERVE(Cars).1"
          [Named Values]
             AccessPermission =
             LaunchPermission =
             RunAs =
       ...
       ...
       \LOCSERVE.EXE = [value not set]
          [Named Values]
             AppID = "{0002DA0A-0000-0000-C000-000000000046}"

Separate entries under HKEY_CLASSES_ROOT are written for the ProgID (LocCar1.0) and the VersionIndependentProgID (LocCar). Both refer unambiguously to the main HKEY_CLASSES_ROOT\CLSID entry. Under this entry are indications that the server is not insertable as an object in an COM compound document. The LocalServer32 entry has the path location of the server's executable. COM can thus start this server on behalf of a client when given the right CLSID.

AppID support was introduced in COM with the release of Windows NT 4.0. AppIDs are used in a registry scheme to store the security attributes of the server and its components. Like CLSIDs, AppIDs are GUIDS (Globally Unique Identifiers). AppIDs provide a way to collect servers and components into one group each member of which has the same security attributes. When COM is presented with a CLSID to create a new object, it can consult the AppID stored in the registry (under the CLSID as an AppID named value) to determine the security attributes for the server and its components. These attributes tell COM how the server can be activated, who can activate it, and who can later access it. These attributes are not used in LOCSERVE. If they were needed their named values would appear in the registry as indicated above for the AccessPermission, LaunchPermission, and RunAs values. If no AppID is present for an out-of-process server, COM transparently uses the system's default security attributes. See the DCDSERVE (later in this series) for more details on the use of these security attributes.

AppIDs are stored in the registry in the branch under \HKEY_CLASSES_ROOT\AppID. In addition to the actual AppID values, the scheme requires that the server's module name also be stored in the AppID branch (see the LOCSERVE.EXE key above). Given the module name, the server's AppID entry can be located in the registry. Likewise, given the CLSID for a component, the appropriate AppID entry can be located. Thus, there are several avenues in the registry back to the AppID entry containing the security attributes for a component.

The UnregisterServer call, also in LOCSERVE.CPP, simply removes from the registry all the entries that are written by RegisterServer.

Local Server Housing

The LOCSERVE application does have a skeleton menu in its main window. As a hidden server, it doesn't need a menu at all, but it has one here for debugging and tutorial purposes. For example, CMainWindow::DoMenu honors the IDM_HELP_ABOUT case. In previous code samples, the server could be directed to show its About dialog box from the client. This is not of major importance, but we illustrate it here by having the client send a message to LOCSERVE's main window. More on this in the LOCCLIEN code tour.

When LOCSERVE is started by COM on behalf of a client, COM uses a standard -Embedding command line switch to notify the server that it has been invoked as a server. Code in the WinMain function handles this switch and sets up LOCSERVE as a server of its components. Here is a code fragment from WinMain (in file LOCSERVE.CPP):

  ...
  ...
  if (FALSE == iRun)
  {
    // If we did not process a command line switch that
    // requires immediate exit, then initialize an instance of
    // the new CMainWindow. This entails creating the main window.
    // Note: if InitInstance fails, then it would have already
    // deleted pWin so we wouldn't need to delete it here.
    if (pWin->InitInstance(hInstance, nCmdShow))
    {
      // Create and register the Class Factories.  But only do so
      // if this application has been started by COM as indicated
      // by the -Embedding command line switch.
      if (0 == lstrcmpiA(lpCmdLine, "-Embedding")
          || 0 == lstrcmpiA(lpCmdLine, "/Embedding"))
        iRun = g_pServer->OpenFactories();
    }
  }
  ...
  ...

In addition to creating the main window, InitInstance also sets up logging from this server to the LOCCLIEN client's trace log display. More on this later.

The important thing now is the OpenFactories call above: it is called only if the -Embedding switch is detected. Regardless of whether this application was compiled for Unicode, the command line is always ANSI, so the ANSI-only version of the lstrcmpi string utility function is used. The g_pServer pointer points to this server's CServer server control object. For this out-of-process server, CServe has two important new methods that it did not have in DLLSERVE. One, OpenFactories, creates and registers all the class factories. The other, CloseFactories, shuts them all down.

We see above when OpenFactories is called. Its matching CloseFactories call is made in CMainWindow's destructor in LOCSERVE.CPP.

  ...
  // Close down the factories (ie, Revoke and release the Class Factories).
  if (NULL != g_pServer)
    g_pServer->CloseFactories();
  ...

The shutdown sequence is initiated by the server itself on the basis of the object and lock counts. For example, see the listing below of the ObjectsDown method of CServer. ObjectsDown detects when there are no longer any existing COM objects or lock counts. If there are none it issues the following call causing an eventual execution of CMainWindow's destructor.

  PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);

When the application's main window is closed in response to the WM_CLOSE message, the main window procedure is called with the WM_DESTROY message. This message is sent when the window is being destroyed in response to a close of the window. Because CMainWindow is derived from APPUTIL's CVirWindow, WM_DESTROY is trapped by CVirWindow's WindowProc function where a delete of CMainWindow is performed. This runs the CMainWindow destructor which calls CloseFactories as above. After CloseFactories shuts down all the class factories, the destructor finally posts a WM_QUIT message to the server application's main thread and causes the exit of the thread's message loop. After this the server application is exited.

We will tour the class factory code in more detail. CServer is used to manage creation and destruction of the class factories. Here is the CServe declaration in SERVER.H.

  class CServer
  {
    public:
      CServer(void);
      ~CServer(void);

      void Lock(void);
      void Unlock(void);
      void ObjectsUp(void);
      void ObjectsDown(void);
      BOOL OpenFactories(void);
      BOOL CloseFactories(void);

      // A place to store the server's instance handle.
      HINSTANCE m_hInstServer;

      // A place to store the server's main window.
      HINSTANCE m_hWndServer;

      // Global DLL Server living Object count.
      LONG m_cObjects;

      // Global DLL Server Client Lock count.
      LONG m_cLocks;

      // Some member variables to store pointers to Class Factories.
      IUnknown* m_pCFCar;
      IUnknown* m_pCFUtilityCar;
      IUnknown* m_pCFCruiseCar;

      // Some member variables to store Class Factory registration keys.
      DWORD m_dwCFCar;
      DWORD m_dwCFUtilityCar;
      DWORD m_dwCFCruiseCar;
  };

Much of this code is the same as that in previous servers in this sample series. We see the prototypes for the OpenFactories and CloseFactories methods. We notice storage for pointers to the IUnknowns of the three class factories (m_pCFCar, m_pCFUtilityCar, m_pCFCruiseCAr) and storage for registration keys for the class factories (m_dwCFCar, m_dwCFUtilityCar, m_dwCFCruiseCar). This registration is different from writing entries in the system registry. See the next section for more details.

Local Server Class Factories

The class factories are registered with COM at run-time, and COM provides a key or token for each registered class factory. Each token is later used to revoke the registration with COM for each class factory. The registration code informs COM that a class factory exists. Because the client has no direct way to call the server to create a class factory on demand, an out-of-process server must create them all during its initialization and destroy them all just before exiting.

Here is OpenFactories in SERVER.CPP:

  BOOL CServer::OpenFactories(void)
  {
    BOOL bOk = FALSE;
    HRESULT hr;

    LOG("L: CServer::OpenFactories. Begin.");

    // Build and register the LocCar factory.
    m_pCFCar = new CFCar(NULL, this);
    if (NULL != m_pCFCar)
    {
      // AddRef this cached pointer to the Class Factory.
      m_pCFCar->AddRef();

      // Now register this class factory with COM.
      hr = CoRegisterClassObject(
             CLSID_LocCar,
             m_pCFCar,
             CLSCTX_LOCAL_SERVER,
             REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED,
             &m_dwCFCar);

      bOk = SUCCEEDED(hr);
      if (!bOk)
      {
        LOGF1("L: CServer::OpenFactories. CFCar failed. hr=0x%X.", hr);
        // If can't register factory then clean up for server exit.
        RELEASE_INTERFACE(m_pCFCar);
      }
    }
    else
      bOk = FALSE;

    ...
    ... <Similar code for the LocUtilityCar and LocCruiseCar class factories.>
    ...

    if (bOk)
    {
      // Now that all the factories exist, open their doors by telling COM
      // it can now use the factories registered for this process.
      CoResumeClassObjects();
      // Tell COM to AddRef the internal lock count for this process so
      // the count can later be used to prevent any object requests
      // during shutdown in CloseFactories.
      hr = CoAddRefServerProcess();
      bOk = SUCCEEDED(hr);
    }

    LOG("L: CServer::OpenFactories. End.");

    return bOk;
  }

The CFCar COM object is created, and the m_pCFCar pointer is assigned. As usual, AddRef must be called on this cached pointer. COM's CoRegisterClassObject function is then called to register the new class factory. It deposits a registration key in the CServer::m_dwCFCar member variable, which is used later to revoke the CFCar class factory registration with COM.

The CLSCTX_LOCAL_SERVER argument informs COM that the execution context of this class factory is that of an out-of-process local server. The REGCLS_MULTIPLEUSE flag informs COM that this server is multiple use for any given class factory. This means that COM doesn't need to start a new instance of the server when another client requests the same class factory. This is one reason that all the class factories are created and registered during server initialization. If they aren't created and registered in the beginning, there would be no way to create a requested class factory that isn't already created, because the server would not be restarted by COM. The REGCLS_SUSPENDED flag tells COM to register the class as suspended. This prevents COM from honoring any client requests for the class factory until a CoResumeClassObjects call explicitly signals COM to honor such requests. This arrangement is used here during OpenFactories to prevent premature requests for class factories until all of the server's class factories are created and registered.

Once the class factory is registered, COM has a pointer to the class factory object's IUnknown. It can then use this pointer to make requests to the class factory on behalf of such client requests as CoCreateInstance.

CoAddRefServerProcess is called to tell COM to AddRef an internal lock count that COM maintains for this process. The count is used later during shutdown of this server (see the CoReleaseServerProcess call below in CloseFactories) to signal COM not to honor any requests for the class factory because the server is terminating and the factory will no longer exist. By the time execution reaches CloseFactories, an inevitable server shutdown is in progress and requests for new objects should be prevented.

Here is the matching CloseFactories method call in SERVER.CPP.

  BOOL CServer::CloseFactories(void)
  {
    BOOL bOk = TRUE;
    HRESULT hr;

    LOG("L: CServer::CloseFactories. Begin.");

    // Tell COM to release the last lock on the server process.
    // This essentially prevents COM from honoring any new requests for
    // objects during this inevitable shutdown.
    CoReleaseServerProcess();

    // Unregister the LocCar class factory with COM.
    if (0 != m_dwCFCar)
    {
      LOG("L: CServer::CloseFactories. Revoke CFCar.");
      hr = CoRevokeClassObject(m_dwCFCar);
      if (FAILED(hr))
        bOk = FALSE;
    }

    ...
    ... <Similar for the LocUtilityCar and LocCruiseCar class factories.
    ...

    // Release any and all of the Class Factory interface pointers.
    LOG("L: CServer::CloseFactories. Release all factory interfaces.");
    RELEASE_INTERFACE(m_pCFCar);
    RELEASE_INTERFACE(m_pCFUtilityCar);
    RELEASE_INTERFACE(m_pCFCruiseCar);

    LOG("L: CServer::CloseFactories. End.");

    return bOk;
  }

The stored registration key is used to revoke the class factory registration with COM. After that is successful for all factories, the outstanding references to the factories are released. Each release that decrements the reference count to 0 causes the associated class factory object to be deleted.

Local Server Lifetime Mechanisms

Before we leave the CServer server control object, here is the ObjectsDown method, also in SERVER.CPP:

  void CServer::ObjectsDown(void)
  {
    InterlockedDecrement((PLONG) &m_cObjects);

    LOGF1("L: CServer::ObjectsDown. New cObjects=%i.", m_cObjects);

    // If no more living objects and no locks then shut the server down.
    if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
    {
      LOG("L: CServer::ObjectsDown. Closing down LOCSERVE server.");
      // Post a message to this local server's message queue requesting
      // a close of the application.
      PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);
    }

    return;
  }

Here the server must take an active role in its own lifetime. When the object and lock counts are 0, the server control object calls PostMessage on the server application itself to send a WM_CLOSE messsage to the application.

A curious twist of logic is also needed in the Unlock method in SERVER.CPP:

  void CServer::Unlock(void)
  {
    InterlockedDecrement((PLONG) &m_cLocks);

    LOGF1("L: CServer::Unlock. New cLocks=%i.", m_cLocks);

    // Use ObjectsDown to force a server shutdown if this warranted.
    InterlockedIncrement((PLONG) &m_cObjects);
    ObjectsDown();

    return;
  }

The first decrement causes the lock count to reach zero but does not shut down the server. To trigger the shutdown, the m_cObjects count is artifically incremented, and then ObjectsDown is called. We saw in the ObjectsDown logic earlier that if the object count transitions to 0, a WM_CLOSE message is sent to the server.

COM Object Classes in Local Servers

The code for the COCar, COUtilityCar, and COCruiseCar has been carried over from DLLSERVE almost unchanged. One significant change was in the COUtilityCar nested component. When its reused subordinate COCar object is created using calls to COM's CoCreateInstance, the execution context is changed from CLSCTX_INPROC_SERVER to CLSCTX_LOCAL_SERVER. Here's an example from the creation of COUtilityCar from UTILCAR.CPP:

  ...
  ...
  // We create an instance of the COCar object and do this via the
  // Containment reuse technique. We ask for the new COM object's
  // ICar interface directly. We pass NULL for the pUnkOuter
  // aggregation pointer because we are not aggregating. It is here
  // that we are reusing the COCar COM Object through the Containment
  // technique. We cache the requested ICar interface pointer in this
  // COUtilityCar COM object for later use. We don't need to AddRef
  // this interface because the CoCreateInstance will do this for us.
  hr = CoCreateInstance(
         CLSID_LocCar,
         NULL,
         CLSCTX_LOCAL_SERVER,
         IID_ICar,
         (PPVOID)&m_pICar);
  ...
  ...

Recall from the DLLSERVE sample that COCruiseCar was constructed by reusing the COCar COM object by aggregation. In this present LOCSERVE local server we must retain the CLSCTX_INPROC_SERVER execution context for the creation of the aggregated COCar. Here is the creation of COCruiseCar's aggregated COCar object from CRUCAR.CPP.

  ...
  ...
  // We create an instance of the COCar object and do this via the
  // Aggregation reuse technique.  Note we pass pUnkOuter as the
  // Aggregation pointer.  It is the 'this' pointer to this present
  // CruiseCar object if we are not being aggregated; otherwise it is the
  // pointer to the outermost object's controlling IUnknown.  Following
  // the rules of Aggregation we must ask for an IID_IUnknown interface.
  // We cache the requested  pointer to the IUnknown of the new COCar COM
  // object for later use in delegating IUnknown calls. Since we know that
  // this LocCar component is housed in this very own server, we can
  // specify an execution context of CLSCTX_INPROC_SERVER. This allows us
  // to aggregate the COCar COM object. Though the object is in a local
  // server, it will be instantiated and run within the same process of
  // this present local server. If we specify CLSCTX_LOCAL_SERVER, COM
  // will not currently permit the creation using aggregation across
  // process boundaries and CoCreateInstance would return an error.
  hr = CoCreateInstance(
         CLSID_LocCar,
         pUnkOuter,
         CLSCTX_INPROC_SERVER,
         IID_IUnknown,
         (PPVOID)&m_pUnkCar);
  ...
  ...

COCruiseCar is instantiating and running an aggregated COCar within the process of this same server. If CLSCTX_LOCAL_SERVER were specified, the creation call would request aggregation across process boundaries. This is not currently supported by COM and CoCreateInstance would return an error.

Another change in the individual COM object implementation modules was to include MICARS.H rather than the ICARS.H file included in earlier code samples. We do this to better ensure consistency with the interface specification in MICARS.IDL. By including MICARS.H in both out-of-process server and client application modules, we ensure that the interface code in these client/server applications corresponds exactly to the same interfaces specified to MIDL for marshaling. MIDL generates our MICARS.H include file, and we code our use of those interfaces using MICARS.H. We could have continued to use the ICARS.H file, but because the content of the two files would be decoupled, there would have been a greater chance for a discrepancy between what is specified in MICARS.IDL and what is specified in ICARS.H.

Integrated Client Logging of Local Server Behavior

In previous servers, we used a CarSample component to log in-process server activity to the client's trace log display. In this out-of-process local server, however, we cannot, for example, pass a g_MsgLog pointer from the client to the server and assume the two processes' address spaces are the same. They are not. To enable LOCSERVE to log to LOCCLIEN, a kind of interprocess communication is required. Win32 offers a convenient way to send data from one process to another on the same machine if the destination window handle is known. This is the WM_COPYDATA message. This approach requires that LOCSERVE get the window handle of the client and use it in a Win32 SendMessage call with the WM_COPYDATA message. The data being sent is the display string to be logged.

Here is a portion of CMainWindow::InitInstance (in file LOCSERVE.CPP):

  ...
  ...
  // If the Client's Main window is found, then set up logging to it.
  hWnd = FindWindow(NULL, TEXT(CLIENT_WINDOW_TITLE));
  if (NULL != hWnd)
  {
    // Tell the CSendLog object that we are logging to client.
    m_pMsgLog->LogToServer(FALSE);
    // Assign the global MsgLog pointer.
    g_pMsgLog = m_pMsgLog;
    m_pMsgLog->SetClient(m_hInst, m_hWnd, hWnd);
    LOGID(IDS_LOGTO_CLIENT);
  }
  ...
  ...

When LOCSERVE is initialized, it calls the FindWindow function to locate LOCCLIEN's main window. (It has special knowledge of the window title). If it doesn't find the client, then the code (not shown) creates a log display in the server itself. This kind of logging within the application is the same as that seen in previous code samples and will not be covered again here. Once LOCSERVE finds the client's main window handle, it can send messages to it. If successful, the logging facility is notified that LogToServer is FALSE, and logging is to the client. The global g_pMsgLog variable is then assigned. The SetClient function stores the destination window handle for the client. The g_pMsgLog is the global pointer used by all the LOGxx macros. However, CMsgLog behaves differently than it did in earlier lessons, so in LOCSERVE.CPP we define g_pMsgLog as:

  CSendLog* g_pMsgLog = NULL;

We are using a different log facility (CSendLog instead of CMsgLog), implemented in APPUTIL, but it has same-named methods for the important logging calls. In past samples, the LOGxx macros were used successfully to call methods on a CMsgLog facility. Because the CSendLog facility mirrors the functionality of CMsgLog (though internally it sends the log messages across process boundaries to the client), the same LOGxx macros perform virtually the same logging funcionality, even though they are calling methods on a different CSendLog facility. Looking briefly in APPUTIL\APPUTIL.CPP, we find the following fragment at the heart of the CSendLog::Msg logging method. And there is the SendMessage call using WM_COPYDATA.

  ...
  ...
  cds.dwData = 0;
  cds.cbData = lstrlen(szMsg)+1;
  cds.lpData = szMsg;

  bResult = SendMessage(
              m_hWndReceiver,
              WM_COPYDATA,
              (WPARAM) m_hWndSender,
              (LPARAM) &cds);
  ...
  ...

The LOCCLIEN client executes the following when it recieves this WM_COPYDATA message:

  ...
  case WM_COPYDATA:
    // We have been sent a trace log message from a server.
    // Log it to our own Client's display.
    {
      LPTSTR pszMsg = (LPTSTR)((COPYDATASTRUCT*)lParam)->lpData;
      g_pMsgLog->Msg(pszMsg);
      #if defined(DEBUG)
      // Bump to next line in the debugger output window.
      ::OutputDebugString(TEXT("\r\n"));
      #endif
    }
    break;
  ...

The server's log message string is recieved and sent to the client's own MsgLog display.

Top Back to page top

© 1995-1998 Microsoft Corporation


LOCCLIEN - Client App of Local Server - Lesson 15

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

LOCCLIEN - Client Application of Local Server

SUMMARY

The LOCCLIEN sample presents the same series of car-related components that were studied in previous lessons. The LOCCLIEN/LOCSERVE client/server pair is analogous to the DLLCLIEN/DLLSERVE pair. The main difference is that LOCCLIEN must cross process boundaries to access components in the out-of-process local server LOCSERVE. In contrast, DLLCLIEN could directly access components in the in-process server DLLSERVE. The LOCCLIEN lesson illustrates how a client can access and control components in an out-of-process local server.

The COM objects that LOCCLIEN manipulates are the car-related ones of previous samples, with the following interfaces: ICar, IUtility, and ICruise. LOCCLIEN works in conjunction with the separate LOCSERVE.EXE, which provides the COCar, COUtilityCar, and COCruiseCar COM objects.

LOCCLIEN.EXE creates its own COUtilityCruiseCar COM object, which is constructed by reusing the COCruiseCar COM object by containment and augmenting it with a native implementation of the IUtility interface. Because the COCruiseCar COM object class is an aggregate--that is, it reuses an inner COCar object by aggregation--LOCCLIEN illustrates nesting COM objects by mixed reuse. The composite COUtilityCruiseCar object is constructed by containment reuse of COCruiseCar, another composite COM object. COCruiseCar is constructed by aggregation reuse of the COCar COM object. The composition of COUtilityCruiseCar is also interesting because the COUtilityCruiseCar object's containment of the COCruiseCar object crosses the process boundary between LOCCLIEN and the out-of-process local server LOCSERVE.EXE. LOCCLIEN thus relies on standard marshaling support for the custom interfaces it uses on the COCruiseCar object. This marshaling support was separately built as MARSHAL.DLL in the earlier MARSHAL code sample.

For functional descriptions and a tutorial code tour of LOCCLIEN, see the Code Tour section in LOCCLIEN.HTM. For details on the external user operation of LOCCLIEN, see both the Usage and Operation sections in LOCCLIEN.HTM. To read LOCCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the LOCCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the LOCCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also LOCSERVE.HTM in the main tutorial directory for more details on how LOCSERVE works and exposes its services to LOCCLIEN. You must build LOCSERVE.EXE before building LOCCLIEN. The makefile for LOCSERVE automatically registers that server in the registry, so you must build LOCSERVE before attempting to run LOCCLIEN.

Because client and server reside in separate processes, both LOCCLIEN and LOCSERVE rely on marshaling for the ICar, IUtility, and ICruise interfaces. This support was provided by the previous MARSHAL code sample, so you must also build the MARSHAL code sample prior to building and running LOCCLIEN and LOCSERVE.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the LOCCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV LOCCLIEN.DSP

You can also simply double-click the LOCCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

LOCCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by LOCCLIEN.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, LOCCLIEN.EXE is the client executable to run for this sample.

OPERATION

The LOCCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, LOCSERVE.EXE out-of-process local server. Here is a summary of operation from the standpoint of LOCCLIEN.EXE as a COM client of the LOCSERVE.EXE COM server.

The LOCCLIEN and LOCSERVE samples are directly analagous to the DLLCLIEN and DLLSERVE samples. The same components are used, and the same menu system exercises those objects. The main difference is that the client must use the standard marshaling support provided by the MARSHAL.DLL handler for the interfaces that reside across process boundaries. This standard marshaling is largely transparent if you use the Microsoft Interface Definition Language (MIDL) compiler and build a standard marshaling DLL.

As in the LOCSERVE code sample, support is provided for a trace logging facility that will display in the client an integrated trace log of activity in both client and server. Logging activity in this lesson is complicated by the process boundary and the methods of achieving such logging are covered in this lesson.

Although LOCCLIEN functions much like the prior DLLCLIEN and COMUSER code samples, we'll give a short review for those readers who are visiting this code sample out of sequence. The COM objects that are used in the LOCCLIEN and LOCSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

LOCCLIEN.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in LOCSERVE.EXE. For details, see the LOCSERVE lesson. COCruiseCar is constructed using aggregation and is also implemented in LOCSERVE.EXE. COUtilityCruiseCar is constructed using containment and is implemented in LOCCLIEN.EXE. In this sample, COUtilityCruiseCar reuses COCruiseCar by containment to illustrate nested object reuse with a mix of aggregation and containment.

LOCCLIEN.EXE presents a menu for each of these four main COM objects. Each menu has commands that call the methods of the various available interfaces. The code samples (both LOCCLIEN and LOCSERVE) have trace message log statements throughout. When you exercise the objects from LOCCLIEN.EXE, the main LOCCLIEN window will display a log of internal activity in these COM objects.

Menu Selection: File/Exit
Exits LOCCLIEN.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/LOCCLIEN Tutorial
Opens the LOCCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/LOCSERVE Tutorial
Opens the LOCSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/MARSHAL Tutorial
Opens the MARSHAL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About LOCCLIEN
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

Menu Selection: Help/About LOCSERVE
Displays the About dialog box for LOCSERVE.EXE, which is used by this application. In this series of code samples, partner servers like LOCSERVE are given their own About dialog box in the native resources of the server. This menu item calls the server function that displays this dialog box.

CODE TOUR

Files          Description
LOCCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
LOCCLIEN.H     The include file for the LOCCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
LOCCLIEN.CPP   The main implementation file for LOCCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
LOCCLIEN.RC    The application resource definition file.
LOCCLIEN.ICO   The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
LOCCLIEN.DSP   Microsoft Visual Studio Project file.

Like all code samples in the series, LOCCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

LOCCLIEN is consistent with earlier lessons to highlight the comparison between out-of-process local servers with in-process servers. LOCCLIEN retains the same set of COM objects as the previous DLLCLIEN lesson. A matching menu system to manipulate these objects is also retained. For details on the implementation and functionality of these COM objects, see the code tours for DLLCLIEN and DLLSERVE.

The goal of this lesson is to examine the client code needed to support cross-process manipulation of the components in the LOCSERVE server. We will tour internal behavior and examine a representative trace log.

The client remains largely the same when we move the car-related COM objects from an in-process server housing (DLLSERVE) to an out-of-process server housing (LOCSERVE). The most important change, while also convenient for instructional variety, is actually dictated by the nature of the current COM marshaling support for aggregation. The native COUtilityCruiseCar COM object in the DLLCLIEN lesson illustrated three levels of nested aggregation. COUtilityCruiseCar aggregated a native IUtility interface implementation with the COCruiseCar object housed in DLLSERVE. The COCruiseCar object, in turn, aggregated an ICruise interface implementation with the COCar object housed in DLLSERVE. This nested aggregation worked well, even though the aggregation spanned an object in the client and an inner object in the server. Despite the client/server distinction, however, the server was in the same process as the client. (DLLSERVE was an in-process server.)

With LOCCLIEN, however, the aggregation that spans the client and server would have to span a process boundary. The standard marshaling currently provided for the CreateInstance method of IClassFactory does not accept a non-NULL pointer to the controlling IUnknown (pUnkOuter). The CLASS_E_NOAGGREGATION error is returned by the marshaling proxy created in the previous MARSHAL code sample. What this means is that while aggregation is a powerful, efficient, and recommended way to reuse COM objects, it is currently supported only for in-process COM objects. For composite COM objects that must span process, apartment model thread, or machine boundaries, COM objects must be reused by containment.

In containment reuse, the composite object implements the interfaces of the contained inner object, creates an instance of the contained object, and caches pointers to the contained object's interfaces. The outer object implements delegating interfaces for the contained object's interfaces. The methods in these interfaces delegate their calls to the corresponding methods in the interfaces of the contained object. This delegation is done using the cached pointers to those interfaces. Delegation was covered in earlier lessons, where the COUtilityCar COM object was studied. The technique is employed in a similar way in LOCCLIEN to construct the COUtilityCruiseCar COM object. We'll now examine this in detail.

Here is the class declaration for COUtilityCruiseCar from UTCRUCAR.H.

  class COUtilityCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COUtilityCruiseCar(IUnknown* pUnkOuter);
      ~COUtilityCruiseCar(void);

      // A general method for initializing a newly created UtilityCruiseCar.
      HRESULT Init(void);

      // IUnknown members. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // We get this ICar interface pointer via containment reuse of the
      // ICar interface in an instantiated COCruiseCar.
      ICar*                m_pICar;

      // We get this ICruise interface pointer via containment reuse of the
      // ICruise interface in an instantiated COCruisecar.
      ICruise*             m_pICruise;

    private:
      // We declare nested class interface implementations here.

      // We implement the ICar interface in this COUtilityCruiseCar COM
      // object class. This is a delegating interface for the one
      // in the contained COCruiseCar COM object.
      class CImpICar : public ICar
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICar(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICar(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICar methods.
          STDMETHODIMP         Shift(short nGear);
          STDMETHODIMP         Clutch(short nEngaged);
          STDMETHODIMP         Speed(short nMph);
          STDMETHODIMP         Steer(short nAngle);

        private:
          // Data private to this interface implementation of ICar.
          ULONG                m_cRefI;     // Interface Ref Count.
          COUtilityCruiseCar*  m_pBackObj;  // Parent Object back pointer.
          IUnknown*            m_pUnkOuter; // Outer unknown for Delegation.
      };

      // We implement the ICruise interface in this COUtilityCruiseCar COM
      // object class. This ICruise implementation is a delegating
      // interface for the one provided in the contained COCruiseCar COM
      // object.
      class CImpICruise : public ICruise
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICruise(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICruise(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ICruise methods.
          STDMETHODIMP Engage(BOOL bOnOff);
          STDMETHODIMP Adjust(BOOL bUpDown);

        private:
          // Data private to this interface implementation of ICruise.
          ULONG                m_cRefI;     // Interface Ref Count.
          COUtilityCruiseCar*  m_pBackObj;  // Parent Object back pointer.
          IUnknown*            m_pUnkOuter; // Outer unknown for Delegation.
      };

      // We implement the IUtility interface in this COUtilityCruiseCar COM
      // object class. This is the native interface that we are using as an
      // augmentation to the existing COCruiseCar COM object class.
      class CImpIUtility : public IUtility
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIUtility(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpIUtility(void);

          // IUnknown members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IUtility members.
          STDMETHODIMP Offroad(short nGear);
          STDMETHODIMP Winch(short nRpm);

        private:
          // Data private to this interface implementation of IUtility.
          ULONG                m_cRefI;     // Interface Ref Count.
          COUtilityCruiseCar*  m_pBackObj;  // Parent Object back pointer.
          IUnknown*            m_pUnkOuter; // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested IUtility, ICruise, and ICar
      // interface implementations friend instantiations of this class.
      friend CImpICar;
      friend CImpICruise;
      friend CImpIUtility;

      // Private data of COUtilityCruiseCar COM objects.

      // Nested ICar implementation instantiation.
      CImpICar        m_ImpICar;

      // Nested ICruise implementation instantiation.
      CImpICruise     m_ImpICruise;

      // Nested IUtility implementation instantiation. This IUtility
      // interface is implemented inside this COUtilityCruiseCar object
      // as a native interface.
      CImpIUtility    m_ImpIUtility;

      // Main Object reference count.
      ULONG           m_cRefs;

      // Outer unknown (aggregation & delegation). Used when this
      // COUtilityCruiseCar object is being aggregated.
      IUnknown*       m_pUnkOuter;
  };

As in all these code samples, the multiple interfaces are implemented as nested classes. Here are nested implementations of ICar, ICruise, and IUtility. If we were aggregating the COCruiseCar COM object rather than containing it, we would not have to nest the ICruise and ICar interface implementations; we would need only the cached pointers to those interfaces (m_pICar and m_pICruise above). With aggregation, such pointers provide direct access to the interfaces. With containment, however, the containing COM object must implement delegating interfaces for the interfaces used on the contained object. In this code example, the COCruiseCar object is contained, and its ICar and ICruise interfaces are implemented in LOCCLIEN as delegating interfaces.

Thus, we have implementations for all three interfaces. But since IUtility is a native implementation in this COUtilityCruiseCar COM object in the client, we only cache pointers to the interfaces exposed by the contained COCruiseCar object (m_pICar and m_pICruise).

The COUtilityCruiseCar::Init method from UTCRUCAR.CPP assigns those pointers.

  HRESULT COUtilityCruiseCar::Init(void)
  {
    HRESULT hr;
    IClassFactory* pICFCruiseCar;

    // Set up the right pIUnknown for delegation. If we are being
    // aggregated then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    LOGF1("C: COUtilityCruiseCar::Init. pUnkOuter=0x%X",pUnkOuter);

    // Get a class factory for LocCruiseCar and issue IClassFactory's
    // CreateInstance method to manufacture a COCruiseCar COM object.
    LOG("C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory.");
    hr = CoGetClassObject(
           CLSID_LocCruiseCar,
           CLSCTX_LOCAL_SERVER,
           NULL,
           IID_IClassFactory,
           (PPVOID)&pICFCruiseCar);
    if (SUCCEEDED(hr))
    {
      LOG("C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained.");
      LOG("C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance.");
      // A NULL pUnkOuter is passed indicating that we are creating a
      // COCruiseCar component via the Containment reuse method. We can
      // currently pass nothing other than this NULL pUnkOuter because
      // aggregation is not supported across process or machine boundaries.
      // Since we are not requesting a COCruiseCar create for aggregation we
      // can directly request for the ICruise interface on the new object.
      hr = pICFCruiseCar->CreateInstance(
                            NULL,
                            IID_ICruise,
                            (PPVOID)&m_pICruise);
      LOG("C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory.");
      pICFCruiseCar->Release();
      if (SUCCEEDED(hr))
      {
        // Now that we have a valid interface pointer (m_pICruise) on the
        // new object, we can obtain and cache a pointer to that contained
        // object's ICar interface as well. We must QI using the obtained
        // ICruise pointer on the object to ensure that the marshaling proxy
        // is involved in the QueryInterface call to provide the pointer to
        // the ICar interface proxy.
        hr = m_pICruise->QueryInterface(IID_ICar, (PPVOID)&m_pICar);
      }
    }

    if (SUCCEEDED(hr))
    {
      LOG("C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded.");
    }
    else
    {
      LOG("C: COUtilityCruiseCar::Init (CruiseCar Containment) Failed.");
    }

    return (hr);
  }

Instead of the separate calls to CoGetClassObject and CreateInstance, we could have made a single call to CoCreateInstance. The two-step sequence is shown here for instructional purposes. The CoGetClassObject call specifies that the server execution context is on the local machine (CLSCTX_LOCAL_SERVER), and the CreateInstance call specifies NULL for the pUnkOuter argument. Because execution context parameter is CLSCTX_LOCAL_SERVER, a non-NULL pUnkOuter would return a CLASS_E_NOAGGREGATION error without ever actually calling the IClassFactory::CreateInstance implementation in the server. The NULL pUnkOuter means we are creating a LocCruiseCar component, which is reused by containment.

The successful CreateInstance call will store the m_pICruise pointer for us. This pointer will actually point into the client's own process space--to the ICruise interface proxy provided by the MARSHAL.DLL marshaling server we built previously. COM will have loaded this DLL on our behalf automatically as part of its support for standard marshaling. COM loads MARSHAL.DLL because the CreateInstance call specified an interface (IID_ICar) that is registered as a marshaled interface. As seen in the MARSHAL lesson, the registry entry for the IID_ICar interface has the path to its associated marshaling handler. From the client's perspective, then, the marshaling is transparent, and the contained object appears as if it were in the client process.

Once we get the pointer to the ICruise interface (m_pICar), we use it to call QueryInterface on the ICar interface of the contained COCruiseCar object. As in the server, we include MICARS.H for the interfaces that are marshaled. MICARS.H is generated during the build of the MARSHAL code sample.

How do those cached interface pointers complete support for containment? Here's an example from UTCRUCAR.CPP.

  STDMETHODIMP COUtilityCruiseCar::CImpICruise::Engage(
                 BOOL bOnOff)
  {
    LOG("C: COUtilityCruiseCar::CImpICruise::Engage Delegating.");

    // Delegate this call to the contained COCruiseCar's ICruise::Engage.
    m_pBackObj->m_pICruise->Engage(bOnOff);

    return NOERROR;
  }

This is the delegating implementation of the ICruise::Engage method. The interface implementation is nested, so the m_pBackObj pointer provides access to the cached m_pICruise pointer. It is then used to make the delegated call to the real interface in the contained COCruiseCar object. When this call is made, it goes through the standard marshaling proxy for this method of the ICruise interface. We built this standard marshaling support in the MARSHAL code sample.

LOCCLIEN supports a Help/About LOCSERVE menu selection. Because the server is in a different process, we can't directly address the server's address space to display its About dialog box. Here's how it's done (from CMainWindow::DoMenu in LOCCLIEN.CPP).

  case IDM_HELP_ABOUTSERVER:
    {
      // Post a message to the Server to show its AboutBox dialog box.
      LOG("C: === Help Menu: About LOCSERVE.");

      HWND hWnd = FindWindow(NULL, TEXT(SERVER_WINDOW_TITLE_STR));
      if (NULL != hWnd)
        PostMessage(hWnd, WM_COMMAND, IDM_HELP_ABOUT, NULL);
    }
    break;

The FindWindow function is called to find the window handle of the running local server. It can get the window handle even if the server is running hidden. The appropriate WM_COMMAND message is posted to the server application, which honors it as though a user had selected it from the server's menu. For this logic to work, both server and client must have the same defintion for IDM_HELP_ABOUT. This menu item works only if LOCCLIEN has already created a COM object provided by the LOCSERVE server. Until then, the server is not loaded and so cannot receive the posted message and display its About dialog box.

For symmetry, we'll list and tour a trace log of the same sequence of menu actions that we studied in the DLLCLIEN and COMUSER lessons. Here is the trace log for the Create command from the UtilityCruiseCar menu. Trace lines that begin with 'C:' are from the LOCCLIEN.EXE client. Lines that begin with 'L:' are from the LOCSERVE.DLL server.

  C: === UtilityCruiseCar Menu: Create.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpICar Constructor. Non-Aggregating.
  C: COUtilityCruiseCar::CImpICruise Constructor. Non-Aggregating.
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init. pUnkOuter=0x870E2C.
  C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory.

After the command is selected in the client, the CreateUtilityCruiseCar function is called. It creates a new COUtilityCruiseCar object. The interface constructors and the main object constructor are executed. The Init method is called to create any inner objects. In this case there is one, COCruiseCar. Init calls the COM function CoGetClassObject to obtain the CruiseCar class factory.

  L: LOCSERVE now logging to client.
  L: CServer::OpenFactories. Begin.
  L: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFCar Constructor. m_pUnkOuter=0x0.
  L: CFCar::AddRef. New cRefs=1.
  L: CFCar::AddRef. New cRefs=2.
  L: CFUtilityCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFUtilityCar Constructor. m_pUnkOuter=0x0.
  L: CFUtilityCar::AddRef. New cRefs=1.
  L: CFUtilityCar::AddRef. New cRefs=2.
  L: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CFCruiseCar::AddRef. New cRefs=1.
  L: CFCruiseCar::AddRef. New cRefs=2.
  L: CServer::OpenFactories. End.

COM runs the LOCSERVE.EXE server, because the CLSID passed with the class factory request is in the system registry. The server creates all of its supported class factories at once by calling the CServer::OpenFactories method. AddRef is called twice to increment the class factory's reference count, once by the server itself and once by COM when the OpenFactories function registers the class factory with COM.

  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCruiseCar::AddRef. New cRefs=3.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L: CFCruiseCar::AddRef. New cRefs=4.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCruiseCar::AddRef. New cRefs=5.
  L: CFCruiseCar::AddRef. New cRefs=6.
  L: CFCruiseCar::Release. New cRefs=5.
  L: CFCruiseCar::AddRef. New cRefs=6.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCruiseCar::AddRef. New cRefs=7.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCruiseCar::Release. New cRefs=6.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCruiseCar::AddRef. New cRefs=7.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCruiseCar::Release. New cRefs=6.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L: CFCruiseCar::Release. New cRefs=5.
  C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained.

This sequence begins with a client request for a specific class factory (CoGetClassObject). The server is then loaded, and all the server's class factories are subsequently created and registered. The log above is a record of COM and the default marshaling for IClassFactory making various QueryInterface and AddRef calls to the CFCruiseCar class factory. The class factory ends up with five outstanding references (cRefs = 5) when control returns successfully to the client and a class factory is obtained.

  C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance.
  L: CFCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L: COCruiseCar::CImpICruise Constructor. Non-Aggregating.
  L: COCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CServer::ObjectsUp. New cObjects=1.
  L: COCruiseCar::Init.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCar::AddRef. New cRefs=3.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L: CFCar::AddRef. New cRefs=4.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=5.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCar::AddRef. New cRefs=7.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCar::Release. New cRefs=6.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCar::AddRef. New cRefs=7.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCar::Release. New cRefs=6.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::Release. New cRefs=4.
  L: CFCar::Release. New cRefs=3.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCar::AddRef. New cRefs=4.
  L: CFCar::Release. New cRefs=3.
  L: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x870F00.
  L: COCar::CImpICar Constructor. Aggregating.
  L: COCar Constructor. m_pUnkOuter=0x870F00.
  L: CServer::ObjectsUp. New cObjects=2.
  L: COCar::QueryInterface. 'this' pIUnknown returned.
  L: COCar::AddRef. New cRefs=1.
  L: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870F28.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCar::Release. New cRefs=2.
  L: COCruiseCar::Init (New Aggregation of COCar) Succeeded.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=1.
  L: CFCruiseCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870F04.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=2.
  L: COCruiseCar::AddRef. New cRefs=2.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: COCruiseCar::AddRef. New cRefs=3.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L: COCruiseCar::AddRef. New cRefs=5.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L: COCruiseCar::Release. New cRefs=4.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L: COCruiseCar::AddRef. New cRefs=5.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L: COCruiseCar::Release. New cRefs=4.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=1.
  L: COCruiseCar::Release. New cRefs=3.

Most of the internal activity logged above is a history of COM and the server creating the COCruiseCar object by aggregating a COCar object, also in the server. The CFCar class factory was invoked to create and aggregate a COCar object for the new COCruiseCar. We also see the events that return the ICruise interface pointer originally requested in the call to CreateInstance. When this activity is completed, the object count for the LOCSERVE server (cObjects) is 2: one for the inner COCar object and one for the COCruiseCar object itself.

  C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCruiseCar::Release. New cRefs=4.
  L: CFCruiseCar::Release. New cRefs=3.
  L: CFCruiseCar::Release. New cRefs=2.
  L: COCruiseCar::QueryInterface. ICar delegating.
  L: COCar::QueryInterface. pICar returned.
  L: COCar::CImpICar::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCar::CImpICar::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCruiseCar::QueryInterface. ICar delegating.
  L: COCar::QueryInterface. pICar returned.
  L: COCar::CImpICar::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=4.
  C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x870E2C.

When the client (still inside COUtilityCruiseCar::Init) has successfully created the inner COCruiseCar object, it releases the class factory pointer it had obtained. This results in a cascade of other releases, some of which are done from inside COM. Init then calls QueryInterface for the ICar interface to cache its address. Control eventually returns to the client, where a COUtilityCruiseCar is successfully created. The client holds a pointer to the IUnknown of COUtilityCruiseCar, and we see the QueryInterface and AddRef calls that obtain this reference. The reference count on this client-side object (cRefs) is only 1. All of the preceding activity was due to one menu selection: UtilityCruiseCar/Create.

Next, we exercise one of the interfaces on this newly created composite object. This interface is actually provided by an inner object (COCar) that is aggregated in a COCruiseCar object, which in turn is contained in the COUtilityCruiseCar object. Here is the ICar::Shift command from the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: COUtilityCruiseCar::QueryInterface. pICar returned.
  C: COUtilityCruiseCar::CImpICar::Addref. Delegating. New cI=1.
  C: COUtilityCruiseCar::AddRef. New cRefs=2.
  C: Interface obtained. *ppv=0x870E38
  C: --Calling pICar->Shift
  C: COUtilityCruiseCar::CImpICar::Shift. Delegating. nGear=1.
  L: COCar::CImpICar::Shift. Called. nGear=1.
  C: --Releasing pICar
  C: COUtilityCruiseCar::CImpICar::Release. Delegating. New cI=0.
  C: COUtilityCruiseCar::Release. New cRefs=1.

This is not too complicated because everything, including the marshaling proxies, is set up. The client's QueryInterface method obtains a pointer to ICar (pICar) and calls ICar::Shift. Because the composite object gets this interface from a contained inner object, the call to Shift makes its way through the delegation interface and is executed directly in the server. Once Shift is called, the client releases the pICar pointer. This call to Release is also delegated--in this case, to the controlling IUnknown's Release--leaving a final reference count on COUtilityCruiseCar of 1 (reflecting the IUnknown pointer the client has on this created object). The ICar interface is released; the COUtilityCruiseCar object exists until we choose the Release command from the UtilityCruiseCar menu. Here it is:

  C: === UtilityCruiseCar Menu: Release.
  C: COUtilityCruiseCar::Release. New cRefs=0.
  C: COUtilityCruiseCar::Destructor.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCar::CImpICar::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=2.
  L: COCruiseCar::Release. New cRefs=1.
  L: COCruiseCar::Release. New cRefs=0.
  L: CServer::ObjectsDown. New cObjects=1.
  L: COCruiseCar::Destructor.
  L: COCar::Release. New cRefs=0.
  L: CServer::ObjectsDown. New cObjects=0.
  L: CServer::ObjectsDown. Closing down LOCSERVE server.
  L: COCar::Destructor.
  L: COCar::CImpICar Destructor.
  L: COCruiseCar::CImpICruise Destructor.

In this scenario, COUtilityCruiseCar is the only object we created from the client. This is contrived so we can show how the out-of-process local server is unloaded. In the log entries above, we see the cascade of releases and destructors on the COUtilityCruiseCar object and its inner objects (COCruiseCar and COCar). Eventually, the server's object count (cObjects) is decremented to 0. The CServer::ObjectsDown method detects this event and posts a WM_CLOSE message to the local server itself. This message causes the server program to exit its message loop, and it calls g_pServer->CloseFactories.

  L: CServer::CloseFactories. Begin.
  C: COUtilityCruiseCar::CImpIUtility Destructor.
  C: COUtilityCruiseCar::CImpICruise Destructor.
  C: COUtilityCruiseCar::CImpICar Destructor.
  L: CServer::CloseFactories. Revoke CFCar.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=3.
  L: CFCar::Release. New cRefs=2.
  L: CFCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Revoke CFUtilityCar.
  L: CFUtilityCar::QueryInterface. 'this' pIUnknown returned.
  L: CFUtilityCar::AddRef. New cRefs=3.
  L: CFUtilityCar::Release. New cRefs=2.
  L: CFUtilityCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Revoke CFCruiseCar.
  L: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCruiseCar::AddRef. New cRefs=3.
  L: CFCruiseCar::Release. New cRefs=2.
  L: CFCruiseCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Release all factory interfaces.
  L: CFCar::Release. New cRefs=0.
  L: CFCar::Destructor.
  L: CFCar::CImpIClassFactory Destructor.
  L: CFUtilityCar::Release. New cRefs=0.
  L: CFUtilityCar::Destructor.
  L: CFUtilityCar::CImpIClassFactory Destructor.
  L: CFCruiseCar::Release. New cRefs=0.
  L: CFCruiseCar::Destructor.
  L: CFCruiseCar::CImpIClassFactory Destructor.
  L: CServer::CloseFactories. End.
  L: Exiting LOCSERVE local server application.

CServer::CloseFactories makes a series of calls to COM (by means of CoRevokeClassObject) to revoke registration of the class factories for CFCar, CFUtilityCar, and CFCruiseCar. We see some resultant calls to Release on these class factories by COM itself--in each case leaving a final reference count on the class factory of 1. CloseFactories then calls Release on the remaining class factory pointers that the client holds, causing reference counts to reach 0, at which time the the class factory COM objects are destroyed. The LOCSERVE server application is finally exited and is unloaded by Windows.

One final note about logging. You can run the LOCSERVE server in a visible state, in which case it will log its internal behavior to its own log display. In this way both LOCCLIEN and LOCSERVE can be visible on screen at the same time, with separate log displays of their own activity. Here's how to do it: Run LOCSERVE first (before LOCCLIEN) with the -Embedding command line switch, and then run LOCCLIEN. Both the LOCSERVE server and the LOCCLIEN client will be present on screen at the same time. Each running EXE will log its internal behavior to its own separate logging display.

Top Back to page top

© 1995-1998 Microsoft Corporation


APTSERVE - Local Server with Multiple Apartments - Lesson 16

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

APTSERVE - Local Server with Multiple Apartments

SUMMARY

COM supports execution contexts called apartments. Execution threads that run within COM objects do so in an apartment. Every COM object is owned by such an apartment. A given thread can execute in only one apartment at a time. COM recognizes two kinds of apartment: the Single-Threaded Apartment (STA) and the Multi-Threaded Apartment (MTA). COM supports only one MTA in a given process. Objects owned by this MTA can be executed concurrently by more than one thread. In contrast to the MTA, an STA is associated with a single unique thread. Objects in such an STA can be executed only by the STA's associated thread. COM supports more than one STA in a given process.

COM maintains apartments and ensures that the objects can be executed only by threads associated with the apartment. A family of COM objects can be owned by an apartment. If the apartment is an STA, then the objects can be executed only by the single thread of the STA. COM enforces this by ensuring that calls from any client thread to objects in an STA will be executed only on the thread of the STA. COM will perform thread switches if necessary. COM will also marshal interfaces as necessary using the marshaling servers registered for those interfaces.

The APTSERVE sample and its matching client, APTCLIEN, introduce COM apartments and focus on the single-threaded apartment. Two later samples, FRESERVE and FRECLIEN, focus on the multi-threaded apartment.

The APTSERVE sample begins with the car-related COM Objects of the previous LOCSERVE sample and rehouses them in an out-of-process local server, APTSERVE.EXE, whose process has three single-threaded apartments. The rehousing requires little change to the COM objects themselves (COCar, COUtilityCar, and COCruiseCar). The COCruiseCar object required some changes. In LOCSERVE COCruiseCar reused the COCar object using aggregation. Since this sample rehouses these objects within different STAs, COCruiseCar must be recoded to reuse COCar using containment. This is necessary because COM currently supports aggregation only within the same apartment. But most importantly, the server housing must undergo significant revision. This sample introduces the new facilities to partition each class factory and its created object instances into a separate STA.

In this sample, an out-of-process COM server is partitioned into three single-threaded apartments, each corresponding to the component types seen in previous samples: AptCar, AptUtilityCar, and AptCruiseCar. These components are known by their CLSIDs as: CLSID_AptCar, CLSID_AptUtilityCar, and CLSID_AptCruiseCar. Appropriate class factories for those components are also provided: CFCar, CFUtilityCar, and CFCruiseCar. As compared to the implementation of these class factories in previous lessons, no significant change to the class factories themselves is required in this sample.

In the series of COM tutorial code samples, APTSERVE works with the APTCLIEN code sample to illustrate APTSERVE's partitioning of the server facilities for creating and manipulating components within separate apartments and to show how the components are accessed by a client.

For functional descriptions and a tutorial code tour of APTSERVE, see the Code Tour section in APTSERVE.HTM. For details on the programmatic usage of APTSERVE, see the Usage section in APTSERVE.HTM. To read APTSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the APTSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the APTSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also APTCLIEN.HTM in the main tutorial directory for more details on the APTCLIEN client application and how it works with APTSERVE.EXE.

You must build APTSERVE.EXE before building or running APTCLIEN. The accompanying makefile automatically registers APTSERVE components in the registry. These components must be registered before APTSERVE is available to outside COM clients as a server for those components. This registration is done using the REGISTER.EXE utility built in the earlier REGISTER lesson. To build or run APTSERVE, you should build the REGISTER code sample first.

Like its predecessors, APTSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code and APPUTIL.HTM, located in the main tutorial directory.

As an out-of-process local server with several single-threaded apartments, APTSERVE relies on standard marshaling for clients to use its interfaces across both thread and process boundaries. Such marshaling is provided in the MARSHAL.DLL server built in the previous lesson. To build or run APTSERVE, you should build the MARSHAL code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the APTSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV APTSERVE.DSP

You can also simply double-click the APTSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Like the LOCSERVE/LOCCLIEN pair, APTSERVE uses APPUTIL's CSendLog trace logging facility to allow display of internal APTSERVE behavior integrated with client behavior in the APTCLIEN log display. See APTCLIEN.HTM for more details setting up this logging operation. See LOCSERVE.HTM for details on how CSendLog works.

Usage

The APTSERVE application is meant to be used as an out-of-process COM server. Out-of-process servers like APTSERVE are registered in the system registry, and APTSERVE has built-in support for registering its components. It accepts the following command line switches to register and unregister:

  -RegServer or /RegServer to register
  -UnregServer or /UnregServer to unregister

String matches on these switches are case-insensitive. APTSERVE also recognizes the standard -Embedding or /Embedding switch, which directs it to run as an out-of-process server. This switch normally means that the server will remain hidden when run by COM on behalf of a client. As an out-of-process server, APTSERVE is not designed to run visable as a stand-alone application. If you attempt to run APTSERVE as a stand-alone application with no command line switches, it will exit with an error.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the APTSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the APTSERVE directory.

  ..\register\register.exe aptserve.exe

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of APTSERVE.EXE.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, APTCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files        Description
APTSERVE.TXT Short sample description.
MAKEFILE     The generic makefile for building the APTSERVE.EXE
             code sample of this tutorial lesson.
APTSERVE.H   The include file for the APTSERVE application. Contains
             class declarations, function prototypes, and resource
             identifiers.
APTSERVE.CPP The main implementation file for APTSERVE.EXE. Has WinMain
             and CMainWindow implementation, as well as the main menu
             dispatching.
APTSERVE.RC  The resource definition file for the executable.
APTSERVE.ICO The icon resource for the executable.
SERVER.H     The include file for the server control C++ object. Also
             used for APTSERVE externs.
SERVER.CPP   The implementation file for the server control object.
             Manages apartment threads, object counts, server lifetime,
             and the creation of class factories.
FACTORY.H    The include file for the server's class factory COM objects.
FACTORY.CPP  The implementation file for the server's class factories.
CAR.H        The include file for the COCar COM object class.
CAR.CPP      The implementation file for the COCar COM object class.
UTILCAR.H    The include file for the COUtililtyCar COM object class.
UTILCAR.CPP  The implementation file for the COUtilityCar COM object class.
CRUCAR.H     The include file for the COCruiseCar COM object class.
CRUCAR.CPP   The implementation file for the COCruiseCar COM object class.
APTSERVE.DSP Microsoft Visual Studio Project file.

With the APTSERVE code sample, we introduce several single-threaded apartments (STAs) in a COM server. In keeping with the graduated tutorial sequence in these code samples, APTSERVE retains the COCar, COUtilityCar, and COCruiseCar components and modifies their server housing and class factories to house, create, and manage those components in separate STAs.

The LOCSERVE out-of-process local server code sample is the starting point. LOCSERVE is a single-thread server: The main process thread is the only thread that directly manipulates the server's class factories and COM objects. This single thread can be viewed as the one and only STA of LOCSERVE.

COM supports multiple threading in both client and server applications. For a server application with multiple apartments, some housekeeping is required to partition the application's threads and to inform COM of the arrangement. In APTSERVE, there is a separate STA apartment thread for each class factory. COM objects reside on the same apartment thread as the class factory that creates them. Each class factory and all of its object instances reside in the same single-threaded apartment. There is an arrangement of three such single-threaded apartments in this sample but it is certainly not the only possible arrangement of multiple apartments and class factories in a server.

Under the STA apartment model, there can be multiple COM objects on a thread, but calls to an object must execute on the thread that "owns" the object. This thread can be called the object's apartment thread and interface requests to the object from another thread must be marshaled to the apartment thread that owns the object, just as if the object were in another process.

In these code samples, interface calls across process boundaries are marshaled to convert the call from the context of the calling process to the context of the owning server process. We studied this in the LOCSERVE and LOCCLIEN lessons, where the necessary marshaling was provided by the MARSHAL.DLL standard marshaling server (created in the MARSHAL sample). When the change in the call context is from one apartment to another, this same standard marshaling is used. This marshaling is required even if the threads reside within the same process, as in this APTSERVE sample.

The following coding rules apply to COM STA apartments:

  1. Every object is owned by only one apartment thread and calls to the object must execute on that thread.
  2. Each apartment thread must call OleInitialize or CoInitialize during thread initialization to initialize COM for each apartment.
  3. To ensure that all requests to an object are made on its apartment thread, all pointers to objects must be marshaled between apartments.
  4. Each apartment with COM objects in it must have a message queue to handle calls from other apartments in the same process or from other processes.
  5. Apartment-aware objects in in-process servers must be registered as being apartment-aware in the system registry.
  6. Apartment-aware objects in in-process servers must write DLL entry points carefully.

APTSERVE has three single-threaded apartments, one for each class of component housed in the server. The CFCar class factory and all COCar COM objects are managed in one apartment. The CFUtilityCar class factory and all COUtilityCar COM objects are managed in a separate apartment, as are the CFCruiseCar class factory and all COCruiseCar COM objects.

Since each class factory and its instantiated objects reside in a separate apartment, client access through their interfaces must be marshaled. This is so even if both apartment threads reside in the same process. In this lesson, some of the composite objects that are created and managed in one apartment of APTSERVE are constructed by reusing objects created and managed in another apartment. For example, the COCruiseCar object of the AptCruiseCar apartment reuses a COCar object of the AptCar apartment.

Almost all the coding changes needed to partition APTSERVE into three apartments are found in the server housing, which is programmed in files SERVER.H and SERVER.CPP.

Single-threaded apartments are implemented as separate program threads, each of which has its own message loop for processing messages sent to the thread. Each also has a main thread procedure. When creating the thread using the _beginthreadex or CreateThread functions, we pass a pointer to an apartment thread procedure. We also pass a pointer to a thread's initialization data. As the operating system then creates the thread, it calls the thread procedure passing the initialization data pointer, as an LPARAM. We will look at these mechanisms in the source code

First there are some CServer member variables needed to identify the three apartments. Here are some appropriate declarations from SERVER.H.

  // Apartment Thread Initialization data.
  enum { NUM_APARTMENTS = 3 };
  enum { APTCAR = 0, APTUTILITYCAR = 1, APTCRUISECAR = 2 };
  struct APT_INIT_DATA
  {
    REFCLSID rclsid;
    IUnknown* pcf;

    // Member initializer MUST be used here because VC++ 4.0+ is strict
    // about const and reference (&) types like REFCLSID that need to
    // be initialized in this app.  For example, VC++ 4.x will not permit
    // a simple assignment of rclsid in the constructor.
    APT_INIT_DATA(REFCLSID rclsidi) : rclsid(rclsidi)
    {
      pcf = NULL;
    };

    ~APT_INIT_DATA() {};
  };

  /*C+C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C+++C
    Class:    CServer

    Summary:  Class to encapsulate control of this COM server (eg, handle
              Lock and Object counting, encapsulate otherwise global data).
              Govern server and Apartment lifetimes.

    Methods:  none
  C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C---C-C*/
  class CServer : public CThreaded
  {
    public:
      CServer(void);
      ~CServer(void);

      void Lock(void);
      void Unlock(void);
      void ObjectsUp(void);
      void ObjectsDown(void);
      BOOL OpenFactories(void);
      BOOL CloseFactories(void);

      // CThreaded method overrides.
      BOOL OwnThis(void);
      void UnOwnThis(void);

      // A place to store the server's instance handle.
      HINSTANCE m_hInstServer;

      // A place to store the server's main window.
      HINSTANCE m_hWndServer;

      // Global Server living Object count.
      LONG      m_cObjects;

      // Global Server Client Lock count.
      LONG      m_cLocks;

      // Some member variables to store pointers to Class Factories.
      IUnknown* m_pCFCar;
      IUnknown* m_pCFUtilityCar;
      IUnknown* m_pCFCruiseCar;

      // Pointers to Apartment init data structures.
      APT_INIT_DATA* m_paiAptCar;
      APT_INIT_DATA* m_paiAptUtilityCar;
      APT_INIT_DATA* m_paiAptCruiseCar;

      // Some member variables to store apartment thread ids.
      DWORD     m_dwAptCar;
      DWORD     m_dwAptUtilityCar;
      DWORD     m_dwAptCruiseCar;

      // An array of handles to the apartment threads.
      HANDLE    m_hApts[NUM_APARTMENTS];
  };

Compared to the previous LOCSERVE sample, we see that the Server control object for APTSERVE has member variable storage for pointers to each apartment's thread init data, thread IDs used later to send messages to the threads, and methods to ensure mutually exclusive access to shared data in this multithreaded server.

It is important to ensure that all threads within the server, as well as those in all outside clients, have controlled access to shared server data. In previous samples in this series, the Win32 functions InterlockedIncrement and InterlockedDecrement were used to ensure mutually exclusive access to the server's object and lock counts among multiple client processes. With this sample, a more general mechanism is used to serialize access to server data shared by apartment threads. This mechanism uses the Win32 Mutex facility. CServer is derived from a base class declared in APPUTIL.H, CThreaded, which provides such a mechanism. Any C++ class can be derived from CThreaded to provide mutually exclusive access by asynchronous multiple threads. See APPUTIL.H for more details on the CThreaded class. We will cover its use here in CServer.

The mutex is created in the CThreaded constructor. Here is the CThreaded class declaration from APPUTIL.H.

  class CThreaded
  {
    protected:
      // Variables for providing mutually exclusive access by multiple
      // threads to objects of classes derived from this class.
      HANDLE    m_hOwnerMutex;
      BOOL      m_bOwned;

    public:
      // Methods.
      CThreaded(void) :
        m_hOwnerMutex(CreateMutex(0,FALSE,0)),
        m_bOwned(FALSE)
      {
      };

      ~CThreaded(void)
      {
        // Close down the mutex.
        CloseHandle(m_hOwnerMutex);
      };

      // These virtual functions have overriding definitions in the
      // derived class to provide convenient trace logging. Rely on
      // the below default definitions for non-tutorial applications.

      virtual BOOL OwnThis(void)
      {
        BOOL bOwned = FALSE;

        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hOwnerMutex, INFINITE))
          m_bOwned = bOwned = TRUE;

        return bOwned;
      };

      virtual void UnOwnThis(void)
      {
        if (m_bOwned)
        {
          m_bOwned = FALSE;
          ReleaseMutex(m_hOwnerMutex);
        }

        return;
      };
  };

CServer inherits the protected m_hOwnerMutex and m_bOwned data members from CThreaded. CServer also provides overriding definitions for the CThreaded virtual functions OwnThis and UnOwnThis. In a normal application we could rely on the default definitions provided in CThreaded for these methods. In this tutorial code sample, however, we provide definitions of OwnThis and UnOwnThis that offer convenient trace logging of their behavior.

The m_hOwnerMutex is created solely to govern access to the CServer C++ object. A Boolean flag is initialized to FALSE and is used to indicate when a requesting thread has been permitted to "own" the CServer object. The owning thread has exclusive access to the object. Member initializers in CThreaded are used to initialize m_hOwnerMutex and m_bOwned. The mutex is closed during the CThreaded destructor.

Once created, the mutex m_hOwnerMutex can be used in other methods of CServer that may be called by multiple threads to access its shared data. The most important shared data in this case is the CServe member data variables m_cObjects and m_cLocks. Here is a representative method (in SERVER.CPP).

  void CServer::ObjectsDown(void)
  {
    if (OwnThis())
    {
      if (m_cObjects > 0)
        m_cObjects -= 1;

      LOGF2("L<%X>: CServer::ObjectsDown. New cObjects=%i.",TID,m_cObjects);

      // If no more living objects and no locks then shut down the server.
      if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
      {
        LOGF1("L<%X>: CServer::ObjectsDown. Closing down APTSERVE server.",TID);

        // Relinquish current thread ownership of CServer before signaling
        // the main server thread to close down the entire server. During
        // shutdown other threads may need to access CServer.
        UnOwnThis();

        // Post a message to this local server's message queue requesting
        // a close of the entire server application. This will force a
        // termination of all apartment threads (including the one that
        // may be executing this ObjectsDown)
        PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);
      }
      else
        UnOwnThis();
    }

    return;
  }

Two shared data members, m_cObjects and m_cLocks, are either changed or accessed by this ObjectsDown method. The mutual exclusion mechanism uses the bracketed pairs of calls to the OwnThis and UnOwnThis methods. Inside the bracketed pair, the mechanism guarantees that no other threads will access or change the shared data. Thus a call to the above ObjectsDown method will block a thread in the OwnThis call until any current owner thread completes its access to CServer and relinquishes its ownership by calling the UnOwnThis method. When there are no longer any existing objects or locks on the server, ownership of CServer by the executing thread is relinquished before a PostMessage call signals the entire server to shut down. This is done above because other threads may need to access CServer during shutdown. When honoring the posted WM_CLOSE, the main server thread terminates the apartment threads. Depending on thread scheduling, ownership of CServer could be held by a thread that was terminated during shutdown. This could block any other threads from ever accessing CServer.

The OwnThis method returns TRUE when the executing thread is permitted access. Here is the overriding OwnThis definition from SERVER.CPP.

  BOOL CServer::OwnThis(void)
  {
    BOOL bOwned = FALSE;

    LOGF1("L: CServer::OwnThis. Thread <%X> waiting to own CServer.",TID);

    if (WAIT_OBJECT_0 == WaitForSingleObject(m_hOwnerMutex, INFINITE))
    {
      m_bOwned = TRUE;
      LOGF1("L: CServer::OwnThis. CServer now owned by Thread <%X>.",TID);
    }

    return bOwned;
  }

The Win32 function WaitForSingleObject waits for the mutex to signal that the mutex is not owned by any thread. This call requests ownership of the mutex for the calling thread, changing the mutex's state to nonsignaled when ownership is granted. Until such access is permitted, some other thread has ownership of CServer, and the current thread must wait. Such a waiting thread is said to be "blocked". During this time, the current owning thread is executing code in its own bracketed OwnThis-UnOwnThis pair. When the current owning thread completes its access, it calls UnOwnThis to relinquish ownership. Here is UnOwnThis from file SERVER.CPP.

  void CServer::UnOwnThis(void)
  {
    if (m_bOwned)
    {
      LOGF1("L: CServer::UnOwnThis. Ownership relinquished by <%X>.",TID);
      m_bOwned = FALSE;
      ReleaseMutex(m_hOwnerMutex);
    }

    return;
  }

Because three different apartment threads run in the APTSERVE server, the same CServer is shared between them. As a result, the CServer methods which access its shared data must all honor this ownership protocol by properly calling the OwnThis and UnOwnThis methods as shown above for ObjectsDown. Thus the Lock, Unlock, ObjectsUp, ObjectsDown, OpenFactories, and CloseFactories methods are all coded using the OwnThis and UnOwnThis pairs.

When the first client requests creation of a component in APTSERVE, COM loads and runs this EXE local server. Like LOCSERVE, when APTSERVE runs, it creates and registers its class factories. When clients make any subsequent creation requests of APTSERVE, COM uses the already running server and its registered class factories. Thus, one running instance of the class factories is shared by all the clients. Because these shared class factories are registered within separate apartment threads, COM's support of the STA model ensures that calls to interfaces in objects created by these class factories will be called on the same thread that originally called on the class factory to create the object. The first point of recognition by COM in this regard is the occasion of the object's first marshaled interface. This is often the IClassFactory interface pointer requested in the CoGetClassObject call.

In APTSERVE the server provides an OpenFactories method, as did the LOCSERVE server. The OpenFactories code used in LOCSERVE requires significant changes to work in APTSERVE. Here is OpenFactories from SERVER.CPP.

  BOOL CServer::OpenFactories(void)
  {
    BOOL bOk = FALSE;
    HRESULT hr;

    LOGF1("L<%X>: CServer::OpenFactories. Begin.",TID);

    if (OwnThis())
    {
      // Create the ClassFactory C++ objects.
      m_pCFCar = new CFCar(NULL, this);
      m_pCFUtilityCar = new CFUtilityCar(NULL, this);
      m_pCFCruiseCar = new CFCruiseCar(NULL, this);

      // Create Structures for Apartment initialization.
      m_paiAptCar = new APT_INIT_DATA(CLSID_AptCar);
      m_paiAptUtilityCar = new APT_INIT_DATA(CLSID_AptUtilityCar);
      m_paiAptCruiseCar = new APT_INIT_DATA(CLSID_AptCruiseCar);

      // Create the Appartment for AptCar.
      LOGF1("L<%X>: CServer::OpenFactories. AptCar.",TID);
      if (NULL != m_pCFCar && NULL != m_paiAptCar)
      {
        // AddRef this cached pointer to the Class Factory.
        m_pCFCar->AddRef();

        // Assign the ClassFactory in the apartment init data and AddRef.
        m_paiAptCar->pcf = m_pCFCar;
        m_paiAptCar->pcf->AddRef();

        // Start the Apartment Thread using local utility function.
        m_hApts[APTCAR] = StartThread(
                            (PCRTTHREADPROC) AptThreadProc,
                            (LPVOID) m_paiAptCar,
                            &m_dwAptCar);

        bOk = (NULL != m_hApts[APTCAR]);
        if (!bOk)
        {
          hr = GetLastError();
          LOGF2("L<%X>: CServer::OpenFactories. AptCar failed. hr=0x%X.",TID,hr);
          // If can't register factory then clean up for server exit.
          m_pCFCar->Release();
          m_paiAptCar->pcf->Release();
          DELETE_POINTER(m_pCFCar);
          DELETE_POINTER(m_paiAptCar);
        }
      }
      else
        bOk = FALSE;

      // Create the Appartment for AptUtiliytCar.
      ...
      ... Code similar to above AptCar
      ...

      // Create the Appartment for AptCruiseCar.
      ...
      ... Code similar to above AptCar
      ...

      UnOwnThis();
    }

    if (bOk)
    {
      // Tell COM to AddRef the internal lock count for this process so
      // the count can later be used to prevent any object requests
      // during shutdown in CloseFactories.
      hr = CoAddRefServerProcess();
      bOk = SUCCEEDED(hr);
    }

    LOGF1("L<%X>: CServer::OpenFactories. End.",TID);

    return bOk;
  }

The class factory objects (CFCar, CFUtilityCar, and CFCruiseCar) are created as before in LOCSERVE. However, we now assign an apartment initialization structure and create a separate thread of execution for each class factory. We will see how this factory is registered with COM on this apartment thread. Notice the use of member initializers for the new APT_INIT_DATA structures. As seen earlier in the declaration of the APT_INIT_DATA structure, the REFCLSID member cannot be initialized in the constructor or assigned later, because the COM/OLE header files declare REFCLSIDs as const references. The newer C++ compilers are strict about this and force the use of member initializers for assigning REFCLSID member variables.

A StartThread function is used to simplify the use of the C Runtime library's _beginthreadex function. StartThread is defined locally in the APTSERVE module. The _beginthreadex function is used in this sample. It has some advantages over the Win32 CreateThread function. When it is anticipated that the new thread will make calls to C Runtime functions, then the _begintheadex call performs some necessary C Runtime setup and memory management which the Win32 CreateThread function does not.

When the StartThread function is called, a pointer to a thread procedure is passed (AptThreadProc above). The operating system will call this thread procedure when it creates the thread. When it does so, it passes the pointer to the APT_INIT_DATA structure as an LPARAM. The same AptThreadProc is re-intrant and can be used by all of the single threaded apartments. Here is the AptThreadProc from SERVER.CPP.

  unsigned WINAPI AptThreadProc(
                     LPARAM lparam)
  {
    HRESULT hr;
    MSG msg;
    DWORD dwCFRegId;
    APT_INIT_DATA* paid = (APT_INIT_DATA*) lparam;

    LOGF1("L: AptThreadProc. Starting Apartment Thread <%X>.",TID);

    // Initialize COM for this apartment thread. Default of apartment
    // model is assumed.
    CoInitialize(0);

    // Now register the class factory with COM.
    LOGF1("L: AptThreadProc. Registering class factory of apartment <%X>.",TID);
    hr = CoRegisterClassObject(
           paid->rclsid,
           paid->pcf,
           CLSCTX_LOCAL_SERVER,
           REGCLS_MULTIPLEUSE,
           &dwCFRegId);
    LOGERROR("L:CoRegisterClassObject",hr);
    if (SUCCEEDED(hr))
    {
      // Provide a message pump for this thread.
      while (GetMessage(&msg, 0, 0, 0))
        DispatchMessage(&msg);

      LOGF1("L: AptThreadProc. Revoking class factory of apartment <%X>.",TID);
      // Unregister the class factory with COM when the thread dies.
      CoRevokeClassObject(dwCFRegId);
    }
    else
    {
      LOGF2("L<%X>: AptThreadProc. RegisterClass failed. hr=0x%X.",TID,hr);
    }

    // Uninitialize COM in the context of this apartment thread.
    CoUninitialize();

    LOGF1("L: AptThreadProc. Apartment Thread <%X> Terminated.",TID);

    return msg.wParam;
  }

The call to CoInitialize in the new thread initializes COM for use by that thread under the STA model. The single-threaded apartment model is requested implicitly in this call because it is the default. The above CoInitialize call is equivalent to:

  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

The newly created class factory is then registered with COM in the context of the new STA thread that is executing. The thread then enters a message loop in this common apartment procedure. The loop will not be exited until the thread receives a WM_QUIT message. We will see such a message sent below in the CloseFactories method. When the loop exits, the registered class factory is revoked with COM. Then a CoUninitialize call is made to complete the shutdown of the apartment.

Here is the CloseFactories method from SERVER.CPP.

  BOOL CServer::CloseFactories(void)
  {
    BOOL bOk = TRUE;
    HRESULT hr;

    LOGF1("L<%X>: CServer::CloseFactories. Begin.",TID);

    // Tell COM to release the last lock on the server process.
    // This essentially prevents COM from honoring any new requests for
    // objects during this inevitable shutdown.
    CoReleaseServerProcess();

    if (OwnThis())
    {
      // Shutdown the AptCar Apartment Thread.
      if (0 != m_dwAptCar)
      {
        LOGF1("L<%X>: CServer::CloseFactories. Terminate AptCar Apartment.",TID);
        bOk = PostThreadMessage(m_dwAptCar, WM_QUIT, 0, 0);
        if (!bOk)
        {
          hr = GetLastError();
          LOGF2("L<%X>: CServer::CloseFactories. AptCar failed. hr=0x%X.",TID,hr);
        }
      }

      // Shutdown the AptUtilityCar Apartment Thread.
      ...
      ... Code similar to above for AptCar
      ...

      // Shutdown the AptCruiseCar Apartment Thread.
      ...
      ... Code similar to above for AptCar
      ...

      if (m_pCFCar && m_pCFUtilityCar && m_pCFCruiseCar)
      {
        // Release any and all of the Class Factory interface pointers.
        RELEASE_INTERFACE(m_pCFCar);
        RELEASE_INTERFACE(m_paiAptCar->pcf);
        RELEASE_INTERFACE(m_pCFUtilityCar);
        RELEASE_INTERFACE(m_paiAptUtilityCar->pcf);
        RELEASE_INTERFACE(m_pCFCruiseCar);
        RELEASE_INTERFACE(m_paiAptCruiseCar->pcf);
        DELETE_POINTER(m_paiAptCar);
        DELETE_POINTER(m_paiAptUtilityCar);
        DELETE_POINTER(m_paiAptCruiseCar);

        // Give CServer back before waiting on threads to die.
        UnOwnThis();

        // Wait a very long time (in CPU terms) for the apartment threads
        // to exit/terminate before closing their thread handles.
        WaitForMultipleObjects(NUM_APARTMENTS, m_hApts, TRUE, LONG_WAIT);
        for (UINT i = 0; i<NUM_APARTMENTS; i++)
          if (NULL != m_hApts[i])
            CloseHandle(m_hApts[i]);
      }
      else
        UnOwnThis();
    }

    LOGF1("L<%X>: CServer::CloseFactories. End.",TID);

    return bOk;
  }

CloseFactories is called in the server application's CMainWindow destructor. The shutdown sequence is initiated by the server itself on the basis of the object and lock counts. For example, see the earlier listing of the ObjectsDown method of CServer. ObjectsDown detects when there are no longer any existing COM objects or lock counts. If so, it issues the following call causing an eventual execution of CMainWindow's destructor.

  PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);

When the application's main window is closed in response to the WM_CLOSE message, the main window procedure is called with the WM_DESTROY message. This message is sent when the window is being destroyed in response to a close of the window. Because CMainWindow is derived from APPUTIL's CVirWindow, WM_DESTROY is trapped by CVirWindow's WindowProc function where a delete of CMainWindow is performed. This runs the CMainWindow destructor which calls the CloseFactories method. CloseFactories posts WM_QUIT messages to the individual apartment threads. The WaitForMultipleObjects function is called above in CloseFactories to wait for the posted WM_QUIT messages to reach their intended threads and cause those threads to terminate. When all the apartment threads have signaled their termination, all the thread handles are closed in a for loop. The CMainWindow destructor finally issues a WM_QUIT message to the application's main STA thread and causes the exit of its message loop and a resultant exit of the entire server application.

The OpenFactories and CloseFactories methods use matching calls to the CoAddRefServerProcess and CoReleaseServerProcess functions. CoAddRefServerProcess is called in OpenFactories to tell COM to AddRef an internal lock count that COM maintins for the process. The count is used later during shutdown of the server (see the CoReleaseServerProcess early in CloseFactories) to signal COM not to honor any requests for new objects because the server is terminating and the factory will no longer exist. By the time execution reaches CloseFactories, an inevitable server shutdown is in progress and requests for new objects should be prevented.

In order to properly build this multithreaded application, the makefile used in the LOCSERVE sample needed some changes. The C++ compilation switches were changed to command the C++ compiler to compile a multithreaded program. Here is an example of the compile command line of the SERVER.CPP file, showing the use of the cvarsmt macro defined in WIN32.MAK file of the Platform SDK. Other single threaded EXE applications required only the use of the cvars macro.

  $(TDIR)\server.obj: server.cpp server.h $(PGM).h
  $(cc) $(cvarsmt) $(cflags) $(CDBG) -Fo$@ server.cpp

The link flags were changed to support the use of the olelibsmt macro defined in WIN32.MAK. Here is the LINKFLAGS macro.

  # If UNICODE=1 is defined then define UNICODE during Compiles.
  # The default is to compile with ANSI for running under both
  # Win95 and WinNT.
  !IFDEF UNICODE
  LINKFLAGS = $(ldebug) /NOD:libc.lib /NOD:msvcrt.lib /NOD:libcd.lib \
    /NOD:libcmtd.lib /NOD:msvcrtd.lib
  CDBG=$(cdebug) -DUNICODE -D_UNICODE
  RCFLAGS = -DWIN32 -DRC_INCLUDE -DUNICODE
  !ELSE
  LINKFLAGS = $(ldebug) /NOD:libc.lib /NOD:msvcrt.lib /NOD:libcd.lib \
    /NOD:libcmtd.lib /NOD:msvcrtd.lib
  CDBG=$(cdebug)
  RCFLAGS = -DWIN32 -DRC_INCLUDE
  !ENDIF

The /NOD (no default) switches to the linker permit the use of the olelibsmt in the final link command. Here it is.

  # Link the object and resource binaries into the final target binary.
  $(PGM).exe: $(PGMOBJS) $(TDIR)\$(PGM).res
    $(LINK) @<<
      $(LINKFLAGS)
      -out:$@
      -map:$(TDIR)\$*.map
      $(PGMOBJS)
      $(TDIR)\$*.res
      $(olelibsmt) $(APPLIBS)
  <<

The WIN32.MAK makefile include is located in the \MSSDK\INCLUDE directory of the installed Platform SDK. WIN32.MAK is included at the start of the makefiles of all code samples in this tutorial series.

Top Back to page top

© 1995-1998 Microsoft Corporation


APTCLIEN - Client of Multiple Apartment Server - Lesson 17

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

APTCLIEN - Client of Multiple Apartment Server

SUMMARY

The APTCLIEN sample shows a client of several single-threaded apartments in the APTSERVE server. APTCLIEN's main purpose is to exercise and reveal for study the multiple apartments in the APTSERVE local out-of-process server. The REMCLIEN sample (later in this series), run in conjunction with APTCLIEN, shows the effects of multiple clients on the APTSERVE multithreaded server, with one client controlling from a remote machine.

The COM objects that APTCLIEN manipulates are the car-related ones from previous samples in this tutorial series, with the following interfaces: ICar, IUtility, and ICruise. APTCLIEN works in conjunction with the separate APTSERVE.EXE, which provides the COCar, COUtilityCar, and COCruiseCar COM objects.

APTCLIEN.EXE creates its own COUtilityCruiseCar COM object by reusing the COCruiseCar COM object by containment and augmenting it with a native implementation of the IUtility interface. Like LOCCLIEN, APTCLIEN's COUtilityCruiseCar composite object reuses COCruiseCar by containment. However, because COCruiseCar and COCar are in different apartments, COCruiseCar reuses COCar by containment rather than the aggregation shown in previous lessons.

The composition of COUtilityCruiseCar is also interesting because the COUtilityCruiseCar object's containment of the COCruiseCar object crosses the process boundary between APTCLIEN and the out-of-process local server APTSERVE.EXE. In addition, COCruiseCar's containment of COCar crosses thread boundaries within APTSERVE. APTCLIEN uses standard marshaling support for the custom interfaces it uses on the COCruiseCar and COCar objects. This marshaling support is provided by the previous MARSHAL code sample, so you must build the MARSHAL code sample prior to building and running APTCLIEN and APTSERVE.

For functional descriptions and a tutorial code tour of APTCLIEN, see the Code Tour section in APTCLIEN.HTM. For details on the external user operation of APTCLIEN, see both the Usage and Operation sections in APTCLIEN.HTM. To read APTCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the APTCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the APTCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also APTSERVE.HTM in the main directory for more details on how APTSERVE works and exposes its services to APTCLIEN. You must build APTSERVE.EXE before building APTCLIEN. The makefile for APTSERVE automatically registers that server in the registry, so you must build APTSERVE before attempting to run APTCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the APTCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV APTCLIEN.DSP

You can also simply double-click the APTCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

APTCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by APTCLIEN.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, APTCLIEN.EXE is the client executable to run for this sample.

OPERATION

The APTCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, APTSERVE.EXE out-of-process local server. Here is a summary of operation from the standpoint of APTCLIEN.EXE as a COM client of the APTSERVE.EXE COM server.

The APTCLIEN and APTSERVE samples are directly analogous to the LOCCLIEN and LOCSERVE samples. The same components are used, and the same menu system exercises those objects.

As in the LOCCLIEN code sample, support is provided for a trace logging facility that will display in the APTCLIEN client application an integrated trace log of activity in both client and server.

Although APTCLIEN functions much like the prior LOCCLIEN sample, here is a short review for those readers who are visiting this code sample out of sequence. The COM objects that are used in the APTCLIEN and APTSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

APTCLIEN.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in APTSERVE.EXE. For details, see the APTSERVE lesson. COCruiseCar is also constructed using containment and is also implemented in APTSERVE.EXE. COUtilityCruiseCar is constructed using containment and is implemented in APTCLIEN.EXE. In this sample, COUtilityCruiseCar reuses COCruiseCar by containment, which in turn reuses COCar by containment to illustrate three levels of nested object reuse by containment.

APTCLIEN.EXE presents a menu for each of these four main COM objects. Each menu has commands that call the methods of the various available interfaces. The code samples (both APTCLIEN and APTSERVE) issue trace message log statements throughout. When you exercise the objects from APTCLIEN.EXE, the main APTCLIEN window will display a log of internal activity in these COM objects.

Here is a description of the menu operation of APTCLIEN.

Menu Selection: File/Exit
Exits APTCLIEN.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/APTCLIEN Tutorial
Opens the APTCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/APTSERVE Tutorial
Opens the APTSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/MARSHAL Tutorial
Opens the MARSHAL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About APTCLIEN
Displays the About dialog box for this application.

Menu Selection: Help/About APTSERVE
Displays the About dialog box for APTSERVE.EXE, which is used by this application.

CODE TOUR

Files          Description
APTCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
APTCLIEN.H     The include file for the APTCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
APTCLIEN.CPP   The main implementation file for APTCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
APTCLIEN.RC    The application resource definition file.
APTCLIEN.ICO   The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
APTCLIEN.DSP   Microsoft Visual Studio Project file.

Like all code samples in the series, APTCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

In addition to covering the coding techniques used, this lesson examines the internal behavior of the APTCLIEN and APTSERVE client/server pair. As a client, APTCLIEN offers nothing beyond what was shown in LOCCLIEN. The significant difference is simply the use of different CLSIDs (CLSID_AptCar, CLSID_AptUtilityCar, and CLSID_AptCruiseCar) for the creation of the APTSERVE objects to be controlled.

We will tour internal behavior in both APTCLIEN and APTSERVE by looking at some representative trace logs. Trace lines that begin with "C:" mark behavior reported in the APTCLIEN.EXE client. Lines that begin with "L:" mark behavior reported in the APTSERVE.EXE local out-of-process server. The internal APTSERVE trace lines will also show the thread ID of the executing thread in angle brackets after the "L". Since the client is single-threaded, its thread ID is not shown. These trace logs were obtained under the Windows 95 operating system. Because a multithreaded program is being traced, your results may be slightly different if you replicate this logged behavior on you own machine. For example, the server's AptThreadProc function will execute at different times depending on the operating system's task load and scheduling priorities.

After running APTCLIEN, choose the Create command from the Car menu. Here is the resulting trace log.

  C: === Car Menu: Create.
  L: APTSERVE now logging to client.
  L<FFFE7005>: CmdLine Switches= -Embedding
  L<FFFE7005>: CServer::OpenFactories. Begin.
  L: CServer::OwnThis. Thread <FFFE7005> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFE7005>.
  L<FFFE7005>: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<FFFE7005>: CFCar Constructor. m_pUnkOuter=0x0.
  L<FFFE7005>: CFUtilityCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<FFFE7005>: CFUtilityCar Constructor. m_pUnkOuter=0x0.
  L<FFFE7005>: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<FFFE7005>: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  L<FFFE7005>: CServer::OpenFactories. AptCar.
  L<FFFE7005>: CFCar::AddRef. New cRefs=1.
  L<FFFE7005>: CFCar::AddRef. New cRefs=2.
  L<FFFE7005>: CServer::OpenFactories. AptUtilityCar.
  L<FFFE7005>: CFUtilityCar::AddRef. New cRefs=1.
  L<FFFE7005>: CFUtilityCar::AddRef. New cRefs=2.
  L<FFFE7005>: CServer::OpenFactories. AptCruiseCar.
  L<FFFE7005>: CFCruiseCar::AddRef. New cRefs=1.
  L: AptThreadProc. Starting Apartment Thread <FFFEE289>.
  L: AptThreadProc. Starting Apartment Thread <FFFEE4B1>.
  L<FFFE7005>: CFCruiseCar::AddRef. New cRefs=2.
  L: AptThreadProc. Registering class factory of apartment <FFFEE289>.
  L: AptThreadProc. Registering class factory of apartment <FFFEE4B1>.
  L<FFFEE289>: CFCar::AddRef. New cRefs=3.
  L<FFFEE4B1>: CFUtilityCar::AddRef. New cRefs=3.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFE7005>.
  L<FFFE7005>: CServer::OpenFactories. End.
  L: AptThreadProc. Starting Apartment Thread <FFFEE811>.
  L: AptThreadProc. Registering class factory of apartment <FFFEE811>.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=3.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<FFFEE289>: CFCar::AddRef. New cRefs=4.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L<FFFEE289>: CFCar::AddRef. New cRefs=5.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: CFCar::AddRef. New cRefs=6.
  L<FFFEE289>: CFCar::AddRef. New cRefs=7.
  L<FFFEE289>: CFCar::Release. New cRefs=6.
  L<FFFEE289>: CFCar::AddRef. New cRefs=7.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE289>: CFCar::AddRef. New cRefs=8.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE289>: CFCar::Release. New cRefs=7.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE289>: CFCar::AddRef. New cRefs=8.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE289>: CFCar::Release. New cRefs=7.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L<FFFEE289>: CFCar::Release. New cRefs=6.
  L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<FFFEE289>: COCar::CImpICar Constructor. Non-Aggregating.
  L<FFFEE289>: COCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>.
  L<FFFEE289>: CServer::ObjectsUp. New cObjects=1.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>.
  L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: COCar::AddRef. New cRefs=1.
  L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870658.
  L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: COCar::AddRef. New cRefs=2.
  L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: COCar::AddRef. New cRefs=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=4.
  L<FFFEE289>: COCar::Release. New cRefs=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=4.
  L<FFFEE289>: COCar::Release. New cRefs=3.
  L<FFFEE289>: COCar::Release. New cRefs=2.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<FFFEE289>: CFCar::Release. New cRefs=5.
  L<FFFEE289>: CFCar::Release. New cRefs=4.
  L<FFFEE289>: CFCar::Release. New cRefs=3.

This log shows that the following threads are started.

  <FFFE7005> - Main thread for the APTSERVE local server application.
  <FFFEE289> - AptCar component apartment thread.
  <FFFEE4B1> - AptUtilityCar component apartment thread.
  <FFFEE811> - AptCruiseCar component apartment thread.

We will abbreviate these to <05>, <89>, <B1>, and <11>. The main thread, <05>, is also called the main STA thread and could be exploited more fully as an apartment in the strict sense. But it is not exploited as such in this code sample. All four of these threads are separate single-threaded apartments.

The main thread, <05>, executes the OpenFactories method. We see the use of the CThreaded ownership mechanism, with the main thread requesting and getting ownership of CServer. On this main STA thread we see the constructors for each class factory. The class factories create the separate STA threads for the AptCar, AptUtilityCar, and AptCruiseCar objects. At some variable length of time after the thread creation calls, the operating system calls each thread's AptThreadProc function to start the thread. We see these calls reported as the threads for AptCar (<89>), AptUtilityCar (<B1>), and AptCruiseCar (<11>) are started. Within AptThreadProc, each thread also registers the STA apartment's new class factory with COM. The main STA thread, <05>, relinquishes ownership of CServer, and the log then reports that OpenFactories has ended.

We then see the CFCar class factory used to create the requested new COCar object. CFCar and the constructor of the new COCar object both execute on the AptCar thread, <89>. When the class factory is successful in creating the COCar object, we see the server's object count incremented to 1 inside the protection of the OwnThis ownership by thread <89>. We then see the delegated release of the IClassFactory held by the client. COM still holds three outstanding references to the registered CFCar class factory and two outstanding references to the COCar object.

Next choose the ICar::Shift command from the Car menu to see the client call the interface methods of the new COCar object.

  C: === Car Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<FFFEE289>: COCar::AddRef. New cRefs=3.
  L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=0.
  L<FFFEE289>: COCar::Release. New cRefs=2.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<FFFEE289>: COCar::AddRef. New cRefs=3.
  C: Interface obtained. *ppv=0x520BC4
  C: --Calling pICar->Shift
  L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=1.
  C: --Releasing pICar

In the process of obtaining the interface pointer, ICar's own AddRef and Release functions are called. Before this Shift call, COCar had two outstanding references. Afterwards, it ended with three references prior to the actual call to pICar->Shift. The Shift method call indicates this is the first call to this object instance's ICar interface.

We see COM efficiency at work if we choose the ICar::Shift command from the Car menu again.

  C: === Car Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: Interface obtained. *ppv=0x520BC4
  C: --Calling pICar->Shift
  L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=2.
  C: --Releasing pICar

COM had cached the interface information during the previous ICar::Shift call, so that on this second call the interface pointer was returned directly without QueryInterface calls to the COCar object. The actual Shift call is executed on thread <89>. This call was marshaled from the client process to this server's AptCar thread, <89>. The ICar interface call counter indicates 2 for this second call to ICar::Shift.

To show the functioning of a composite multithreaded object in the server, choose the Create command from the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: Create.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpICar Constructor. Non-Aggregating
  C: COUtilityCruiseCar::CImpICruise Constructor. Non-Aggregating
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init. pUnkOuter=0x870138
  C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory.
  L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=4.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=5.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE811>: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=6.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=7.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=6.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=7.
  L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=8.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=7.
  L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=8.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=7.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=6.
  C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained.
  C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<FFFEE811>: COCruiseCar::CImpICar Constructor. Non-Aggregating
  L<FFFEE811>: COCruiseCar::CImpICruise Constructor. Non-Aggregating.
  L<FFFEE811>: COCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <FFFEE811> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE811>.
  L<FFFEE811>: CServer::ObjectsUp. New cObjects=2.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE811>.
  L<FFFEE811>: COCruiseCar::Init.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<FFFEE289>: CFCar::AddRef. New cRefs=4.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L<FFFEE289>: CFCar::AddRef. New cRefs=5.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: CFCar::AddRef. New cRefs=6.
  L<FFFEE289>: CFCar::AddRef. New cRefs=7.
  L<FFFEE289>: CFCar::Release. New cRefs=6.
  L<FFFEE289>: CFCar::AddRef. New cRefs=7.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE289>: CFCar::AddRef. New cRefs=8.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE289>: CFCar::Release. New cRefs=7.
  L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned.
  L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<FFFEE289>: CFCar::AddRef. New cRefs=8.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<FFFEE289>: CFCar::Release. New cRefs=7.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L<FFFEE289>: CFCar::Release. New cRefs=6.
  L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<FFFEE289>: COCar::CImpICar Constructor. Non-Aggregating.
  L<FFFEE289>: COCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>.
  L<FFFEE289>: CServer::ObjectsUp. New cObjects=3.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<FFFEE289>: COCar::AddRef. New cRefs=1.
  L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x8706BC.
  L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating.
  L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=2.
  L<FFFEE289>: COCar::AddRef. New cRefs=2.
  L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating.
  L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating.
  L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating.
  L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: COCar::AddRef. New cRefs=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=4.
  L<FFFEE289>: COCar::Release. New cRefs=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=4.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=5.
  L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=2.
  L<FFFEE289>: COCar::Release. New cRefs=4.
  L<FFFEE289>: COCar::QueryInterface. pICar returned.
  L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=3.
  L<FFFEE289>: COCar::AddRef. New cRefs=5.
  L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=2.
  L<FFFEE289>: COCar::Release. New cRefs=4.
  L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=1.
  L<FFFEE289>: COCar::Release. New cRefs=3.
  L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<FFFEE289>: CFCar::Release. New cRefs=5.
  L<FFFEE289>: CFCar::Release. New cRefs=4.
  L<FFFEE289>: CFCar::Release. New cRefs=3.
  L<FFFEE811>: COCruiseCar::Init (New Containment of COCar) Succeeded.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned.
  L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=1.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=1.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870698.
  L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned.
  L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=2.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=2.
  L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L<FFFEE811>: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=3.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=3.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned.
  L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=5.
  L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=4.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned.
  L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=5.
  L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=4.
  L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=1.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=3.
  C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory.
  L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=5.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=4.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=3.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICar returned
  L<FFFEE811>: COCruiseCar::CImpICar::Addref. Delegating. New cI=1.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4.
  L<FFFEE811>: COCruiseCar::CImpICar::Release. Delegating. New cI=0.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=3.
  L<FFFEE811>: COCruiseCar::QueryInterface. pICar returned
  L<FFFEE811>: COCruiseCar::CImpICar::Addref. Delegating. New cI=1.
  L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4.
  C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x870138.

COUtilityCruiseCar is a composite COM object created in the client. It reuses the COCruiseCar COM object provided by the server. COCruiseCar in turn reuses the COCar COM object provided by the server. In the server, we see the CruiseCar class factory executing on the AptCruiseCar thread, <11>, to create a new COCruiseCar whose constructor also runs on this thread. The server object count is incremented to 2 by this thread when it obtains ownership of CServer. The object count is now 2, one for the original COCar created at the start of this lesson and one for the new COCruiseCar.

To complete the creation of COCruiseCar its Init method is called on the AptCruiseCar thread, <11>. We see the resulting CFCar class factory executed on the AptCar thread, <89>. The creation of this new COCar object is completed on this thread, and it increments the server's object count to 3. Through the remainder of the log, the behavior winds back out of the object creations with COM, causing various QueryInterface, AddRef, and Release calls. Upon completion, there are three outstanding references on CFCar, three on the new COCar object, three on CFCruiseCar, and four on the new COCruiseCar object. Most of these outstanding references to our custom interfaces are held within COM.

Now choose the ICar::Shift command from the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: COUtilityCruiseCar::QueryInterface. pICar returned
  C: COUtilityCruiseCar::CImpICar::Addref. Delegating. New cI=1.
  C: COUtilityCruiseCar::AddRef. New cRefs=2.
  C: Interface obtained. *ppv=0x870144
  C: --Calling pICar->Shift
  C: COUtilityCruiseCar::CImpICar::Shift. Delegating. nGear=1.
  L<FFFEE811>: COCruiseCar::CImpICar::Shift. Delegating. nGear=1.
  L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=1.
  C: --Releasing pICar
  C: COUtilityCruiseCar::CImpICar::Release. Delegating. New cI=0.
  C: COUtilityCruiseCar::Release. New cRefs=1.

The interface obtained is on the composite COUtilityCruiseCar object that is created within the client. Using this interface, the Shift call is delegated to the contained COCruiseCar object. At this point COM has to marshal the call from the APTCLIEN client process to thread <11>, where the COCruiseCar object resides. COCruiseCar is a composite object and delegates the ICar::Shift call to its contained COCar object. COM now has to marshal the call again within the server from the COCruiseCar apartment thread, <11>, to the COCar thread, <89>. On this AptCar thread, <89>, we see the Shift action finally performed.

For this call to ICar::Shift on the COUtilityCruiseCar object, COM enforces the STA apartment model and ensures that, regardless of the calling thread, the call performed in the particular object is executed on the STA thread of that object. The ICar interface call counter shows 1, indicating a first call to this interface on the new contained COCar object. This count confirms that this is a new instance of COCar. If the COCar originally created at the start of this lesson had been somehow mistakenly used, we would see an interface call count of 3 because of the two Shift calls to that COCar object earlier in this lesson.

To watch the shutdown of the COUtilityCruiseCar object, choose the Release command from the UtilityCruiseCar menu.

  C: === UtilityCruiseCar Menu: Release.
  C: COUtilityCruiseCar::Release. New cRefs=0.
  C: COUtilityCruiseCar::Destructor.
  L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=0.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=3.
  L<FFFEE811>: COCruiseCar::CImpICar::Release. Delegating. New cI=0.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=2.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=1.
  L<FFFEE811>: COCruiseCar::Release. New cRefs=0.
  L: CServer::OwnThis. Thread <FFFEE811> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE811>.
  L<FFFEE811>: CServer::ObjectsDown. New cObjects=2.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE811>.
  L<FFFEE811>: COCruiseCar::Destructor.
  L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=0.
  L<FFFEE289>: COCar::Release. New cRefs=2.
  L<FFFEE289>: COCar::Release. New cRefs=1.
  L<FFFEE289>: COCar::Release. New cRefs=0.
  L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>.
  L<FFFEE289>: CServer::ObjectsDown. New cObjects=1.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>.
  L<FFFEE289>: COCar::Destructor.
  L<FFFEE289>: COCar::CImpICar Destructor.
  L<FFFEE811>: COCruiseCar::CImpICruise Destructor.
  L<FFFEE811>: COCruiseCar::CImpICar Destructor.
  C: COUtilityCruiseCar::CImpIUtility Destructor.
  C: COUtilityCruiseCar::CImpICruise Destructor.
  C: COUtilityCruiseCar::CImpICar Destructor.

This event causes the COUtilityCruiseCar destructor to run. It releases the IUnknown of the contained COCruiseCar object. In the server, we see the resulting releases of interfaces on COCruiseCar held by the client and by COM. These releases run on the AptCruiseCar thread, <11>, and decrement the COCruiseCar reference count to 0, forcing the destruction of the COCruiseCar object. Thread <11> gains ownership of CServer and decrements the server's object count to 2. The COCruiseCar destruction causes this object to release its hold on its contained COCar. We see the resulting releases of the interfaces on COCar held by COCruiseCar and by COM. These run on the AptCar thread, <89>, and decrement of the COCar reference count to 0, which forces the destruction of the COCar object. The AptCar thread, <89>, gains ownership of CServer and decrements the server's object count to 1. Finally, we see the destructors for all the various interface implementation C++ objects.

The resulting object count of 1 reflects the remaining existence of the original COCar object created at the start of this lesson. We will now release this only remaining object supplied by APTSERVE and watch the server shutdown. From the Car menu, choose Release.

  C: === Car Menu: Release.
  L<FFFEE289>: COCar::Release. New cRefs=2.
  L<FFFEE289>: COCar::Release. New cRefs=1.
  L<FFFEE289>: COCar::Release. New cRefs=0.
  L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>.
  L<FFFEE289>: CServer::ObjectsDown. New cObjects=0.
  L<FFFEE289>: CServer::ObjectsDown. Closing down APTSERVE server.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>.
  L<FFFE7005>: CServer::CloseFactories. Begin.
  L<FFFEE289>: COCar::Destructor.
  L: CServer::OwnThis. Thread <FFFE7005> waiting to own CServer.
  L<FFFEE289>: COCar::CImpICar Destructor.
  L: CServer::OwnThis. CServer now owned by Thread <FFFE7005>.
  L<FFFE7005>: CServer::CloseFactories. Terminate AptCar Apartment.
  L<FFFE7005>: CServer::CloseFactories. Terminate AptUtilityCar Apartment.
  L: AptThreadProc. Revoking class factory of apartment <FFFEE289>.
  L<FFFE7005>: CServer::CloseFactories. Terminate AptCruiseCar Apartment.
  L: AptThreadProc. Revoking class factory of apartment <FFFEE4B1>.
  L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFE7005>: CServer::CloseFactories. Releasing all Classfactory interfaces.
  L<FFFEE289>: CFCar::AddRef. New cRefs=4.
  L<FFFE7005>: CFCar::Release. New cRefs=3.
  L<FFFE7005>: CFCar::Release. New cRefs=2.
  L: AptThreadProc. Revoking class factory of apartment <FFFEE811>.
  L<FFFEE4B1>: CFUtilityCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE289>: CFCar::Release. New cRefs=1.
  L<FFFE7005>: CFUtilityCar::Release. New cRefs=2.
  L<FFFEE811>: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<FFFEE4B1>: CFUtilityCar::AddRef. New cRefs=3.
  L<FFFEE289>: CFCar::Release. New cRefs=0.
  L<FFFE7005>: CFUtilityCar::Release. New cRefs=2.
  L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=4.
  L<FFFEE4B1>: CFUtilityCar::Release. New cRefs=1.
  L<FFFEE289>: CFCar::Destructor.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=3.
  L<FFFEE4B1>: CFUtilityCar::Release. New cRefs=0.
  L<FFFEE289>: CFCar::CImpIClassFactory Destructor.
  L<FFFE7005>: CFUtilityCar::Destructor.
  L<FFFEE811>: CFCruiseCar::Release. New cRefs=2.
  L: AptThreadProc. Apartment Thread <FFFEE4B1> Terminated.
  L<FFFE7005>: CFUtilityCar::CImpIClassFactory Destructor.
  L: AptThreadProc. Apartment Thread <FFFEE811> Terminated.
  L: AptThreadProc. Apartment Thread <FFFEE289> Terminated.
  L<FFFE7005>: CFCruiseCar::Release. New cRefs=1.
  L<FFFE7005>: CFCruiseCar::Release. New cRefs=0.
  L<FFFE7005>: CFCruiseCar::Destructor.
  L<FFFE7005>: CFCruiseCar::CImpIClassFactory Destructor.
  L: CServer::UnOwnThis. Ownership relinquished by <FFFE7005>.
  L<FFFE7005>: CServer::CloseFactories. End.
  L<FFFE7005>: Exiting APTSERVE local server application.

The client releases the IUnknown of the COCar object. In the server, we see a resulting cascade of releases of COCar interfaces held by the client and by COM. These are executed on the AptCar thread, <89>, and decrement the COCar reference count to 0, which forces the destruction of the COCar object. Thread <89> gains ownership of CServer and decrements the server's object count to 0, triggering a shutdown of the entire server. This happens in the ObjectsDown method, where a WM_CLOSE message is sent to the main thread, <05>, of the server process. This message causes an exit of the server application's message loop. The server's CServer::CloseFactories method is then called.

Within CloseFactories, we see the unregistering of the class factories with COM and the termination of the AptCar, AptUtilityCar, and AptCruiseCar threads. The apartment thread message loops are exited after WM_QUIT messages are sent to each apartment thread from CloseFactories, which is running on the main thread, <05>. Before CloseFactories returns, we see the releases of the interfaces held on the class factories. These releases decrement each factory's reference count to 0 and cause the destructors to run for the CFCar, CFUtilityCar, and CFCruiseCar objects. After CloseFactories, execution exits the main application, and the operating system unloads APTSERVE.

Top Back to page top

© 1995-1998 Microsoft Corporation


REMCLIEN - DCOM Remote Client - Lesson 18

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

REMCLIEN - Distributed COM (DCOM) Remote Client

SUMMARY

The REMCLIEN sample shows how a client can access and control components in an out-of-process remote server. In this lesson the previously studied out-of-process server APTSERVE is used, but now it is accessed across machine boundaries by REMCLIEN. In earlier lessons, APTCLIEN accessed APTSERVE's components on the same machine by crossing only process and thread boundaries.

No changes to APTSERVE are required for REMCLIEN to work. Distributed COM (DCOM) gives COM support for local/remote transparency between client and server.

The REMCLIEN sample presents the car-related components that were studied in previous lessons. These COM objects use the following interfaces: ICar, IUtility, and ICruise. REMCLIEN works in conjunction with the separate APTSERVE.EXE, which provides the COCar, COUtilityCar, and COCruiseCar COM objects.

REMCLIEN.EXE creates its own COUtilityCruiseCar COM object by reusing the COCruiseCar COM object by containment and augmenting it with a native implementation of the IUtility interface. Because the COCruiseCar COM object class is a composite--that is, it reuses an inner COCar object by containment--REMCLIEN illustrates nested reuse of COM objects. The composite COUtilityCruiseCar object reuses COCruiseCar, another composite COM object, by containment. COCruiseCar further reuses the COCar COM object by containment.

The composition of COUtilityCruiseCar is also interesting because of the COUtilityCruiseCar object's containment of reuse the COCruiseCar object crosses the machine boundary between REMCLIEN and the out-of-process remote server APTSERVE.EXE. REMCLIEN uses standard marshaling support for the custom interfaces it uses on the COCruiseCar object. This marshaling support was separately built as MARSHAL.DLL in the earlier MARSHAL code sample.

Because client and server reside in separate processes on different machines, both REMCLIEN and APTSERVE rely on marshaling for the ICar, IUtility, and ICruise interfaces. This support was provided by the previous MARSHAL code sample, so you must also build the MARSHAL code sample on both machines prior to building and running REMCLIEN with APTSERVE.

To set up for the proper operation of REMCLIEN on one machine to control APTSERVE on the other, both machines must have DCOM (Distributed COM) installed. This is the case in Windows NT 4.0 or above and in Windows 98. It is also the case in Windows 95 with DCOM95 installed. Earlier versions of these operating systems do not support Distributed COM. The two machines must be connected in a properly configured network. For details on setting up a network, see your Windows NT product documentation or the Windows NT Resource Kit. Also see the "Network and Setup Issues" section at the end of this tutorial text for more details on machine and network setup for running REMCLIEN.

For functional descriptions and a tutorial code tour of REMCLIEN, see the Code Tour section in REMCLIEN.HTM. For details on the external user operation of REMCLIEN, see both the Usage and Operation sections in REMCLIEN.HTM. To read REMCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the REMCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the REMCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also APTSERVE.HTM in the main tutorial directory for more details on how APTSERVE works and exposes its services to REMCLIEN. The makefile for APTSERVE automatically registers that server in the registry of the host machine, so you must build APTSERVE on the remote machine before attempting to run REMCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the REMCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV REMCLIEN.DSP

You can also simply double-click the REMCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

REMCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by REMCLIEN. REMCLIEN will currently run on the Windows 95 operating system with the DCOM95 update for Win95 installed. It will run on the Windows 98 operating system. It will also run under version 4 or higher of Windows NT Workstation or Windows NT Server.

OPERATION

The REMCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, APTSERVE.EXE out-of-process remote server. Here is a summary of operation from the standpoint of REMCLIEN.EXE as a client of APTSERVE.EXE.

The REMCLIEN and APTSERVE samples are directly analogous to the APTCLIEN and APTSERVE samples presented in earlier lessons. The same components are used, and almost the same menu system exercises those objects. The main difference is that the client must use the standard marshaling support provided by the MARSHAL.DLL server for the interfaces that reside across machine boundaries rather than across process boundaries. This standard marshaling is largely transparent if you use the Microsoft Interface Definition Language (MIDL) compiler to build a standard marshaling DLL.

Since two machines are involved in this lesson, we will refer to the machine where APTSERVE runs as Machine-S (server) and to the machine where REMCLIEN runs as Machine-C (client).

To run REMCLIEN and obtain the log listings for this tutorial, Machine-C was set up to compile Win32 applications, and all of the COM tutorial code samples were compiled there under Windows NT 4.x. Among other things, this build process registered the necessary interfaces in the registry on Machine-C for standard marshaling using the MARSHAL marshaling server on that machine. Using the C$ drive share predefined on Machine-S, a complete copy of the built \MSSDK\SAMPLES\COM\TUTSAMP branch was copied across the network from Machine-C to Machine-S.

With all the sample executables on Machine-S, the REGALL batch file was run from the TUTSAMP directory there. This registered the APTSERVE server and its interfaces in the registry on Machine-S. The MARSHAL standard marshaling server located on that machine registers the interfaces there.

Alternately, you can copy the uncompiled source of the samples branch from Machine-C to Machine-S and run MAKEALL.BAT on Machine-S. This will perform a complete build of all the executables and automatically register the APTSERVE and MARSHAL servers on Machine-S. However, it is quicker to perform a complete build on one machine and copy the TUTSAMP branch (with built executables) to the other machine and run REGALL.BAT on the other machine.

With the executables present and the servers properly registered on both machines, the REMCLIEN application can be run on Machine-C. From REMCLIEN you specify the network name of Machine-S. Then when you create an object from REMCLIEN you rely on COM to automatically locate and load the remote APTSERVE server on Machine-S. You must be logged on under the same user name and password on both machines, and those accounts on both machines must have Administrator privileges. See the "Network and Setup Issues" section below for more details.

As in the APTCLIEN code sample, support is provided in REMCLIEN for a trace logging facility. However, REMCLIEN displays only the trace log of its own internal client behavior. Because the APTSERVE server is running on a remote machine, APTSERVE's own trace logging facility is used to display its internal behavior on the remote Machine-S display.

To run client and server so that both trace logs are displayed on each machine, start APTCLIEN on Machine-S before running REMCLIEN on Machine-C. On Machine-S, from the APTCLIEN Car menu, choose Create. This command will load the APTSERVE out-of-process server on Machine-S. As in the APTCLIEN and LOCCLIEN lessons, loading APTSERVE will provide an integrated logging display of the internal behavior of both APTCLIEN and APTSERVE on Machine-S.

You can then start REMCLIEN on Machine-C, where its internal client behavior will be logged in its own separate trace log display on Machine-C. From the REMCLIEN File menu, choose Set Remote Machine to set the remote machine's name (for example, Machine-S) in the Remote Server Info dialog box. This name is the network machine name of the computer where the remote APTSERVE server is located and registered. You must specify this name before you can run REMCLIEN. DCOM permits the machine name to be specified in a valid UNC format (for example, Machine-S or \\Machine-S) or DNS format (for example, www.mynode.com or 135.5.33.19).

If you then use REMCLIEN's Car menu to create a car object (from Machine-C), COM will find APTSERVE already loaded on Machine-S and will access this loaded instance. You will then see the log (on Machine-S) of APTSERVE's internal server behavior as you control it from either APTCLIEN on Machine-S or from REMCLIEN on machine-C. This example illustrates how a single COM server can be controlled simultaneously from multiple clients, in this case one local and one remote. APTSERVE supports such multiple client access to it by partitioning the COM server into separate apartment model threads and ensuring mutually exclusive access to shared server and class factory data.

By configuring the server trace log in the manner used above, you can see the inner behavior of a multithreaded server with multiple clients. On Machine-S you can freely create and manipulate components in APTSERVE, treating it as an out-of-process local server from APTCLIEN (on the same machine). At the same time, on Machine-C you can freely create and manipulate other component instances in APTSERVE, treating it as an out-of-process remote server from REMCLIEN. In both cases you can watch internal APTSERVE behavior in the APTCLIEN trace log display on Machine-S.

Although REMCLIEN functions much like APTCLIEN, LOCCLIEN, DLLCLIEN and COMUSER, we'll give a short review for those readers who are visiting this code sample out of sequence. The COM objects that are used in the REMCLIEN and APTSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.

REMCLIEN.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in APTSERVE.EXE. For details, see the APTSERVE lesson. COCruiseCar is constructed using containment and is also implemented in APTSERVE.EXE. COUtilityCruiseCar is constructed using containment and is implemented in REMCLIEN.EXE. In this sample, COUtilityCruiseCar reuses COCruiseCar by containment to illustrate nested COM object reuse that spans thread and machine boundaries.

REMCLIEN.EXE presents a menu for each of these four main COM objects. Each menu has commands that call the methods of the various available interfaces. The code samples (both REMCLIEN and APTSERVE) have trace message log statements throughout. When you exercise the objects from REMCLIEN.EXE, the main REMCLIEN window will display a log of only the internal client activity on Machine-C. As described above, the trace log of internal APTSERVE server activity will be displayed on Machine-S.

Menu Selection: File/Exit
Exits REMCLIEN.

Menu Selection: File/Set Remote Machine...
Displays the Remote Server Info dialog box, in which you specify the network machine name of the computer where the remote APTSERVE server is located. You must specify this name in a valid UNC or DNS format before you can create and operate any remote APTSERVE COM objects from REMCLIEN. Example machine name: \\MyRemoteMachine.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside the menu item indicates that logging is on. Logging can be engaged but simply turned on or off. Unchecking this command turns the trace message logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows Clipboard.

Menu Selection: Help/REMCLIEN Tutorial
Opens the REMCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/APTSERVE Tutorial
Opens the APTSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/MARSHAL Tutorial
Opens the MARSHAL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About REMCLIEN
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

CODE TOUR

Files          Description
REMCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
REMCLIEN.H     The include file for the REMCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
REMCLIEN.CPP   The main implementation file for REMCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
REMCLIEN.RC    The application resource definition file.
REMCLIEN.ICO   The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.
REMCLIEN.DSP   Microsoft Visual Studio Project file.

Like all code samples in the series, REMCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

REMCLIEN is consistent with earlier lessons to highlight the comparison of out-of-process remote servers with out-of-process local servers. REMCLIEN retains the same set of COM objects as the previous LOCCLIEN lesson. A matching menu system to manipulate these objects is also retained. For details on the implementation and functionality of these COM objects, see the code tours for LOCCLIEN, LOCSERVE, APTCLIEN, and APTSERVE.

This lesson examines the client code needed to support cross-machine remote manipulation of the components in the multiple apartments of the APTSERVE server. We will tour the client source code and its internal behavior using representative trace logs.

REMCLIEN needs COM's DCOM facility to run. Early in its WinMain function, REMCLIEN calls APPUTIL's DComOk function to confirm that DCOM is installed on the system. DComOk does this by ensuring that the CoInitializeEx function is exported from the OLE32.DLL system library. CoInitializeEx was initially supplied with the DCOM functionality in Windows NT 4.0 and its presence is thus a simple way to confirm that DCOM is present. Simularly, DComOk will return FALSE on Windows 95 if the DCOM95 add-on is not installed.

No changes to the APTSERVE server sample need to be made, and only minimal changes are needed to transform the APTCLIEN local client into the REMCLIEN remote client. This emphasizes COM's local/remote transparency. The following code tour will thus focus on evolving the APTCLIEN code into the REMCLIEN code.

The main changes center on a different way of using COM to create instances of the components housed in the remote server. The CoGetClassObject call is made with appropriate parameters to direct COM to locate, load, and set up standard marshaling for components in the remote server.

The most important new information that CoGetClassObject needs is defined in a COSERVERINFO structure. Here is the code for this in REMCLIEN.CPP.

  // Here is a structure for storing the remote server info.
  COSERVERINFO g_ServerInfo;
  // Storage for the user entered remote machine name.
  TCHAR g_szMachineName[MAX_PATH] = TEXT("");
  OLECHAR g_wszMachineName[MAX_PATH];

The important member of this structure is a pointer to string storage for the network name of the machine where the remote COM server resides. Above are two such string variables: g_szMachineName is used for normal user input string storage from a dialog box, and g_wszMachineName is the actual storage pointed to by the g_ServerInfo structure. g_wszMachineName is used by COM and must be of Unicode type OLECHAR. Depending on whether REMCLIEN is compiled for Unicode, a string conversion may be required to obtain the Unicode version of the string. The g_ServerInfo structure must be initialized with the pointer to g_wszMachineName. This is done in the application's InitApplication function in REMCLIEN.CPP as follows

    ...
    // Zero the g_ServerInfo structure.
    memset(&g_ServerInfo, 0, sizeof(COSERVERINFO));

    // Init the remote machine name.
    g_wszMachineName[0] = 0;
  #ifdef UNICODE
    g_ServerInfo.pwszName = &g_szMachineName[0];
  #else
    g_ServerInfo.pwszName = &g_wszMachineName[0];
  #endif
    ...

First, a memset call is used to zero the entire g_ServerInfo structure contents. If the code is compiled for Unicode, no conversions are needed, and the g_ServerInfo.pwszName pointer is initialized to the TCHAR string array, g_szMachineName. Otherwise, it is initialized to point to a string array that is explicitly defined as type OLECHAR, g_wszMachineName.

We will examine later how the string is assigned a value. For now, we assume this string has the correct machine name. The g_ServerInfo structure is used when component instances are created at the request of the REMCLIEN user: Car, UtilityCar, and CruiseCar in source file REMCLIEN.CPP; UtilityCruiseCar in source file UTCRUCAR.CPP. For example, when the user requests creation of a CruiseCar component, the following code in REMCLIEN'S DoMenu function creates the remote COCruiseCar COM object residing in the APTSERVE server. The following code is found in REMCLIEN.CPP.

    ...
    case IDM_CCAR_CREATE:
      LOG("C: === CruiseCar Menu: Create.");
      if (NULL == m_pCruiseCar)
      {
        // Call COM services to create a remote instance.
        hr = CreateRemote(
               &g_ServerInfo,
               CLSID_AptCruiseCar,
               (PPVOID)&m_pCruiseCar);
        if (SUCCEEDED(hr))
        {
          ::CheckMenuItem(
              hMenu,
              IDM_CCAR_CREATE,
              MF_BYCOMMAND | MF_CHECKED);
          LOG("C: CruiseCar creation succeeded.");
        }
        else
          LOG("C: ???? CruiseCar creation failed.");
      }
      else
        LOG("C: ???? CruiseCar already exists.");
      break;
    ...

A common CreateRemote function is used in several such creation calls in DoMenu. Here is its definition in REMCLIEN.CPP.

  HRESULT CreateRemote(
            COSERVERINFO* pServerInfo,
            REFCLSID rclsid,
            PPVOID ppv)
  {
    HRESULT hr = E_FAIL;
    IClassFactory* pICF = NULL;
    HCURSOR hCurPrev;
    HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);

    // Ensure a remote machine name was specified by user.
    if (pServerInfo->pszName[0])
    {
      // Change cursor to the hour glass. Things could take awhile working
      // across the network.
      hCurPrev = SetCursor(hCurWait);

      // Get the COM Object Class Factory.
      hr = CoGetClassObject(
             rclsid,
             CLSCTX_REMOTE_SERVER,
             pServerInfo,
             IID_IClassFactory,
             (PPVOID)&pICF);
      LOGERROR("C:CoGetClassObject.",hr);
      if (SUCCEEDED(hr))
      {
        // Use Class Factory to create an instance of the COM object.
        hr = pICF->CreateInstance(
                     NULL,
                     IID_IUnknown,
                     ppv);
        pICF->Release();
        LOGERROR("C:CreateInstance.",hr);
      }

      // Set Cursor back to what it was.
      SetCursor(hCurPrev);
    }
    else
    {
      LOG("C: ???? Specify remote machine name first.");
    }

    return hr;
  }

This is a familiar sequence. A call to CoGetClassObject is used to obtain a class factory interface for a component. The class factory is then called to create an instance of the COM object and to return an interface pointer for the new object. The significant difference in REMCLIEN is how the CLSCTX_REMOTE_SERVER parameter is used to direct COM to find the server remotely. A pointer to the g_ServerInfo structure is also passed to provide the necessary network machine name of the computer where the remote server resides.

The LOGERROR macro is used to check an HRESULT for error and if necessary, fetch the associated human-readable error message string from the system tables and log that message for display. See APPUTIL.HTM for more details on the use of the LOGERROR macro.

In this code sample, a remote server is explicitly requested. Here are some of the important CLSCTX_* values that can be passed for the execution context. Others are also honored. See the Platform SDK documentation for more details.

  CLSCTX_INPROC_SERVER
    Load the in-process server (DLL).
  CLSCTX_LOCAL_SERVER
    Load the out-of-process local server (EXE).
  CLSCTX_REMOTE_SERVER
    Load the out-of-process remote server (EXE).
  CLSCTX_SERVER
    Load in-process, out-of-process local, or out-of-process remote server
    with COM making the attempt in that order.

For example, if the code above had specified CLSCTX_SERVER, then COM would consult the Registry and find the requested CLSID_AptCruiseCar component in the APTSERVE server on the local machine first, rather than attempting to find the one on the remote machine.

COM supports other mechanisms not covered in this lesson for designating how and where a remote component is instantiated. There are facilities for launch security, access security, and call-level security. See the DCDSERVE sample for more details.

The REMCLIEN client could be configured to run the server remotely at a particular machine whenever the CLSCTX_REMOTE_SERVER flag is passed to an activation function without a COSERVERINFO structure. This is done by adding an appropriate \HKEY_CLASSES_ROOT\CLSID\[clsid]\RemoteServerName key for the CLSID of the component in the registry of the client machine. However, if a subsequent activation function is called (for example, CoCreateInstance) that passes a COSERVERINFO structure, it will override the RemoteServerName setting of the component's CLSID in the registry.

Once the new COM object is created, REMCLIEN can use its interfaces, as in the previous APTCLIEN code sample. For example, the ICar::Shift method on the remote COM object can be called across machine boundaries. The standard marshaling provided by the MARSHAL server takes care of marshaling the ICar, IUtility, and ICruise interfaces across process, thread, and machine boundaries. But the MARSHAL server must be built and registered on both the client machine, where REMCLIEN runs, and on the server machine, where APTSERVE runs.

The following code from the DialogProc procedure of the Remote Server Info dialog box (in REMCLIEN.CPP) assigns the machine name supplied by the user through the Set Remote Machine command on the File menu.

    ...
    case WM_COMMAND:
      {
        WORD wCmd = LOWORD(wParam);

        if (wCmd == IDOK)
        {
          // Obtain the machine name from the edit control.
          GetDlgItemText(hWndDlg, IDC_EDIT_MACHINE, g_szMachineName, MAX_PATH);
  #if !defined(UNICODE)
          // Convert to WideChar Unicode if we are NOT compiled for Unicode.
          AnsiToUc(g_szMachineName, g_wszMachineName, 0);
  #endif
          ::EndDialog(hWndDlg, TRUE);
        }
        else if (wCmd == IDCANCEL)
          ::EndDialog(hWndDlg, TRUE);
      }
      break;
    ...

If REMCLIEN is not compiled for Unicode, the ANSI string obtained in the dialog box must be converted to a wide character array (WCHAR or OLECHAR) for later use by COM. This conversion is performed by calling the Win32 function MultiByteToWideChar inside the APPUTIL function, AnsiToUc.

We will now tour internal behavior in both APTSERVE and REMCLIEN by looking at some representative trace logs. Trace lines that begin with 'C:' mark behavior reported in the REMCLIEN.EXE client. Lines that begin with 'L:' mark behavior reported in the remote APTSERVE.EXE local server. The internal APTSERVE trace lines will also show the thread ID of the action after the 'L'. These trace logs were obtained from machines that were connected in a network as described in the Summary section above. APTCLIEN is used on the remote machine to display the APTSERVE internal behavior.

First run APTCLIEN on remote Machine-S. Choose the Car menu's Create choice. Here is the resulting trace log in APTCLIEN.

  C: === Car Menu: Create.
  L: APTSERVE now logging to client.
  L<3B>: CmdLine Switches= -Embedding
  L<3B>: CServer::OpenFactories. Begin.
  L: CServer::OwnThis. Thread <3B> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <3B>.
  L<3B>: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<3B>: CFCar Constructor. m_pUnkOuter=0x0.
  L<3B>: CFUtilityCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<3B>: CFUtilityCar Constructor. m_pUnkOuter=0x0.
  L<3B>: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  L<3B>: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  L<3B>: CServer::OpenFactories. AptCar.
  L<3B>: CFCar::AddRef. New cRefs=1.
  L<3B>: CFCar::AddRef. New cRefs=2.
  L<3B>: CServer::OpenFactories. AptUtilityCar.
  L<3B>: CFUtilityCar::AddRef. New cRefs=1.
  L<3B>: CFUtilityCar::AddRef. New cRefs=2.
  L: AptThreadProc. Starting Apartment Thread <65>.
  L<65>: CFCar::AddRef. New cRefs=3.
  L<3B>: CServer::OpenFactories. AptCruiseCar.
  L<3B>: CFCruiseCar::AddRef. New cRefs=1.
  L<65>: CFCar::QueryInterface. pIClassFactory returned.
  L: AptThreadProc. Starting Apartment Thread <7D>.
  L<3B>: CFCruiseCar::AddRef. New cRefs=2.
  L<65>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<7D>: CFUtilityCar::AddRef. New cRefs=3.
  L: CServer::UnOwnThis. Ownership relinquished by <3B>.
  L<65>: CFCar::AddRef. New cRefs=4.
  L: AptThreadProc. Starting Apartment Thread <34>.
  L<3B>: CServer::OpenFactories. End.
  L<65>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<34>: CFCruiseCar::AddRef. New cRefs=3.
  L<65>: COCar::CImpICar Constructor. Non-Aggregating.
  L<65>: COCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <65> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <65>.
  L<65>: CServer::ObjectsUp. New cObjects=1.
  L: CServer::UnOwnThis. Ownership relinquished by <65>.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=1.
  L<65>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x771680.
  L<65>: COCar::AddRef. New cRefs=2.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=3.
  L<65>: COCar::AddRef. New cRefs=4.
  L<65>: COCar::Release. New cRefs=3.
  L<65>: COCar::Release. New cRefs=2.
  L<65>: COCar::Release. New cRefs=1.
  L<65>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<65>: CFCar::Release. New cRefs=3.

Our main goal here is to establish the integrated logging in the client log display of both client and server behavior. We will not cover the rest of this APTCLIEN-driven log in detail because we did so in the APTCLIEN lesson. Our main focus here is the distributed COM behavior driven by REMCLIEN.

As in the APTCLIEN lesson, when the following threads are started, each of the server's component class factories is registered with COM on Machine-S:

  <3B> - Main thread for the APTSERVE local server.
  <65> - AptCar component apartment thread.
  <7D> - AptUtilityCar component apartment thread.
  <34> - AptCruiseCar component apartment thread.

The main thread is also an apartment in the strict sense. It is not exploited as such in this code sample.

As in the APTSERVE lesson, starting of the apartment threads is reported in the same order that class factories are created, but the entries are scattered later in the log. This is because this trace log reflects asynchronous multi-tasking, in which internal operating system scheduling determines when the processor resource is given to the various threads. Though deterministic, this behavior can appear somewhat random at the application level.

To retain symmetry with the APTCLIEN sample we'll issue the Car menu ICar::Shift method. Here is the APTCLIEN log.

  C: === Car Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<65>: COCar::AddRef. New cRefs=2.
  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=2.
  L<65>: COCar::AddRef. New cRefs=3.
  L<65>: COCar::CImpICar::Release. Delegating. New cI=1.
  L<65>: COCar::Release. New cRefs=2.
  C: Interface obtained. *ppv=0x14C8DC
  C: --Calling pICar->Shift
  L<65>: COCar::CImpICar::Shift. Called. ICar calls=1.
  C: --Releasing pICar

We see that the ICar interface call counter registers 1 for this created instance of the COCar COM object.

We now start REMCLIEN on Machine-C. When it is running, click the Set Remote Machine command on the File menu and enter the remote machine name in the dialog box, and then click the Create command from the Car menu.

In the REMCLIEN log we see the following.

  C: === Car Menu: Create.
  C: Car creation succeeded.

In the server log (actually the APTCLIEN log) we see the following.

  L<65>: CFCar::QueryInterface. pIClassFactory returned.
  L<65>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<65>: CFCar::AddRef. New cRefs=4.
  L<65>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<65>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<65>: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L<65>: CFCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: CFCar::AddRef. New cRefs=5.
  L<65>: CFCar::AddRef. New cRefs=6.
  L<65>: CFCar::Release. New cRefs=5.
  L<65>: CFCar::QueryInterface. pIClassFactory returned.
  L<65>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L<65>: CFCar::AddRef. New cRefs=6.
  L<65>: CFCar::QueryInterface. pIClassFactory returned.
  L<65>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<65>: CFCar::AddRef. New cRefs=7.
  L<65>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<65>: CFCar::Release. New cRefs=6.
  L<65>: CFCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L<65>: CFCar::Release. New cRefs=5.
  L<65>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<65>: COCar::CImpICar Constructor. Non-Aggregating.
  L<65>: COCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <65> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <65>.
  L<65>: CServer::ObjectsUp. New cObjects=2.
  L: CServer::UnOwnThis. Ownership relinquished by <65>.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=1.
  L<65>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x7716B0.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=2.
  L<65>: COCar::AddRef. New cRefs=3.
  L<65>: COCar::Release. New cRefs=2.
  L<65>: COCar::Release. New cRefs=1.
  L<65>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<65>: CFCar::Release. New cRefs=4.
  L<65>: CFCar::Release. New cRefs=3.

These actions create a new instance of COCar as indicated by the server's cObjects count showing a value of 2. The first object was the COCar created by APTCLIEN above. Here is a call to ICar::Shift (still in REMCLIEN).

In the REMCLIEN log:

  C: === Car Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: Interface obtained. *ppv=0x14D81C
  C: --Calling pICar->Shift
  C: --Releasing pICar

In the APTCLIEN log:

  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<65>: COCar::AddRef. New cRefs=2.
  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=2.
  L<65>: COCar::AddRef. New cRefs=3.
  L<65>: COCar::CImpICar::Release. Delegating. New cI=1.
  L<65>: COCar::Release. New cRefs=2.
  L<65>: COCar::CImpICar::Shift. Called. ICar calls=1.

Now, from the REMCLIEN CruiseCar menu, click Create.

In the REMCLIEN log:

  C: === CruiseCar Menu: Create.
  C: CruiseCar creation succeeded.

In the APTCLIEN log:

  L<34>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<34>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<34>: CFCruiseCar::AddRef. New cRefs=4.
  L<34>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<34>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<34>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L<34>: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<34>: CFCruiseCar::AddRef. New cRefs=5.
  L<34>: CFCruiseCar::AddRef. New cRefs=6.
  L<34>: CFCruiseCar::Release. New cRefs=5.
  L<34>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<34>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L<34>: CFCruiseCar::AddRef. New cRefs=6.
  L<34>: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L<34>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L<34>: CFCruiseCar::AddRef. New cRefs=7.
  L<34>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L<34>: CFCruiseCar::Release. New cRefs=6.
  L<34>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L<34>: CFCruiseCar::Release. New cRefs=5.
  L<34>: CFCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<34>: COCruiseCar::CImpICar Constructor. Non-Aggregating
  L<34>: COCruiseCar::CImpICruise Constructor. Non-Aggregating.
  L<34>: COCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <34> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <34>.
  L<34>: CServer::ObjectsUp. New cObjects=3.
  L: CServer::UnOwnThis. Ownership relinquished by <34>.
  L<34>: COCruiseCar::Init.
  L<65>: CFCar::QueryInterface. pIClassFactory returned.
  L<65>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L<65>: CFCar::AddRef. New cRefs=4.
  L<65>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L<65>: COCar::CImpICar Constructor. Non-Aggregating.
  L<65>: COCar Constructor. m_pUnkOuter=0x0.
  L: CServer::OwnThis. Thread <65> waiting to own CServer.
  L: CServer::OwnThis. CServer now owned by Thread <65>.
  L<65>: CServer::ObjectsUp. New cObjects=4.
  L: CServer::UnOwnThis. Ownership relinquished by <65>.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=1.
  L<65>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x771720.
  L<65>: COCar::AddRef. New cRefs=2.
  L<65>: COCar::QueryInterface. 'this' pIUnknown returned.
  L<65>: COCar::AddRef. New cRefs=3.
  L<65>: COCar::AddRef. New cRefs=4.
  L<65>: COCar::Release. New cRefs=3.
  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=1.
  L<65>: COCar::AddRef. New cRefs=4.
  L<65>: COCar::QueryInterface. pICar returned.
  L<65>: COCar::CImpICar::Addref. Delegating. New cI=2.
  L<65>: COCar::AddRef. New cRefs=5.
  L<65>: COCar::CImpICar::Release. Delegating. New cI=1.
  L<65>: COCar::Release. New cRefs=4.
  L<65>: COCar::Release. New cRefs=3.
  L<65>: COCar::Release. New cRefs=2.
  L<65>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<65>: CFCar::Release. New cRefs=3.
  L<34>: COCruiseCar::Init (New Containment of COCar) Succeeded.
  L<34>: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<34>: COCruiseCar::AddRef. New cRefs=1.
  L<34>: CFCruiseCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x7716E0.
  L<34>: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L<34>: COCruiseCar::AddRef. New cRefs=2.
  L<34>: COCruiseCar::AddRef. New cRefs=3.
  L<34>: COCruiseCar::Release. New cRefs=2.
  L<34>: COCruiseCar::Release. New cRefs=1.
  L<34>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L<34>: CFCruiseCar::Release. New cRefs=4.
  L<34>: CFCruiseCar::Release. New cRefs=3.

The server has now created two new object instances, a COCruiseCar and COCar object, with the COCar object reused by containment to make the COCruiseCar. The cObjects=4 thus indicates four outstanding object instances for the server. COCruiseCar is managed by a newly encountered apartment thread, <34>. The three COCar objects remain managed by a single apartment thread, <65>, even though one is held by APTCLIEN, one is held by REMCLIEN, and one is held by APTCLIEN as a contained part of a composite COCruiseCar.

Using the CruiseCar menu we can now call ICar::Shift.

  C: === CruiseCar Menu: ICar::Shift
  C: --Obtaining Interface Pointer.
  C: Interface obtained. *ppv=0x14D284
  C: --Calling pICar->Shift
  C: --Releasing pICar

In the APTCLIEN log:

  L<34>: COCruiseCar::QueryInterface. pICar returned
  L<34>: COCruiseCar::CImpICar::Addref. Delegating. New cI=1.
  L<34>: COCruiseCar::AddRef. New cRefs=2.
  L<34>: COCruiseCar::QueryInterface. pICar returned
  L<34>: COCruiseCar::CImpICar::Addref. Delegating. New cI=2.
  L<34>: COCruiseCar::AddRef. New cRefs=3.
  L<34>: COCruiseCar::CImpICar::Release. Delegating. New cI=1.
  L<34>: COCruiseCar::Release. New cRefs=2.
  L<34>: COCruiseCar::CImpICar::Shift. Delegating. nGear=4.
  L<65>: COCar::CImpICar::Shift. Called. ICar calls=1.

The ICar interface call counter indicates only one call, providing additional proof that this is a newly created instance of COCar managed by the apartment thread <65>. The call had to be delegated within the server from COCruiseCar object's apartment thread <34> to the contained COCar object's apartment thread <65>. This means that the interface call had to be marshaled at least twice: First, from the main thread of REMCLIEN through DCOM (and thus RPC) across the network to the remote server CruiseCar apartment thread <34>, then from the CruiseCar apartment thread <34> within the same APTSERVE server process to the Car apartment thread <65>.

This illustrates component composition within the same process but across thread boundaries, as well as the distributed COM functioning of this composite object remotely across machine boundaries.

The release of the CruiseCar and Car objects is essentially the same as seen in the APTCLIEN lesson, even though these Release calls are marshaled across machine boundaries.

To shut down the samples on both machines, first click Release on the REMCLIEN CruiseCar menu. This shuts down the COCruiseCar (with its contained COCar instance) held by the REMCLIEN client. Then click Release on the APTCLIEN Car menu. This shuts down the original COCar held by the APTCLIEN client and leaves the server with an object count of 1. Finally, click Release on the REMCLIEN Car menu. As seen in the APTCLIEN lesson, this causes an orderly termination of the apartment threads during execution of the CloseFactories function in APTSERVE, after which the APTSERVE server application exits and unloads.

NETWORK AND SETUP ISSUES

The test setup used during development of REMCLIEN consisted of the following two machines: Machine-S (server) running Windows NT 4.0 Workstation with an Ethernet network adapter installed, and Machine-C (client) running Windows NT 4.0 Workstation with an Ethernet network adapter installed. The two machines were connected using shielded twisted pair cable and a IEEE 802.3 10Base-T multiport repeater. This is called a peer-to-peer network, and Windows NT Workstation supports this out of the box. The same network workgroup was created for both machines. The same user account was created on each machine for this workgroup. Each of these accounts had Administrator privilege. After logging on to each machine with this account, the predefined C$ share name on Machine-S was used to copy files from Machine-C's drive C across the network to Machine-S. See your Windows NT product documentation or the Windows NT Resource Kit for details on using the predefined C$ and D$ shares or on creating shared access to a drive among networked machines.

Your network setup might vary. For example, you might have the two machines connected to a client-server network, and your machines might be configured in the same network domain rather than in the same workgroup on a peer-to-peer network. In this case, to capture the log displays in APTCLIEN as used above, each machine must have a domain user account with the same name and password. These domain accounts on each machine must be members of the Administrators group on each machine.

Don't forget that a MyUserName/MyPassword login account with administrator privileges on the local machine is not the same login account as a domain login account using the same MyUserName/MyPassword. If you are setting up Machine-S and Machine-C in an NT network domain so that you are logging on to both machines with your domain login, make sure you add the domain login account to the Administrators group on both machines. You do this in the Windows NT User Manager. You do not need to do this for Machine-C if it is running Windows 95 or Windows 98.

For DCOM operation, the Windows NT operating system version 4 or greater can be installed on each machine. A DCOM95 technology add-on for the Windows 95 operating system is also available. DCOM95 can currently be obtained by download from Microsoft's world wide Web site at: http://www.microsoft.com/com/. If Win95 is involved, then the REMCLIEN.EXE client is typically run under DCOM95 on the Win95 machine while the APTSERVE.EXE server is run on the remote machine under version 4 or greater of Windows NT Workstation or Windows NT Server. DCOM is built into version 4 or greater of the Windows NT operating system. It is also built into Windows 98.

The TCP/IP network protocol, installed and configured with IP address, subnet mask, and default gateway IP address, is recommended but not required by DCOM. For example, if your network uses the NetBEUI protocol instead of TCP/IP, DCOM will still work. Currently, if the COM client is run under DCOM95 on a Win95 machine, then the TCP/IP protocol must be used. Also, if the APTSERVE.EXE server is run under DCOM95 on a Win95 machine, then that server must be launched manually, as DCOM95 will not launch it automatically on behalf of a remote request from REMCLIEN.EXE. This lesson assumes that the TCP/IP protocol is used and that Machine-S is Windows NT Server or Workstation. Thus, this lesson assumes that if Windows 95 or Windows 98 is involved it is running on Machine-C.

If the computers you use for this lesson are part of a Windows NT Server domain, then it is likely that a DHCP server is available on that network which can be used to automatically assign an IP address for your TCP/IP protocol operation.

If the computers you use for this lesson are configured as an isolated peer-to-peer network, you may need to manually configure the TCP/IP protocol with an IP address if a DHCP server is not available. In this case, you can use the Network option of the Windows NT Control Panel to configure TCP/IP. In the Network dialog box, choose the Protocols tab and select the TCP/IP protocol in the list box. Click the Properties button. In the Microsoft TCP/IP Properties dialog box, click the Specify An IP Address radio button and enter values in the IP Address, Subnet Mask, and Default Gateway boxes. The values to specify depend on your network. Your network administrator may be responsible for dispensing static IP addresses for machines on the network. If you intend the two machines to remain in an isolated peer-to-peer network, you can assign your own IP address values.

For example you can use values similar to the following.

  Machine-S                              Machine-C
  IP Address= 222.3.4.5                  IP Address= 222.3.4.6
  Subnet Mask= 255.255.255.0             Subnet Mask= 255.255.255.0
  Default Gateway= 222.3.4.5             Default Gateway= 222.3.4.6

For these makeshift values, the IP address and default gateway are the same on each machine but must differ on different machines. The subnet mask above will work for most situations.

Note: If you manually specify such values and later wish to connect either machine to an existing corporate client-server network, you should first delete this IP address information. In the Network option of the Windows NT Control Panel, choose the Protocols tab and select the TCP/IP protocol in the list box. In the Microsoft TCP/IP Properties dialog box, click the Obtain An IP Address From a DHCP Server button. Save this change, and then restart Windows NT.

DCOM will not work if TCP/IP is installed but the machine has no assigned IP address. This situation may show up as error 0x80070776 (The object exporter specified was not found) returned from a failed CoGetClassObject or CoCreateInstance call.

The operation of COM uses some entries in the registry to control distributed COM security. You can use the REGEDIT, DCOMCNFG, or OLEVIEWER tools to manually view and edit these entries. COM can be configured by default so that only machine accounts with Administrator privilege can remotely start and access objects of existing COM component classes. This restriction is set as a configurable machine-wide default registry entries on the remote machine. It is located in the registry as follows.

  \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE\DefaultLaunchPermission
  \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE\DefaultAccessPermission

You can view these values in the Machine-S registry using REGEDIT. These values are binary ACLs (Access Control Lists) and can be overridden for individual component classes by specifying a LaunchPermission or AccessPermission named value on the AppID entry for the APTSERVE server in the Machine-S registry. During self-registration, the APTSERVE server on Machine-S does not modify DefaultLaunchPermission or DefaultAccessPermission. It also does not override these defaults with its own values on its AppID entry.

Thus, the successful launching and accessing of the components in APTSERVE by REMCLIEN depends on a proper security configuraton for APTSERVE. You can manually set the LaunchPermission and AccessPermission values for the components in the APTSERVE server using a DCOM configuration utility. DCOM includes the DCOMCNFG utility for use in configuring 32-bit COM and DCOM applications. To run this tool, click Start, click Run, and then type DCOMCNFG. Before you use an application with DCOM, you can use DCOMCNFG to set application properties, such as security and location. On the computer running the client application you can specify the location of the server application that will be accessed or started. For the server application you can specify the user account that will have permission to access or start the application, and the user account that will be used to run the application.

Follow the these instructions to configure the security of the APTSERVE server so that REMCLIEN can access its components using distributed COM.

  1. Login on Machine-S (running Windows NT Server or Workstation). This must be an account that has administrator privileges.
  2. Make sure APTSERVE is compiled and/or registered on Machine-S.
  3. Run the DCOMCNFG utility by choosing the "Run" choice on the desktop "Start" menu--just enter DCOMCNFG. DCOMCNFG.EXE is located in the \WINNT\SYSTEM32 directory.
    1. Select the "AptCar Component" in the "Applications" list.
    2. Click the "Properties" button.
    3. Click the "Security" tab.
    4. Select the "Use custom access permissions" radio button and click the "Edit" button for it.
    5. In the "Registry Value Permissions" dialog click the "Add" button.
    6. In the "Add Users and Group" dialog select the "Everyone" entry in the "Names" list and click the "Add" button.
    7. Click the "OK" button in the "Registry Value Permissions" dialog.
    8. Back in the "Security" tab, select the "Use custom launch permissions" radio button and click the "Edit" button for it.
    9. In the "Registry Value Permissions" dialog click the "Add" button.
    10. In the "Add Users and Groups" dialog select the "Everone" entry in the "Names" list and click the "Add" button.
    11. Click the "OK" button in the "Registry Value Permissions" dialog.
    12. Click the "OK" button to complete the properties for the APTSERVE components.
    13. Finally, click the "OK" button to close the DCOMCNFG utility.

Because DCOM security is not significantly addressed within the coding of either REMCLIEN or APTSERVE, to launch and function the APTSERVE server on the remote Machine-S from the REMCLIEN client on the client Machine-C, you must be logged in--with administrator privilages--as the same user on both machines. Both machines must reside in the same workgroup or domain. See the later DCOMDRAW sample for more coverage of coding DCOM security within the client and server.

In this sample you run APTCLIEN on Machine-S to obtain a logging display of the APTSERVE server behavior in the APTCLIEN display. Of course you can also simply run REMCLIEN on Machine-C without explicitly running APTCLIEN on Machine-S. COM will simply locate and run APTSERVE on Machine-S. This is the preferred transparent behavior under normal application conditions.

Top Back to page top

© 1995-1998 Microsoft Corporation


FRESERVE - Free-threaded Server - Lesson 19

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

FRESERVE - Free-threaded Server

SUMMARY

COM supports execution contexts called apartments. Execution threads that run within COM objects do so in an apartment. Every COM object is owned by such an apartment. A given thread can execute in only one apartment at a time. COM recognizes two kinds of apartment: the Single-Threaded Apartment (STA) and the Multi-Threaded Apartment (MTA). COM supports only one MTA in a given process. Objects owned by this MTA can be executed concurrently by more than one thread. In contrast to the MTA, an STA is associated with a single unique thread. Objects in such an STA can be executed only by its associated thread.

The FRESERVE sample and its matching client, FRECLIEN, introduce the Multi-Threaded Apartment (MTA) and show how to construct and access a shared COM object owned by an MTA in a free-threaded in-process server. Two samples earlier in this series, APTSERVE and APTCLIEN, cover single-threaded apartments.

FRESERVE departs from the sport utility vehicle metaphor and associated interfaces used in other samples of this series. FRESERVE introduces a new custom interface, IBall, and a new COM object, COBall. COBall implements the IBall interface. Both COBall and its in-process server are coded to support COM free threading in anticipation of their use by the free-threaded client, FRECLIEN, in the next lesson.

The CThreaded facility in APPUTIL is used to achieve thread safety as it was in the previous APTSERVE sample. COBall objects are derived from the CThreaded class and so inherit its OwnThis and UnOwnThis methods. These methods enforce mutually exclusive access to the FRESERVE server and to free-threaded COBall objects managed by the server.

FRESERVE works with the FRECLIEN code sample to illustrate FRESERVE's COM server facilities in a free-threaded server and the subsequent manipulation of its components by a free-threaded client.

For functional descriptions and a tutorial code tour of FRESERVE, see the Code Tour section in FRESERVE.HTM. For details on setting up the programmatic usage of FRESERVE, see the Usage section in FRESERVE.HTM. To read FRESERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the FRESERVE lesson in the table of lessons. You can also achieve the same thing by clicking the FRESERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also FRECLIEN.HTM in the main tutorial directory for more details on the FRECLIEN client application and how it works with FRESERVE.DLL. You must build FRESERVE.DLL before building or running FRECLIEN.

FRESERVE's makefile automatically registers FRESERVE's DllBall component in the registry. This component must be registered before FRESERVE is available to outside COM clients as a server for that component. This self-registration is done using the REGISTER.EXE utility built in the previous REGISTER lesson. To build or run FRESERVE, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the FRESERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV FRESERVE.DSP

You can also simply double-click the FRESERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use FRESERVE, a client program does not need to include FRESERVE.H or link to FRESERVE.LIB. A COM client of FRESERVE obtains access solely through its component's CLSID and COM services. For FRESERVE, that CLSID is CLSID_DllBall (defined in file BALLGUID.H in the INC sibling directory). The FRECLIEN code sample shows how the client obtains this access.

FRESERVE is a DLL that is intended primarily as a free-threaded COM server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within COM's CoGetClassObject function. FRESERVE is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the FRESERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the FRESERVE directory.

  ..\register\register.exe freserve.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of FRESERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, FRECLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
FRESERVE.TXT  Short sample description.
MAKEFILE      The generic makefile for building the FRESERVE.DLL
              code sample of this lesson.
FRESERVE.H    The include file for declaring as imported or defining as
              exported the service functions in FRESERVE.DLL.
FRESERVE.CPP  The main implementation file for FRESERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
FRESERVE.DEF  The module definition file. Exports server housing functions.
FRESERVE.RC   The DLL resource definition file for the executable.
FRESERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
BALL.H        The include file for the COBall COM object class.
BALL.CPP      The implementation file for the COBall COM object class.
FRESERVE.DSP  Microsoft Visual Studio Project file.

FRESERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial samples. This tour assumes that you have some exposure to those previous samples. It does not revisit earlier topics of basic interface implementation techniques, COM object construction, in-process server construction, and class factory construction. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • an overview of how FRESERVE works
  • thread-safe mechanisms in FRESERVE
  • an overview of COM free threading
  • self-registration of COM objects in a free-threaded server
  • the IBall interface
  • the construction of the COBall COM object
  • issues in class factory construction in a free-threaded server

The COBall COM object is the single object type managed by this FRESERVE in-process server. COBall is constructed as an aggregatable COM object with a native implementation of the IBall interface. COBall exposes the IBall interface to allow clients to perform a small set of operations on an instance of COBall. The COBall object encapsulates data that defines a moving ball. Data such as position, size, and color are encapsulated. No graphical images are managed in this object. When a COBall object is initialized with a call to its Reset method, it is given a rectangle (a standard Win32 RECT structure) that defines the boundaries within which the ball may move. The COBall object contains coded logic to move the ball within those boundaries, to bounce the ball off any boundary when appropriate, and to provide clients with current data on the ball's location, size, and color. The object maintains a current color property indicating the particular executing thread that most recently moved the ball by calling the ball's Move method.

A client can then use the IBall interface to move the ball and to obtain the data necessary to paint an image of the moving ball. The COBall object is coded to support the free-threaded COM model, in which any number of threads may freely make asynchronous calls directly to the object's interface methods. In the following FRECLIEN code sample, several threads running concurrently attempt to move a single COBall object. An independent client thread runs concurrently to continuously obtain ball position and color data and to paint snapshot images that graphically reflect the COBall's changing data. Many different threads may concurrently attempt to change and read the ball's data. The ball is kept alive by one set of client threads, while another client thread passively paints images of the moving ball. Because the ball updates its own data to reflect the executing threads, the ball image painted by the client changes color as it moves to continuously reflect which thread most recently moved the ball.

Aside from the COBall logic needed to maintain the bouncing ball as a mathematical entity, FRESERVE also requires some special code to support free threading. We will start with this code, because it borrows from techniques previously shown in the APTSERVE sample. Like APTSERVE, the FRESERVE server housing is constructed to guard shared data in the server from conflicting access by multiple concurrent threads of execution. The technique used to enforce serialized access to this server data is based on the use of Win32 mutexes. APPUTIL's CThreaded utility base class encapsulates the mutex protection mechanism. This utility is presented in detail in the APTSERVE lesson.

In the FRESERVE server housing, the CServer C++ class is derived from the CThreaded base class to inherit the OwnThis and UnOwnThis methods. These methods are used in bracketed pairs to protect access to CServer's data, such as the server's object and lock counts. One new addition to this in-process server housing is the CServer::CanUnloadNow method. It uses the CThreaded facility. Here it is from SERVER.CPP.

  HRESULT CServer::CanUnloadNow(void)
  {
    HRESULT hr = S_FALSE;
    LONG cObjects, cLocks;

    if (OwnThis())
    {
      cObjects = m_cObjects;
      cLocks = m_cLocks;

      hr = (0L==cObjects && 0L==cLocks) ? S_OK : S_FALSE;

      UnOwnThis();
    }

    return hr;
  }

The OwnThis, UnOwnThis pair is used to protect access to the server's m_CObjects and m_cLocks variables. Within the range of this protection, copies of the variable values are made on the local stack, and the copies are then used for most logic. The logic implemented here is to support the in-process server's familiar DllCanUnloadNow exported function. This arrangement is convenient because DllCanUnloadNow is not a method of a class that is derived from CThreaded, such that it can benefit from the OwnThis protection mechanism. Yet such protection is needed, because the server data is vulnerable to concurrent access by multithreaded clients. In fact, the server data is also potentially vulnerable to concurrent access by multiple threads that could be spawned within the server or its components. The CanUnloadNow method benefits from CThreaded not only for the protection it offers, but also for the simple programming encapsulation it provides. Such encapsulation will pay off later if the free-threaded server evolves to one that spawns multiple threads within it.

The default model for multithreaded programming with COM is the Single-Threaded Apartment (STA) model. This model was presented in the previous APTSERVE and APTCLIEN samples, where multiple STA apartment threads were provided within an in-process local server. COM's support of the STA apartment model ensures that calls to interface methods on objects created within an STA will always be executed on the thread of that apartment. The first point of recognition by COM in this regard is the occasion of the object's first marshaled interface when the object is created. This is often the IClassFactory interface pointer requested in the CoGetClassObject call. At this point of recognition COM associates the object with its apartment thread.

The STA apartment model supported by COM is convenient and largely transparent to the client. It enforces a serialized access among contending threads to a COM object. The object is always called on the thread that owns it, even if the caller is on a different thread. This model entails some overhead (beyond that of normal interface marshaling) as COM performs thread switching during such cross-thread calls.

Performance can be significantly improved by using the Multi-Threaded Apartment (MTA). Objects that support multi-threaded execution in the MTA are often called free-threaded objects. In such objects COM does not make thread switches on behalf of cross-thread calls to interface methods executed in the MTA. Instead, COM freely ushers the call through on the same thread originating the call. This means that the thread-safe serialized access to the object that was enforced by the STA apartment model is not enforced by the MTA model. COM objects in the MTA must therefore provide their own serialization for access by multiple threads. Earlier in this lesson we saw the CThreaded mechanism that provides such access safety in this sample.

The way that COM is informed that this in-process server and its managed components support the free-threaded MTA model is by an additional entry in the component's registration in the registry. As with all the previous in-process servers of this series, the FRESERVE server self-registers the components it houses. Here is the DllRegisterServer function from FRESERVE.CPP.

  STDAPI DllRegisterServer(void)
  {
    HRESULT  hr = NOERROR;
    TCHAR    szID[GUID_SIZE+1];
    TCHAR    szCLSID[GUID_SIZE+32];
    TCHAR    szModulePath[MAX_PATH];

    // Obtain the path to this module's executable file for later use.
    GetModuleFileName(
      g_pServer->m_hDllInst,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    /*--------------------------------------------------------------------
      Create registry entries for the DllBall Component.
    --------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(CLSID_DllBall, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);

    // Create ProgID keys.
    SetRegKeyValue(
      TEXT("DllBall1.0"),
      NULL,
      TEXT("DllBall Component - FRESERVE Code Sample"));
    SetRegKeyValue(
      TEXT("DllBall1.0"),
      TEXT("CLSID"),
      szID);

    // Create VersionIndependentProgID keys.
    SetRegKeyValue(
      TEXT("DllBall"),
      NULL,
      TEXT("DllBall Component - FRESERVE Code Sample"));
    SetRegKeyValue(
      TEXT("DllBall"),
      TEXT("CurVer"),
      TEXT("DllBall1.0"));
    SetRegKeyValue(
      TEXT("DllBall"),
      TEXT("CLSID"),
      szID);

    // Create entries under CLSID.
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("DllBall Component - FRESERVE Code Sample"));
    SetRegKeyValue(
      szCLSID,
      TEXT("ProgID"),
      TEXT("DllBall1.0"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("DllBall"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("InprocServer32"),
      szModulePath);
    AddRegNamedValue(
      szCLSID,
      TEXT("InprocServer32"),
      TEXT("ThreadingModel"),
      TEXT("Both"));

    return hr;
  }

The important new code here is the call to AddRegNamedValue. A new named value must be added to the InprocServer32 key. Note that this value is a named value rather than a subkey of InprocServer32. Registry keys can have a series of named values associated with them. The definition of the AddRegNamedValue is straightforward and is defined in FRESERVE.CPP. The named value added for the DllBall component is 'ThreadingModel=Both'. Since our component and server are fully thread-safe, they are coded to support either the free-threaded model or the Apartment model. In this case, we specify 'Both'. This informs COM how it should act when either an MTA client or an STA client calls on objects housed in the server. This means that clients of this server can be initialized with COM as either COINIT_MULTITHREADED (for MTA) or COINIT_APARTMENTTHREADED (for STA). We will examine this for an MTA free-threaded client in the next lesson, FRECLIEN.

The string values for ThreadingModel are 'Apartment', 'Free', and 'Both'. In-process servers can be designated as STA (Apartment), MTA (Free), or both (Both). If the ThreadingModel value is missing from the InprocServer32 key, the main STA apartment of the process is assumed by COM as the default model. COM objects created under each of these disignations can be used by any COM client, regardless of the threading model used by that client. All combinations of threading model interoperability are allowed between clients and in-process objects. Interaction between a client and an in-process object that use different threading models is exactly like the interaction between clients and out-of-process servers. For an in-process server, when the threading model of the client and in-process server differ, COM must be relied upon as intermediary between the client and the object to ensure the necessary marshaling and call synchronization.

When this is the case, both single and multi-threaded apartments will be present. Such a mixed model will have a single multi-threaded apartment (MTA), containing all threads initialized as COINIT_MULTITHREADED, and one or more single-threaded apartments (STAs). Interface pointers must be marshaled between any of these apartments but can be used within an apartment without marshaling.

Thus, if we wished to provide for a FRECLIEN client having multiple apartment threads (one or more single-threaded apartments and a single optional multi-threaded apartment), we would likely need to provide for marshaling of any interfaces for which pointers are passed between client apartment threads. COM offers some convenient functions to perform run-time marshaling of such interfaces (for example, CoCreateFreeThreadedMarshaler, CoMarshalInterface and CoMarshalInterThreadInterfaceInStream). If a more permanent solution is also appropriate, MIDL and the techniques for standard marshaling seen in the previous MARSHAL sample can also be used to create and register a marshaling server for the custom interfaces.

The FRESERVE and FRECLIEN samples do not require a standard marshaling server. If there was an expectation that FRESERVE would be used by a variety of apartment model clients, then standard marshaling support for the IBall interface would be appropriate. With such standard marshaling support for the IBall interface, it would also be appropriate to register FRESERVE's DllBall component with 'ThreadingModel=Both'. This would publish the component as fully supporting client control in either single-threaded or multi-threaded apartments.

The COBall COM objects manufactured by FRESERVE implement the IBall custom interface. Here is the IBall interface declaration from IBALL.H, located in the INC sibling directory.

  DECLARE_INTERFACE_(IBall, IUnknown)
  {
    // IUnknown methods.
    STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE;
    STDMETHOD_(ULONG,AddRef)  (THIS) PURE;
    STDMETHOD_(ULONG,Release) (THIS) PURE;

    // IBall methods.
    STDMETHOD(Reset)    (THIS_ RECT*, SHORT) PURE;
    STDMETHOD(GetBall)  (THIS_ POINT*, POINT*, COLORREF*) PURE;
    STDMETHOD(Move)     (THIS_ BOOL) PURE;
  };

Here are the matching implementation declarations within the COBall object class. See the CImpIBall nested class declaration in BALL.H.

     ...
     ...
     STDMETHODIMP Reset(RECT* pNewRect, short nBallSize);
     STDMETHODIMP GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
     STDMETHODIMP Move(BOOL bAlive);
     ...
     ...

The Reset method accepts a RECT structure, which specifies the initial boundaries of the rectangle within which the ball entity is permitted to move. This rectangle typically matches the client area of a client application's window. Reset also accepts an initial ball diameter, specified in pixels.

The GetBall method is the way the client obtains current properties of the COBall object: its diameter, its color, and the location of its origin.

The Move method simply directs the COBall to move itself. The semantics of this method are left up to the COBall object. In the current sample, internal calculation logic is employed to give some continuity to the ball's motion. The ball moves in the current direction until it hits a boundary, at which point it bounces. This bounce is a reflection of its angle of incidence with the boundary. However, the angle of reflection does not always equal the angle of incidence. A small random skew factor is used both for the reflection angle and for the speed of the motion. This skew factor makes the ball's movement appear more natural.

When the bAlive parameter is set to FALSE, the Move method destroys the ball. In this sample, multiple threads give life to the ball. Because one thread could issue this termination command before another could know about it, Move also returns an HRESULT value to notify any other threads that call Move if the ball is dead or alive. A return value of E_FAIL indicates that the ball is dead. A dead ball no longer moves. Any thread can move the ball, and any thread can kill the ball.

The construction of the COBall COM object is based on techniques seen in earlier samples of this series. The familiar technique of nested class declarations are used for the multiple interface implementations. Here is the COBall class declaration in BALL.H.

  class COBall : public IUnknown, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COBall(IUnknown* pUnkOuter, CServer* pServer);
      ~COBall(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIBall : public IBall, public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIBall(COBall* pBackObj, IUnknown* pUnkOuter);
          ~CImpIBall(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IBall methods.
          STDMETHODIMP Reset(RECT* pNewRect, short nBallSize);
          STDMETHODIMP GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
          STDMETHODIMP Move(BOOL bAlive);

        private:
          // Data private to this COBall interface implementation of IBall.
          COBall*      m_pBackObj;     // Parent Object back pointer.
          IUnknown*    m_pUnkOuter;    // Outer unknown for Delegation.

          // The following private data and methods constitute the working
          // heart of COBall as an actual application object.
          BOOL         m_bAlive;
          RECT         m_WinRect;
          int          m_nWidth;
          int          m_nHeight;
          int          m_xDirection;
          int          m_yDirection;
          BOOL         m_bNewPosition;
          int          m_xPosition;
          int          m_yPosition;
          short        m_xSkew;
          short        m_ySkew;
          COLORREF     m_crColor;
          CXForm       m_XForm;
          CBallThread  m_BallThreads[MAX_BALLTHREADS];

          // Private methods for internal use.
          void GetDimensions(POINT*);
          void SetDimensions(int,int);
          void GetDirection(POINT*);
          void SetDirection(int,int);
          void GetPosition(POINT*);
          void SetPosition(int,int);
          void CheckBounce(void);
          void FindThread(void);
      };

      // Make the otherwise private and nested IBall interface
      // implementation a friend to COM object instantiations of this
      // COBall COM object class.
      friend CImpIBall;

      // Private data of COBall COM objects.

      // Nested IBall implementation instantiation.  This IBall interface
      // is instantiated inside this COBall object as a native interface.
      CImpIBall        m_ImpIBall;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*        m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*         m_pServer;
  };

The heart of the COBall object is coded inside the CImpIBall nested interface implementation. The advantage of this design is that it doesn't burden the main COBall object class with the details of the ball's motion. It encapsulates the coding logic of the IBall interface entirely within the interface implementation. Another advantage of this is that, if you were evolving something like a CBall C++ class from legacy code into a COM COBall implementation, the transition from CBall to CImpIBall is somewhat less complicated than it would be from CBall to the outer COBall class, where the nested interface implementations tend to dominate attention. In this sample the issue is not as pronounced as a case where the outer COBall class might have many nested interface class declarations.

Notice that the outer COBall class and the nested CImpIBall class are both derived from CThreaded to inherit the OwnThis thread safety mechanism. The methods of both these classes need this mechanism to protect data encapsulated in their C++ objects.

The CImpIBall class implements many internal private methods, such as SetPostion and FindThread. Of all these CImpIBall methods, only the IUnknown and IBall interface methods are exposed as public in the C++ sense. The IBall interface exposes only the public Reset, GetBall, and Move methods. Other of the private methods, such as SetPosition, could be promoted to the status of public members of some new IBall2 evolution of the IBall interface. Should such an evolution occur, the COM contract requires that the new interface adopt a name and a new interface identifier (IID). It also must be derived from IBall to inherit and retain IBall's prior semantics.

The data that defines the ball is declared in the private area of CImpIBall. The m_BallThreads array is maintained by the object to map color attributes to the threads that call the object's Move method. In conjunction with the FindThread method, program logic assigns colors to passing threads and reuses those colors when previous threads revisit the object. As new threads are added to the array, each is assigned a new color. In the current sample, a random selection of 64 such thread colors is accommodated using the compile-time macro MAX_BALL_THREADS.

The CXForm class is also declared in BALL.H. It is part of the inner algorithms that govern ball behavior and is not relevant to the threading model presented in this lesson.

The class factory for the DllBall component, CFBall, is declared in FACTORY.H and implemented in FACTORY.CPP. This code is borrowed from many previous samples in this series. Like COBall, CFBall is derived from IUnknown and CThreaded using multiple inheritance. CThreaded gives the class factory its thread safety using the OwnThis mechanism seen earlier. There is one special issue worth mentioning.

The CFBall::Release has an odd arrangement of the OwnThis, UnOwnThis pair. Here is the code from FACTORY.CPP.

  STDMETHODIMP_(ULONG) CFBall::Release(void)
  {
    ULONG cRefs;
    CServer* pServer;

    if (OwnThis())
    {
      cRefs = --m_cRefs;

      if (0 == cRefs)
      {
        // Save a local copy of the pointer to the server control object.
        pServer = m_pServer;

        // We artificially bump the main reference count to prevent
        // reentrancy via the main object destructor.  Not really needed
        // in this CFBall but a good practice because we are aggregatable
        // and may at some point in the future add something entertaining
        // like some Releases to the CFBall destructor.
        m_cRefs++;
        UnOwnThis();
        delete this;

        // We've reached a zero reference count for this COM object and
        // we have deleted the COM object. So we tell the server housing
        // to decrement its global object count so that the server will
        // be unloaded if appropriate.
        if (NULL != pServer)
          pServer->ObjectsDown();
      }
      else
        UnOwnThis();
    }

    return cRefs;
  }

The extra call to UnOwnThis is needed because of the 'delete this' statement. The UnOwnThis call must precede the delete statement so that the currently owning thread will relinquish ownership of this object before an attempt is made to destroy the entire object. The object must remain in existence as long as OwnThis is using the governing mutex, but no longer.

Top Back to page top

© 1995-1998 Microsoft Corporation


FRECLIEN - Client of Free-threaded server - Lesson 20

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

FRECLIEN - Client of Free-threaded Server

NOTE: FRECLIEN uses CoInitializeEx. CoInitializeEx is implemented only on Windows NT 4.0 and later versions. It is also implemented in the DCOM95 addition to Win95. This sample will not run on earlier versions of Windows NT or on Win95 without DCOM95.

SUMMARY

The FRECLIEN sample spawns multiple threads to create and use the COBall COM object in the Multi-Threaded Apartment (MTA). COBall is housed in the FRESERVE free-threaded in-process server. The COBall object itself spawns no threads; it passively responds to IBall interface requests from multiple client threads. The FRECLIEN client creates a shared, single COBall object and controls it through the IBall interface that the object exposes. As three of FRECLIEN's threads concurrently move the ball through calls to IBall::Move, the remaining main thread uses a system timer to obtain timed updates of the COBall object's position, size, and color. This main thread uses that data, obtained by calling the IBall::GetBall method, to display graphical snapshot images of the ball in the client's main window. The FRECLIEN process thus has four concurrent threads running in the MTA and accessing the shared COBall object.

As explained in the FRESERVE sample, the COBall object internally updates its color property to reflect the last thread that called the object's Move method. The display thread uses this data for each ball image it displays. As the ball moves, it changes color to reflect the changing threads that move the ball. As the ball moves, it also leaves a trail that provides a striking visual history of the passing threads. This trail demonstrates that, with COM's MTA model, all threads that make interface requests to the same object access the object on the different calling threads. Each different color of the single ball object represents a different calling thread. Since the display thread is asynchronous with respect to the ball-moving threads, there is no guarantee that the display trail will include a record of every thread that moved the ball. Based on thread scheduling and on the load of other applications running, not all threads that move the ball are caught in the act by the display thread.

The MTA free threading model is in contrast to the STA model presented in the APTSERVE and APTCLIEN samples. In STA model threading, a COM object is associated with a single-threaded apartment. Calls to the object from multiple client threads are switched by COM to the thread of the STA apartment that owns the object. Only one thread, the object's STA apartment thread, is permitted to execute the methods of the object. This provides a serialization of access to the object's data.

For functional descriptions and a tutorial code tour of FRECLIEN, see the Code Tour section in FRECLIEN.HTM. For details on the external user operation of FRECLIEN, see both the Usage and Operation sections in FRECLIEN.HTM. To read FRECLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the FRECLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the FRECLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also FRESERVE.HTM in the main tutorial directory for more details on how FRESERVE works and exposes its services to FRECLIEN. You must build the FRESERVE DLL before building FRECLIEN. The makefile for FRESERVE automatically registers that server in the system registry, so you must build FRESERVE before attempting to run FRECLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the FRECLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV FRECLIEN.DSP

You can also simply double-click the FRECLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

FRECLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by FRECLIEN. FRECLIEN currently requires the free-threading facilities provided in more recent releases of COM such as found in version 4.0 and later releases of the Windows NT operating system. The Windows 95 operating system does not currently support free threading, so FRECLIEN will not run on it. However, a DCOM95 add-on to Win95 will permit the FRECLIEN/FRESERVE samples to run on Win95. DCOM95 is currently nearing release.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, FRECLIEN.EXE is the client executable to run for this sample.

OPERATION

The FRECLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, FRESERVE.DLL to graphically illustrate the free threading model. As the ball image created by FRECLIEN moves, it leaves a trail. This trail shows a history of the various threads that have executed the COBall object's Move method.

Here is a summary of operation from the standpoint of FRECLIEN.EXE as a COM client of the FRESERVE.DLL COM server.

There is a minimal menu in FRECLIEN. All the operation is automatic. The main application window's client area is used for visual display of the moving ball.

Menu Selection: File/Exit
Exits FRECLIEN.

Menu Selection: Help/FRECLIEN Tutorial
Opens the FRECLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/FRESERVE Tutorial
Opens the FRESERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About FRECLIEN
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

CODE TOUR

Files          Description
FRECLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
FRECLIEN.H     The include file for the FRECLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
FRECLIEN.CPP   The main implementation file for FRECLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
FRECLIEN.RC    The application resource definition file.
FRECLIEN.ICO   The application icon resource.
GUIBALL.H      The class declaration for the CGuiBall C++ class.
GUIBALL.CPP    Implementation file for the CGuiBall C++ class.
FRECLIEN.DSP   Microsoft Visual Studio Project file.

FRECLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

The program logic governing the ball's motion is encapsulated in the COBall object. See the FRESERVE sample and FRESERVE.HTM for details.

The major topics covered in this code tour are:

  • initializing COM for free-threaded access to the FRESERVE server and its components
  • the construction of the CGuiBall C++ object
  • multithreading in FRECLIEN
  • the use of the system timer; and shutdown of the client

The main FRECLIEN process initializes COM for MTA multi-threaded operation. It does this in the WinMain function. Here is the code from FRECLIEN.CPP.

  ...
  // Call to initialize the COM Library.  Use the SUCCEEDED macro
  // to detect success.  If fail, then exit app with error message.
  // Initialize COM here in the main process thread to establish this
  // main thread as the founder of the multi-threaded apartment (MTA)
  // of this process. Other worker free-threads will also live in this
  // multi-threaded apartment. The app can have only one such multi-
  // threaded apartment but could have a mixed model with other
  // single-threaded apartments running. This sample has no other
  // single-threaded apartments.
  if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
  {
    ...
    ... Init application and enter main message loop, etc
    ...
  }
    ...

The CoInitializeEx function is called with the COINIT_MULTITHREADED flag. This means that COM is initialized to use the MTA multi-threading model in FRECLIEN's main message loop thread. Other worker threads will be created and initialized as COINIT_MULTITHREADED. These worker threads will thereby also execute in the MTA and operate on a single instance of FRESERVE's COBall COM object.

FRECLIEN uses a CGuiBall C++ class to encapsulate the data and behavior of the client's graphical user interface (GUI) moving ball.

Here is the declaration of the CGuiBall class from file GUIBALL.H.

  class CGuiBall
  {
    private:
      HWND     m_hWnd;
      IBall*   m_pIBall;
      COLORREF m_crColor;

      // Pointers to thread init data structures.
      CThreadInitData m_BallThreadData1;
      CThreadInitData m_BallThreadData2;
      CThreadInitData m_BallThreadData3;

    public:
      // Some member variables to store thread ids.
      DWORD m_dwBallThread1;
      DWORD m_dwBallThread2;
      DWORD m_dwBallThread3;

      // An array of handles to the ball threads.
      HANDLE m_hBallThreads[3];

      CGuiBall(void);
      ~CGuiBall(void);
      BOOL Init(HWND hWnd);
      void PaintBall(void);
      void Restart(void);
      void PaintWin(void);
  };

The main process thread uses CGuiBall by calling its main public methods: Init, Restart, PaintBall, and PaintWin. Calls through the IBall interface on FRESERVE's COBall object are encapsulated within the methods of CGuiBall. The COBall object is accessed through an IBall interface pointer, m_pIBall, privately held in CGuiBall.

CGuiBall has an Init method in which an instance of FRESERVE's COBall object is created. This Init method also spawns three threads whose sole job is to continuously call COBall::Move to move the ball entity defined in COBall. These three calling threads keep the ball moving while the main display thread continuously paints snapshot images of the moving ball. CGuiBall keeps the identifiers of these three threads. Several CThreadInitData structures are used to create the threads.

Here is the CGuiBall::Init method definition from GUIBALL.CPP.

  BOOL CGuiBall::Init(
         HWND hWnd)
  {
    BOOL bOk = FALSE;
    HRESULT hr;

    if (hWnd)
    {
      m_hWnd = hWnd;

      // Call COM service to create the single COBall instance.
      // We are not aggregating it so we ask for its IBall interface
      // directly.
      hr = CoCreateInstance(
             CLSID_DllBall,
             NULL,
             CLSCTX_INPROC_SERVER,
             IID_IBall,
             (PPVOID)&m_pIBall);
      if (SUCCEEDED(hr))
      {
        // Now start up 3 client BallThreads that will all try to move the
        // Ball concurrently. They will bring independent asynchronous life
        // to the ball. The separate main process thread displays the ball.
        // These threads (including the main display thread) will all
        // comprise the multi-threaded apartment of the main process.

        // Create Structures for thread initialization.
        m_BallThreadData1.m_hWnd = hWnd;
        m_BallThreadData1.m_nDelay = BALL_MOVE_DELAY;
        m_BallThreadData1.m_pIBall = m_pIBall;
        m_BallThreadData2.m_hWnd = hWnd;
        m_BallThreadData2.m_nDelay = BALL_MOVE_DELAY;
        m_BallThreadData2.m_pIBall = m_pIBall;
        m_BallThreadData3.m_hWnd = hWnd;
        m_BallThreadData3.m_nDelay = BALL_MOVE_DELAY;
        m_BallThreadData3.m_pIBall = m_pIBall;

        // Create the ball-moving thread #1.
        m_hBallThreads[0] = CreateThread(
                              0,
                              0,
                              (LPTHREAD_START_ROUTINE) BallThreadProc,
                              (LPVOID) &m_BallThreadData1,
                              0,
                              &m_dwBallThread1);

        bOk = (NULL != m_hBallThreads[0]);
        if (bOk)
        {
          // AddRef for the copy handed out to free-thread1.
          m_pIBall->AddRef();

          // Create the Ball Moving Thread #2.
          m_hBallThreads[1] = CreateThread(
                                0,
                                0,
                                (LPTHREAD_START_ROUTINE) BallThreadProc,
                                (LPVOID) &m_BallThreadData2,
                                0,
                                &m_dwBallThread2);

          bOk = (NULL != m_hBallThreads[1]);
          if (bOk)
          {
            // AddRef for the copy handed out to free-thread2.
            m_pIBall->AddRef();

            // Create the Ball Moving Thread #3.
            m_hBallThreads[2] = CreateThread(
                                  0,
                                  0,
                                  (LPTHREAD_START_ROUTINE) BallThreadProc,
                                  (LPVOID) &m_BallThreadData3,
                                  0,
                                  &m_dwBallThread3);

            bOk = (NULL != m_hBallThreads[2]);
            if (bOk)
            {
              // AddRef for the copy handed out to free-thread3.
              m_pIBall->AddRef();
            }
          }
        }
      }
      else
        HrMsg(hWnd, TEXT(NOCOMOBJ_ERROR_STR), hr);

      if (!bOk)
      {
        hr = GetLastError();
        if (FAILED(hr))
          HrMsg(hWnd, TEXT(NOTHREAD_ERROR_STR), hr);
      }
    }

    if (bOk)
    {
      // Set up the client process to periodically paint the ball
      // thru WM_TIMER messages to the main Window proc.
      SetTimer(hWnd, 1, BALL_PAINT_DELAY, NULL);
    }

    return (bOk);
  }

CoCreateInstance is called to create an instance of the FRESERVE server's COBall object specified by its CLSID, CLSID_DllBall. This CLSID is defined in BALLGUID.H, located in the sibling INC directory. Because aggregation is not required, CoCreateInstance directly requests the IBall interface, which on return is stored in m_pIBall. The Init method then launches the three moving threads after setting appropriate CThreadInitData structures. This is the CThreadInitData as declared in GUIBALL.H.

  // A small utility struct providing an encapsulation of data needed when
  // worker threads are initialized.
  struct CThreadInitData
  {
    HWND   m_hWnd;
    IBall* m_pIBall;
    DWORD  m_nDelay;
  };

As presented in the previous APTSERVE sample, a pointer to a CThreadInitData structure is passed as an LPARAM to the thread procedure of the newly launched thread. Using this structure, the client's main window handle, an interface pointer to the IBall interface on the COBall object, and a value for the time delay between ball movements are all passed to the new threads. Following the COM contract, the Init method calls m_pIBall->AddRef for each copy of the interface pointer returned to the newly created thread. These AddRef calls are later matched by Release calls in the CGuiBall destructor.

Note that the interface pointer in member m_pIBall is passed from one thread, the main thread, to the newly created thread. This requires no marshaling because both threads are in the same multi-threaded apartment of the process. If the creating thread was a the client's single-threaded apartment that was creating a new thread in the multi-threaded apartment then explicit marshaling of the passed m_pIBall interface would be required. The CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream functions are convenient for such explicit runtime marshaling of interfaces between threads within a process.

The m_hBallThreads array holds handles to the created threads. It is used for later calls to WaitForMultipleObjects to ensure an orderly shutdown of the threads before the client program exits. This occurs in the CGuiBall destructor. Here is the destructor from GUIBALL.CPP.

  CGuiBall::~CGuiBall(void)
  {
    BOOL bOk = TRUE;

    if (m_pIBall)
    {
      // Kill the client's app timer for its repaints.
      KillTimer(m_hWnd, 1);

      // Call down to the server's COBall and tell it to shutdown.
      m_pIBall->Move(FALSE);

      // Wait for the threads to terminate before closing their
      // thread handles.
      WaitForMultipleObjects(3, m_hBallThreads, TRUE, INFINITE);
      for (size_t i = 0; i<3; i++)
        CloseHandle(m_hBallThreads[i]);

      // Release for each of the thread copies handed out.
      m_pIBall->Release();
      m_pIBall->Release();
      m_pIBall->Release();
      // Do final Release for the main copy held in CGuiBall.
      RELEASE_INTERFACE(m_pIBall);
    }
  }

The main process thread relies on a recurrent system timer's WM_TIMER message. This was set up within the Init method of CGuiBall shown above. These periodic timer messages drive the client's independent asynchronous display process. The timer is killed in the destructor (see above) with the KillTimer call. Each WM_TIMER message sent to the main window procedure is honored by a call to the COBall::GetBall method to obtain the ball's display data. Here is the main window procedure from FRECLIEN.CPP.

  LRESULT CMainWindow::WindowProc(
            UINT uMsg,
            WPARAM wParam,
            LPARAM lParam)
  {
    LRESULT lResult = FALSE;

    switch (uMsg)
    {
      case WM_CREATE:
        break;

      case WM_MEASUREITEM:
        // Get setup for painting text in this window.
        {
          LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
          lpmis->itemHeight = m_tm.tmHeight + m_tm.tmExternalLeading;
          lpmis->itemWidth = m_wWidth;
          lResult = TRUE;
        }

      case WM_SIZE:
        // Handle a resize of this window.
        m_wWidth = LOWORD(lParam);
        m_wHeight = HIWORD(lParam);
        // Handle a resize of this window.
        // Restart the ball from upper left, clear window.
        m_pGuiBall->Restart();
        break;

      case WM_TIMER:
        // This is our timed attempt to continuously paint the moving ball.
        // It doesn't move it. Other non-GUI threads move the virtual ball.
        m_pGuiBall->PaintBall();
        break;

      case WM_COMMAND:
        // Dispatch and handle any Menu command messages received.
        lResult = DoMenu(wParam, lParam);
        break;

      case WM_CHAR:
        if (wParam == 0x1b)
        {
          // Exit this app if user hits ESC key.
          PostMessage(m_hWnd,WM_CLOSE,0,0);
          break;
        }
      case WM_LBUTTONUP:
      case WM_PAINT:
        // If something major happened or user clicks or hits key then
        // repaint the whole window.
        m_pGuiBall->PaintWin();
        break;

      case WM_CLOSE:
        // The user selected Close on the main window's System menu.
      case WM_QUIT:
        // If the app is quit by the File/Exit main menu then close
        //   any associated help windows too.
        ::WinHelp(m_hWnd, m_szHelpFile, HELP_QUIT, 0);
      default:
        // Defer all messages NOT handled here to the Default Window Proc.
        lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
        break;
    }

    return(lResult);
  }

The CGuiBall::PaintBall method is called to paint the ball when the WM_TIMER messages arrive. Other main client application messages of interest are WM_SIZE, WM_PAINT, and WM_LBUTTONUP. For WM_SIZE, if the user resizes the client application's main window, CGuiBall::Restart is called. The WM_SIZE message handler calls COBall::Reset, which relocates the ball to the upper left corner of the window and also resets the ball size. If the user clicks the left mouse button (causing WM_LBUTTONUP) or a general WM_PAINT condition occurs, the window is repainted, but the ball is not reset.

The threads each run the following common thread procedure, BallThreadProc in GUIBALL.CPP.

  DWORD WINAPI BallThreadProc(
                 LPARAM lparam)
  {
    CThreadInitData* pInitData = (CThreadInitData*) lparam;
    DWORD nEndCount = 0;
    HRESULT hr;
    BOOL bAlive;

    // Initialize COM for use by this thread. Tell COM this new thread
    // is another free-threaded thread in the multi-threaded apartment.
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    bAlive = SUCCEEDED(hr);

    if (bAlive)
    {
      // Continuously move the ball while it is still alive.
      while (bAlive)
      {
        // Use system timer to slow down the ball motion to the range
        // of the humanly perceptible.
        if (GetTickCount() > nEndCount)
        {
          // After the delay, call from this thread thru IBall interface
          // to move the ball that lives in the single COBall COM object.
          // Set bAlive flag FALSE if the COBall can no longer move.
          hr = pInitData->m_pIBall->Move(TRUE);
          bAlive = (NOERROR == hr);

          // Set new timer end count.
          nEndCount = GetTickCount() + pInitData->m_nDelay;
        }
      }

      // UnInitialize COM for use by this thread.
      CoUninitialize();
    }

    return 0;
  }

Within each launched worker thread, COM is initialized specifying COINIT_MULTITHREADED to indicate the new thread will execute as part of the process's multi-threaded apartment. Each of the three threads executes this same re-entrant thread procedure. Within the procedure, each thread executes an indefinite while loop containing a call to the IBall::Move method on the COBall object. Because these move calls would be far too fast for human perception, each thread is kept busy in a delay countdown using the system timer tick count. This sample uses delays in the neighborhood of 100 milliseconds. When the delay is over, the Move method is called on the COBall object. The three threads are running concurrently and could make the Move calls at conflicting times. As explained in the FRESERVE sample, the Move method on COBall is guarded by the CThreaded OwnThis mechanism. If one of the threads currently owns the COBall object, a Move call by another thread will block the calling thread until it can obtain ownership.

Each thread's while loop terminates in BallThreadProc when the Move call that is made there returns E_FAIL. This return signals that some other thread had killed the ball by calling the Move method with the bAlive parameter set to FALSE. This terminating call is actually made by the main application thread and not by any of the ball-moving threads. The call is made in the CGuiBall destructor shown above. The destructor is run when the main CGuiBall object is deleted because the user has closed or exited the main application, causing an exit of the main process thread's message loop in WinMain.

Top Back to page top

© 1995-1998 Microsoft Corporation


CONSERVE - Connectable Object Server - Lesson 21

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

CONSERVE - Connectable Object Server

SUMMARY

The CONSERVE sample shows how to construct a connectable COM object, COBall, in a thread-safe in-process server. To expose connectable object features, COBall implements the standard COM interface, IConnectionPointContainer. COBall also implements the custom interface, IBall, that was introduced in the FRESERVE sample.

The principal focus of CONSERVE is the connectable object support in COBall and how connected clients are notified of ball events. CONSERVE works with the CONCLIEN client code sample.

CONSERVE's COBall implements a virtual moving ball similar to the one coded in the FRESERVE sample. However, rather than FRESERVE's free-threaded COM object, CONSERVE houses COBall in an apartment model server. COBall defines the internal logic of a moving ball that exists within a specified two-dimensional bounding rectangle. COBall provides only a description of a ball. It is up to the client to create a graphical representation based on the data managed in COBall.

Clients move the virtual ball using its IBall interface. COBall's internal logic will bounce the ball when it collides with a boundary. The client can obtain the ball's current position, size, and color to permit display of the ball's moving image. The client can also use the connection facilities of COBall to receive notifications of ball bounce events. The CONCLIEN sample will use these connection facilities to produce various sounds when the ball bounces.

CONSERVE uses the CThreaded facility in APPUTIL for thread safety, as in the FRESERVE sample. COBall objects are derived from the CThreaded class and inherit its OwnThis and UnOwnThis methods. These methods allow only one thread at a time to have access to the CONSERVE server and to COBall objects managed by the server.

For functional descriptions and a tutorial code tour of CONSERVE, see the Code Tour section in CONSERVE.HTM. For details on setting up the programmatic usage of CONSERVE, see the Usage section in CONSERVE.HTM. To read CONSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the CONSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the CONSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also CONCLIEN.HTM in the main tutorial directory for more details on the CONCLIEN client application and how it works with CONSERVE.DLL. You must build CONSERVE.DLL before building or running CONCLIEN.

CONSERVE's makefile automatically registers the DllSndBall component in the registry. This component must be registered before CONSERVE is available to outside clients as a server for that component. This self-registration is done using the REGISTER.EXE utility built in the REGISTER lesson. To build or run CONSERVE, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the CONSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV CONSERVE.DSP

You can also simply double-click the CONSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use CONSERVE, a client program does not need to include CONSERVE.H or link to CONSERVE.LIB. A client of CONSERVE obtains access solely through its object's CLSID and COM services. For CONSERVE, that CLSID is CLSID_DllSndBall (defined in BALLGUID.H in the \INC sibling directory). The CONCLIEN code sample shows how the client obtains this access.

CONSERVE is a DLL that is intended primarily as an in-process server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within COM's CoGetClassObject function. CONSERVE is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the CONSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the CONSERVE directory.

  ..\register\register.exe conserve.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of CONSERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, CONCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
CONSERVE.TXT  Short sample description.
MAKEFILE      The generic makefile for building the CONSERVE.DLL
              code sample of this lesson.
CONSERVE.H    The include file for declaring as imported or defining as
              exported the service functions in CONSERVE.DLL.
CONSERVE.CPP  The main implementation file for CONSERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
CONSERVE.DEF  The module definition file. Exports server housing functions.
CONSERVE.RC   The DLL resource definition file for the executable.
CONSERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerators objects.
BALL.H        The include file for the COBall object class.
BALL.CPP      The implementation file for the COBall object class
              and the connection points.
CONSERVE.DSP  Microsoft Visual Studio Project file.

CONSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling \APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial samples. This tour assumes that you have some exposure to those previous samples. It does not revisit earlier topics of basic interface implementation techniques, COM object construction, in-process server construction, and class factory construction. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • An overview of how CONSERVE connectability works
  • Connectable object mechanisms in COBall
  • The connection point container in COBall
  • The connection point object
  • Event notification
  • The connection enumerator object
  • The connection point enumerator object

The COBall object is the single object type managed by this CONSERVE in-process server. COBall is constructed as a connectable COM object with an implementation of the standard IConnectionPointContainer interface and an implementation of the custom IBall interface.

COBall exposes the IBall interface so clients can perform a small set of ball-related operations on an instance of COBall. Methods are exposed to reset, move, and obtain current ball position, size, and color. This IBall interface is the same interface used in the FRESERVE sample. See FRESERVE.HTM for more details on the IBall interface.

The COBall object encapsulates data, such as position, size, and color, that defines a moving ball. No graphical images are managed in COBall. When a COBall object is initialized with a call to its Reset method, it is given a rectangle (a Win32 RECT structure) that defines the boundaries within which the ball may move. The COBall object contains coded logic to move the ball within those boundaries, to bounce the ball off any boundary when appropriate, and to provide clients with current data on the ball's location, size, and color. A client can use the IBall interface to move the ball and to obtain the data necessary to paint an image of the moving ball.

COBall exposes the IConnectionPointContainer interface so clients can connect to COBall to receive notifications of specified events that occur in COBall. By exposing this interface, COBall becomes a connectable object. A client can request this interface with a call to QueryInterface and use it to obtain the object's connection points. The client participation in this scheme is covered in the associated CONCLIEN sample.

Basically, the client implements what is called a sink object with a sink interface. The sink interface receives event notifications from COBall after the sink is properly connected to a COBall instance. The client makes the connection by using a connection point object that is managed by COBall. There can be numerous connection points on a connectable object. In the CONSERVE sample, COBall has only one connection point, which reports ball bounce events.

A connection point can support numerous client connections. The connection point in COBall maintains an array of connections that can grow dynamically at run time. We will see later in this tour how COBall maintains and uses this array to broadcast event notifications.

To make COBall fully connectable, several COM object classes must be implemented: COEnumConnectionPoints, COConnectionPoint, and COEnumConnections. The implementation of the standard IConnectionPointContainer interface within COBall is also necessary. The COEnumConnectionPoints object exposes the standard IEnumConnectionPoints interface so clients can enumerate or sequence through the connection points of the connectable COM object. The COConnectionPoint object exposes the standard IConnectionPoint interface so that clients can connect to the connectable COM object using the connection point. The COEnumConnections object exposes the standard IEnumConnections interface so clients can enumerate through the current connections managed by a connection point. In this CONSERVE sample, the bulk of the code for these implementations is in files CONNECT.H and CONNECT.CPP. The rest of this tour will focus on these implementations.

Enumeration objects are common in COM programming. The basic scheme relies on a dynamically created enumeration object that exposes an interface used for subsequent access to an enumerated sequence of other objects that are managed by the object being enumerated. For example, the COBall implementation of the IConnectionPointContainer interface has an EnumConnectionPoints method. This method allows clients to enumerate the connection points of the COBall object. It dynamically creates a special object, COEnumConnectionPoints, whose sole purpose is to enable access by the client through a standard IEnumConnectionPoint interface to an enumerated sequence of the connection points. This interface is implemented by the programmer in a COEnumConnectionPoints object. It has Next, Skip, Reset, and Clone methods which clients can use to sequence through the enumerated connection points. In this sample, connection points themselves support enumeration of the connections they manage. A COEnumConnections object can be dynamically created. On the same programming model as IEnumConnectionPoints, COEnumConnections exposes an IEnumConnections interface with Next, Skip, Reset, and Clone methods.

The COBall implementation of IConnectionPointContainer must provide the FindConnectionPoint and EnumConnectionPoints methods. Once a client obtains an IConnectionPointContainer interface on COBall, it can call FindConnectionPoint to obtain a specific connection point, or it can call EnumConnectionPoints to enumerate all connection points. Once the client obtains a connection point, it can connect to it (Advise method), disconnect from it (Unadvise method), enumerate all current connections to that connection point (EnumConnections method), obtain the container of the connection point (GetConnectionPointContainer method), and obtain the outgoing interface of the client notification sink (GetConnectionInterface method). When the client connects to a COBall connection point, it passes a pointer to the sink interface on its sink object. The connection point object maintains a dynamic list of such sink interfaces. When events occur within COBall that require sending a notification to the sink in the client, logic in COBall can use each connection point's sink interface list to broadcast appropriate notifications.

In this connection scheme, the client implements sink objects for the connection points it expects to find in COBall. On the server side, COBall must implement the IConnectionPointContainer interface. This interface implementation, in turn, requires certain other COM objects. Each connection point requires a COConnectionPoint object. To enumerate connection points in COBall requires a COEnumConnectionPoints object, and to enumerate the connections of a connection point requires a COEnumConnections object. We will cover enumerator objects later in this code tour.

COBall's implementation of the IConnectionPointContainer interface is coded in BALL.H and BALL.CPP. The construction of the COBall object is based on techniques presented in earlier samples of this series. Nested class declarations are used for the multiple interface implementations. Here is the COBall class declaration in BALL.H.

  class COBall : public IUnknown, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COBall(IUnknown* pUnkOuter, CServer* pServer);
      ~COBall(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIConnectionPointContainer : public IConnectionPointContainer,
                                            public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIConnectionPointContainer(
            COBall* pBackObj,
            IUnknown* pUnkOuter);
          ~CImpIConnectionPointContainer(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IConnectionPointContainer methods.
          STDMETHODIMP         FindConnectionPoint(
                                 REFIID,
                                 IConnectionPoint**);
          STDMETHODIMP         EnumConnectionPoints(IEnumConnectionPoints**);

        private:
          // Data private to this interface implementation.
          COBall*       m_pBackObj;     // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      class CImpIBall : public IBall, public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIBall(COBall* pBackObj, IUnknown* pUnkOuter);
          ~CImpIBall(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IBall methods.
          STDMETHODIMP         Reset(RECT* pNewRect, short nBallSize);
          STDMETHODIMP         GetBall(
                                 POINT* pNewOrg,
                                 POINT* pNewExt,
                                 COLORREF* pcrColor);
          STDMETHODIMP         Move(BOOL bAlive);

        private:
          // Data private to this interface implementation of IBall.
          COBall*       m_pBackObj;     // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.

          // The following private data and methods constitute the working
          // heart of COBall as an actual application object.
          BOOL          m_bAlive;
          RECT          m_WinRect;
          int           m_nWidth;
          int           m_nHeight;
          int           m_xDirection;
          int           m_yDirection;
          BOOL          m_bNewPosition;
          int           m_xPosition;
          int           m_yPosition;
          short         m_xSkew;
          short         m_ySkew;
          COLORREF      m_crColor;
          CXForm        m_XForm;
          CBallThread   m_aBallThreads[MAX_BALLTHREADS];

          // Private utility methods for internal support of the custom
          // IBall semantics.
          void GetDimensions(POINT*);
          void SetDimensions(int,int);
          void GetDirection(POINT*);
          void SetDirection(int,int);
          void GetPosition(POINT*);
          void SetPosition(int,int);
          void FindThread(void);
          DWORD CheckBounce(void);
      };

      // Make the otherwise private and nested IBall and
      // IConnectionPointContainer interface implementations a friend to
      // COM object instantiations of this COBall COM object class.
      friend CImpIConnectionPointContainer;
      friend CImpIBall;

      // Private method of main connectable COBall COM object to broadcast
      // event notifications to all connected listening sinks.
      HRESULT NotifySinks(DWORD dwEvent);

      // Private data of COBall COM objects.

      // Nested IBall implementation instantiation.  This IBall interface
      // is instantiated inside this COBall object as a native interface.
      CImpIBall         m_ImpIBall;

      // Nested IConnectionPointContainer implementation instantiation.
      CImpIConnectionPointContainer m_ImpIConnectionPointContainer;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;

      // The array of connection points for this connectable COM object.
      IConnectionPoint* m_aConnectionPoints[MAX_CONNECTION_POINTS];
  };

The outer COBall class and the nested CImpIBall and CImpIConnectionPointContainer classes are derived from CThreaded to inherit the OwnThis thread safety mechanism. The methods of these classes use this mechanism to ensure mutually exclusive access to their objects by multiple threads. Though COBall is not registered as a free-threaded component in this sample, it is coded for thread-safety and could therefore easily be used in a free-threaded program.

Much of this COBall class declaration was presented in the FRESERVE sample. The items related to connectable object support are the CImpIConnectionPointContainer implementation, the COBall::NotifySinks private method, and the COBall::m_aConnectionPoints array.

NotifySinks is an internal convenience method that consolidates the broadcast to client sinks of all appropriate events occurring in COBall. We will look at NotifySinks in detail later.

The m_aConnectionPoints array is COBall's main array of connection points. In this sample it is declared with a compile-time constant, MAX_CONNECTION_POINTS. This means that the connection points supported by COBall are determined at compile time. The m_aConnectionPoints array is initially assigned during COBall::Init in file BALL.CPP.

  HRESULT COBall::Init(void)
  {
    HRESULT hr = NOERROR;
    COConnectionPoint* pCOConnPt;

    // Rig this COBall COM object to be connectable. Assign the connection
    // point array. This object's connection points are determined at
    // compile time--it currently has only one connection point:
    // the CONNPOINT_BALLSINK connection point. Create a connection
    // point object for this and assign it into the array. This array could
    // easily grow to support additional connection points in the future.

    // First try creating a new connection point object. Pass 'this' as the
    // pHostObj pointer used by the connection point to pass its AddRef and
    // Release calls back to the host connectable object.
    pCOConnPt = new COConnectionPoint(this);
    if (NULL != pCOConnPt)
    {
      // If creation succeeded then initialize it (including creating
      // its initial dynamic connection array).
      hr = pCOConnPt->Init(IID_IBallSink);

      // If the init succeeded then use QueryInterface to obtain the
      // IConnectionPoint interface on the new connection point object.
      // The interface pointer is assigned directly into the
      // connection point array. The QI also does the needed AddRef.
      if (SUCCEEDED(hr))
        hr = pCOConnPt->QueryInterface(
                          IID_IConnectionPoint,
                          (PPVOID)&m_aConnectionPoints[CONNPOINT_BALLSINK]);
    }
    else
      hr = E_OUTOFMEMORY;

    return hr;
  }

The call to COBall::Init is done in COBall's class factory in the CreateInstance method, which is called when a client asks COM to create an instance of a DllSndBall object. The class factory for CONSERVE's DllSndBall object, CFBall, is declared in FACTORY.H and implemented in FACTORY.CPP. This factory code is adapted from many previous samples in this series. Like COBall, CFBall is derived from IUnknown and CThreaded using multiple inheritance. CThreaded gives the class factory its thread safety using the OwnThis mechanism seen earlier.

This COBall::Init method creates the connection point object, initializes it, and uses a QueryInterface call on the connection point to assign the new connection point to COBall's connection point array. The CONNPOINT_BALLSINK connection point is the only connection point currently supported by COBall objects. To add other connection points would require additional code in Init for each one. CONNPOINT_BALLSINK is a constant defined in CONNECT.H. Similar constants would be needed for additional connection points.

The COConnectionPoint object class is declared in CONNECT.H.

  class COConnectionPoint : public IConnectionPoint, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COConnectionPoint(IUnknown* pHostObj);
      ~COConnectionPoint(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(REFIID riid);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // IConnectionPoint methods.
      STDMETHODIMP GetConnectionInterface(IID*);
      STDMETHODIMP GetConnectionPointContainer(IConnectionPointContainer**);
      STDMETHODIMP Advise(IUnknown*, DWORD*);
      STDMETHODIMP Unadvise(DWORD);
      STDMETHODIMP EnumConnections(IEnumConnections**);

    private:
      // Private utility methods of COConnectionPoint.
      HRESULT GetSlot(UINT* puiFreeSlot);
      HRESULT FindSlot(DWORD dwCookie, UINT* puiSlot);

      // Private data of COConnectionPoint COM objects.

      // Main Object reference count.
      ULONG          m_cRefs;

      // IUnknown pointer to host COM object offering this connection point.
      IUnknown*      m_pHostObj;

      // The IID of the sink interface associated with this connection point.
      IID            m_iidSink;

      // The current connection cookie (key) counter.
      DWORD          m_dwNextCookie;

      // The current number of live sink connections to this connection point.
      UINT           m_cConnections;

      // The current maximum index into the dynamic connection array.
      UINT           m_uiMaxIndex;

      // The dynamic array of sink connections to this connection point.
      CONNECTDATA*   m_paConnections;
  };

This code includes many of the fixtures that are common to COM objects housed in an in-process server. For example, the member m_cRefs is the object reference counter. IConnectionPoint is not implemented in COConnectionPoint in the familiar pattern of nested class implementation. Instead, COConnectionPoint is derived directly from IConnectionPoint. This direct derivation can be done as a convenience because COConnectionPoint objects are not aggregatable and are not exposed as public components housed in this server. COConnectionPoint objects are managed as internal objects within the server.

The m_pHostObj pointer is passed into the constructor for use later in the connection point's GetConnectionPointContainer method.

The m_iidSink member variable stores a reference to the IID of the sink interface in the client. This member is solely for the use of the GetConnectionInterface method.

COConnectionPoint objects manage a dynamic list of current connections to the connection point using the m_dwNextCookie, m_cConnections, m_uiMaxIndex, and m_paConnections member variables. The m_paConnections member points to the current dynamic array of connections. Each connection in the array is represented by a COM-declared CONNECTDATA structure. The structure contains the following.

  typedef struct tagCONNECTDATA
  {
    IUnknown* pUnk;
    DWORD     dwCookie;
  } CONNECTDATA;

The pUnk member contains the interface pointer for the sink in the client. The dwCookie member contains a unique key used to designate the connection.

The initial dynamic array of these elements is created by COConnectionPoint::Init. Here is Init from CONNECT.CPP.

  HRESULT COConnectionPoint::Init(
            REFIID riid)
  {
    HRESULT hr = NOERROR;
    CONNECTDATA* paConns;

    // Keep a copy of the reference to the IID of the sink interface
    // associated with this connection point. Needed for potential
    // use by the GetConnectionInterface method.
    m_iidSink = riid;

    // Build the initial dynamic array for connections.
    paConns = new CONNECTDATA[ALLOC_CONNECTIONS];
    if (NULL != paConns)
    {
      // Zero the array.
      memset(paConns, 0, ALLOC_CONNECTIONS * sizeof(CONNECTDATA));

      // Rig this connection point object so that it will use the
      // new internal array of connections.
      m_uiMaxIndex = ALLOC_CONNECTIONS;
      m_paConnections = paConns;
    }
    else
      hr = E_OUTOFMEMORY;

    return (hr);
  }

This implementation of Init creates an initial allocated array of empty connection elements and assigns member m_paConnections as a pointer to that array. The content of these array elements is assigned when COConnectionPoint::Advise is called, and the content is cleared by COConnectionPoint::Unadvise. Here is Advise from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::Advise(
                 IUnknown* pUnkSink,
                 DWORD* pdwCookie)
  {
    HRESULT hr = NOERROR;
    UINT uiFreeSlot = 0;
    IUnknown* pISink = NULL;

    if (OwnThis())
    {
      // Zero the output connection key.
      *pdwCookie = 0;

      // Get the specific associated client Sink interface that this
      // connection point expects to use for notifications.
      hr = pUnkSink->QueryInterface(m_iidSink, (PPVOID)&pISink);
      if (SUCCEEDED(hr))
      {
        // Store the specific sink interface in this connection point's
        // array of live connections. First find a free slot (expand the
        // array if needed).
        hr = GetSlot(&uiFreeSlot);
        if (SUCCEEDED(hr))
        {
          // Assign the new slot with the connection entry.
          m_paConnections[uiFreeSlot].pUnk = pISink;
          m_paConnections[uiFreeSlot].dwCookie = m_dwNextCookie;

          // Assign the output Cookie value.
          *pdwCookie = m_dwNextCookie;

          // Increment the Cookie counter.
          m_dwNextCookie++;

          // Increment the number of live connections.
          m_cConnections++;
        }
      }
      else if (hr == E_NOINTERFACE)
      {
         // The sink does not support m_iidSink.
         hr = CONNECT_E_CANNOTCONNECT;
      }

      UnOwnThis();
    }

    return hr;
  }

Advise is called by a client to establish a connection of its sink interface to the connection point of a connectable object. The client passes a pointer to the IUnknown of its sink object and a pointer to a "cookie" variable. Advise assigns a unique key value to this client cookie variable and adds the new connection to the connection array created by Init. The connection point maintains m_dwNextCookie as a counter to provide such cookies. When a connection is properly assigned, Advise also increments a counter of current live connections, m_cConnections. Because these counters might be accessed by multiple client threads, the entire COConnectionPoint class is derived from CThreaded to permit the Advise and Unadvise methods to use the OwnThis mechanism to ensure thread-safety. For details on the CThreaded class, see APTSERVE.HTM and the APTSERVE code sample of this series.

Advise calls the GetSlot private method to obtain an index into the connection array of an open slot. Here is GetSlot from CONNECT.CPP.

  HRESULT COConnectionPoint::GetSlot(
            UINT* puiFreeSlot)
  {
    HRESULT hr = NOERROR;
    BOOL bOpen = FALSE;
    UINT i;
    CONNECTDATA* paConns;

    // Zero the output variable.
    *puiFreeSlot = 0;

    // Loop to find an empty slot.
    for (i=0; i<m_uiMaxIndex; i++)
    {
      if (m_paConnections[i].dwCookie == 0)
      {
        // We found an open empty slot.
        *puiFreeSlot = i;
        bOpen = TRUE;
        break;
      }
    }

    if (!bOpen)
    {
      // We didn't find an existing open slot in the array--it's full.
      // Expand the array by ALLOC_CONNECTIONS entries and assign the
      // appropriate output index.
      paConns = new CONNECTDATA[m_uiMaxIndex + ALLOC_CONNECTIONS];
      if (NULL != paConns)
      {
        // Copy the content of the old full array to the new larger array.
        for (i=0; i<m_uiMaxIndex; i++)
        {
          paConns[i].pUnk = m_paConnections[i].pUnk;
          paConns[i].dwCookie = m_paConnections[i].dwCookie;
        }

        // Zero (ie mark as empty) the expanded portion of the new array.
        for (i=m_uiMaxIndex; i<m_uiMaxIndex+ALLOC_CONNECTIONS; i++)
        {
          paConns[i].pUnk = NULL;
          paConns[i].dwCookie = 0;
        }

        // New larger array is ready--delete the old array.
        delete [] m_paConnections;

        // Rig the connection point to use the new larger array.
        m_paConnections = paConns;

        // Assign the output free slot as first entry in new expanded area.
        *puiFreeSlot = m_uiMaxIndex;

        // Calculate the new max index.
        m_uiMaxIndex += ALLOC_CONNECTIONS;
      }
      else
        hr = E_OUTOFMEMORY;
    }

    return hr;
  }

GetSlot first attempts to find an open slot in the existing array and will return it if one is found. Such an open slot exists because a previous client call to Unadvise cleared the entry. However, if the current array is full and GetSlot can find no existing open slot, GetSlot will allocate an entirely new larger array with room for ALLOC_CONNECTIONS more entries. ALLOC_CONNECTIONS is a constant declared in CONNECT.H. The m_uiMaxIndex member is used to maintain the current maximum index of the array. The m_uiMaxIndex member stores the size of the array so any for loops that iterate through the array always work, no matter how the size of the array has changed in dynamic reallocations of the array.

The client calls Unadvise to disconnect its sink interface from the connection point. Here is Unadvise from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::Unadvise(
                 DWORD dwCookie)
  {
    HRESULT hr = NOERROR;
    UINT uiSlot;

    if (0 != dwCookie)
    {
      if (OwnThis())
      {
        hr = FindSlot(dwCookie, &uiSlot);
        if (SUCCEEDED(hr))
        {
          // Release the sink interface.
          RELEASE_INTERFACE(m_paConnections[uiSlot].pUnk);

          // Mark the array entry as empty.
          m_paConnections[uiSlot].dwCookie = 0;

          // Decrement the number of live connections.
          m_cConnections--;
        }

        UnOwnThis();
      }
    }
    else
      hr = E_INVALIDARG;

    return hr;
  }

Unadvise releases the sink interface of the specified connection and sets the associated pointer to NULL, zeroes the associated cookie value, and decrements the number of live connections. Unadvise is passed a cookie value that was obtained by a previous call to Advise. This cookie value is used to locate the connection in the connection array. The FindSlot private method is called to find the array index to the connection entry corresponding to the specified cookie value. Here is FindSlot from CONNECT.CPP.

  HRESULT COConnectionPoint::FindSlot(
            DWORD dwCookie,
            UINT* puiSlot)
  {
    HRESULT hr = CONNECT_E_NOCONNECTION;
    UINT i;

    // Loop to find the Cookie.
    for (i=0; i<m_uiMaxIndex; i++)
    {
      if (dwCookie == m_paConnections[i].dwCookie)
      {
        // If a cookie match is found, assign the output slot index.
        *puiSlot = i;
        hr = NOERROR;
        break;
      }
    }

    return hr;
  }

When a match of the specified cookie is found, the *puiSlot index is assigned.

With an array of client sink connections established by client calls to Advise, COBall can use the array to broadcast notifications to the client sinks. We return to BALL.CPP to study notifications. Here is COBall::Move.

  STDMETHODIMP COBall::CImpIBall::Move(
                 BOOL bAlive)
  {
    HRESULT hr = E_FAIL;
    DWORD dwEvent;

    if (OwnThis())
    {
      if (bAlive && m_bAlive)
      {
        // Find thread that is now executing this code. Remember its Id and
        // assign it a color. If this thread previously visited here then
        // use its remembered values. In any case, set a color value in
        // m_crColor of its existing or newly assigned color.
        FindThread();

        // Ask the Ball if it has hit any of the edges of the current window
        // rectangle. If so, it will recalculate its position and direction
        // to achieve a "bounce" effect in its motion the next time it is
        // painted. CheckBounce also determines and returns any notification
        // events.
        dwEvent = CheckBounce();

        // Send notification of each bounce event to any listening sinks.
        m_pBackObj->NotifySinks(dwEvent);

        // Calculate and set new Ball position.
        if(m_bNewPosition)
        {
          m_bNewPosition = FALSE;
          m_XForm.Clear();
          m_XForm.Trans(m_xPosition, m_yPosition);
        }
        else
          m_XForm.Trans(m_xDirection, m_yDirection);
      }
      else
        m_bAlive = FALSE;

      hr = m_bAlive ? NOERROR : E_FAIL;

      UnOwnThis();
    }

    return hr;
  }

This implementation is very similar to that of the Move method presented in the COBall object of the FRESERVE code sample. In the CONSERVE sample, when the CONCLIEN client calls IBall::Move on this COBall object, COBall has internal logic in the CheckBounce private method to determine if certain events have occurred. CheckBounce returns a constant indicating the event. Move uses this event constant to call the NotifySinks private method. Here is NotifySinks from BALL.CPP.

  HRESULT COBall::NotifySinks(
         DWORD dwEvent)
  {
    HRESULT hr = NOERROR;
    IConnectionPoint* pIConnectionPoint;
    IEnumConnections* pIEnum;
    CONNECTDATA ConnData;

    // If there was a bounce event, broadcast appropriate notifications to
    // all Sinks connected to each connection point.
    if (BOUNCE_NONE != dwEvent)
    {
      // Here is the section for the BallSink connection point--currently
      // this is the only connection point offered by COBall objects.
      pIConnectionPoint = m_aConnectionPoints[CONNPOINT_BALLSINK];
      if (NULL != pIConnectionPoint)
      {
        pIConnectionPoint->AddRef();
        hr = pIConnectionPoint->EnumConnections(&pIEnum);
        if (SUCCEEDED(hr))
        {
          // Loop thru the connection point's connections and if the
          // listening connection supports IBallSink (ie, BallSink events)
          // then dispatch the dwEvent event notification to that sink.
          while (NOERROR == pIEnum->Next(1, &ConnData, NULL))
          {
            IBallSink* pIBallSink;

            hr = ConnData.pUnk->QueryInterface(
                                  IID_IBallSink,
                                  (PPVOID)&pIBallSink);
            if (SUCCEEDED(hr))
            {
              switch (dwEvent)
              {
                case BOUNCE_BOTTOM:
                  pIBallSink->BounceBottom();
                  break;
                case BOUNCE_LEFT:
                  pIBallSink->BounceLeft();
                  break;
                case BOUNCE_RIGHT:
                  pIBallSink->BounceRight();
                  break;
                case BOUNCE_TOP:
                  pIBallSink->BounceTop();
                  break;
                default:
                  break;
              }
              pIBallSink->Release();
            }
            ConnData.pUnk->Release();
          }
          pIEnum->Release();
        }
        pIConnectionPoint->Release();
      }
    }

    return hr;
  }

Since this COBall object has one connection point determined at compile time, NotifySinks can index into the connection point array for specific connection points. In this case, only one connection point is known, and its index is CONNPOINT_BALLSINK.

Once the connection point is obtained, a connection enumerator is used. The connection point's EnumConnections method is called to obtain an IEnumConnections interface pointer on a COEnumConnections object. A while loop iterates through the connections with calls to pIEnum->Next. Once the data for each connection is obtained, the client's sink interface is used to make specific notification calls. In this case, the IBallSink interface has methods to handle the BOUNCE_BOTTOM, BOUNCE_LEFT, BOUNCE_RIGHT, and BOUNCE_BOTTOM events. The custom client sink interface was designed to match the expected connection points in the connectable objects.

This enumeration scheme is a common pattern in COM programming. Here is the COConnectionPoint::EnumConnections method from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::EnumConnections(
                 IEnumConnections** ppIEnum)
  {
    HRESULT hr = OLE_E_NOCONNECTION;
    CONNECTDATA* paConns;
    COEnumConnections* pCOEnum;
    UINT i,j;

    if (OwnThis())
    {
      // Zero the output enumerator interface pointer.
      *ppIEnum = NULL;

      if (0 != m_cConnections)
      {
        // Create an array of CONNECTDATA structures.
        paConns = new CONNECTDATA[(UINT)m_cConnections];
        if (NULL != paConns)
        {
          for (i=0, j=0; i<m_uiMaxIndex && j<m_cConnections; i++)
          {
            // Copy non-empty entries only.
            if (0 != m_paConnections[i].dwCookie)
            {
              // Assign the occupied entry.
              paConns[j].pUnk = (IUnknown*)m_paConnections[i].pUnk;
              paConns[j].dwCookie = m_paConnections[i].dwCookie;
              j++;
            }
          }

          // Create a new COM object for enumerating connections. Pass
          // 'this' as a pHostObj pointer used later to ensure the host
          // connection point object stays alive as long as the enumerator
          // that enumerates connections to that connection point.
          pCOEnum = new COEnumConnections(this);
          if (NULL != pCOEnum)
          {
            // Use the previously constructed (paConns) array of connections
            // to init the new COEnumConnections COM object. The Init will
            // build yet another internal copy of this array. Set the
            // initial enumerator index to 0.
            hr = pCOEnum->Init(m_cConnections, paConns, 0);

            // QueryInterface to return the requested interface pointer.
            // An AddRef will be conveniently done by the QI.
            if (SUCCEEDED(hr))
              hr = pCOEnum->QueryInterface(
                              IID_IEnumConnections,
                              (PPVOID)ppIEnum);
          }
          else
            hr = E_OUTOFMEMORY;

          // We're done with the locally constructed array copy--delete it.
          delete [] paConns;
        }
        else
          hr = E_OUTOFMEMORY;
      }

      UnOwnThis();
    }

    return hr;
  }

A special copy of the connection array is constructed containing only the active connections. Other empty entries are ignored. This array copy is used in the initialization of a COEnumConnections COM object. Once created and initialized, this object is queried for its IEnumConnections interface. A pointer to this interface is passed to the caller of COConnectionPoint::EnumConnections.

To enumerate the connections of a connection point, a COEnumConnections object must be created. Here is the declaration of COEnumConnections from CONNECT.H.

  class COEnumConnections : public IEnumConnections
  {
    public:
      // Main Object Constructor & Destructor.
      COEnumConnections(IUnknown* pHostObj);
      ~COEnumConnections(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(
                ULONG cConnections,
                CONNECTDATA* paConnections,
                ULONG iEnumIndex);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // IEnumConnections methods.
      STDMETHODIMP         Next(ULONG, CONNECTDATA*, ULONG*);
      STDMETHODIMP         Skip(ULONG);
      STDMETHODIMP         Reset(void);
      STDMETHODIMP         Clone(IEnumConnections**);

    private:
      // Private data of COEnumConnections COM objects.

      // Main Object reference count.
      ULONG            m_cRefs;

      // IUnknown pointer to host connection point COM object being
      // enumerated.
      IUnknown*        m_pHostObj;

      // Connection index variable.
      ULONG            m_iEnumIndex;

      // Number of Connections being enumerated.
      ULONG            m_cConnections;

      // Allocated array of live Connections only.
      CONNECTDATA*     m_paConnections;
  };

To ensure that the host connection point object remains in existence as long as the COEnumConnections enumerator object, the m_pHostObj member is used to delegate AddRef and Release calls. The value for m_pHostObj is initially passed into COEnumConnections in its constructor. To support the IEnumConnections interface, a current connection array index is maintained in member m_iEnumIndex. The total number of connections in the array is also retained in member m_cConnections. An internal copy of the connection array is also kept by COEnumConnections. The m_paConnections member points to this array.

The COEnumConnections::Init method assigns m_pHostObj, m_cConnections, and m_iEnumIndex. The Init method also allocates and assigns an internal copy of the connection array and assigns m_paConnections to point to it.

Enumerator objects offer the Next, Skip, Reset, and Clone methods of the standard IEnumConnections interface. The IEnumConnections interface is implemented directly by COEnumConnections rather than being coded as a nested interface implementation. This is convenient for such ad hoc COM objects as COEnumConnections, which are subordinate to the other objects housed in the server and which are not components in their own right.

The COEnumConnections::Next method is called to return a specified number of next connections from the connection array. Here is Next from CONNECT.CPP.

  STDMETHODIMP COEnumConnections::Next(
                 ULONG cReq,
                 CONNECTDATA* paConnections,
                 ULONG* pcEnumerated)
  {
    HRESULT hr = NOERROR;
    ULONG cRet = 0;

    // Make sure the argument values passed are valid.
    if (NULL != m_paConnections)
    {
      if (NULL != paConnections)
      {
        if (m_iEnumIndex < m_cConnections)
        {
          if (NULL != pcEnumerated)
            *pcEnumerated = 0L;
          else
            if (1L != cReq)
              hr = E_POINTER;
        }
        else
          hr = S_FALSE;
      }
      else
        hr = E_POINTER;
    }
    else
      hr = S_FALSE;

    if (SUCCEEDED(hr))
    {
      // Starting at the current Enumerator index, loop to assign the
      // requested number of output connection data structures.
      for (; m_iEnumIndex < m_cConnections && cReq > 0;
             paConnections++, m_iEnumIndex++, cRet++, cReq--)
      {
        // Because we are assigning a copy of a connection's data, AddRef
        // its sink interface pointer.
        if (NULL != m_paConnections[m_iEnumIndex].pUnk)
          m_paConnections[m_iEnumIndex].pUnk->AddRef();

        // Assign a connection's data from the inside Enumerator array to
        // the specified output receiving array.
        *paConnections = m_paConnections[m_iEnumIndex];
      }

      // Assign the output number of connections enumerated.
      if (NULL != pcEnumerated)
        *pcEnumerated = cRet;
    }

    return hr;
  }

Argument cReq is the number of connection items requested. Argument paConnections is a pointer to a caller-allocated array of the CONNECTDATA items requested. The caller must ensure that the array size is large enough to accept the number of requested items. Argument pcEnumerated is a pointer to a variable that will contain, upon return, the number of items actually returned. Because copies of the connection data are being passed back to the caller, AddRef must be called on the sink interface pointer in each CONNECTDATA element.

The Skip method is called to start at the current enumeration index and skip a specified number of connection entries. The Reset method is called to reset the current enumeration index to 0. The Clone method is called to create a new copy of the entire COEnumConnections object, with internal connection array and index the same as the original.

Earlier in this tour we saw that COBall's connection points could also be enumerated by a call to COBall's implementation of the IConnectionPointContainer interface. IConnectionPointContainer has an EnumConnectionPoints method that can be called by clients that want to enumerate all the connection points of the connectable object. Unlike the EnumConnections presented earlier, EnumConnectionPoints is not called by any object within the CONSERVE server. The IConnectionPointContainer::EnumConnectionPoints method relies on the creation of a COEnumConnectionPoints COM object and works very similar to the COEnumConnections just presented. It implements the IEnumConnectionPoints interface with methods Next, Skip, Reset, and Clone. You can study the source for COEnumConnectionPoints in files CONNECT.H and CONNECT.CPP.

Top Back to page top

© 1995-1998 Microsoft Corporation


CONCLIEN - Client of Connectable Object Server - Lesson 22

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

CONCLIEN - Client of Connectable Object Server

SUMMARY

The CONCLIEN sample creates and uses the connectable COBall COM object that is housed in the CONSERVE server as the DllSndBall component. The CONCLIEN client creates one COBall object and controls it through the IBall interface that the object exposes. CONCLIEN sets up a system timer to use IBall to periodically move the ball and obtain updates of data describing the COBall object's position, size, and color. It uses that data, obtained by calling the IBall::GetBall method, to display graphical snapshot images of the ball in the client's main window.

CONCLIEN also implements a COBallSink COM object and connects it to an appropriate connection point in the server's COBall object. In response to the system timer's WM_TIMER messages, which are sent periodically to the display window, CONCLIEN uses the IBall interface to move and paint images of the ball. COBall also sends notifications back to CONCLIEN, using the IBallSink interface, when the ball bounces off of a side of its bounding rectangle. CONCLIEN responds to these event notifications to produce a different sound when the ball collides with a Top, Side, or Bottom boundary of the display window.

For functional descriptions and a tutorial code tour of CONCLIEN, see the Code Tour section in CONCLIEN.HTM. For details on the external user operation of CONCLIEN, see both the Usage and Operation sections in CONCLIEN.HTM. To read CONCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the CONCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the CONCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also CONSERVE.HTM in the main tutorial directory for more details on how CONSERVE works and exposes its services to CONCLIEN. You must build the CONSERVE DLL before building CONCLIEN. The makefile for CONSERVE automatically registers that server in the system registry, so you must build CONSERVE before attempting to run CONCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the CONCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV CONCLIEN.DSP

You can also simply double-click the CONCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

CONCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by CONCLIEN.

The Win32 Beep function is used in this sample to produce ball bounce sounds of various frequencies. This works under Windows NT 4.0 and later. Under the Windows 95 operating system, the Win32 Beep function currently uses whatever is assigned in the Control Panel's Sound dialog for the default sound and thus ignores the frequency values passed to Beep. Under current releases of Windows 95, the sound will not be different when the ball collides with the top, bottom, or side of the display window.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, CONCLIEN.EXE is the client executable to run for this sample.

OPERATION

The CONCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, CONSERVE.DLL to demonstrate connectable objects.

Here is a summary of operation from the standpoint of CONCLIEN.EXE as a client of the CONSERVE.DLL COM server.

There is a minimal menu in CONCLIEN. Most of the ball moving operation is automatic. The main application window's client area is used for visual display of the moving ball.

Menu Selection: File/Exit
Exits CONCLIEN.

Menu Selection: Sound/Connect Ball Sound
Connects the COBallSink object's IBallSink interface to the COBall
object's BallSink connection point. Ball bounce sounds are the result.
A check mark on this menu choice indicates that the sound events are
connected.

Menu Selection: Sound/Disconnect Ball Sound
Disconnects the COBallSink object's IBallSink interface from the COBall
object's BallSink connection point. Ball bounce sounds are disabled. A
check mark on this menu choice indicates that the sound events are
disconnected.

Menu Selection: Help/CONCLIEN Tutorial
Opens the CONCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/CONSERVE Tutorial
Opens the CONSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from
this lesson or another one in the Windows Notepad.

Menu Selection: Help/About CONCLIEN
Displays the About dialog box for this application, a standard part of
this series of code samples.

CODE TOUR

Files          Description
CONCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
CONCLIEN.H     The include file for the CONCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
CONCLIEN.CPP   The main implementation file for CONCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
CONCLIEN.RC    The application resource definition file.
CONCLIEN.ICO   The application icon resource.
SINK.H         The class declaration for the COBallSink class.
SINK.CPP       Implementation file for the COBallSink class.
GUIBALL.H      The class declaration for the CGuiBall C++ class.
GUIBALL.CPP    Implementation file for the CGuiBall C++ class.
CONCLIEN.DSP   Microsoft Visual Studio Project file.

CONCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling \APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

The program logic governing the ball's motion is encapsulated in the COBall object. See the CONSERVE sample and CONSERVE.HTM for details.

The major topics covered in this code tour are:

  • An overview of how CONCLIEN works as a client of connectable objects
  • CGuiBall's support for connectable objects
  • The COBallSink object
  • The IBallSink interface
  • Use of the system timer
  • Shutdown of the client and server

When you first start CONCLIEN, the moving ball starts automatically. CONCLIEN issues various sounds when the ball bounces against a boundary of the display window. The sounds can be disabled using the Sound menu's Disconnect Ball Sound command. The sounds can be enabled using the Sound menu's Connect Ball Sound command. These commands connect or disconnect a sink object in CONCLIEN to or from an associated connection point in the server's COBall object. The COBallSink object in CONCLIEN implements the custom IBallSink interface designed specifically for ball bounce events. To connect the sound events, a pointer to this interface is given to a connection point object in CONSERVE's COBall object. The COBall object determines when the bounce events occur. When such an event occurs, COBall calls the proper IBallSink method to notify the client of the bounce event. The IBall interface is regarded as an incoming interface of the COBall object; the IBallSink interface can be regarded as an outgoing interface of the COBall object.

As in the FRECLIEN sample, CONCLIEN uses a CGuiBall C++ class to encapsulate the data and behavior of the client's graphical user interface (GUI) moving ball image. This class was presented in detail in the FRECLIEN sample. We'll focus below on the sink and connection related code that must be added to CGuiBall to use the COBall connectable object in CONSERVE.

Here is the declaration of the CONCLIEN's CGuiBall class from GUIBALL.H.

  class CGuiBall
  {
    public:
      CGuiBall(void);
      ~CGuiBall(void);
      BOOL Init(HWND hWnd);
      void PaintBall(void);
      void Restart(void);
      void PaintWin(void);
      void BounceBottom(void);
      void BounceSide(void);
      void BounceTop(void);
      HRESULT ConnectBallSink(void);
      HRESULT DisconnectBallSink(void);

    private:
      HWND       m_hWnd;
      IBall*     m_pIBall;
      COLORREF   m_crColor;
      POINT      m_OldPos;
      POINT      m_OldExt;
      IUnknown*  m_pCOBallSink;
      DWORD      m_dwBallSink;
      DWORD      m_dwBounceSndDur;
      DWORD      m_dwBounceBotFreq;
      DWORD      m_dwBounceSideFreq;
      DWORD      m_dwBounceTopFreq;

      IConnectionPoint* GetConnectionPoint(REFIID riid);
  };

This C++ object encapsulates all the GUI ball behavior in the client. Three methods are provided to handle bounce events: BounceBottom, BounceSide, and BounceTop. A private method, GetConnectionPoint, is used internally to obtain the COBall object's connection point interface that corresponds to a specified sink interface IID. ConnectBallSink and DisconnectBallSink methods are also provided to connect and disconnect the client sink to or from its matching connection point in the server's COBall object.

Here is ConnectBallSink from GUIBALL.CPP.

  HRESULT CGuiBall::ConnectBallSink(void)
  {
    HRESULT hr = E_FAIL;
    DWORD dwKey;
    IConnectionPoint* pConnPoint;

    if (!m_dwBallSink)
    {
      // Get the Ball Sink connection point.
      pConnPoint = GetConnectionPoint(IID_IBallSink);
      if (NULL != pConnPoint)
      {
        // Connect the object in the server to the Ball Sink in this client.
        hr = pConnPoint->Advise(m_pCOBallSink, &dwKey);
        if (SUCCEEDED(hr))
          m_dwBallSink = dwKey;

        // Release the connection point. We're done with it.
        RELEASE_INTERFACE(pConnPoint);
      }
    }

    return hr;
  }

The connection point is obtained and passed in an Advise call to establish the connection. The Advise method is presented in the CONSERVE code sample. The pointer to the connection point is obtained temporarily and is released above after the Advise method returns.

After CGuiBall is first created in CONCLIEN, ConnectBallSink is called to connect CONCLIEN's sink to enable ball sounds. See CMainWindow::InitInstance in CONCLIEN.CPP.

The main process thread uses CGuiBall by calling the public methods Init, Restart, PaintBall, and PaintWin. The server's COBall object is accessed through an IBall interface pointer, m_pIBall, privately held in CGuiBall. Thus client calls through the IBall interface on CONSERVE's COBall object are encapsulated within the methods of CGuiBall.

CGuiBall has an Init method, in which an instance of CONSERVE's COBall object is created. Here is the CGuiBall::Init method definition from GUIBALL.CPP.

  BOOL CGuiBall::Init(
         HWND hWnd)
  {
    BOOL bOk = FALSE;
    HRESULT hr;
    COBallSink* pCob = NULL;

    if (hWnd)
    {
      m_hWnd = hWnd;

      // Call COM service to create a COBall instance. We are not
      // aggregating it so we ask for its IBall interface directly.
      hr = CoCreateInstance(
             CLSID_DllSndBall,
             NULL,
             CLSCTX_INPROC_SERVER,
             IID_IBall,
             (PPVOID)&m_pIBall);
      if (SUCCEEDED(hr))
      {
        // Create the COBallSink Sink object to receive COBall events.
        pCob = new COBallSink(NULL, this);
        if (NULL != pCob)
        {
          // Save a pointer to the COBall IUnknown interface. AddRef
          // because of this saved copy.
          m_pCOBallSink = pCob;
          m_pCOBallSink->AddRef();
        }
        else
          hr = E_OUTOFMEMORY;

        if (SUCCEEDED(hr))
        {
          // Set up the client process to periodically move & paint the ball
          // thru WM_TIMER messages to the specified hWnd's Window proc.
          SetTimer(hWnd, 1, BALL_PAINT_DELAY, NULL);
          bOk = TRUE;
        }
      }
    }

    return (bOk);
  }

The COM function CoCreateInstance is called to create an instance of the CONSERVE server's COBall object specified by its CLSID, CLSID_DllSndBall. This CLSID is defined in BALLGUID.H, located in the sibling \INC directory. Because aggregation is not required, CoCreateInstance directly requests the IBall interface, which on return is stored in CGuiBall's m_pIBall member. Once the server has created a COBall object, a COBallSink object is created here in the client. A pointer to this sink object's IUnknown is assigned to CGuiBall's m_pCOBallSink member. CGuiBall's Init method also starts a system timer that will periodically send WM_TIMER messages to the display window's window procedure. In this case the procedure is CMainWindow::WindowProc.

The COBallSink COM object is created locally in the client. This object implements a custom sink event interface, IBallSink. The COBall object calls the methods of IBallSink to notify CONCLIEN of events that occur in COBall. Here is the declaration of IBallSink from IBALL.H in the sibling \INC directory.

  DECLARE_INTERFACE_(IBallSink, IUnknown)
  {
    // IUnknown methods.
    STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE;
    STDMETHOD_(ULONG,AddRef)  (THIS) PURE;
    STDMETHOD_(ULONG,Release) (THIS) PURE;

    // IBallSink methods.
    STDMETHOD(BounceBottom)   (THIS) PURE;
    STDMETHOD(BounceLeft)     (THIS) PURE;
    STDMETHOD(BounceRight)    (THIS) PURE;
    STDMETHOD(BounceTop)      (THIS) PURE;
  };

This custom interface declares methods for ball bounce events. A method is provided for each of the four boundaries that the ball can collide with in a bounding rectangle: the bottom, left, right, or top.

The COBallSink object implements the IBallSink interface and its methods. This COM object is created solely in the client to receive IBallSink event calls from the COBall server object. The COBallSink object is coded in SINK.H and SINK.CPP. Here is the COBallSink class declaration from SINK.H.

  class COBallSink : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COBallSink(IUnknown* pUnkOuter, CGuiBall* pGuiBall);
      ~COBallSink(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIBallSink : public IBallSink
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIBallSink(COBallSink* pBackObj, IUnknown* pUnkOuter);
          ~CImpIBallSink(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IBallSink methods.
          STDMETHODIMP         BounceBottom(void);
          STDMETHODIMP         BounceLeft(void);
          STDMETHODIMP         BounceRight(void);
          STDMETHODIMP         BounceTop(void);

        private:
          // Data private to this interface implementation of IBall.
          COBallSink*  m_pBackObj;     // Parent Object back pointer.
          IUnknown*    m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested IBall interface
      // implementation a friend to COM object instantiations of this
      // COBall COM object class.
      friend CImpIBallSink;

      // Private data of COBallSink COM objects.

      // Nested IBallSink implementation instantiation. This IBall interface
      // is instantiated inside this COBallSink object as a native interface.
      CImpIBallSink    m_ImpIBallSink;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation delegation). Used when this COM object
      // is being aggregated.
      IUnknown*        m_pUnkOuter;

      // Pointer to the main object that can service the Sink events.
      CGuiBall*        m_pGuiBall;
  };

The IBallSink interface is coded as a nested interface within the COBallSink COM object. A pointer to a C++ object that can handle the sink events is maintained in the m_pGuiBall member variable. This value is passed at creation time to the COBallSink constructor, as shown earlier in the CGuiBall::Init method, where COBallSink's constructor was passed CGuiBall's 'this' pointer.

IBallSink's BounceRight method implementation is representative of the others. Here it is, from SINK.CPP.

  STDMETHODIMP COBallSink::CImpIBallSink::BounceRight(
                 void)
  {
    HRESULT hr = NOERROR;

    // Ask the GUI Ball to issue an appropriate sound indicating the ball
    // bouncing off the right boundary.
    m_pBackObj->m_pGuiBall->BounceSide();

    return hr;
  }

In this nested interface implementation, the m_pBackObj pointer is used to refer back to COBallSink's m_pGuiBall pointer, which is used, in turn, to call CGuiBall's BounceSide method, which issues a sound. Both the Left and Right bounce event notifications that are sent through IBallSink calls are dispatched in COBallSink to the same CGuiBall::BounceSide method. Here it is from GUIBALL.CPP.

  void CGuiBall::BounceSide(void)
  {
    // Use the Win32 Beep call.
    Beep(m_dwBounceSideFreq, m_dwBounceSndDur);

    return;
  }

This implementation is also representative of CGuiBall's other event-handling methods, BounceTop and BounceBottom. The Win32 Beep function is called to produce a different sound when the ball collides with the top, side, or bottom boundary.

The main process relies on the recurrent system timer's WM_TIMER messages. This was set up within the Init method of CGuiBall shown earlier. These periodic timer messages drive the client's display process. Each WM_TIMER message sent to the main window procedure is honored by a call to the COBall::PaintBall method to move the ball, obtain the ball's resultant display data, and paint an image of the ball. Here is the main window procedure from CONCLIEN.CPP.

  LRESULT CMainWindow::WindowProc(
            UINT uMsg,
            WPARAM wParam,
            LPARAM lParam)
  {
    LRESULT lResult = FALSE;

    switch (uMsg)
    {
      case WM_CREATE:
        break;

      case WM_SIZE:
        // Handle a resize of this window.
        // Clear window and Restart the ball from upper left.
        m_wWidth = LOWORD(lParam);
        m_wHeight = HIWORD(lParam);
        m_pGuiBall->Restart();
        // Turn off sounds if app is minimized.
        if (SIZE_MINIMIZED == wParam)
          DoMenu(IDM_SOUND_BALL_OFF, 0);
        break;

      case WM_TIMER:
        // This is our timed attempt to continuously move & paint the ball.
        // In this app PaintBall both moves and paints the ball.
        m_pGuiBall->PaintBall();
        break;

      case WM_COMMAND:
        // Dispatch and handle any Menu command messages received.
        lResult = DoMenu(wParam, lParam);
        break;

      case WM_CHAR:
        if (wParam == 0x1b)
        {
          // Exit this app if user hits ESC key.
          PostMessage(m_hWnd,WM_CLOSE,0,0);
          break;
        }
      case WM_LBUTTONUP:
      case WM_PAINT:
        // If something major happened or user clicks or hits key then
        // repaint the whole window.
        m_pGuiBall->PaintWin();
        break;

      case WM_CLOSE:
        // The user selected Close on the main window's System menu.
      case WM_QUIT:
        // If the app is quit by the File/Exit main menu then close
        // any associated help windows too.
        ::WinHelp(m_hWnd, m_szHelpFile, HELP_QUIT, 0);
      default:
        // Defer all messages NOT handled here to the Default Window Proc.
        lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
        break;
    }

    return(lResult);
  }

The CGuiBall::PaintBall method is called to paint the ball when the WM_TIMER messages arrive. Other main client application messages of interest are WM_SIZE, WM_PAINT, and WM_LBUTTONUP. For WM_SIZE, if the user resizes the client application's main window, CGuiBall::Restart is called. Restart calls COBall::Reset, which relocates the ball to the upper left corner of the window and also resets the ball size. If WM_SIZE was sent because the CONCLIEN application was minimized, then ball sounds are disabled. If the user clicks the left mouse button (causing WM_LBUTTONUP) or a general WM_PAINT condition occurs, the window is repainted, but the ball is not reset. In response to WM_CHAR, if the user pressed the ESC key, the application is sent the WM_CLOSE message to shut down CONCLIEN and CONSERVE.

The operating system responds to a WM_CLOSE message by destroying the window. It sends a WM_DESTROY message to CMainWidow::WindowProc where it causes a delete of the CMainWindow object. The delete causes CMainWindow's destructor to run. Here is the destructor from CONCLIEN.CPP.

  CMainWindow::~CMainWindow()
  {
    // CMainWindow is derived from CVirWindow which traps the WM_DESTROY
    // message and causes a delete of CMainWindow which in turn causes this
    // destructor to run. The WM_DESTROY results when the window is destoyed
    // after a close of the window. Prior to exiting the main message loop:

    // We delete the CGuiBall and CMsgBox objects that were made in
    // Initinstance.
    DELETE_POINTER(m_pGuiBall);
    DELETE_POINTER(m_pMsgBox);

    // We then post a WM_QUIT message to cause an exit of the main thread's
    // message loop and an exit of this instance of the application.
    PostQuitMessage(0);
  }

The destructor, in turn, deletes the application's instance of CGuiBall. The deletion of m_pGuiBall executes the CGuiBall destructor. This destructor shuts down the client's sink object, COBallSink, and the server's COBall object with its connectable object support. Here is the CGuiBall destructor from file GUIBALL.CPP.

  CGuiBall::~CGuiBall(void)
  {
    BOOL bOk = TRUE;

    // Kill the client's app timer that drives repaints. Stops the WM_TIMER
    // messages to the display window.
    KillTimer(m_hWnd, 1);

    if (m_pIBall)
    {
      // Call down to the server's COBall and tell it about the shutdown.
      // This sets COBall's m_bAlive to FALSE and thus neutralizes all
      // subsequent Move calls and thus prevents any more NotifySinks calls
      // from within the Move method.
      m_pIBall->Move(FALSE);

      // Disconnect all Sinks--currently only one: BallSink. This officially
      // stops all BallSink notifications.
      DisconnectBallSink();

      // Release the reference to the BallSink object.
      RELEASE_INTERFACE(m_pCOBallSink);

      // Release the main interface pointer copy held in CGuiBall.
      RELEASE_INTERFACE(m_pIBall);
    }
  }

The system timer that sends WM_TIMER messages is killed. COBall's IBall::Move method is told to kill any future ball movement. This prevents any future NotifySinks calls from within the Move method. The connection between the BallSink and the COBall's connection point is severed by a call to CGuiBall::DisconnectBallSink. This method obtains the interface pointer to the associated connection point in COBall and calls the connection point's Unadvise method to terminate the connection. Finally, the remaining interface references to the COBallSink and the COBall are released. These final releases cause the destructors of these objects to run. The COBall destructor does final releases on the connection points in COBall, which destroys those objects in the server.

CMainWindow's destructor finally posts a WM_QUIT message to the application's main messgae loop. This causes an exit of the loop. Here is the message loop from the WinMain function in CONCLIEN.CPP.

  ...
  while (GetMessage(&msg, NULL, 0, 0))
  {
    if (!TranslateAccelerator(pWin->GetHwnd(), hAccel, &msg))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  // We also ask COM to unload any unused COM Servers, including our
  // friend, CONSERVE.
  CoFreeUnusedLibraries();
  ...

After all this shutdown activity, a call to COM's CoFreeUnusedLibraries function ensures that all unused COM server's that were loaded for CONCLIEN are unloaded. The CONCLIEN application is then exited and is itself unloaded.

Top Back to page top

© 1995-1998 Microsoft Corporation


STOSERVE - Structured Storage Server - Lesson 23

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

STOSERVE - Structured Storage Server

SUMMARY

The STOSERVE sample introduces the COPaper COM object, which models a sheet of white drawing paper. COPaper objects expose a set of features for free-form drawing on the paper surface using "ink" of specified color and width. The functionality is outwardly similar to the "scribble" tutorial samples in many versions of the Microsoft Visual C++ product. The difference in the STOSERVE/STOCLIEN samples is an architecture based primarily on COM technology. The electronic drawing paper features of COPaper objects are available to clients through a custom IPaper interface. COPaper implements the IPaper interface. A clear architectural distinction is kept between client and server. No graphical user interface (GUI) is provided by COPaper. The design of the COPaper object relies on the client for all GUI behavior. COPaper encapsulates only the server-based capture and storage of the drawn ink data.

The ink data that is drawn on the COPaper surface can be stored in and loaded from compound files. The IPaper Save and Load methods accept an IStorage interface pointer. COPaper uses this client-provided IStorage interface to store the drawing data.

The primary focus of this code sample is on the use of structured storage services as provided in the compound files implementation. The use of the standard IStorage and IStream interfaces is covered. STOSERVE works with the STOCLIEN code sample to illustrate the joint use of compound file storage by client and server.

COPaper is housed in an in-process server and is made publicly available as a custom COM component. Like all other servers in this tutorial series, STOSERVE is a self-registering COM server. It makes the COPaper object type available to clients as the DllPaper component using a CLSID_DllPaper registration in the Registry.

As was the case in the previous CONSERVE server, connectable object features are supported in COPaper. The IConnectionPointContainer interface is exposed, and an appropriate connection point is implemented. In this context, an outgoing custom IPaperSink interface is declared for use in sending notifications to the client.

The two IPaper and IPaperSink custom interfaces are declared in IPAPER.H located in the common sibling \INC directory. The GUIDs for the interfaces and objects are defined in PAPGUIDS.H located in that same common include directory.

The CThreaded facility in APPUTIL is used by STOSERVE to achieve thread safety, as it was in the FRESERVE sample. COPaper objects are derived from the CThreaded class and inherit its OwnThis and UnOwnThis methods. These methods allow only one thread at a time to have access to the STOSERVE server and to COPaper objects managed by the server.

For functional descriptions and a tutorial code tour of STOSERVE, see the Code Tour section in STOSERVE.HTM. For details on setting up the programmatic usage of STOSERVE, see the Usage section in STOSERVE.HTM. To read STOSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the STOSERVE lesson in the table of lessons. You can also achieve the same thing by clicking the STOSERVE.HTM file after locating the main tutorial directory in the Windows Explorer. See also STOCLIEN.HTM in the main tutorial directory for more details on the STOCLIEN client application and how it works with STOSERVE.DLL. You must build STOSERVE.DLL before building or running STOCLIEN.

STOSERVE's makefile automatically registers STOSERVE's DllPaper COM component in the registry. This component must be registered before STOSERVE is available to outside COM clients as a server for that component. This self-registration is done using the REGISTER.EXE utility built in the REGISTER sample. To build or run STOSERVE, you should build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the STOSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV STOSERVE.DSP

You can also simply double-click the STOSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use STOSERVE, a client program does not need to include STOSERVE.H or link to STOSERVE.LIB. A COM client of STOSERVE obtains access solely through its object's CLSID and COM services. For STOSERVE, that CLSID is CLSID_DllPaper (defined in file PAPGUIDS.H in the \INC sibling directory). The STOCLIEN code sample shows how the client obtains this access.

STOSERVE is a DLL that is intended primarily as a COM server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within the COM function CoGetClassObject. STOSERVE is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the STOSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the STOSERVE directory.

  ..\register\register.exe stoserve.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of STOSERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, STOCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
STOSERVE.TXT  Short sample description.
MAKEFILE      The generic makefile for building the STOSERVE.DLL
              code sample of this lesson.
STOSERVE.H    The include file for declaring as imported or defining as
              exported the service functions in STOSERVE.DLL.
STOSERVE.CPP  The main implementation file for STOSERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
STOSERVE.DEF  The module definition file. Exports server housing functions.
STOSERVE.RC   The DLL resource definition file for the executable.
STOSERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control C++ object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerator objects.
PAPER.H       The include file for the COPaper COM object class.
PAPER.CPP     The implementation file for the COPaper COM object class
              and the connection points.
STOSERVE.DSP  Microsoft Visual Studio Project file.

STOSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial code samples. This tour assumes that you have some exposure to those previous samples. It does not revisit earlier topics of basic interface implementation techniques, COM object construction, in-process server construction, class factory construction, and connectable object construction. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • An overview of how STOSERVE is designed to work with connected clients
  • The IPaper interface
  • COPaper's management of paper drawing data
  • COPaper's use of structured storage in compound files to save and load paper data
  • COPaper's use of the IPaperSink interface for client notifications
  • Unicode considerations

The COPaper COM object is the single object type managed by this STOSERVE in-process server. COPaper is constructed as a connectable COM object with an implementation of the standard IConnectionPointContainer interface and an implementation of the custom IPaper interface. COPaper exposes the IPaper interface so clients can perform a small set of electronic paper operations on an instance of COPaper. The essential operations are starting an ink drawing sequence, drawing the ink data on COPaper's virtual paper surface, and ending the ink drawing sequence. In this scheme, the client is assumed to be a GUI application driven by a mouse or tablet device. The client is responsible for translating the mouse movements into requests to COPaper, which saves these requests as ink data.

There are two levels of ink data saving in COPaper. COPaper saves the ink data in a RAM-based array that represents the current drawing, and COPaper persistently saves an entire drawing into a compound file. Methods in the IPaper interface perform both.

Since the client does not manage the drawn paper data but is responsible for rendering it as an image on the screen, the IPaper implementation in COPaper must expose a method that allows the client to obtain the drawing data. The connectable object technology in COPaper is used for this purpose. A CONNPOINT_PAPERSINK connection point is implemented by COPaper so clients can connect to COPaper to recieve the ink data for drawing. The client first calls the IPaper::Redraw method on the COPaper object to request all the ink data of the current drawing. COPaper's implementation of Redraw then uses the client's implementation of IPaperSink to pass the data to the client.

As the user interactively draws in the client, it paints the data immediately to the screen while also sending it to COPaper for saving. When the client needs the screen to be repainted, it calls COPaper's Redraw method. Such repainting is common in applications. For example, repainting occurs when the client's window is overlaid by another application's window. The client has a bitmap rendering of the drawn image, but the bitmap is easily lost and must often be repainted. The client relies on COPaper in the server for the ink data needed for repainting.

This is a common client/server division of labor. It is especially appropriate when there is a need for multiple clients to share the data. COPaper is coded to allow for this eventuality. It uses APPUTIL's CThreaded facility to achieve thread safety. An application that might exploit this design is a shared whiteboard application, where multiple clients can contribute to a commonly viewed drawing. COM's support for Distributed COM (DCOM) supports this kind of workgroup application usage across the network. See the earlier REMCLIEN sample for more details on DCOM.

A more modest use of COPaper's client/server scheme is to integrate behavior for objects implemented in different applications on the same machine. In this case, the clients might be ActiveX containers in separate applications that share data managed by the same object. This data may not be the ink drawing data that the DllPaper component supports. STOSERVE can be used as a starting framework for programming components that manage other kinds of shared data.

STOSERVE provides COPaper objects, which are controlled primarily by their native IPaper interface. Here is a summary of IPaper's methods from IPAPER.H in the sibling \INC directory.

  InitPaper
    Initializes paper object and create ink data array.

  Lock
    Gives client control of the paper and locks out other clients.

  Unlock
    Relinquishes client control of the paper.

  Load
    Loads paper content from client's compound file and notifies sinks.

  Save
    Saves paper content to client's compound file.

  InkStart
    Starts color ink drawing to the paper surface.

  InkDraw
    Puts ink data points on the electronic paper surface.

  InkStop
    Stops ink drawing to the paper surface.

  Erase
    Erase the current paper content and notifies sinks.

  Resize
    Resizes the drawing paper rectangle size and notifies sinks.

  Redraw
    Redraws contents of paper object and notifies sinks.

Of interest for this code sample on compound files are the Load and Save methods. We will cover these in detail below, but first we will look at the ink data that will be loaded from and saved to compound files.

InkStart, InkDraw, and InkStop are the methods used by clients to command COPaper to record ink drawing sequences. The client will typically respond to a WM_LBUTTONDOWN message as the start of an ink drawing sequence by calling InkStart on COPaper. As the user moves the mouse or pen to draw while holding down the left button, the client will respond to repeated WM_MOUSEMOVE messages with corresponding calls to InkDraw. When the user releases the left mouse button, the client will respond to a WM_LBUTTONUP message with a call to InkStop, which marks the end of the ink drawing sequence.

InkStart tells COPaper the start position for the drawing sequence in client window coordinates. It also passes the currently selected ink color and width. The client maintains these selections; COPaper merely records them when the InkStart call is made. InkDraw is called repeatedly to tell COPaper the succession of window coordinates that represent the drawing motion of the mouse or pen. InkStop tells COPaper to mark the end of a drawing sequence.

COPaper packages the pen color, width, and coordinates into INKDATA structures and stores them in a dynamically allocated array that it manages in memory. Here are the declarations for the INKDATA structure from PAPER.H.

  // The types of Ink Data.
  #define INKTYPE_NONE  0
  #define INKTYPE_START 1
  #define INKTYPE_DRAW  2
  #define INKTYPE_STOP  3

  // The Ink Data structure.
  typedef struct _INKDATA
  {
    SHORT nType;            // Ink Type.
    SHORT nX;               // X-coordinate of ink point.
    SHORT nY;               // Y-coordinate of ink point.
    SHORT nWidth;           // Ink line width in pixels.
    COLORREF crColor;       // Ink color.
  } INKDATA;

The dynamic array of these INKDATA packets is pointed to by m_paInkData, a member of the IPaper implementation class. The array is created within the IPaper::InitPaper method with an initial allocation. For details, see the InitPaper method and the private NextSlot utility method of the CImpIPaper implementation in PAPER.H. The InkStart, InkDraw, and InkStop methods use NextSlot to obtain new slots in the array. The array is expanded dynamically by NextSlot as the need arises.

The client calls the IPaper::Erase method to erase the current drawing. This method does not reallocate the array; it simply marks all current ink data as INKTYPE_NONE and resets the array's end-of-data index to zero.

The client calls the IPaper::Lock and Unlock methods to manage ownership of COPaper for drawing. These methods are provided to organize access among multiple clients to the drawing held in a shared COPaper.

The client calls the IPaper::Resize method to tell COPaper that the user resized the current drawing paper rectangle. This coordinate data is kept in a PAPER_PROPERTIES structure, which is stored with the ink data when all the paper data is stored in a compound file. Here is the PAPER_PROPERTIES declaration from PAPER.H.

  #define PAPER_TITLE_SIZE 64
  typedef struct _PAPER_PROPERTIES
  {
    LONG lInkDataVersion;
    LONG lInkArraySize;
    COLORREF crWinColor;
    RECT WinRect;
    WCHAR wszTitle[PAPER_TITLE_SIZE];
    WCHAR wszAuthor[PAPER_TITLE_SIZE];
    WCHAR wszReserved1[PAPER_TITLE_SIZE];
    WCHAR wszReserved2[PAPER_TITLE_SIZE];
  } PAPER_PROPERTIES;

The PAPER_PROPERTIES structure is designed so that new ink data formats can be added at any time as the DllPaper component evolves. The IPaper interface is general enough that a subsequent version of the DllPaper component may store a different ink data format while implementing the same IPaper interface. Because the methods of IPaper do not depend on a specific ink data format, a new version of the DllPaper component that does support a different ink data format can use this same interface.

The paper properties stored in a compound file record the current size of the ink data array. The proper array size can then be allocated to accommodate the ink data read from the file.

The PAPER_PROPERTIES structure also stores the paper surface's drawing rectangle size and background window color.

Though not used in the STOSERVE/STOCLIEN samples, a drawing title and an author name can also be stored.

Creation date and last modified dates are not included in these paper properties, because the IStorage interface used to access compound files manages this information.

The principal focus in this code sample is how COPaper can load and save its paper data in compound files. We now look at the IPaper Load and Save method implementations in detail.

Here is IPaper::Save from PAPER.CPP.

  STDMETHODIMP COPaper::CImpIPaper::Save(
                 SHORT nLockKey,
                 IStorage* pIStorage)
  {
    HRESULT hr = E_FAIL;
    IStream* pIStream;
    ULONG ulToWrite, ulWritten;

    if (OwnThis())
    {
      if (m_bLocked && m_cLockKey == nLockKey && NULL != pIStorage)
      {
        // First use COM service to mark this compound file as one that is
        // handled by our server component, DllPaper.
        WriteClassStg(pIStorage, CLSID_DllPaper);

        // Use COM Service to write user-readable clipboard format into
        // the compound file.
        WriteFmtUserTypeStg(pIStorage, m_ClipBdFmt, TEXT(CLIPBDFMT_STR));

        // Create the stream to be used for the actual paper data.
        // Call it "PAPERDATA".
        hr = pIStorage->CreateStream(
               STREAM_PAPERDATA_USTR,
               STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               0,
               &pIStream);
        if (SUCCEEDED(hr))
        {
          // Got a stream. Now write data into it.
          // First write PAPER_PROPERTIES structure.
          m_PaperProperties.lInkArraySize = m_lInkDataEnd+1;
          m_PaperProperties.crWinColor = m_crWinColor;
          m_PaperProperties.WinRect.right = m_WinRect.right;
          m_PaperProperties.WinRect.bottom = m_WinRect.bottom;
          ulToWrite = sizeof(PAPER_PROPERTIES);
          hr = pIStream->Write(&m_PaperProperties, ulToWrite, &ulWritten);
          if (SUCCEEDED(hr) && ulToWrite != ulWritten)
            hr = STG_E_CANTSAVE;
          if (SUCCEEDED(hr))
          {
            // Now write the complete array of Ink Data.
            ulToWrite = m_PaperProperties.lInkArraySize * sizeof(INKDATA);
            hr = pIStream->Write(m_paInkData, ulToWrite, &ulWritten);
            if (SUCCEEDED(hr) && ulToWrite != ulWritten)
              hr = STG_E_CANTSAVE;
          }

          // We are done with the stream so release it.
          pIStream->Release();
        }
      }

      UnOwnThis();
    }

    // Notify all other connected clients that Paper is now saved.
    if (SUCCEEDED(hr))
      m_pBackObj->NotifySinks(PAPER_EVENT_SAVED, 0, 0, 0, 0);

    return hr;
  }

In the division of labor between client and server, COPaper does not create the compound file that is used to store paper data. For both the Save and Load methods, the client passes an IStorage interface pointer for an existing compound file. It then uses the IStorage to write and read data in that compound file. In IPaper::Save above, several types of data are stored.

The CLSID for DllPaper, CLSID_DllPaper, is serialized and stored in a special COM-controlled stream within the storage object called "\001CompObj". The WriteClassStg service function performs this storage. This stored CLSID data can be used to associate the storage's content with the DllPaper component that created and can interpret it. In this sample, the root storage is passed by STOCLIEN, and thus the entire compound file is associated with the DllPaper component. This CLSID data can be retrieved later with a call to the ReadClassStg service function.

Since DllPaper deals with editable data, it is also appropriate to record a clipboard format in the storage. The WriteFmtUserTypeString service function is called to store both a clipboard format designation and a user-readable name for the format. The user-readable name is meant for GUI display in selection lists. The name passed above uses a macro, CLIPBDFMT_STR, which is defined as "DllPaper 1.0" in PAPER.H. This stored clipboard data can be retrieved later with a call to the service function ReadFmtUserTypeStg. This function returns a string value that is allocated using the task memory allocator. The caller is responsible for freeing the string.

Save next creates a stream in the storage for the COPaper paper data. The stream is called "PAPERDATA" and is passed using the STREAM_PAPERDATA_USTR macro. The IStorage::CreateStream method requires that this string be in Unicode. Since the string is fixed at compile time, the macro is defined as Unicode in PAPER.H.

  #define STREAM_PAPERDATA_USTR L"PAPERDATA"

The 'L' before the string, indicating LONG, achieves this.

The CreateStream method creates and opens a stream within the specified storage. An IStream interface pointer for the new stream is passed in a caller's interface pointer variable. AddRef is called on this interface pointer within CreateStream, and the caller must release this pointer after using it. The CreateStream function is passed numerous access mode flags, as follows.

  STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE

STGM_CREATE creates a new stream or overwrites an existing one of the same name. STGM_WRITE opens the stream with write permission. STGM_DIRECT opens the stream for direct access, as opposed to transacted access. STGM_SHARE_EXCLUSIVE opens the file for exclusive, non-shared use by the caller.

After the PAPERDATA stream is successfully created, the IStream interface is used to write into the stream. The IStream::Write method is used to first store the content of the PAPER_PROPERTIES structure. This is essentially a properties header at the front of the stream. Because the version number is the first thing in the file, it can be read independently to determine how to deal with the data that follows. If the amount of data actually written does not equal the amount requested, the Save method is aborted, and it returns STG_E_CANTSAVE.

Saving the entire array of ink data into the stream is simple.

  // Now write the complete array of Ink Data.
  ulToWrite = m_PaperProperties.lInkArraySize * sizeof(INKDATA);
  hr = pIStream->Write(m_paInkData, ulToWrite, &ulWritten);
  if (SUCCEEDED(hr) && ulToWrite != ulWritten)
    hr = STG_E_CANTSAVE;

Since the IStream::Write method operates on a byte array, the number of bytes of stored ink data in the array is calculated and the write operation begins at the start of the array. If the amount of data actually written does not equal the amount requested, the Save method returns STG_E_CANTSAVE.

After the stream is written, the IPaper::Save method releases the IStream pointer it was using.

The Save method also calls the client's IPaperSink (in COPaper's internal NotifySinks method) to notify the client that the save operation is complete. At this point the Save method returns to the calling client, which will typically release the IStorage pointer.

Here is the Save method's opposite, IPaper::Load from PAPER.CPP.

  STDMETHODIMP COPaper::CImpIPaper::Load(
                 SHORT nLockKey,
                 IStorage* pIStorage)
  {
    HRESULT hr = E_FAIL;
    IStream* pIStream;
    INKDATA* paInkData;
    ULONG ulToRead, ulReadIn;
    LONG lNewArraySize;
    PAPER_PROPERTIES NewProps;

    if (OwnThis())
    {
      if (m_bLocked && m_cLockKey == nLockKey && NULL != pIStorage)
      {
        // Open the "PAPERDATA" stream where the paper data is stored.
        hr = pIStorage->OpenStream(
               STREAM_PAPERDATA_USTR,
               0,
               STGM_READ | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               &pIStream);
        if (SUCCEEDED(hr))
        {
          // We have the paper data stream. First read the Paper Properties.
          ulToRead = sizeof(PAPER_PROPERTIES);
          hr = pIStream->Read(
                           &NewProps,
                           ulToRead,
                           &ulReadIn);
          if (SUCCEEDED(hr) && ulToRead != ulReadIn)
            hr = E_FAIL;
          if (SUCCEEDED(hr))
          {
            // Deal with the different versions of ink data format.
            switch (NewProps.lInkDataVersion)
            {
              case INKDATA_VERSION10:
                // Allocate an ink data array big enough--add some
                // extra too.
                lNewArraySize = NewProps.lInkArraySize + INKDATA_ALLOC;
                paInkData = new INKDATA[(LONG) lNewArraySize];
                if (NULL != paInkData)
                {
                  // Delete the entire old ink data array.
                  delete [] m_paInkData;

                  // Assign the new array.
                  m_paInkData = paInkData;
                  m_lInkDataMax = lNewArraySize;

                  // Now read the complete array of Ink Data.
                  ulToRead = NewProps.lInkArraySize * sizeof(INKDATA);
                  hr = pIStream->Read(m_paInkData, ulToRead, &ulReadIn);
                  if (SUCCEEDED(hr) && ulToRead != ulReadIn)
                    hr = E_FAIL;
                  if (SUCCEEDED(hr))
                  {
                    // Rig COPaper to use the new PAPER_PROPERTIES info.
                    m_lInkDataEnd = NewProps.lInkArraySize-1;
                    m_crWinColor = NewProps.crWinColor;
                    m_WinRect.right = NewProps.WinRect.right;
                    m_WinRect.bottom = NewProps.WinRect.bottom;

                    // Copy the new properties into current properties.
                    memcpy(
                      &m_PaperProperties,
                      &NewProps,
                      sizeof(PAPER_PROPERTIES));
                  }
                }
                else
                  hr = E_OUTOFMEMORY;
                break;
              default:
                hr = E_FAIL;  // Bad version.
                break;
            }
          }

          // We are done with the stream so release it.
          pIStream->Release();
        }
      }

      UnOwnThis();
    }

    // Notify all other connected clients that Paper is now loaded.
    // If we didn't load then erase to a safe, empty ink data array.
    if (SUCCEEDED(hr))
      m_pBackObj->NotifySinks(PAPER_EVENT_LOADED, 0, 0, 0, 0);
    else
      Erase(nLockKey);

    return hr;
  }

This time the IStorage::OpenStream method is called to open the existing stream in the storage called "PAPERDATA". Access mode flags are for read only, direct, and non-shared exclusive access. Once the stream is open, the IStream::Read method is called to read the PAPER_PROPERTIES structure. If the amount actually read does not equal the amount requested, the load operation is aborted, and E_FAIL is returned. If the format version in the newly read PAPER_PROPERTIES is not recognized, then the load operation is aborted and Load returns E_FAIL.

With a valid ink data format version, the size of the new ink data array from the PAPER_PROPERTIES that was read in is used to allocate a new ink data array of the required size. The existing ink data is deleted, and its data is lost. If this data was valuable, it should have been saved before Load was called. After the new array is allocated, IStream::Read is called again to read the data into the array from the stream. If this call succeeds, the values in the newly read paper properties are adopted as the current values for COPaper.

During this load operation, a temporary PAPER_PROPERTIES structure, NewProps, was used to hold the new properties read in. If all succeeds with the load, NewProps is copied into the PAPER_PROPERTIES structure, m_PaperProperties. As before, after Load is done and the IStream is no longer needed, the IStream pointer is released.

If there is an error at the end of Load, the ink data array is erased, because it may contain damaged data.

If there is no error at the end of Load, the client's IPaperSink::Loaded method is called (in COPaper's internal NotifySinks method) to notify the client that the load operation is comleted. This is an important notification for the client, because it needs to display this newly loaded ink data. This notification makes significant use of COPaper's connectable object features.

COPaper exposes the IConnectionPointContainer interface so clients can connect to COPaper in order to receive notifications of specified events that occur in COPaper. By exposing this interface, COPaper becomes a connectable object. A client can call QueryInterface for this interface and use it to obtain the object's connection points. The client participation in this scheme is covered in the associated STOCLIEN sample.

Basically, the client implements what is called a sink in the form of a sink object with a sink interface. The sink interface receives outgoing event notification calls from COPaper after the sink is properly connected by the client to a COPaper instance. The client makes the connection by using a connection point object that is managed by COPaper. There can be numerous connection points on a single connectable COM object. In the STOSERVE sample, COPaper has only one connection point, which handles drawing paper events.

Any number of clients can connect to a single connection point. The CONNPOINT_PAPERSINK connection point in COPaper maintains a group of connections that can grow dynamically at run time. The full details on COPaper's connectable object support is coded in files CONNECT.H and CONNECT.CPP and will not be covered here. The construction is very similar to what was studied in the CONSERVE code sample.

The STOCLIEN client implements appropriate sink objects for the connection points it expects to find in COPaper. From the context of COPaper, the important sink object that STOCLIEN implements exposes the IPaperSink interface. This is the outgoing interface used by COPaper to notify STOCLIEN of various events in COPaper. Here is a summary of the methods in IPaperSink from IPAPER.H in the \INC sibling directory.

  Locked
    A client has taken control of and locked the paper.

  Unlocked
    A client has relinquished control of the paper.

  Loaded
    A client has loaded paper content from its own compound file.

  Saved
    A client has saved paper content to its own compound file.

  InkStart
    A client started a color ink drawing sequence to the paper.

  InkDraw
    A client is putting ink data points on the paper surface.

  InkStop
    A client has stopped its ink drawing sequence to the paper.

  Erased
    A client has erased all ink data from the paper.

  Resized
    A client has resized the paper.

These methods are largely self-explanatory. Although the sink must implement all these methods in some fashion, many are implemented as stubs and are not used in the STOSERVE/STOCLIEN samples.

An important method used in these samples is the Loaded method. When COPaper is told by the client to load a new drawing from file, it needs a way to notify the client when the new data is loaded. To do this, COPaper Calls IPaperSink::Loaded on the client sink. The client can then call IPaper::Redraw to request that COPaper play back the new data to the client. Redraw causes the loaded drawing in the client's display to be repainted. When the load is completed, the newly loaded drawing is displayed automatically in the window provided by the client.

The IPaper::Redraw method relies on the connection point technology. Here is Redraw from PAPER.CPP.

  STDMETHODIMP COPaper::CImpIPaper::Redraw(
                 SHORT nLockKey)
  {
    HRESULT hr = E_FAIL;
    IConnectionPoint* pIConnectionPoint;
    IEnumConnections* pIEnum;
    CONNECTDATA ConnData;
    IPaperSink* pIPaperSink;
    SHORT nInkType;
    LONG i;

    if (OwnThis())
    {
      if (m_bLocked && m_cLockKey == nLockKey)
      {
        // Broadcast InkData notifications to all Sinks connected to
        // each connection point.

        // Here is the section for the PaperSink connection point--this is
        // currently the only connection point offered by COPaper objects.
        pIConnectionPoint =
          m_pBackObj->m_aConnectionPoints[CONNPOINT_PAPERSINK];
        if (NULL != pIConnectionPoint)
        {
          pIConnectionPoint->AddRef();
          hr = pIConnectionPoint->EnumConnections(&pIEnum);
          if (SUCCEEDED(hr))
          {
            // Loop thru the connection point's connections and if the
            // listening connection supports IPaperSink (ie, PaperSink
            // events) then send all the current Paper's Ink Data to it.
            while (NOERROR == pIEnum->Next(1, &ConnData, NULL))
            {
              hr = ConnData.pUnk->QueryInterface(
                                    IID_IPaperSink,
                                    (PPVOID)&pIPaperSink);
              if (SUCCEEDED(hr))
              {
                // Loop thru all the Ink Data and send it to this connected
                // client sink.
                for (i=0; i<m_lInkDataEnd+1; i++)
                {
                  nInkType = m_paInkData[i].nType;
                  switch (nInkType)
                  {
                    case INKTYPE_START:
                      pIPaperSink->InkStart(
                                     m_paInkData[i].nX,
                                     m_paInkData[i].nY,
                                     m_paInkData[i].nWidth,
                                     m_paInkData[i].crColor);
                      break;
                    case INKTYPE_DRAW:
                      pIPaperSink->InkDraw(
                                     m_paInkData[i].nX,
                                     m_paInkData[i].nY);
                      break;
                    case INKTYPE_STOP:
                      pIPaperSink->InkStop(
                                     m_paInkData[i].nX,
                                     m_paInkData[i].nY);
                      break;
                    default:
                      break;
                  }
                }
                pIPaperSink->Release();
              }
              ConnData.pUnk->Release();
            }
            pIEnum->Release();
          }
          pIConnectionPoint->Release();
        }
      }

      UnOwnThis();
    }

    return hr;
  }

The IPaperSink methods InkStart, InkDraw, and InkStop are used to send individual ink data packets back to the client. On reception, the client paints them in its display in the same manner as when it originally sent them to COPaper. The above logic is general enough to allow for multiple clients that are all listening on the same CONNPOINT_PAPERSINK connection point. If a common COPaper instance was shared by these clients, they could all display the same drawing by connecting to this connection point.

The WriteFmtUserTypeStg service function used in the Save method shown above requires Unicode string parameters. This is the case with COM/OLE service calls that take string parameters. When compiling for ANSI strings, the expected Unicode parameters need conversion from ANSI to Unicode. This process is achieved using some macros in APPUTIL.H that may obscure the conversions that are performed. Take WriteFmtUserTypeStg as an example. The following call appears in the Save method.

  WriteFmtUserTypeStg(pIStorage, m_ClipBdFmt, TEXT(CLIPBDFMT_STR));

When STOSERVE is compiled for ANSI (not for Unicode), this call actually reduces to a call to an internal APPUTIL surrogate function. To support this, the following macro scheme is used in APPUTIL.H, which is an included file in all code sample .CPP files.

  #if !defined(UNICODE)

  STDAPI A_WriteFmtUserTypeStg(IStorage*, CLIPFORMAT, LPSTR);

  #if !defined(_NOANSIMACROS_)

  #undef WriteFmtUserTypeStg
  #define WriteFmtUserTypeStg(a, b, c) A_WriteFmtUserTypeStg(a, b, c)

  #endif

  #endif

The scheme uses surrogate service call functions. The ANSI versions of the calls begin with A_. These surrogate ANSI functions are implemented in APPUTIL.CPP. They are used when the code sample is being compiled for ANSI strings (the default in the makefiles). The service functions that the surrogates stand in place of support only Unicode string parameters. This forces some string conversions from ANSI to Unicode before the real COM/OLE service call is made inside the surrogate.

For example, if UNICODE is not defined during compiling, any calls in the samples to the WriteFmtUserTypeStg COM service function are actually changed by the macros into calls to an A_WriteFmtUserTypeStg function that is implemented in APPUTIL.CPP. This function accepts the input ANSI string pointer, converts it to a Unicode copy, and passes that Unicode copy as the string parameter in a call to the actual WriteFmtUserTypeStg function.

Here is A_WriteFmtUserTypeStg from APPUTIL.CPP.

  STDAPI A_WriteFmtUserTypeStg(
           IStorage* pIStorage,
           CLIPFORMAT ClipFmt,
           LPSTR pszUserType)
  {
    HRESULT hr = E_INVALIDARG;
    WCHAR wszUc[MAX_PATH];

    if (NULL != pszUserType)
    {
      // Convert from Ansi in pszUserType to Unicode in wszUc.
      AnsiToUc(pszUserType, wszUc, MAX_PATH);

      // Use the Unicode string in the actual service call.
      hr = WriteFmtUserTypeStg(pIStorage, ClipFmt, wszUc);
    }

    return hr;
  }

Top Back to page top

© 1995-1998 Microsoft Corporation


STOCLIEN - Client of Structured Storage Server - Lesson 24

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

STOCLIEN - Client of Structured Storage Server

SUMMARY

The STOCLIEN sample introduces a simple drawing application. The user can use a mouse or tablet device to do free-form drawing in the client window. The color and width of the electronic ink can be chosen, and the drawings can be saved in files.

This functionality is outwardly similar to the "scribble" tutorial samples in many versions of the Microsoft Visual C++® product. The difference in the STOCLIEN/STOSERVE samples is an internal architecture based on COM technology. A clear architectural distinction is kept between COM client and COM server. A COPaper COM object encapsulates only the server-based storage of the drawing paper data: No graphical user interface (GUI) behavior is provided on the server side. All GUI behavior is isolated in the client. The data managment and storage features of COPaper objects are available only through an COM custom interface, IPaper.

STOCLIEN can load and save its drawings in the structured storage of COM compound files. The principal focus of the STOCLIEN sample is how the client uses this structured storage and how it directs a server component to use this storage. The programming of structured storage services is shown in the sample.

The STOCLIEN sample creates and uses the connectable COPaper COM object that is provided as the CLSID_DllPaper component in the STOSERVE server. The STOCLIEN client creates a COPaper object and controls it through the IPaper interface that the object exposes. STOCLIEN obtains drawing data from the user and graphically represents it in a window that it manages. STOCLIEN uses COPaper's IPaper interface to save the drawing data in COPaper and to direct file storage operations on this data.

STOCLIEN cooperates with the COPaper to load and save COPaper's drawing data. STOCLIEN obtains an IStorage interface for the storage object in a compound file. In its load and save operations, STOCLIEN passes a pointer to this IStorage interface to COPaper in the server. COPaper uses the provided IStorage to create streams in the storage. COPaper can then use the standard IStream interface for reading and writing the drawing data it manages.

COPaper only manages the drawing data; it performs no GUI actions. STOCLIEN provides the GUI for the drawing application. It encapsulates this in a central CGuiPaper C++ object.

STOCLIEN also implements the custom IPaperSink interface in a COPaperSink COM object and connects this interface to an appropriate connection point in the server's COPaper object. COPaper uses the connected IPaperSink interface to send notifications back to STOCLIEN. The normal GUI repainting of COPaper's drawing data is done in STOCLIEN using COPaper's connectable object technology.

For functional descriptions and a tutorial code tour of STOCLIEN, see the Code Tour section in STOCLIEN.HTM. For details on the external user operation of STOCLIEN, see both the Usage and Operation sections in STOCLIEN.HTM. To read STOCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the STOCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the STOCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also STOSERVE.HTM in the main tutorial directory for more details on how STOSERVE works and exposes its services to STOCLIEN. You must build the STOSERVE DLL before building STOCLIEN. The makefile for STOSERVE automatically registers that server in the system registry, so you must build STOSERVE before attempting to run STOCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the STOCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV STOCLIEN.DSP

You can also simply double-click the STOCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

STOCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. STOCLIEN accepts an optional file name parameter on the command line. For example:

  STOCLIEN c:\drawings\drawing.pap

Where drawing.pap is a compound file containing DllPaper-compatible structured storage of drawing data. If no command line file name parameter is specified, STOCLIEN uses the default file name STOCLIEN.PAP and attempts to open it in the same directory as the executing STOCLIEN.EXE.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, STOCLIEN.EXE is the client executable to run for this sample.

OPERATION

The STOCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, STOSERVE.DLL to demonstrate both client and server use of COM structured storage in compound files.

Here is a summary of operation from the standpoint of STOCLIEN.EXE as a COM client of the STOSERVE.DLL COM server.

The STOCLIEN application window's client area is used for visual display of freeform drawing created with a mouse or tablet device. To draw with the mouse, press and hold the left mouse button while moving the mouse. Releasing the left mouse button ends the drawing of a line.

The following menu is provided.

Menu Selection: File/Open
Shows the Open dialog box to obtain a name and path for an existing paper drawing file to open. A default .PAP file extension for these files is assumed. If changes were made to the existing drawing when this menu item is chosen, a separate dialog will first be shown asking the user if the current drawing should be saved into its associated compound file.

Menu Selection: File/Save
Saves the current drawing into its associated compound file.

Menu Selection: File/Save As
Shows the Save As dialog box to obtain a name and path for a new paper drawing file to create. The current drawing becomes the saved content of the new file, and the new file becomes the new associated compound file for the drawing.

Menu Selection: File/Exit
Exits STOCLIEN.

Menu Selection: Draw/Drawing On
Turns on drawing between the STOCLIEN client and the COPaper object in the server. This command locks the COPaper object for exclusive use by this client and prevents other clients from accessing the same COPaper instance in the server.

Menu Selection: Draw/Drawing Off
Turns off drawing between the STOCLIEN client and the COPaper object in the server. This command unlocks the COPaper for exclusive use by this client and allows other clients to access the same COPaper instance in the server.

Menu Selection: Draw/Redraw
Redraws in the client the current drawing data held in the COPaper object in the server.

Menu Selection: Draw/Erase
Erases the current drawing content and clears the window image.

Menu Selection: Pen/Color
Shows the Choose Color dialog box to obtain a new pen color for drawing.

Menu Selection: Pen/Thin
Chooses the thin width for drawing. A check mark on this menu choice indicates that thin is the current pen width.

Menu Selection: Pen/Medium
Chooses the medium width for drawing. A check mark on this menu choice indicates that medium is the current pen width.

Menu Selection: Pen/Thick
Chooses the thick width for drawing. A check mark on this menu choice indicates that thick is the current pen width.

Menu Selection: Sink/Connect
Connects the COPaperSink's IPaperSink interface to the COPaper object's PaperSink connection point. Repainting of the current drawing image in STOCLIEN relies on the sink connection. A check mark on this menu choice indicates that repainting is connected.

Menu Selection: Sink/Disconnect
Disconnects the COPaperSink's IPaperSink interface from the COPaper object's PaperSink connection point. Repainting of the current drawing image in STOCLIEN relies on the sink connection. A check mark on this menu choice indicates that repainting is disconnected.

Menu Selection: Help/STOCLIEN Tutorial
Opens the STOCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/STOSERVE Tutorial
Opens the STOSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About STOCLIEN
Displays the About dialog box for this application.

CODE TOUR

Files          Description
STOCLIEN.TXT   Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
STOCLIEN.H     The include file for the STOCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
STOCLIEN.CPP   The main implementation file for STOCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
STOCLIEN.RC    The application resource definition file.
STOCLIEN.ICO   The application icon resource.
STOCLIEN.PAP   A default paper drawing file for the application.
PENCIL.CUR     A pencil image for the client window cursor.
SINK.H         The class declaration for the COPaperSink COM object class.
SINK.CPP       Implementation file for the COPaperSink COM object class.
PAPFILE.H      The class declaration for the CPapFile C++ class.
PAPFILE.CPP    Implementation file for the CPapFile C++ class.
GUIPAPER.H     The class declaration for the CGuiPaper C++ class.
GUIPAPER.CPP   Implementation file for the CGuiPaper C++ class.
STOCLIEN.DSP   Microsoft Visual Studio Project file.

STOCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

STOCLIEN works in a cooperative fashion with a COPaper object in a COM server to achieve persistent storage of drawings in COM compound files. See the STOSERVE sample and STOSERVE.HTM for details on COPaper's use of streams in the compound file that is provided to COPaper by STOCLIEN. COPaper's construction and its IPaper interface are also covered in the STOSERVE sample.

The major topics covered in this code tour are:

  • How CGuiPaper encapsulates the GUI behavior of STOCLIEN's electronic drawing paper
  • How STOCLIEN captures and displays interactive drawing activity
  • How the CGuiPaper object uses COPaper to record drawing data
  • How an IPaperSink connection is used in repainting
  • How the CPapFile Load and Save methods use the structured storage in compound files

As the CGuiBall class used in the FRECLIEN and CONCLIEN samples encapsulated the behavior of a bouncing ball, STOCLIEN uses a CGuiPaper C++ class to encapsulate the data and GUI behavior of electronic drawing paper.

Here is the CGuiPaper class declaration from GUIPAPER.H.

  class CGuiPaper
  {
    public:
      CGuiPaper(void);
      ~CGuiPaper(void);
      BOOL Init(HINSTANCE hInst, HWND hWnd, TCHAR* pszCmdLineFile);
      HRESULT DrawOn(void);
      HRESULT DrawOff(void);
      HRESULT ClearWin(void);
      HRESULT PaintWin(void);
      HRESULT Erase(void);
      HRESULT Resize(WORD wWidth, WORD wHeight);
      HRESULT InkWidth(SHORT nInkWidth);
      HRESULT InkColor(COLORREF crInkColor);
      HRESULT InkSaving(BOOL bInkSaving);
      HRESULT InkStart(SHORT nX, SHORT nY);
      HRESULT InkDraw(SHORT nX, SHORT nY);
      HRESULT InkStop(SHORT nX, SHORT nY);
      HRESULT ConnectPaperSink(void);
      HRESULT DisconnectPaperSink(void);
      HRESULT Load(void);
      HRESULT Save(void);
      int     AskSave(void);
      HRESULT Open(void);
      HRESULT SaveAs(void);
      COLORREF PickColor(void);

    private:
      HINSTANCE  m_hInst;
      HWND       m_hWnd;
      HDC        m_hDC;
      RECT       m_WinRect;
      IPaper*    m_pIPaper;
      SHORT      m_nLockKey;
      HPEN       m_hPen;
      SHORT      m_nInkWidth;
      COLORREF   m_crInkColor;
      BOOL       m_bInkSaving;
      BOOL       m_bInking;
      BOOL       m_bPainting;
      POINT      m_OldPos;
      IUnknown*  m_pCOPaperSink;
      DWORD      m_dwPaperSink;
      BOOL       m_bDirty;
      CPapFile*    m_pPapFile;
      OPENFILENAME m_ofnFile;
      TCHAR        m_szFileFilter[MAX_PATH];
      TCHAR        m_szFileName[MAX_PATH];
      TCHAR        m_szFileTitle[MAX_PATH];
      CHOOSECOLOR  m_ChooseColor;
      COLORREF     m_acrCustColors[16];

      IConnectionPoint* GetConnectionPoint(REFIID riid);
  };

There is much here that is not relevant to the main focus of this sample and will not be covered in detail. COM connection technology is used significantly in the STOCLIEN/STOSERVE samples but is not covered here in detail. See the CONCLIEN/CONSERVE samples for more on this technology.

CGuiPaper maintains the current GUI properties of the drawing paper. Members m_crInkColor, m_crInkWidth, m_WinRect contain values for the current ink color, ink width, and drawing rectangle. Of course, a handle is needed for the window where painting is done. It is stored in member m_hWnd. The actual painting of images is done using a handle to a device context held in member m_hDC. A handle to the current drawing pen is kept in member m_hPen. The pen is destroyed and recreated when its color or width is changed by the user. Members m_pCOPaperSink and m_dwPaperSink hold values necessary for connecting with COPaper to receive incoming notifications through the IPaperSink interface. Member m_bDirty holds a flag indicating that the user has changed the drawing and that it no longer reflects the data stored in its file.

Member m_pIPaper holds the main interface pointer to the COPaper object instantiated in the server. It is through this pointer that all of the COPaper functionality is accessed.

The m_nLockKey member is used to support a client locking scheme that is used with multiple clients to allow a client exclusive access to a shared COPaper object. The m_nLockKey is assigned by COPaper during a IPaper Lock call and is passed as a parameter by the client in subsequent calls to COPaper. COPaper will perform the work in those calls only if the lock key that is passed matches the key last handed out to a client by COPaper. This locking scheme and the multiclient scenarios it supports are not covered in this tour.

Member m_pPapFile holds a pointer to a CPapFile object. This object will be examined in detail below. It is a C++ object that encapsulates load and save operations on a structured storage compound file. CPapFile works with the underlying server-based COPaper object to load and save COPaper's drawing data.

CGuiPaper's methods are summarized as follows.

  BOOL Init(HINSTANCE hInst, HWND hWnd, TCHAR* pszCmdLineFile);
    Initializes the GuiPaper. Asks server to create a COPaper object.

  HRESULT DrawOn(void);
    Locks paper for drawing exclusively by this client.

  HRESULT DrawOff(void);
    Unlocks paper to allow other clients to draw.

  HRESULT ClearWin(void);
    Clears display window but retains ink data.

  HRESULT PaintWin(void);
    Clears window and repaints with current ink data.

  HRESULT Erase(void);
    Erases current drawing content and clears display window.

  HRESULT Resize(WORD wWidth, WORD wHeight);
    Resizes the display window.

  HRESULT InkWidth(SHORT nInkWidth);
    Sets current ink width for drawing.

  HRESULT InkColor(COLORREF crInkColor);
    Sets current ink color for drawing.

  HRESULT InkSaving(BOOL bInkSaving);
    Turns ink data saving in COPaper on and off.

  HRESULT InkStart(SHORT nX, SHORT nY);
    Starts ink drawing sequence.

  HRESULT InkDraw(SHORT nX, SHORT nY);
    Draws ink sequence data.

  HRESULT InkStop(SHORT nX, SHORT nY);
    Stops ink drawing sequence.

  HRESULT ConnectPaperSink(void);
    Connects the client PaperSink object to the server COPaper source.

  HRESULT DisconnectPaperSink(void);
    Disconnect the client PaperSink object from the server COPaper source.

  HRESULT Load(void);
    Loads ink data from current compound file.

  HRESULT Save(void);
    Saves existing ink data to current compound file.

  HRESULT AskSave(void);
    Checks if drawing changed. If so, displays dialog box asking
    user whether to save changes and responds appropriately.

  HRESULT Open(void);
    Shows Win32 common dialog box. Opens existing paper data compound file.

  HRESULT SaveAs(void);
    Shows Win32 common dialog box. Saves current paper data in renamed file.

  COLORREF PickColor(void);
    Shows Win32 ommon dialog box. Asks user to choose new pen color.

These methods are all implemented in GUIPAPER.CPP.

The Init method creates the server-based COPaper object and assigns CGuiPaper's m_pIPaper member.

The AskSave, Open, SaveAs, and PickColor methods provide familiar GUI behavior using Win32 common dialogs. For example, the Open method uses the Win32 Open File Name dialog box to ask the user to specify a file name for opening.

The Load and Save methods will be covered in detail later in this tour.

InkSaving, InkStart, InkDraw, and InkStop are the central methods for the drawing functionality of the STOCLIEN application. STOCLIEN uses these CGuiPaper methods to capture, display, and store the interactive drawing data as it occurs under user control. They perform a dual role of painting the drawn image to the client window as well as passing the drawing data to COPaper in the server. COPaper translates the drawing data into ink data packets for storage.

The user presses the left mouse button (or the pen tip switch in tablet devices) to initiate a line drawing sequence. The user holds down the button and moves the mouse to draw a line. The sequence is ended when the left mouse button is released. The Windows operating system translates these actions into standard window messages and sends them to STOCLIEN's main window procedure. Here is CMainWindow::WindowProc from STOCLIEN.CPP.

  LRESULT CMainWindow::WindowProc(
            UINT uMsg,
            WPARAM wParam,
            LPARAM lParam)
  {
    LRESULT lResult = FALSE;

    switch (uMsg)
    {
      case WM_CREATE:
        break;

      case WM_ACTIVATE:
        // If we were newly activated by a mouse click then don't just sit
        // there--re-paint. This is needed when another window was topped
        // over part of STOCLIEN and the user then topped STOCLIEN using
        // a mouse click on the visible part of STOCLIEN. In any case let
        // the windows default WindowProc handle this message as well.
        if (WA_CLICKACTIVE == LOWORD(wParam))
          m_pGuiPaper->PaintWin();
        lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
        break;

      case WM_SIZE:
        // Handle a resize of this window.
        m_wWidth = LOWORD(lParam);
        m_wHeight = HIWORD(lParam);
        // Inform CGuiPaper of the change.
        m_pGuiPaper->Resize(m_wWidth, m_wHeight);
        break;

      case WM_PAINT:
        // If something major happened repaint the whole window.
        {
          PAINTSTRUCT ps;

          if(BeginPaint(m_hWnd, &ps))
            EndPaint(m_hWnd, &ps);

          m_pGuiPaper->PaintWin();
        }
        break;

      case WM_LBUTTONDOWN:
        // Start sequence of ink drawing to the paper.
        m_pGuiPaper->InkStart(LOWORD(lParam), HIWORD(lParam));
        break;

      case WM_MOUSEMOVE:
        // Draw inking sequence data.
        m_pGuiPaper->InkDraw(LOWORD(lParam), HIWORD(lParam));
        break;

      case WM_LBUTTONUP:
        // Stop an ink drawing sequence.
        m_pGuiPaper->InkStop(LOWORD(lParam), HIWORD(lParam));
        break;

      case WM_COMMAND:
        // Dispatch and handle any Menu command messages received.
        lResult = DoMenu(wParam, lParam);
        break;

      case WM_CHAR:
        if (wParam == 0x1b)
        {
          // Exit this app if user hits ESC key.
          ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
        }
        break;

      case WM_CLOSE:
        // The user selected Close on the main window's System menu
        // or Exit on the File menu.
        // If there is ink data that has not been saved then ask user
        // if it should be saved. If user cancels then cancel the exit.
        if (IDCANCEL == m_pGuiPaper->AskSave())
          break;
      case WM_QUIT:
        // If the app is being quit then close any associated help windows.
        // ::WinHelp(m_hWnd, m_szHelpFile, HELP_QUIT, 0);
      default:
        // Defer all messages NOT handled above to the Default Window Proc.
        lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
        break;
    }

    return(lResult);
  }

A line drawing sequence is started when the WM_LBUTTONDOWN message is received with mouse position data. CMainWindow has a pointer to the CGuiPaper object and calls the CGuiPaper::InkStart method to start the line drawing sequence. As the mouse is moved to draw, a sequence of separate WM_MOUSEMOVE messages is received with mouse position data. CGuiPaper's InkDraw method is called with this data. When the left mouse button is released, the WM_LBUTTONUP message is received. CGuiPaper's InkStop method is then called to stop the line drawing sequence.

Here is the InkStart method from GUIPAPER.CPP.

  HRESULT CGuiPaper::InkStart(
                       SHORT nX,
                       SHORT nY)
  {
    HRESULT hr = E_FAIL;

    if (m_nLockKey || (!m_nLockKey && !m_bInkSaving))
    {
      // Start an ink drawing sequence only if one is not in progress.
      if (!m_bInking)
      {
        // Remember start position.
        m_OldPos.x = nX;
        m_OldPos.y = nY;

        // Delete old pen.
        if (m_hPen)
          DeleteObject(m_hPen);

        // Create and select the new drawing pen.
        m_hPen = CreatePen(PS_SOLID, m_nInkWidth, m_crInkColor);
        SelectObject(m_hDC, m_hPen);

        hr = NOERROR;

        // Ask the Paper object to mark the start of the ink drawing
        // sequence in the current ink color.
        if (m_pIPaper && m_bInkSaving)
        {
          hr = m_pIPaper->InkStart(
                            m_nLockKey,
                            nX,
                            nY,
                            m_nInkWidth,
                            m_crInkColor);
          // Capture the Mouse.
          SetCapture(m_hWnd);

          // We've modified the ink data--it is now "dirty" with
          // respect to the compound file image. Set dirty flag.
          m_bDirty = TRUE;
        }

        // Set inking flag to TRUE.
        m_bInking = TRUE;
      }
    }

    return hr;
  }

InkStart, InkDraw, and InkStop all use Win32 GUI constructs such as device contexts and pen objects. This illustrates why CGuiPaper is needed as a separate level of encapsulation. The GUI aspects of the drawing paper are handled in CGuiPaper. The COPaper object is sent only ink data.

Another design reason for the CGuiPaper level of encapsulation is the dual nature of its InkStart, InkDraw, and InkStop methods. They not only call on COPaper to record the ink data as it occurs, they also paint a visual image of the drawing as it occurs. CGuiPaper keeps an m_bInkSaving flag to manage this dual nature. When m_bInkSaving is FALSE, the image is drawn on screen but the data is not sent to COPaper for recording. This scheme is used in repainting when the user is not moving the mouse but COPaper's ink data is being resent to CGuiPaper for automatic repainting. CGuiPaper can be told to set the m_bInkSaving flag by calling its InkSaving method.

CGuiPaper also keeps an m_bInking flag. InkStart sets it to TRUE to signal that a drawing sequence is in process. For example, the InkDraw method uses this flag to determine whether it should paint and save ink data. Here is the InkDraw method from GUIPAPER.CPP.

  HRESULT CGuiPaper::InkDraw(
                       SHORT nX,
                       SHORT nY)
  {
    if (m_bInking)
    {
      // Start this ink line at previous old position.
      MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);

      // Assign new old position and draw the new line.
      LineTo(m_hDC, m_OldPos.x = nX, m_OldPos.y = nY);

      // Ask the Paper object to save this data.
      if (m_bInkSaving)
        m_pIPaper->InkDraw(m_nLockKey, nX, nY);
    }

    return NOERROR;
  }

This method does nothing if m_bInking is FALSE. This is the condition when the user is simply moving the mouse over the client window without pressing the left mouse button.

InkDraw clearly has a dual responsibility. The Win32 MoveToEx and LineTo calls are made to draw line images on the GUI screen (using the device context handle kept in m_hDC). The ink data is also passed to the COPaper object for recording using the IPaper interface's InkDraw method. When m_bInkSaving is FALSE, InkDraw paints the line image but does not store the data in COPaper. This condition is used during repainting.

Here is CGuiPaper's PaintWin method from GUIPAPER.CPP.

  HRESULT CGuiPaper::PaintWin(void)
  {
    HRESULT hr = E_FAIL;
    COLORREF crInkColor;
    SHORT nInkWidth;

    if (m_pIPaper && !m_bPainting && !m_bInking)
    {
      m_bPainting = TRUE;
      // Save and restore ink color and width since redraw otherwise
      // ends up changing these values in CGuiPaper.
      crInkColor = m_crInkColor;
      nInkWidth = m_nInkWidth;
      hr = m_pIPaper->Redraw(m_nLockKey);
      m_nInkWidth = nInkWidth;
      m_crInkColor = crInkColor;
      m_bPainting = FALSE;
    }

    return hr;
  }

PaintWin essentially calls COPaper's Redraw method. In the STOSERVE sample, the Redraw method was shown to broadcast COPaper's entire ink data array to all connected sinks. PaintWin calls an object on the server side to send back the drawing data to the client. STOCLIEN receives this data in the form of calls to the connected IPaperSink interface in its COPaperSink object. These methods correspond to the similar InkStart, InkDraw, and InkStop methods in CGuiPaper. For example, here is IPaperSink's InkStart method from SINK.CPP.

  STDMETHODIMP COPaperSink::CImpIPaperSink::InkStart(
                                              SHORT nX,
                                              SHORT nY,
                                              SHORT nWidth,
                                              COLORREF crInkColor)
  {
    // Turn off ink saving to the COPaper object.
    m_pBackObj->m_pGuiPaper->InkSaving(FALSE);

    // Play the data back to the CGuiPaper for display only.
    m_pBackObj->m_pGuiPaper->InkWidth(nWidth);
    m_pBackObj->m_pGuiPaper->InkColor(crInkColor);
    m_pBackObj->m_pGuiPaper->InkStart(nX, nY);

    return NOERROR;
  }

When InkStart is called in the sink, it calls CGuiPaper to turn off ink saving to COPaper. COPaper does not need to receive an echoed copy of the data it is sending. When InkDraw is called in the sink, it simply passes the call on to CGuiPaper::InkDraw. When InkStop is called in the sink, CGuiPaper is called to turn ink saving back on. The result is a playback of COPaper's ink data to CGuiPaper for display only.

You can test the behavior of STOCLIEN when its IPaperSink is disconnected by choosing the Disconnect choice on the Sink menu. As an experiment, after disconnecting the sink, choose About from the Help menu. This will show the About dialog box, which will cover part of the STOCLIEN's drawing. Click OK in the About dialog box. Notice that the portion of the drawing that was covered is now gone. The drawing data is not lost, but part of the image is. The client received the WM_PAINT message and issued the IPaper Redraw method. But because the sink was not connected, it did not receive the IPaperSink calls to repaint the drawing.

You can also test this behavior by minimizing STOCLIEN and then restoring it. In this case, the entire drawing image is lost and needs repainting. To repaint the drawing image after either of these tests, use the Sink menu to reconnect, and then choose Redraw from the Draw menu.

STOCLIEN relies on COPaper to record drawing data. It also relies on COPaper to store the data in a compound file. However, in a typical division of labor between COM client and server, STOCLIEN shares part of the responsibility for file storage. This division of labor is important in COM applications where the client is a container and the server is an embedded object. In this arrangement, the client is responsible for creating or opening a structured storage file, while the server object is responsible for using that storage for its own data storage purposes. This may involve the server object creating substorages in the storage that is given to it. It usually involves the server object creating stream objects in the storage. COPaper's use of storage streams is detailed in the STOSERVE sample.

The IStorage interface is used by both client and server object to perform file operations. The compound files implementation of the Structured Storage architecture is used. Standard service functions are used for operations on compound files. For example, the StgCreateDocfile function initially creates a compound file and passes back an IStorage pointer that can be used to manipulate the file. This particular function is called in STOCLIEN. The IStorage interface it obtains is passed as a parameter to COPaper for its use. The COPaper object does not create or open compound files on its own: It uses the IStorage and IStream interfaces to work in compound files that are given to it.

These IStorage and IStream interfaces are not implemented within STOCLIEN or STOSERVE: They are implemented within the COM libraries. When a pointer to one of these interfaces is obtained, their methods are essentially used as a set of services to operate on a compound file.

STOCLIEN encapsulates its compound file operations in a CPapFile C++ object. Here is the CPapFile class declaration from PAPFILE.H.

  class CPapFile
  {
    public:
      CPapFile(void);
      ~CPapFile(void);
      HRESULT Init(TCHAR* pszFileName, IPaper* pIPaper);
      HRESULT Load(SHORT nLockKey, TCHAR* pszFileName);
      HRESULT Save(SHORT nLockKey, TCHAR* pszFileName);

    private:
      TCHAR          m_szCurFileName[MAX_PATH];
      IPaper*        m_pIPaper;
      IStorage*      m_pIStorage;
  };

The CPapFile object keeps a current file name in member m_szCurFileName. This file name is used as a default in the Load and Save methods when they do not explicitly receive a file name.

Member m_pIPaper keeps an interface pointer to the COPaper's IPaper interface.

Member m_pIStorage keeps a pointer to the IStorage interface for the current compound file that STOCLIEN is using for structured storage.

Here is a summary of CPapFile's methods.

  HRESULT Init(TCHAR* pszFileName, IPaper* pIPaper);
    Initializes CPapFile.

  HRESULT Load(SHORT nLockKey, TCHAR* pszFileName);
    Loads default paper data file or a specified paper data file.

  HRESULT Save(SHORT nLockKey, TCHAR* pszFileName);
    Saves drawing data to the current paper data file or to a specified
    paper data file.

Here is CPapFile's Init method from PAPFILE.CPP.

  HRESULT CPapFile::Init(
                      TCHAR* pszAppFileName,
                      IPaper* pIPaper)
  {
    HRESULT hr = E_FAIL;

    if (NULL != pIPaper)
    {
      // Keep CPapFile copy of pIPaper. Thus AddRef it.
      // Released in Destructor.
      m_pIPaper = pIPaper;
      m_pIPaper->AddRef();

      if (NULL != pszAppFileName)
      {
        // Set the default current file name.
        lstrcpy(m_szCurFileName, pszAppFileName);

        hr = NOERROR;
      }
    }

    return (hr);
  }

The pszAppFileName parameter is used to initialize CPapFile with a default file name for any subsequent load or save operations. A copy is kept in member m_szCurFileName for the first load. In STOCLIEN, such a load is attempted when the application first starts after CPapFile has been initialized with pszAppFileName assigned "STOCLIEN.PAP". This file name was constructed in CGuiPaper's constructor by obtaining the current executing module's name. (The Win32 GetModuleFileName function is called).

The pIPaper parameter is needed by CPapFile because it uses this interface on the COPaper object to perform its load and save operations. AddRef is called on this stored copy of pIPaper, according to COM rules. The matching Release is called in the CPapFile destructor.

Once CPapFile is initialized, it can be used to save the current drawing data that is held in the COPaper object. Here is CPapFile's Save method from PAPFILE.CPP.

  HRESULT CPapFile::Save(
                      SHORT nLockKey,
                      TCHAR* pszFileName)
  {
    HRESULT hr = E_FAIL;
    BOOL bNewFile = (NULL != pszFileName);
    TCHAR* pszFile;

    // If NULL file name passed then use current file name.
    pszFile = bNewFile ? pszFileName : m_szCurFileName;

    // Use COM service to re-open (or newly create) the compound file.
    hr = StgCreateDocfile(
           pszFile,
           STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
           0,
           &m_pIStorage);
    if (SUCCEEDED(hr))
    {
      // We've got the IStorage and the compound file is open.
      // Now tell the COPaper object on the server side to save its
      // paper data into the compound file using the IStorage of our
      // client-provided compound file.
      hr = m_pIPaper->Save(nLockKey, m_pIStorage);
      if (SUCCEEDED(hr))
      {
        // The paper data was saved and we have a new current compound
        // file name. Copy it for later use in a saves and loads.
        if (bNewFile)
          lstrcpy(m_szCurFileName, pszFileName);
      }

      // We are done with the Storage for now. We don't hold the file
      // open. We re-obtain the IStorage again later (and thus re-open
      // the compound file) when we need it for another save or load.
      RELEASE_INTERFACE(m_pIStorage);
    }

    return (hr);
  }

If NULL is passed for the pszFileName parameter, the stored content of member m_szCurFileName is used for the file name. The nLockKey is the client's lock key, which was obtained in a previous call to COPaper's Lock method in the IPaper interface.

The COM standard StgCreateDocfile service function is called to create the compound file in which the drawing data will be saved.

The name of the compound file to create is passed as the first parameter. This is expected by StgCreateDocfile as a Unicode string. When STOCLIEN is compiled for ANSI strings (UNICODE is not defined, which is the default in the makefile), this call actually reduces to a call to an internal APPUTIL function, A_StgCreateDocfile. This function performs the proper conversion of the ANSI pszFile parameter to a Unicode version before calling StgCreateDocfile. See APPUTIL.H and STOSERVE.HTM for more details on how this works.

The access mode flags are passed as the second parameter and determine what access modes are permitted on the new file. The STGM_CREATE access mode flag creates a new compound file or overwrites an existing one of the same name. STGM_READWRITE opens the file with read-write permission. STGM_DIRECT opens the file for direct access, as opposed to transacted access. STGM_SHARE_EXCLUSIVE opens the file for exclusive, non-shared use by the caller.

The third parameter is reserved and must be 0.

The address of pointer variable m_pIStorage is passed to StgCreateDocfile as the fourth parameter. When the call successfully returns, m_pIStorage contains an interface pointer to an IStorage interface. This is the interface that is used for any subsequent operations on the file.

The important operation in this case is to have the COPaper object store its drawing data in the file. This is done above using the Save method of COPaper's IPaper interface. If COPaper succeeds in saving its data in the storage object provided, the name of the compound file is copied into m_szCurFileName as the new current file name.

When these operations are successfully completed, the IStorage interface that was obtained is released. This closes the file and means that the file is not held open during other client operations. It will be reopened when needed.

One such case is CPapFile's Load method. Here it is from PAPFILE.CPP.

  HRESULT CPapFile::Load(
                      SHORT nLockKey,
                      TCHAR* pszFileName)
  {
    HRESULT hr = E_FAIL;
    BOOL bNewFile = (NULL != pszFileName);
    TCHAR* pszFile;

    // If NULL file name passed then use current file name.
    pszFile = bNewFile ? pszFileName : m_szCurFileName;

    // First check if the file is out there using APPUTIL function.
    if (FileExist(pszFile))
    {
      // Use COM service to next check if the file is actually a valid
      // compound file.
      hr = StgIsStorageFile(pszFile);
      if (SUCCEEDED(hr))
      {
        // We're go. Use COM service to open the compound file and
        // obtain a IStorage interface.
        hr = StgOpenStorage(
               pszFile,
               NULL,
               STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
               NULL,
               0,
               &m_pIStorage);
        if (SUCCEEDED(hr))
        {
          // We have an IStorage. Now ask the COPaper object on the server
          // side to load into itself the file's paper data using the
          // IStorage for our client-provided compound file.
          hr = m_pIPaper->Load(nLockKey, m_pIStorage);
          if (SUCCEEDED(hr))
          {
            // The paper data was loaded and we have a current compound
            // file name. Copy it for later use in a saves and loads.
            if (bNewFile)
              lstrcpy(m_szCurFileName, pszFileName);
          }

          // We are done with the Storage for now. We don't hold the file
          // open. We re-obtain the IStorage again later (and thus re-open
          // the compound file) when we need it for another save or load.
          RELEASE_INTERFACE(m_pIStorage);
        }
      }
    }

    return (hr);
  }

As with the Save method, if NULL is passed for the pszFileName parameter, the stored content of member m_szCurFileName is used for the file name. Since an existing file may be opened, APPUTIL's FileExist function is first used to check if the file exists. If it exists, the standard StgIsStorageFile service function is called to check the format of the file to determine if it is a valid compound file.

If the file is valid, the standard StgOpenStorage service function is called to open the file and return a pointer to an IStorage interface for it.

The first parameter is the name of the compound file to open. As with the StgCreateDocfile call, this string is expected in the Unicode form, and during ANSI compilation an APPUTIL macro is used to ensure the ANSI parameter is converted to the expected Unicode.

The second priority storage parameter is NULL, indicating it is not used in this sample.

The access mode flags are passed as the third parameter to specify what access modes are permitted on the opened file. STGM_READ opens the file with read-only permission. STGM_DIRECT opens the file for direct access, as opposed to transacted access. STGM_SHARE_EXCLUSIVE opens the file for exclusive, non-shared use by the caller.

The fourth element exclusion parameter in NULL, indicating it is not used in this sample.

The fifth parameter is reserved for future use and must be 0.

The address of pointer variable m_pIStorage is passed as the sixth parameter. When the call successfully returns, m_pIStorage contains a pointer to an IStorage interface. This is the interface that is used for any subsequent operations on the opened file.

The important operation in this case is to have the COPaper object load its drawing data from the file. This is done above using the Load method of COPaper's IPaper interface. If COPaper succeeds in loading its data using the IStorage provided, the name of the compound file is copied into m_szCurFileName as the new current file name.

When these operations are successfully completed, the IStorage interface that was obtained is released. This closes the file and means that the file is not held open during other client operations. It will be reopened when needed.

Top Back to page top

© 1995-1998 Microsoft Corporation


PERSERVE - IPersistStream Persistent Object Server - Lesson 25

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

PERSERVE - IPersistStream Persistent Object Server

SUMMARY

The PERSERVE sample introduces persistent COM objects. This is the first of four samples covering various techniques for implementing COM object persistence. PERSERVE covers persistence using the implementation of the IPersistStream standard interface. PERTEXT covers persistence using the implementation of the IPersistStreamInit standard interface. PERDRAW covers persistence using the implementation of the IPersistStorage standard interface. PERCLIEN covers how a client exploits the different kind of persistence supported by each of the components in the PERSERVE, PERTEXT, and PERDRAW servers.

The PERSERVE sample introduces the COPageList COM object, which encapsulates a page list. The list contains elements that correspond to pages of text or drawing data. COPageList objects expose a set of interfaces that make the objects connectable, persistent managers of the list data. Client access to this data is available through an IPageList custom interface. COPageList implements the IPageList interface.

A clear architectural distinction is kept between client and server. COPageList provides no graphical user interface (GUI). Instead, the COPageList object relies on the client for all GUI behavior.

The PERCLIEN client provides the GUI display and manages the list of pages the content of which are stored in a compound file that contains both the contents of the list and of each page in that list. The user of PERCLIEN can edit the content of two type of pages: Text pages and drawing pages. Text pages have text data that the user can edit using a simple windowed text editor. Drawing pages have drawing data that the user can edit using free-form, scribble-like functionality based on the earlier STOSERVE and STOCLIEN samples. Both types of editing are done in separate client windows. The details of the client are covered in the PERCLIEN sample. For more information, see PERCLIEN.HTM.

Storage in the compound file is achieved because the components provide persistent COM objects that encapsulate the page list and edited page data. PERSERVE houses a persistent object that encapsulates the single page list kept in each compound file containing such pages. PERTEXT houses a persistent object that encapsulates the edited text data for each text page. PERDRAW houses a persistent object that encapsulates the drawing data for each drawing page.

The COPageList object in the this PERSERVE sample encapsulates the persistent page list data. COPageList implements the IPersistStream standard interface to expose control of the page list storage located in the client-provided stream of a compound file. The COTextPage object in the PERTEXT sample encapsulates the data of an edited text page. COTextPage implements the IPersistStreamInit standard interface to expose control of the text data storage that is located in the client-provided stream of a compound file. In contrast to these stream-based persistent objects, the CODrawPage object in the PERDRAW sample encapsulates the persistent drawing-ink data. CODrawPage implements the IPersistStorage standard interface to expose control of the drawing's ink data storage located in the client-provided substorage of a compound file.

This code sample focuses primarily on COPageList's implementation of the IPersistStream interface to provide stream-based persistence for a COM object. PERSERVE works with the PERCLIEN code sample to illustrate the joint use by client and server of this IPersistStream-based persistence.

COPageList's support for object persistence is the primary means of storing the page list data. COPageList stores its list data in a stream under the root storage of a structured-storage compound file. The compound file has a unique format because of the various streams and storages used. The client identifies these compound files as page files with a .PAG extension. Only one page-list stream is present in each page file. The client controls the use of the containing compound file and provides COPageList with an IStream pointer for its use in loading and saving its list data in the compound file. The IStream pointer is passed to COPageList in method calls to the IPersistStream interface.

COPageList also exposes an IPageList custom interface to manipulate the encapsulated list data. IPageList exposes the Get, Set, Add, Delete, and Clear methods.

Connectable object features are also supported in COPageList. The IConnectionPointContainer interface is exposed, and an appropriate connection point is implemented. In this context, an outgoing custom IPageListSink interface is declared to send notifications to the client.

The two IPageList and IPageListSink custom interfaces are declared in IPAGES.H located in the common INC directory. PAGEGUID.H, which contains the GUIDs for the interfaces and objects, is located in that same directory.

The PERSERVE sample uses the CThreaded facility in APPUTIL to achieve thread safety in the server housing and the class factory. Because PERSERVE.DLL is generally accessed from a Single Threaded Apartment (STA) as an in-process server, COPageList instances are not coded as thread-safe. The CLSID_PageList component is registered as supporting the apartment threading model.

For functional descriptions and a tutorial code tour of PERSERVE, see the Code Tour section in PERSERVE.HTM. For details on setting up the programmatic usage of PERSERVE.DLL, see the Usage section in PERSERVE.HTM. To read PERSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the PERSERVE lesson in the table of lessons. You can also do the same thing by double-clicking the PERSERVE.HTM file after locating the main tutorial directory in Windows Explorer. See also PERCLIEN.HTM in the main tutorial directory for more details on the PERCLIEN client application and how it works with PERSERVE.DLL. You must build PERSERVE.DLL before running the PERCLIEN sample.

The PERSERVE server provides a PageList component that can create instances of the COPageList COM object. COPageList is housed in the PERSERVE.DLL in-process server and is made publicly available as a custom COM component. Like all other servers in this tutorial series, PERSERVE.DLL is a self-registering COM server. It makes the COPageList object type available to clients as the PageList component in the PERSERVE server using a CLSID_PageList registration in the Registry.

PERSERVE's makefile automatically registers its PageList COM component in the registry, which it must do before clients can use PERSERVE.DLL as a server for the PageList component. This self-registration is started in the makefile using the REGISTER.EXE utility built in the REGISTER sample. To build or run PERSERVE.DLL, you must build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the PERSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV PERSERVE.DSP

You can also simply double-click the PERSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use PERSERVE, a client program does not need to include PERSERVE.H or link to PERSERVE.LIB. A COM client of PERSERVE obtains access solely through COM services and the PageList object's CLSID. For PERSERVE, that CLSID is CLSID_PageList (defined in file PAGEGUID.H in the INC sibling directory). For more information about how the client obtains this access, see the PERCLIEN code sample.

PERSERVE.DLL is intended primarily as a COM server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within the CoGetClassObject function. PERSERVE is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the PERSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the PERSERVE directory.

  ..\register\register.exe perserve.dll

These registration commands require a prior build of both the REGISTER sample and PERSERVE.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Microsoft Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which you can use in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, PERCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
PERSERVE.TXT  Short description of sample.
MAKEFILE      The generic makefile for building the PERSERVE.DLL
              code sample of this lesson.
PERSERVE.H    The include file for declaring as imported or defining as
              exported the service functions in PERSERVE.DLL.
PERSERVE.CPP  The main implementation file for PERSERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
PERSERVE.DEF  The module definition file. Exports server housing functions.
PERSERVE.RC   The DLL resource definition file for the executable.
PERSERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control C++ object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerators objects.
PAGELIST.H    The include file for the COPageList COM object class.
PAGELIST.CPP  The implementation file for the COPageList COM object class
              and the connection points.
PERSERVE.DSP  Microsoft Visual Studio Project file.

PERSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial code samples and assumes that you have had some exposure to the previous samples. It does not revisit earlier topics such as basic interface implementation techniques, COM object construction, in-process server construction, class-factory construction, connectable object construction, and techniques of using structured storage in compound files. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • How PERSERVE is designed to work with connected clients;
  • COPageList's use of the IPageList interface for managing page lists.
  • The data constructs that make up COPageList's persistent data.
  • COPageList's implementation of the IPersistStream interface to provide persistence.
  • COPageList's use of the IPageListSink interface for client notifications.

The COPageList COM object is the single object type managed by this PERSERVE in-process server. COPageList is a connectable COM object with an implementation of the IConnectionPointContainer standard interface, an implementation of the IPersistStream standard interface, and an implementation of the IPageList custom interface.

COPageList exposes the IPageList custom interface so clients can perform a small set of operations on the page list that is encapsulated by COPageList. The following is a summary of the IPageList methods from IPAGES.H located in the common INC directory:

  Get
    Get a page list item for a given page number. This includes
    open status, page type, page title, and page data name.

  Set
    Put a page list item of specified page number with the page's
    title and open status.

  Add
    Add a new page list item of specified type with specified title.

  Delete
    Delete the page list item that correspondes to the specified page
    number.

  Clear
    Clear (and delete) all page list items from the page list.

Get, Set, Add, and Delete all act upon individual list entries, while Clear acts upon the entire list. The implementation of IPageList is straightforward and is not covered in detail here. A set of properties for the list is maintained and the list array itself is maintained. For more details, see PAGELIST.H and PAGELIST.CPP.

COPageList manages the array of its list data in RAM. However, because COPageList is also a persistent object, it also manages the persistent image of this list data in a stream of a structured storage compound file. COPageList exposes an implementation of the IPersistStream interface so that clients can control the persistence features of COPageList. The principal focus of this sample is COPageList's implementation of IPersistStream.

The client is responsible for a presentation of the list to the user. PERCLIEN does this with a standard List Box control. The client uses the IPageList interface on COPageList to obtain the data to display in this list box and to update COPageList with any user-requested changes to the list data. The client does not primarily manage the list data. COPageList does this in the PERSERVE server. The client does manage creation or opening of the compound file containing the list data. When the client needs to control the persistence of COPageList it specifies an IStream interface to COPageList. This is typically done during such IPersistStream method-calls as Load and Save. COPageList then uses that stream for all persistent storage of its object-state data.

Two major data constructs are stored for this object-state data: a page list item and the page list properties. The page list data contains an array of the following PAGEITEM structures whose format is declared in PAGELIST.H.

  // The PageList item structure.
  typedef struct _PAGEITEM
  {
    SHORT     nType;                       // Page Type.
    BOOL      bOpen;                       // Page Open status.
    INT       iPage;                       // Page Number.
    WCHAR     wszDataName[PAGE_NAME_SIZE]; // Page Storage/Stream Data Name.
    WCHAR     wszTitle[PAGE_TITLE_SIZE];   // Page Title.
  } PAGEITEM;

The nType member contains the page type. Currently the following types are defined in the IPAGES.H file found in the common INC directory.

  // Here are the page types.
  #define PAGETYPE_NONE    0
  #define PAGETYPE_DELETED 1
  #define PAGETYPE_TEXT    2
  #define PAGETYPE_DRAWING 3

The bOpen Boolean member is for future evolution of the application, and is not used by the PERCLIEN client. The intention is to allow a client to restore the opened pages across invocations of the client and doing this requires persistence on the open status of the opened pages.

The iPage member is the page sequence number of the list item. It is used to associate the client's list box item with the associated page data. For example, the client may want to delete the page after the list is loaded from persistent storage. A delete call (through IPageList::Delete) would specify the page number of the page to delete. Because of deletes and other activity on COPageList's dynamic page list, the array index of the items in COPageList's list array may not match the associated page number. Thus, the page associated with the list item that the user sees in the client must be found in COPageList's list array by finding a match on the iPage member. The pages are numbered within the array and are not numbered according to their "slot" in the dynamic list array.

The wszDataName member is the name of the stream in which the page's data is stored. This page data is the text data of text pages and the drawing data of drawing pages. Note that this stream name is a wide character string that supports Unicode. Each page in the compound file has such a named stream for its data. The stream name is an internal, automatically generated name of the forms, Drawing00000 or Text00000. The generation algorithm for this data stream name (see the NextName internal method in PAGELIST.CPP) guarantees that this name will always be unique within the same compound file. PAGE_NAME_SIZE is defined in IPAGES.H as 16. Likewise, PAGE_TITLE_SIZE is defined in IPAGES.H as 64.

The wszTitle member is the user displayable title of the page. This title is displayed for each page in the client list box. This title is a wide character string.

The other important persistent data that is stored in the page list's data stream is a set of properties for the page list. These are properties of the page list and not properties of individual pages and characterize the contents of the entire compound file. The following is the PAGELISTPROPS structure from PAGELIST.H:

  // Properties of the PageList.
  typedef struct _PAGELISTPROPS
  {
    LONG lPageListVersion;
    LONG lPageListSize;
    LONG lPageNameCounter;
    WCHAR wszTitle[PAGE_TITLE_SIZE];
    WCHAR wszAuthor[PAGE_TITLE_SIZE];
    WCHAR wszReserved1[PAGE_TITLE_SIZE];
  } PAGELISTPROPS;

The lPageListVersion member holds a version number for the page list. This allows the first member to be checked to determine possible variations in treatment based on the page list version.

The lPageListSize member holds the number of page list items in the page list array.

The lPageNameCounter member holds a counter used in generating unique data names for streams and storages used in the compound file.

The wszTitle, wszAuthor, and wszReserved1 members are for future evolution of the application and are not used by PERSERVE or PERCLIEN.

In the division of labor between client and server, COPageList does not create the compound file that is used to store page list data. The client uses the IPersistStream interface for both the Save and Load of the COPageList's persistent data. The client passes an IStream interface pointer for a stream located in an existing compound file. COPageList then uses the IStream interface to read and write data in the compound file.

Here is the declaration of the COPageList COM object from PAGELIST.H:

  class COPageList : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COPageList(IUnknown* pUnkOuter, CServer* pServer);
      ~COPageList(void);

      // A general public method for initializing this newly created
      // object. Creates any subordinate arrays, structures, or objects.
      // Not exposed as part of an interface. Used by Class Factory.
      HRESULT Init(void);

      // Main object IUnknown methods. Non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIConnectionPointContainer : public IConnectionPointContainer
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIConnectionPointContainer(COPageList* pCO, IUnknown* pUnkOuter);
          ~CImpIConnectionPointContainer(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IConnectionPointContainer methods.
          STDMETHODIMP         FindConnectionPoint(REFIID, IConnectionPoint**);
          STDMETHODIMP         EnumConnectionPoints(IEnumConnectionPoints**);

        private:
          // Data private to this interface implementation.
          COPageList*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      class CImpIPersistStream : public IPersistStream
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIPersistStream(COPageList* pCO, IUnknown* pUnkOuter);
          ~CImpIPersistStream(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IPersistStream methods.
          STDMETHODIMP         GetClassID(CLSID* pClassID);
          STDMETHODIMP         IsDirty(void);
          STDMETHODIMP         Load(IStream* pIStream);
          STDMETHODIMP         Save(IStream* pIStream, BOOL bClearDirty);
          STDMETHODIMP         GetSizeMax(ULARGE_INTEGER* pcbSize);

        private:
          // Data private to this interface implementation.
          COPageList*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      class CImpIPageList : public IPageList
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIPageList(COPageList* pCO, IUnknown* pUnkOuter);
          ~CImpIPageList(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IPageList methods.
          STDMETHODIMP         Get(
                                   INT     iPage,
                                   BOOL*   pbOpen,
                                   SHORT*  pnType,
                                   WCHAR*  pwszTitle,
                                   WCHAR*  pwszDataName);
          STDMETHODIMP         Set(
                                   INT     iPage,
                                   SHORT   nOpenStatus,
                                   WCHAR*  pwszNewTitle);
          STDMETHODIMP         Add(
                                   INT   iPage,
                                   SHORT nType,
                                   WCHAR* pwszTitle,
                                   INT* piNewPage);
          STDMETHODIMP         Delete(INT iPage);
          STDMETHODIMP         Clear(void);

        private:
          // Private utility methods of this interface implementation.
          HRESULT FindSlot(INT iPage, LONG* plSlot);
          HRESULT NextSlot(INT iPage, LONG* plSlot);
          HRESULT NextName(SHORT nType, WCHAR* pwszDataName);

          // Data private to this interface implementation of IPageList.
          COPageList*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested interface implementations
      // friends to COM object instantiations of this COM object class.
      friend CImpIConnectionPointContainer;
      friend CImpIPersistStream;
      friend CImpIPageList;

      // Private method of main connectable COPageList COM object to
      // broadcast event notifications to all connected listening sinks.
      HRESULT NotifySinks(PAGELIST_EVENT PageListEvent, INT iPage);

      // Private method to Clear entire page list array.
      HRESULT Clear(void);

      // Private data of COPageList COM objects.

      // Nested IConnectionPointContainer implementation instantiation.
      CImpIConnectionPointContainer m_ImpIConnectionPointContainer;

      // Nested IPersistStream implementation instantiation.
      CImpIPersistStream m_ImpIPersistStream;

      // Nested IPageList implementation instantiation. This IPageList
      // interface is instantiated as a native interface of COPageList.
      CImpIPageList     m_ImpIPageList;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;

      // The array of connection points for this connectable COM object.
      IConnectionPoint* m_aConnectionPoints[MAX_CONNECTION_POINTS];

      // The following private data and methods constitute the working
      // heart of COPageList as an actual application object.
      PAGELISTPROPS     m_PageListProps;// For file storage.
      PAGEITEM*         m_paPageList;   // Dynamic page list array pointer.
      LONG              m_lPageListEnd; // Current end of the page list.
      LONG              m_lPageListMax; // Current end of the page list array.
      BOOL              m_bDirty;       // RAM doesn't match file--save needed.
      CLSID             m_ClassID;      // CLSID of this COM Object.
  };

COPageList has nested implementations of the IUnknown, IConnectionPointContainer, IPersistStream, and IPageList interfaces. The techniques of these interface implementations follow the pattern used in many previous samples. Implementation of IUnknown was introduced in the COMOBJ sample. IConnectionPointContainer was introduced in the CONSERVE sample. IPageList is a custom interface that is introduced in this sample, but its implementation is very similar to custom interface implementations in previous samples.

The m_PageListProps member is a structure holds various properties of the page list. See above for a summary. The m_bDirty member is used internally to determine when the RAM-resident page list array has changed relative to the last time it was loaded from or saved to persistent storage. The m_ClassID stores the CLSID of the COPageList component for later use within the methods of COPageList. The m_lPageListEnd and m_lPageListMax members are used to maintain the currently active content of the page list array. The index of the current end of the array is kept in m_lPageListEnd. This end index can vary up to the maximum index kept in m_lPageListMax. The array can grow dynamically at run-time.

COPageList manages this page list array in RAM. The main pointer to this array is kept in the m_paPageList variable. The array is initially allocated during IPersistStream::Load after COPageList is created. The Load method is covered in more detail below.

The implementation of the IPersistStream standard interface deserves a more detailed examination. By studying each of the IPersistStream methods, you can see how the implementation of the IPersistStream interface gives persistence to the property and page list data in COPageList. If you have not used streams (IStream) or storages (IStorage) in structured storage compound files, see the STOSERVE and STOCLIEN samples for coverage of this topic. The IPersistStream methods declared above are as follows:

    STDMETHODIMP         GetClassID(CLSID* pClassID);
    STDMETHODIMP         IsDirty(void);
    STDMETHODIMP         Load(IStream* pIStream);
    STDMETHODIMP         Save(IStream* pIStream, BOOL bClearDirty);
    STDMETHODIMP         GetSizeMax(ULARGE_INTEGER* pcbSize);

Clients call the GetClassID method to obtain from COPageList its component CLSID. This method is really inherited from the IPersist interface from which IPersistStream is derived. The following is the implementation of GetClassID from PAGELIST.CPP:

  STDMETHODIMP COPageList::CImpIPersistStream::GetClassID(
                 CLSID* pClassID)
  {
    HRESULT hr = E_POINTER;

    if (NULL != pClassID)
    {
      // Use overloaded '=' operator to copy the Class ID.
      *pClassID = m_pCO->m_ClassID;
      hr = NOERROR;
    }

    return hr;
  }

This simple method uses the overloaded '=' operator to copy COPageList's main copy of the CLSID to the client's address space using the specified pClassID pointer. The convenient overloading of the '=' operator is provided in the COM and OLE header files included at the front of PAGELIST.CPP. The m_ClassID value was initially assigned the value CLSID_PageList in COPageList's constructor. CLSID_PageList is defined in PAGEGUID.H located in the common INC directory. This file also contains other GUIDs--such as interface IIDs--that are used in the PERSERVE, PERTEXT, PERDRAW, and PERCLIEN samples.

Clients call the IsDirty method to determine if changes have been made to this COPageList's persistent data since it was last loaded, initialized, or saved. The following is the implementation of IsDirty from PAGELIST.CPP:

  STDMETHODIMP COPageList::CImpIPersistStream::IsDirty(
                 void)
  {
    HRESULT hr = E_FAIL;

    hr = m_pCO->m_bDirty ? S_OK : S_FALSE;

    return hr;
  }

This method simply returns the current "dirty" status of the RAM-resident data in terms of the standard HRESULT return codes of S_OK or S_FALSE. S_OK means the data needs saving; S_FALSE means the data matches its counterpart in persistent storage.

Clients call the Load method is called to command COPageList to load its persistent data from a specified stream in a compound file starting at the current seek pointer offset. Thus, the client supplies the stream to COPageList; the stream is not created by COPageList. The Load method assumes that the stream's seek pointer is the same as it was before Save was last called. The method must leave the seek pointer with the same value it had when Save was last completed regardless of success or failure of the load. The rationale for these seek pointer rules is that IPersistStream can support a consecutive series of stored COPageList object states. The client controls the stream and, if the seek pointer rules are followed within Load and Save, then the client can issue a series of Save calls. Between these calls the client may issue other interface calls to COPageList that could change the data state of COPageList. The consecutive series that are saved would then reflect different data states of the same COPageList object.

The Load method implementation should not store a copy of the passed pIStream pointer within COPageList. The passed stream interface pointer should be used only within this method call. Here is the implementation of Load from PAGELIST.CPP.

  STDMETHODIMP COPageList::CImpIPersistStream::Load(
                 IStream* pIStream)
  {
    HRESULT hr = E_POINTER;
    PAGEITEM* paPageList;
    ULONG ulToRead, ulReadIn;
    LONG lNewArraySize;
    PAGELISTPROPS NewProps;

    if (NULL != pIStream)
    {
      // We have the PageList data stream. First read the
      // PageList Properties.
      ulToRead = sizeof(PAGELISTPROPS);
      hr = pIStream->Read(
                       &NewProps,
                       ulToRead,
                       &ulReadIn);
      if (SUCCEEDED(hr) && ulToRead != ulReadIn)
        hr = E_FAIL;
      if (SUCCEEDED(hr))
      {
        // Deal with the different PageList versions.
        switch (NewProps.lPageListVersion)
        {
          case PAGELIST_VERSION10:
            // Allocate a page list array big enough--add some extra
            // for later expansion by user.
            lNewArraySize = NewProps.lPageListSize + PAGELIST_ALLOC;
            paPageList = new PAGEITEM[(LONG) lNewArraySize];
            if (NULL != paPageList)
            {
              // Delete the entire old Page List array.
              if (NULL != m_pCO->m_paPageList)
                delete [] m_pCO->m_paPageList;

              // Assign the new array.
              m_pCO->m_paPageList = paPageList;
              m_pCO->m_lPageListMax = lNewArraySize;

              // Now read the complete array of Page List items.
              ulToRead = NewProps.lPageListSize * sizeof(PAGEITEM);
              hr = pIStream->Read(m_pCO->m_paPageList, ulToRead, &ulReadIn);
              if (SUCCEEDED(hr) && ulToRead != ulReadIn)
                hr = E_FAIL;
              if (SUCCEEDED(hr))
              {
                // Rig COPageList to use the PAGELISTPROPS info.
                m_pCO->m_lPageListEnd = NewProps.lPageListSize - 1;

                // Copy the new properties into current properties.
                memcpy(
                  &m_pCO->m_PageListProps,
                  &NewProps,
                  sizeof(PAGELISTPROPS));
              }
            }
            else
              hr = E_OUTOFMEMORY;
            break;
          default:
            hr = E_FAIL;  // Bad version.
            break;
        }
      }
    }

    // Notify all other connected clients that PageList is now loaded.
    // If we didn't load then clear to a safe, empty Page List array.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(PAGELIST_EVENT_LOADED,0);
    else
      m_pCO->Clear();

    return hr;
  }

Load is called by a client when this COPageList object already has a persistent state stored in a stream. A complete copy of that state data is read from the stream into newly allocated, RAM-resident space for the data. Two kinds of state data are concatenated in the stream: a group of object properties followed by the page list array. The Load method first reads the PAGELISTPROPS properties structure into a temporary NewProps. If the load is successful, these properties are copied to COPageList::m_PageListProps. This is done to allow reverting to the current COPageList data if the entire load of new data is not safely completed.

After the m_PageListProps are read, the lPageListVersion property is used in a switch statement. This allows for different load behavior or data formats based on the version of the data that was stored. Because the version number is the first thing in the stream's object data, it can be read independently to determine how to deal with the data that follows. The lPageListSize property is used to allocate a new array in RAM that is big enough for the array data stored in the stream. After the array is allocated, the page list data is read into that array from the stream. At this point COPageList has been loaded from persistent storage.

If the load is successful, a call to the internal NotifySinks method notifies all connected clients that the load was completed. This notification to the client usually triggers an appropriate display of the newly loaded data. This notification makes use of COPageList's connectable object features. If the load was unsuccessful, a call to the internal Clear method clears the page list to a safe, empty list.

Clients call the Save method to command COPageList to save its persistent data from RAM to a specified stream in a compound file starting at the current seek pointer offset. As with the Load method, the client supplies the stream interface to Save. On exit from this method the seek pointer is assumed to be at the end of the data saved. Again, as with the Load method, the rules governing the seek pointer in the Load and Save methods allow a series of contiguous persistent objects to be saved into the same stream.

The Save method implementation should not store a copy of the passed pIStream pointer within COPageList. The passed stream interface pointer should be used only within this method call. The following is the implementation of Save from PAGELIST.CPP:

  STDMETHODIMP COPageList::CImpIPersistStream::Save(
                 IStream* pIStream,
                 BOOL bClearDirty)
  {
    HRESULT hr = E_POINTER;
    ULONG ulToWrite, ulWritten;

    if (NULL != pIStream)
    {
      // Got a stream. Now write data into it.
      // First write PAGELISTPROPS structure.
      m_pCO->m_PageListProps.lPageListSize = m_pCO->m_lPageListEnd + 1;
      ulToWrite = sizeof(PAGELISTPROPS);
      hr = pIStream->Write(&m_pCO->m_PageListProps, ulToWrite, &ulWritten);
      if (SUCCEEDED(hr) && ulToWrite != ulWritten)
        hr = STG_E_CANTSAVE;
      if (SUCCEEDED(hr))
      {
        // Now write the complete array of Page List Data.
        ulToWrite = m_pCO->m_PageListProps.lPageListSize * sizeof(PAGEITEM);
        hr = pIStream->Write(m_pCO->m_paPageList, ulToWrite, &ulWritten);
        if (SUCCEEDED(hr) && ulToWrite != ulWritten)
          hr = STG_E_CANTSAVE;
        if (SUCCEEDED(hr))
        {
          // Clear this COM object's dirty flag if instructed. Otherwise,
          // preserve its content.
          if (bClearDirty)
            m_pCO->m_bDirty = FALSE;
        }
      }
    }

    // Notify all other connected clients that PageList is now saved.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(PAGELIST_EVENT_SAVED,-1);

    return hr;
  }

The Save method is essentially the reverse of the Load method. The content of COPageList's m_PageListProps structure is written into the specified stream at the stream's current seek pointer offset. This structure is essentially a properties header at the front of the state data. If the PAGELISTPROPS data is successfully saved, the current array of page list data (located in RAM at m_paPageList) is written to the stream. The seek pointer is left pointing just past the end of the saved array. In either of these data writes to the stream, if the amount of data actually written does not equal the amount attempted, the Save method is aborted and returns STG_E_CANTSAVE.

A bClearDirty parameter determines if this method should clear COPageList's m_bDirty flag. If bClearDirty is TRUE then COPageList::m_bDirty is set to FALSE (that is, cleared). Usually, a Save will achieve a match between the RAM-resident data and its corresponding image stored in the stream. However, there may be cases when you want to issue a save to a stream while not clearing COPageList's dirty flag. This might occur if the client is saving a copy of the data state to a stream that is different than the current unsaved stream. If COPageList is not saved to the original unsaved stream, the state of m_bDirty would need to be preserved across the operation. A value of FALSE for the bClearDirty parameter accomplishes this preservation.

If the save is successful, a call to the internal NotifySinks method notifies all connected clients that the save was completed.

Clients call the GetSizeMax method to obtain the maximum size of the state data that can be saved when the Save method is next called. The following is the implementation of GetSizeMax from PAGELIST.CPP:

  STDMETHODIMP COPageList::CImpIPersistStream::GetSizeMax(
                 ULARGE_INTEGER* pcbSize)
  {
    HRESULT hr = E_POINTER;
    DWORD dwProps, dwData;

    if (NULL != pcbSize)
    {
      dwProps = sizeof(PAGELISTPROPS);
      dwData = (m_pCO->m_lPageListEnd+1) * sizeof(PAGEITEM);
      ULISet32(*pcbSize, dwProps+dwData);
      hr = NOERROR;
    }

    return hr;
  }

The caller passes the address of a ULARGE_INTEGER variable that the GetSizeMax method assigns. The client can use this to set the size of a stream using IStream::SetSize, which may be necessary if the client is saving multiple objects in the same stream. If known in advance within COPageList, GetSizeMax should return the maximum potential size that a future Save would consume in the stream.

COPageList exposes the IConnectionPointContainer interface so clients can connect to COPageList in order to receive notifications of specified events that occur in COPageList. Exposing this interface makes COPageList a connectable object. A client can call QueryInterface for this interface and use it to obtain the object's connection points.

Basically, the client implements what is called a sink which is sink object with a sink interface. The sink interface receives outgoing event notification calls from COPageList after the sink is properly connected by the client to a COPageList instance. The client makes the connection by using a connection point object that is managed by COPageList. There can be numerous connection points on a single connectable COM object. In the PERSERVE sample, COPageList has only one connection point, which handles page list events. For more details about how clients participate, see the PERCLIEN sample.

Any number of clients can connect to a single connection point. The CONNPOINT_PAGELISTSINK connection point in COPageList maintains a group of connections that can grow dynamically at run time. The full implementation of COPageList's connectable object support is coded in files CONNECT.H and CONNECT.CPP and will not be covered here. The construction is very similar to that in the CONSERVE code sample.

The PERCLIEN client implements appropriate sink objects for the connection points it expects to find in COPageList. From the context of COPageList, the sink object that PERCLIEN implements exposes the IPageListSink interface. This is the outgoing interface used by COPageList to notify PERCLIEN of various events in COPageList. Here is a summary of the methods in IPageListSink from IPAGES.H in the common INC directory.

  Loaded
    The PageList was loaded from persistent storage.

  Saved
    The PageList was saved to persistent storage.

  Cleared
    The entire PageList was cleared.

  PageAdded(INT iPage);
    A new PageItem was added to the PageList.

  PageDeleted(INT iPage);
    A PageItem was deleted from the PageList.

  PageSet(INT iPage);
    A PageItem entry was set with new data.

These methods are largely self-explanatory. Although the sink must implement all these methods in some fashion, many are implemented as stubs and are not used in the PERSERVE or PERCLIEN samples.

An important method used in these samples is the Loaded method. When the client tells COPageList to load a new page list from file, COPageList needs a way to notify the client when the new data is loaded. To do this, COPageList calls IPageListSink::Loaded on the client sink. The client is thereby signaled that it can display the newly loaded data.

Top Back to page top

© 1995-1998 Microsoft Corporation


PERTEXT - IPersistStreamInit Persistent Object Server - Lesson 26

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

PERTEXT - IPersistStreamInit Persistent Object Server

SUMMARY

PERTEXT is the second sample in a group of samples that covers various techniques for implementing COM object persistence. The first sample, PERSERVE, covers persistence by implementing the IPersistStream standard interface. This sample, PERTEXT, covers persistence by implementing the IPersistStreamInit standard interface. The third sample, PERDRAW, covers persistence by implementing the IPersistStorage standard interface. The fourth sample, PERCLIEN, covers how a client exploits the kinds of persistence provided the PERSERVE, PERTEXT, and PERDRAW server components.

The PERTEXT sample introduces the COTextPage COM object, which encapsulates the data of an editable page of text. COTextPage objects expose a set of interfaces whose methods make the objects connectable, persistent managers of text data. Client access to this data is available through an ITextPage custom interface. COTextPage implements the ITextPage interface.

The PERTEXT sample keeps a clear architectural distinction between client and server. Among other things, COTextPage provides no graphical user interface (GUI), relying instead upon the client for all GUI behavior.

The PERCLIEN client provides the GUI display and manages the list of pages, the contents of which are stored in a compound file that contains both the contents of the list and of each page in that list. The user of PERCLIEN can edit the content of two types of pages: text pages and drawing pages. Text pages have data that the user can edit using a simple windowed text editor. Drawing pages have drawing data that the user can edit using free-form, scribble-like functionality based on the earlier STOSERVE and STOCLIEN samples. Both types of editing are done in separate client windows. For more information, see PERCLIEN.HTM.

Storage in the compound file is achieved because the components provide persistent COM objects that encapsulate the page list and edited page data. PERSERVE houses a persistent object that encapsulates the single page list kept in each compound file containing such pages. PERTEXT houses a persistent object that encapsulates the edited text data for each text page. PERDRAW houses a persistent object that encapsulates the drawing data for each drawing page.

The COPageList object in the PERSERVE sample encapsulates the persistent page list data. COPageList implements the IPersistStream standard interface to expose control of the page list storage located in the client-provided stream of a compound file. The COTextPage object in this PERTEXT sample encapsulates the data of an edited text page. COTextPage implements the IPersistStreamInit standard interface to expose control of the text data storage that is located in the client-provided stream of a compound file. In contrast to these stream-based persistent objects, the CODrawPage object in the PERDRAW sample encapsulates the persistent drawing-ink data. CODrawPage implements the IPersistStorage standard interface to expose control of the drawing-ink data storage located in the client-provided substorage of a compound file.

This code sample focuses primarily on COTextPage's implementation of the IPersistStreamInit interface to provide stream-based persistence for a COM object. IPersistStreamInit's InitNew method is the main difference between the IPersistStream interface used in the PERSERVE sample and the IPersistStreamInit interface used in the PERTEXT sample. This sample covers the InitNew method in detail. PERTEXT works with the PERCLIEN code sample to illustrate the joint use by client and server of IPersistStreamInit-based persistence.

COTextPage's support for object persistence is the primary means of storing the text page data. COTextPage stores its text data in a client-provided stream located in a structured storage compound file. The compound file has a unique format because of the various streams and storages used. The client identifies these compound files as page files with a .PAG extension. The client controls the use of the containing compound file and provides COTextPage with a pointer to the IStream interface to load and save its text data in the compound file. The IStream pointer is passed to COTextPage in calls to the IPersistStreamInit interface methods.

COTextPage also exposes an ITextPage custom interface to manipulate the text that is encapsulated in the text page. ITextPage exposes the GetLength, GetText, PutText, and Clear methods.

COTextPage also supports connectable object features. The IConnectionPointContainer interface is exposed, an appropriate connection point is implemented, and an outgoing custom ITextPageSink interface is declared to send notifications to the client.

The two ITextPage and ITextPageSink custom interfaces are declared in IPAGES.H located in the common INC directory. PAGEGUID.H, which contains the GUIDs for the interfaces and objects, is also located in that same directory.

The PERTEXT sample uses the CThreaded facility in APPUTIL to achieve thread safety in the server housing and the class factory. Because the PERTEXT.DLL is generally accessed from a Single Threaded Apartment (STA) as an in-process server, COTextPage instances are not coded as thread-safe using the CThreaded facility. The CLSID_TextPage component is registered as supporting the apartment threading model.

For functional descriptions and a tutorial code tour of the PERTEXT sample, see the Code Tour section in PERTEXT.HTM. For details on setting up the programmatic usage of PERTEXT.DLL, see the Usage section in PERTEXT.HTM. To read PERTEXT.HTM, run TUTORIAL.EXE in the main tutorial directory and click the PERTEXT lesson in the table of lessons. You can also do the same thing by double-clicking the PERTEXT.HTM file after locating the main tutorial directory in Windows Explorer. For more details on the PERCLIEN client application and how it works with PERTEXT.DLL, see also PERCLIEN.HTM in the main tutorial directory. You must build PERTEXT.DLL before running the PERCLIEN sample.

The PERTEXT server provides a TextPage component that can create instances of the COTextPage COM object. COTextPage is housed in the PERTEXT.DLL in-process server and is made publicly available as a custom COM component. Like all other servers in this tutorial series, PERTEXT.DLL is a self-registering COM server. It makes the COTextPage object type available to clients as the TextPage component in the PERTEXT server using a CLSID_TextPage registration in the Registry.

PERTEXT's makefile automatically registers its TextPage COM component in the registry, which it must do before clients can use PERTEXT.DLL as a server for the TextPage component. This self-registration is started in the makefile using the REGISTER.EXE utility built in the REGISTER sample. To build or run PERTEXT.DLL, you must build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the PERTEXT sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV PERTEXT.DSP

You can also simply double-click the PERTEXT.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use PERTEXT.DLL, a client program does not need to include PERTEXT.H or link to PERTEXT.LIB. A COM client of PERTEXT.DLL obtains access solely through its object's CLSID and COM services. For PERTEXT.DLL, that CLSID is CLSID_TextPage (defined in PAGEGUID.H in the INC sibling directory). The PERCLIEN code sample shows how the client obtains this access.

PERTEXT.DLL is intended primarily as a COM server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within COM's CoGetClassObject function. PERTEXT.DLL is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the PERTEXT directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the PERTEXT directory.

  ..\register\register.exe pertext.dll

These registration commands require a prior build of both the REGISTER sample and PERTEXT.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Microsoft Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, PERCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
PERTEXT.TXT   Short description of sample.
MAKEFILE      The generic makefile for building the PERTEXT.DLL
              code sample of this lesson.
PERTEXT.H     The include file for declaring as imported or defining as
              exported the service functions in PERTEXT.DLL.
PERTEXT.CPP   The main implementation file for PERTEXT.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
PERTEXT.DEF   The module definition file. Exports server housing functions.
PERTEXT.RC    The DLL resource definition file for the executable.
PERTEXT.ICO   The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control C++ object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerators objects.
TEXTPAGE.H    The include file for the COTextPage COM object class.
TEXTPAGE.CPP  The implementation file for the COTextPage COM object class
              and the connection points.
PERTEXT.DSP   Microsoft Visual Studio Project file.

PERTEXT.DLL uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial code samples and assumes that you have had some exposure to those samples. It does not revisit basic interface implementation techniques, COM object construction, in-process server construction, class factory construction, connectable object construction, or use of structured storage in compound files. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • How PERTEXT works with connected clients;
  • COTextPage's use of the ITextPage interface for managing text pages.
  • The data constructs that make up COTextPage's persistent data.
  • COTextPage's implementation of the IPersistStreamInit interface to provide persistence.
  • COTextPage's use of the ITextPageSink interface for client notifications.

The COTextPage COM object is the single object type managed by this PERTEXT in-process server. COTextPage is a connectable COM object that implements the IConnectionPointContainer standard interface, the IPersistStreamInit standard interface, and the ITextPage custom interface.

COTextPage exposes the ITextPage custom interface so clients can perform a small set of operations on the text data that is encapsulated by COTextPage. Here is a summary of the ITextPage methods from IPAGES.H located in the common INC directory.

  GetLength
    Get the current length (in WCHARs) of the text page.

  GetText
    Get the current page text held in this object.

  PutText
    Put specified wide character text of specified length
    into this text page object. Does not save persistently.

  Clear
    Clears all text from the entire text page.

The implementation of ITextPage is straightforward: a set of properties for the text page is maintained and the text data itself is maintained. For more details, see TEXTPAGE.H and TEXTPAGE.CPP.

COTextPage manages its text data in RAM. However, since COTextPage is also a persistent object, it also manages the persistent image of this text data in a stream of a structured storage compound file. COTextPage exposes the IPersistStreamInit interface so that clients can control the perisitence features of COTextPage. The implementation of IPersistStreamInit in COTextPage is the principal focus of this sample.

The client is responsible for a presentation of the text to the user. In PERCLIEN a standard multiline edit control in a separate window is used. The client uses the ITextPage interface on COTextPage to obtain the data for display in this edit control and to update COTextPage with any user-requested changes to the text data. The client cooperates with COTextPage to manage the text data. The client manages creation or opening of a stream in the compound file containing the data. When the client needs to control the persistence of COTextPage it passes an IStream interface to COTextPage, usually during calls to the IPersistStreamInit Load and Save methods. COTextPage then uses that stream for all its persistent storage of its object state data.

Two major data constructs are stored for COTextPage's state data: the page text and the page text properties. The text data is stored in the page's data stream as a wide character string. The persistent properties are stored in the same stream. Here is the TEXTPROPS structure from TEXTPAGE.H.

  // Properties of the TextPage.
  typedef struct _TEXTPROPS
  {
    ULONG ulVersion;
    ULONG ulMaxLength;
    ULONG ulLength;
    WCHAR wszTitle[PAGE_TITLE_SIZE];
  } TEXTPROPS;

The ulVersion member holds a version number for the text page. This allows the first member to be checked to determine possible variations in treatment based on the text page version.

The ulMaxLength member holds the maximum length that the text page can grow.

The ulLength member holds the current length of the page text.

The wszTitle member is for future evolution of the application and are not used by PERTEXT or PERCLIEN.

In the division of labor between client and server, COTextPage does not create the compound file that is used to store text page data. To both Save and Load the persistent data the client uses the Save and Load methods of the IPersistStreamInit interface, passing an IStream interface pointer for a stream located in an existing compound file. COTextPage then uses the IStream interface to read and write data in the compound file.

The following code is the declaration of the COTextPage COM object from TEXTPAGE.H:

  class COTextPage : public IUnknown
  {
    public:
      // Main COM Object Constructor & Destructor.
      COTextPage(IUnknown* pUnkOuter, CServer* pServer);
      ~COTextPage(void);

      // A general public method for initializing this newly created
      // object. Creates any subordinate arrays, structures, or objects.
      // Not exposed as part of an interface. Used by Class Factory.
      HRESULT Init(void);

      // Main COM Object IUnknown interface. Non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // Standard Connectable Object features.
      class CImpIConnectionPointContainer : public IConnectionPointContainer
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIConnectionPointContainer(COTextPage* pCO, IUnknown* pUnkOuter);
          ~CImpIConnectionPointContainer(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IConnectionPointContainer methods.
          STDMETHODIMP         FindConnectionPoint(REFIID, IConnectionPoint**);
          STDMETHODIMP         EnumConnectionPoints(IEnumConnectionPoints**);

        private:
          // Data private to this interface implementation.
          COTextPage*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Standard Object Persistence (in Streams) features.
      class CImpIPersistStreamInit : public IPersistStreamInit
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIPersistStreamInit(COTextPage* pCO, IUnknown* pUnkOuter);
          ~CImpIPersistStreamInit(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IPersistStreamInit methods.
          STDMETHODIMP         GetClassID(CLSID* pClassID);
          STDMETHODIMP         IsDirty(void);
          STDMETHODIMP         Load(IStream* pIStream);
          STDMETHODIMP         Save(IStream* pIStream, BOOL bClearDirty);
          STDMETHODIMP         GetSizeMax(ULARGE_INTEGER* pcbSize);
          STDMETHODIMP         InitNew(void);

        private:
          // Data private to this interface implementation.
          COTextPage*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Custom Text Entry/Edit page features.
      class CImpITextPage : public ITextPage
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpITextPage(COTextPage* pCO, IUnknown* pUnkOuter);
          ~CImpITextPage(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // ITextPage methods.
          STDMETHODIMP         GetLength(INT* piLength);
          STDMETHODIMP         GetText(WCHAR* pwszText);
          STDMETHODIMP         PutText(WCHAR* pwszText, INT iLength);
          STDMETHODIMP         Clear(BOOL bSaveNeeded);

        private:
          // Data private to this interface implementation of ITextPage.
          COTextPage*   m_pCO;          // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested interface implementations
      // friends to instantiations of this COTextPage COM object class.
      friend CImpIConnectionPointContainer;
      friend CImpIPersistStreamInit;
      friend CImpITextPage;

      // Private Methods of COTextPage COM objects.

      // Method to clear the COTextPage's current page text and reset
      // the page properties appropriately.
      HRESULT Clear(BOOL bSaveNeeded);

      // Method of main connectable COTextPage COM object to broadcast
      // event notifications to all connected listening sinks.
      HRESULT NotifySinks(TEXTPAGE_EVENT TextPageEvent);

      // Private Data of COTextPage COM objects.

      // Nested IConnectionPointContainer implementation instantiation.
      CImpIConnectionPointContainer m_ImpIConnectionPointContainer;

      // Nested IPersistStreamInit implementation instantiation.
      CImpIPersistStreamInit m_ImpIPersistStreamInit;

      // Nested ITextPage implementation instantiation. This ITextPage
      // interface is instantiated as a native interface of COTextPage.
      CImpITextPage     m_ImpITextPage;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;

      // The array of connection points for this connectable COM object.
      IConnectionPoint* m_aConnectionPoints[MAX_CONNECTION_POINTS];

      // The following private data and methods constitute the working
      // heart of COTextPage as an actual application object.
      TEXTPROPS         m_TextProps;    // For saving properties in stream.
      CLSID             m_ClassID;      // CLSID of this COM Object.
      WCHAR*            m_pwszPageText; // The text to edit.
      BOOL              m_bInitNew;     // TRUE=>obj newly initialized in RAM.
      BOOL              m_bDirty;       // RAM no match file--save needed.
  };

COTextPage has nested implementations of the IUnknown, IConnectionPointContainer, IPersistStreamInit, and ITextPage interfaces. The techniques of these interface implementations follow the pattern used in earlier samples. The COMOBJ sample, for example, implemented IUnknown and the CONSERVE sample implemented IConnectionPointContainer. This samples introduces the ITextPage custom interface, but its implementation is very similar to the custom interface implementations in many previous samples.

The m_TextProps member is a TEXTPROPS structure that holds properties of the text page. See above for a summary of the TEXTPROPS structure. The m_ClassID stores the CLSID of the COTextPage component for later use within the method implementations of COTextPage. The m_bInitNew member is indicates whether the object was newly initialized by the IPersistStreamInit::InitNew method. The m_bDirty member is used internally to determine when the RAM-resident text data has changed relative to the last time it was loaded from or saved to persistent storage.

COTextPage keeps the text data in a RAM-resident string the pointer to which is kept in the m_pwszPageText variable. The string is initially allocated during either IPersistStreamInit::Load or IPersistStreamInit::InitNew after COTextPage is created. The following paragraphs discuss IPersistStreamInit methods in more detail.

The following IPersistStreamInit methods are declared:

    STDMETHODIMP         GetClassID(CLSID* pClassID);
    STDMETHODIMP         IsDirty(void);
    STDMETHODIMP         Load(IStream* pIStream);
    STDMETHODIMP         Save(IStream* pIStream, BOOL bClearDirty);
    STDMETHODIMP         GetSizeMax(ULARGE_INTEGER* pcbSize);
    STDMETHODIMP         InitNew(void);

Examining these methods in detail shows how the implementation of the standard IPersistStreamInit interface provides persistence for the text and property data in COTextPage. If you have not used streams (IStream) or storages (IStorage) in structured storage compound files, see the STOSERVE and STOCLIEN samples for coverage of this topic.

Clients call the GetClassID method to obtain the component CLSID of COTextPage. This method is actually inherited from the IPersist interface from which IPersistStreamInit is derived. The following code is the implementation of GetClassID from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::GetClassID(
                 CLSID* pClassID)
  {
    HRESULT hr = E_POINTER;

    if (NULL != pClassID)
    {
      // Use overloaded '=' operator to copy the Class ID to caller.
      *pClassID = m_pCO->m_ClassID;
      hr = NOERROR;
    }

    return hr;
  }

This simple method uses the overloaded '=' operator to copy COTextPage's main copy of the CLSID to the client's address space using the specified pClassID pointer. The convenient overloading of the '=' operator is provided in the COM and OLE header files that are included at the front of TEXTPAGE.CPP. The COTextPage constructor initially assigned the m_ClassID value to the value CLSID_TextPage, which is defined in the PAGEGUID.H file located in the common INC directory. PAGEGUID.H contains other GUIDs--such as interface IIDs--that are used in the PERTEXT, PERTEXT, PERDRAW, and PERCLIEN samples.

Clients call the IsDirty method to determine if changes have been made to this COTextPage's persistent data since it was last loaded, initialized, or saved. The following code is the implementation of IsDirty from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::IsDirty(
                 void)
  {
    HRESULT hr;

    hr = m_pCO->m_bDirty ? S_OK : S_FALSE;

    return hr;
  }

This method simply returns the current "dirty" status of the RAM-resident data in terms of the standard HRESULT return codes of S_OK or S_FALSE. S_OK means the data needs saving; S_FALSE means the data matches its counterpart in persistent storage.

The Load method implementation should not store a copy of the passed pIStream pointer within COTextPage. The stack copy of the passed stream interface pointer should be used only within this method call. The following code is the implementation of Load from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::Load(
                 IStream* pIStream)
  {
    HRESULT hr = E_POINTER;
    ULONG ulToRead, ulReadIn;
    TEXTPROPS NewProps;
    WCHAR* pwszPageText;

    if (NULL != pIStream)
    {
      if (!m_pCO->m_bInitNew)
      {
        // We have the TextPage data stream. First read the TextPage
        // Properties.
        ulToRead = sizeof(TEXTPROPS);
        hr = pIStream->Read(
                         &NewProps,
                         ulToRead,
                         &ulReadIn);
        if (SUCCEEDED(hr) && ulToRead != ulReadIn)
          hr = E_FAIL;
        if (SUCCEEDED(hr))
        {
          // Deal with the different versions.
          switch (NewProps.ulVersion)
          {
            case TEXTPAGE_VERSION10:
              if (NewProps.ulMaxLength <= TEXTPAGE_V10_MAX)
              {
                // Allocate an page text array big enough for the largest
                // text page.
                pwszPageText = new WCHAR[(ULONG) NewProps.ulMaxLength];
                if (NULL != pwszPageText)
                {
                  m_pCO->m_pwszPageText = pwszPageText;
                  // First zero the page text area.
                  memset(
                    pwszPageText,
                    0,
                    NewProps.ulMaxLength * sizeof(WCHAR));
                  ulToRead = NewProps.ulLength * sizeof(WCHAR);
                  if (ulToRead >= 0 && ulToRead < (ULONG)NewProps.ulMaxLength)
                  {
                    // Now read in the complete text page.
                    hr = pIStream->Read(
                           pwszPageText,
                           ulToRead,
                           &ulReadIn);
                    if (SUCCEEDED(hr) && ulToRead != ulReadIn)
                      hr = E_FAIL;
                    if (SUCCEEDED(hr))
                    {
                      // Copy the new properties into current properties.
                      memcpy(
                        &m_pCO->m_TextProps,
                        &NewProps,
                        sizeof(TEXTPROPS));

                      // We are loaded and clean (ie, COTextPage data
                      // matches file data). Clear dirty flag.
                      m_pCO->m_bDirty = FALSE;
                    }
                  }
                  else
                    hr = E_FAIL;
                }
                else
                  hr = E_OUTOFMEMORY;
              }
              break;
            default:
              hr = E_FAIL;  // Bad version.
              break;
          }
        }
      }
      else
        hr = E_UNEXPECTED;
    }

    // Notify all other connected clients that TextPage is now loaded.
    // If we didn't load then clear to a safe, empty text page.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(TEXTPAGE_EVENT_LOADED);
    else
      m_pCO->Clear(TRUE);

    return hr;
  }

The Load method is called instead of the InitNew method when the COTextPage COM object already has a persistent state stored in a stream. A complete copy of that state data (a group of object properties followed by the text data) is read from the stream into newly allocated, RAM-resident space for the data. Load first reads a copy of the TEXTPROPS properties structure into a temporary NewProps structure. If Load returns successfully, these properties are copied to COTextPage::m_TextProps. This is done to allow reverting to the current COTextPage data if Load fails.

After the m_TextProps are read, the ulVersion property is used in a switch statement which allows for different load behavior or data formats based on the version of the data that was stored. Because the version number is the first thing in stream's data for the object, it can be separately read to determine how to subsequently read and deal with the data that follows. The ulMaxLength property is used to allocate a new string character array in RAM that is big enough for the largest text page permitted. The string is allocated as an array of wide characters (WCHAR) to accomodate Unicode strings. After the string is allocated, the text data is read into that string from the stream. At this point COTextPage has been loaded from persistent storage, and because this has caused the in-RAM data to match the file data, the m_bDirty flag is set to FALSE. This means that a save is currently not needed to achieve such a match.

If the load is successful, a call to the internal NotifySinks method notifies all connected clients that the load was completed. This notification makes use of COTextPage's connectable object features, and usually triggers an appropriate display of the newly loaded data. If the load was unsuccessful, a call to the internal Clear method clears the text page to a safe, empty page.

Clients call the Save method to command COTextPage to save its persistent data from RAM to a specified stream in a compound file starting at the current seek-pointer offset. As with the Load method, the client passes an IStream pointer Save. On exit from this method the seek pointer is assumed to be at the end of the data saved. As was discussed above for the Load method, the rules governing the seek pointer in the Load and Save methods allow a series of contiguous persistent objects to be saved into the same stream.

The Save method, like Load, should not store a copy of the passed pIStream pointer within COTextPage. The stack copy of the passed stream interface pointer should be used only within this method call. The following code is the implementation of Save from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::Save(
                 IStream* pIStream,
                 BOOL bClearDirty)
  {
    HRESULT hr = E_POINTER;
    ULONG ulToWrite, ulWritten;

    if (NULL != pIStream)
    {
      // Got a stream. Now write data into it.

      // First write TEXTPROPS structure.
      ulToWrite = sizeof(TEXTPROPS);
      hr = pIStream->Write(&m_pCO->m_TextProps, ulToWrite, &ulWritten);
      if (SUCCEEDED(hr) && ulToWrite != ulWritten)
        hr = STG_E_CANTSAVE;
      if (SUCCEEDED(hr))
      {
        // Now write the complete page text data.
        ulToWrite = m_pCO->m_TextProps.ulLength * sizeof(WCHAR);
        hr = pIStream->Write(m_pCO->m_pwszPageText, ulToWrite, &ulWritten);
        if (SUCCEEDED(hr) && ulToWrite != ulWritten)
          hr = STG_E_CANTSAVE;
        if (SUCCEEDED(hr))
        {
          // Clear this COM object's dirty flag if instructed.
          if (bClearDirty)
            m_pCO->m_bDirty = FALSE;
        }
      }
    }

    // Notify all other connected clients that TextPage is now saved.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(TEXTPAGE_EVENT_SAVED);

    return hr;
  }

The Save method is essentially the reverse of the Load method. The content of COTextPage's m_TextProps structure is written into the specified stream at the stream's current seek offset. This structure is essentially a properties header at the front of the state data. If the TEXTPROPS data is saved successfully, the current text string data (located in RAM at m_pwszPageText) is written to the stream. The seek pointer is left pointing just past the end of the saved text. In either of these data writes to the stream, if the amount of data actually written does not equal the amount attempted, the Save method fails and returns STG_E_CANTSAVE.

A bClearDirty parameter determines if this method should clear COTextPage's m_bDirty flag. If bClearDirty is TRUE then COTextPage::m_bDirty is set to FALSE (that is, cleared). Usually, with a Save call the RAM-resident data and its corresponding image stored in the stream will match. However, there may be cases when you want to save to a stream without clearing COTextPage's dirty flag. This might occur if the client is saving a copy of the data state to a stream that is different than the current unsaved stream. If COTextPage is not saved to the original unsaved stream, the state of m_bDirty will need to be preserved across the operation. A value of FALSE for the bClearDirty parameter accomplishes this preservation.

If the save is successful, a call to the internal NotifySinks method notifies all connected clients that the save was completed.

Clients call the GetSizeMax method to obtain the maximum size of the object state data that can be saved when the Save method is next called. The following code is the implementation of GetSizeMax from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::GetSizeMax(
                 ULARGE_INTEGER* pcbSize)
  {
    HRESULT hr = E_POINTER;
    ULONG ulMax = m_pCO->m_TextProps.ulMaxLength;

    if (NULL != pcbSize)
    {
      ULISet32(*pcbSize, sizeof(TEXTPROPS) + (ulMax * sizeof(WCHAR)));
      hr = NOERROR;
    }

    return hr;
  }

The caller passes the address of a ULARGE_INTEGER variable that the GetSizeMax method assigns. The ULISet32 macro (defined in OBJBASE.H) performs the assignment of the caller's ULARGE_INTEGER variable. Clients can use this assigned variable to set the size of a stream using IStream::SetSize. This may be necessary if the client is saving multiple objects in the same stream. If it is known in advance within COTextPage, GetSizeMax should return the maximum potential size that a future Save would consume in the stream.

Clients call the InitNew method to instruct COTextPage to initialize its object state data in RAM for the first time. InitNew is called instead of Load when the newly created object instance must be initialized with new data rather than with persistent data previously saved in a stream. The following is the implementation of InitNew from TEXTPAGE.CPP:

  STDMETHODIMP COTextPage::CImpIPersistStreamInit::InitNew(
                 void)
  {
    HRESULT hr = E_FAIL;
    WCHAR* pwszPageText;

    if (!m_pCO->m_bInitNew)
    {
      // There is no previous persistent data for this COTextPage object.
      // Create and init a new persistence data for an empty text page.

      // Allocate an page text array big enough for the largest text page.
      // This maximum is hard-coded here for this Version 1.0 of Text Pages.
      pwszPageText = new WCHAR[(LONG) TEXTPAGE_V10_MAX];
      if (NULL != pwszPageText)
      {
        m_pCO->m_pwszPageText = pwszPageText;
        // Use the private Clear method to clear the new text array and to
        // set the Page Properties structure appropriately. Also sets dirty.
        hr = m_pCO->Clear(TRUE);
        m_pCO->m_bInitNew = TRUE;
      }
      else
        hr = E_OUTOFMEMORY;
    }
    else
      hr = E_UNEXPECTED;

    return hr;
  }

You must call either InitNew or Load (but not both) to command COTextPage to assign its state data. COTextPage's m_bInitNew flag is used to enforce this rule. If you call InitNew when m_bInitNew is TRUE then the method will fail and return the E_UNEXPECTED error. The Load method uses the same logic to return the E_UNEXPECTED error if m_bInitNew is TRUE. When COTextPage is created it assigns m_bInitNew to FALSE in the constructor. During InitNew, if initialization is successful, it sets m_bInitNew to TRUE. By doing this, any subsequent calls to Load (or to InitNew again) fail by returning E_UNEXPECTED.

If InitNew successfully creates a new text string array for COTextPage, COTextPage's internal Clear method is used to fill the string array with zeroes, initialize appropriate constants to the members of m_TextProps, and initialize the m_bDirty flag. The dirty flag is assigned to TRUE because a later save to file is needed for this persistent COTextPage object.

COTextPage exposes the IConnectionPointContainer interface so clients can connect to COTextPage and receive notifications of certain events that occur in COTextPage. By exposing this interface, COTextPage is seen by clients as a connectable object. A client can call QueryInterface for this interface and use it to obtain the object's connection points.

The client implements what is called a sink in the form of a sink object with a sink interface. The sink interface receives outgoing event notification calls from COTextPage after the sink is properly connected by the client to a COTextPage instance. The client makes the connection by using a connection point object that is managed by COTextPage. (For more detailed information on the client participation in this scheme, see the PERCLIEN sample.) There can be numerous connection points on a single connectable COM object, but in the PERTEXT sample, COTextPage has only one connection point to handle text page events.

Any number of clients can connect to a single connection point. The CONNPOINT_TEXTPAGESINK connection point in COTextPage maintains a group of connections that can grow dynamically at run time. The full implementation of COTextPage's connectable object support is coded in files CONNECT.H and CONNECT.CPP and will not be covered here. The construction is very similar to that in the CONSERVE code sample.

The PERCLIEN client implements appropriate sink objects for the connection points it expects to find in COTextPage. From the context of COTextPage, the sink object that PERCLIEN implements exposes the ITextPageSink interface. This is the outgoing interface used by COTextPage to notify PERCLIEN of various events in COTextPage. Here is a summary of the methods in ITextPageSink from IPAGES.H in the common INC directory.

  Loaded
    The TextPage was loaded from a persistent stream.

  Saved
    The TextPage was saved to a persistent stream.

  Put
    New text content was put into the TextPage object.

  Cleared
    The entire TextPage was cleared.

These methods are largely self-explanatory. The Loaded, Put, and Cleared methods implemented in the client sink will typically trigger a redisplay of the text page. Although the sink must implement all these methods in some fashion, many are implemented as stubs and are not used in the PERTEXT and PERCLIEN samples.

Top Back to page top

© 1995-1998 Microsoft Corporation


PERDRAW - IPersistStorage Persistent Object Server - Lesson 27

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

PERDRAW - IPersistStorage Persistent Object Server

SUMMARY

PERDRAW is the third sample in a group of samples that illustrate various techniques for implementing COM object persistence. The first sample, PERSERVE, illustrates persistence by implementing the IPersistStream standard interface. The second sample, PERTEXT, illustrates persistence by implementing the IPersistStreamInit standard interface. This sample, PERDRAW, illustrates persistence by implementing the IPersistStorage standard interface. The fourth sample, PERCLIEN, shows how a client exploits the kinds of persistence provided by the PERSERVE, PERTEXT, and PERDRAW server components.

The PERDRAW sample introduces the CODrawPage COM object, which models a sheet of white drawing paper. CODrawPage objects expose a set of features for free-form drawing on a virtual surface using "ink" of specified color and width. CODrawPage is based on the scribble-like functionality of the COPaper object from the STOSERVE sample.

CODrawPage objects expose a set of interfaces whose methods make the objects connectable, persistent managers of the drawing data. Clients access this data through an IDrawPage custom interface exposed by CODrawPage.

PERDRAW maintains a clear architectural distinction between client and server. Among other things, CODrawPage provides no graphical user interface (GUI); instead, the CODrawPage object relies on the client for all GUI behavior.

The PERCLIEN client provides the GUI display and manages a list of pages, the contents of which are stored in a compound file that contains both the contents of the list and of each page in that list. The user of PERCLIEN can edit the content of two types of pages: text pages and drawing pages. Text pages have data that the user can edit using a simple windowed text editor. Drawing pages have drawing data that the user can edit using free-form, scribble-like functionality based on the earlier STOSERVE and STOCLIEN samples. Both kinds of editing are done in separate client windows. For more details see PERCLIEN.HTM.

Storage in the compound file is achieved because the components provide persistent COM objects that encapsulate the page list and edited page data. PERSERVE houses a persistent object that encapsulates the single page list kept in each compound file containing such pages. PERTEXT houses a persistent object that encapsulates the edited text data for each text page. PERDRAW houses a persistent object that encapsulates the drawing data for each drawing page.

The COPageList object in the PERSERVE sample encapsulates the persistent page list data. COPageList implements the IPersistStream standard interface to expose control of the page list storage located in the client-provided stream of a compound file. The COTextPage object in the previous PERTEXT sample encapsulates the data of an edited text page. COTextPage implements the IPersistStreamInit standard interface to expose control of the text data storage that is located in the client-provided stream of a compound file. In contrast to these stream-based persistent objects, the CODrawPage object in this PERDRAW sample encapsulates the persistent drawing-ink data that comprises a drawing. CODrawPage implements the IPersistStorage standard interface to expose control of the drawing-ink data storage located in the client-provided substorage of a compound file.

This code sample focuses primarily on the CODrawPage implementation of the IPersistStorage interface to provide storage-based persistence for a COM object. PERDRAW works with the PERCLIEN sample to demonstrate the joint use by client and server of this IPersistStorage-based persistence.

CODrawPage's support for object persistence is the primary means of storing the ink data of the drawing page. CODrawPage stores its ink data in client-provided substorages located in a structured storage compound file. The compound file has a unique format because of the various streams and storages used. The client identifies these compound files as page files with a .PAG file extension. The client controls the use of the containing compound file and provides CODrawPage with an IStorage pointer to load and save its drawing data in the compound file. The IStorage pointer is passed to CODrawPage in calls to the IPersistStorage interface methods.

CODrawPage also exposes an IDrawPage custom interface to manipulate the drawing data that is encapsulated by the drawing page. IDrawPage exposes the InkStart, InkDraw, InkStop, Clear, Resize, and Redraw methods.

CODrawPage also supports connectable object features. It exposes the IConnectionPointContainer interface, an appropriate connection point is implemented, and an outgoing custom IDrawPageSink interface is declared to send notifications to the client.

The two IDrawPage and IDrawPageSink custom interfaces are declared in IPAGES.H, which is located in the common INC directory. PAGEGUID.H, which contains the GUID definitions for the for the interfaces and objects, is in that same directory.

The PERDRAW sample uses the CThreaded facility in APPUTIL to achieve thread safety in the server housing and the class factory. Because PERDRAW.DLL is generally accessed from a Single Threaded Apartment (STA) as an in-process server, CODrawPage instances are not coded as thread-safe using the CThreaded facility. The CLSID_DrawPage component is registered as supporting the apartment threading model.

For functional descriptions and a tutorial code tour of the PERDRAW sample, see the Code Tour section in PERDRAW.HTM. For details on setting up the programmatic usage of PERDRAW.DLL, see the Usage section in PERDRAW.HTM. To read PERDRAW.HTM, run TUTORIAL.EXE in the main tutorial directory and click the PERDRAW lesson in the table of lessons. You can do the same thing by double-clicking the PERDRAW.HTM file after locating the main tutorial directory in Windows Explorer. For more details on the PERCLIEN client application and how it works with PERDRAW.DLL, see PERCLIEN.HTM in the main tutorial directory. You must build PERDRAW.DLL before running the PERCLIEN sample.

The PERDRAW server provides a DrawPage component that can create instances of the CODrawPage COM object. CODrawPage is housed in the PERDRAW.DLL in-process server and is made publicly available as a custom COM component. Like all other servers in this tutorial series, PERDRAW.DLL is a self-registering COM server. It makes the CODrawPage object type available to clients as the DrawPage component in the PERDRAW server using a CLSID_DrawPage registration in the Registry.

PERDRAW's makefile automatically registers its DrawPage COM component in the registry, which it must do before clients can use PERDRAW.DLL as a server for the DrawPage component. This self-registration is started in the makefile using the REGISTER.EXE utility built in the REGISTER sample. To build or run PERDRAW.DLL, you must build the REGISTER code sample first.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the PERDRAW sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV PERDRAW.DSP

You can also simply double-click the PERDRAW.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

To use PERDRAW.DLL, a client program does not need to include PERDRAW.H or link to PERDRAW.LIB. A COM client of PERDRAW.DLL obtains access solely through its object's CLSID and COM services. For PERDRAW, that CLSID is CLSID_DrawPage (defined in PAGEGUID.H in the common INC directory). The PERCLIEN code sample shows how the client obtains this access.

PERDRAW.DLL is intended primarily as a COM server. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used after an explicit LoadLibrary call, usually from within COM's CoGetClassObject function. PERDRAW is a self-registering in-process server.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the PERDRAW directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the PERDRAW directory.

  ..\register\register.exe perdraw.dll

These registration commands require a prior build of both the REGISTER sample and PERDRAW.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Microsoft® Platform SDK and Visual C++® include a utility, REGSVR32.EXE, which can be used in a similar fashion to register in-process servers and marshaling DLLs.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, PERCLIEN.EXE is the client executable to run for this sample.

CODE TOUR

Files         Description
PERDRAW.TXT   Short description of the sample.
MAKEFILE      The generic makefile for building the PERDRAW.DLL
              code sample of this lesson.
PERDRAW.H     The include file for declaring as imported or defining as
              exported the service functions in PERDRAW.DLL.
PERDRAW.CPP   The main implementation file for PERDRAW.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
PERDRAW.DEF   The module definition file. Exports server housing functions.
PERDRAW.RC    The DLL resource definition file for the executable.
PERDRAW.ICO   The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control C++ object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerators objects.
DRAWPAGE.H    The include file for the CODrawPage COM object class.
DRAWPAGE.CPP  The implementation file for the CODrawPage COM object class
              and the connection points.
PERDRAW.DSP   Microsoft Visual Studio Project file.

The PERDRAW sample uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the APPUTIL directory and read the APPUTIL.HTM lesson in the main tutorial directory.

This sample is part of a graduated series of tutorial code samples and assumes that you have had some exposure to those samples. It does not revisit basic interface implementation techniques, COM object construction, in-process server construction, class factory construction, connectable object construction, or use of structured storage in compound files. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • How PERDRAW is designed to work with connected clients
  • CODrawPage's use of the IDrawPage interface for managing text pages.
  • The data constructs that make up CODrawPage's persistent data.
  • CODrawPage's implementation of the IPersistStorage interface to provide persistence.
  • CODrawPage's use of the IDrawPageSink interface for client notifications.

The CODrawPage COM object is the single object type managed by the PERDRAW in-process server. CODrawPage is constructed as a connectable COM object that implements the IConnectionPointContainer standard interface, the IPersistStorage standard interface, and the IDrawPage custom interface.

CODrawPage exposes the IDrawPage custom interface so clients can perform a set of operations on the drawing data that is encapsulated by CODrawPage. Here is a summary of the IDrawPage methods from IPAGES.H located in the common INC directory.

  InkStart
    Client starts color ink drawing to the drawing surface.

  InkDraw
    Client puts ink data points on the drawing surface.

  InkStop
    Client stops ink drawing to the drawing surface.

  Clear
    Clear the current drawing content. Notify sinks.
    Mark dirty flag if bSaveNeeded==TRUE.

  Resize
    Resize the drawing rectangle size.

  Redraw
    Redraw content of drawing object via sink notification.

The implementation of IDrawPage is straightforward. A set of properties for the drawing page and the drawing data itself are maintained. The functionality is very similar to the scribble-like functionality in the IPaper interface that is implemented in the STOSERVE and STOCLIEN samples. For more details on the implementation of IDrawPage, see DRAWPAGE.H and DRAWPAGE.CPP.

CODrawPage manages its drawing data in RAM. However, since CODrawPage is also a persistent object, it also manages the persistent representation of this drawing data in a stream of a structured storage compound file. CODrawPage exposes the IPersistStorage interface so that client's can control the persitence features of CODrawPage. This samples focuses primarily on CODrawPage's implementation of IPersistStorage.

The client is responsible for displaying the drawing to the user. The PERCLIEN sample does this using a separate drawing window. The client uses the IDrawPage interface on CODrawPage to obtain the data to display in that window and to update CODrawPage with any user-requested changes to the drawing data. The client cooperates with CODrawPage to manage the drawing data. The client manages the creation or opening of a storage in the compound file containing the data. When the client needs to control the persistence of CODrawPage it passes an IStorage interface pointer to CODrawPage, usually during such IPersistStorage method calls as InitNew, Load, and Save. CODrawPage then uses that storage to store persistently all its object state data. CODrawPage creates streams under the provided storage and uses them for the actual storage of its object state data.

CODrawPage stores two major data constructs for its object state data: the ink data which makes up the drawing and the drawing page properties. The ink data is stored in a stream created under the storage branch provided. This stream always has the name "DRAWDATA". The ink data is stored as an array of INKDATA structures. The following is the INKDATA structure from DRAWPAGE.H:

  // The Ink Data structure.
  typedef struct _INKDATA
  {
    SHORT nType;            // Ink Type.
    SHORT nX;               // X-coordinate of ink point.
    SHORT nY;               // Y-coordinate of ink point.
    SHORT nWidth;           // Ink line width.
    COLORREF crColor;       // Ink color.
  } INKDATA;

Each of these INKDATA structure elements in the array represents an ink point of specific color and width. The drawing is made up of these ink points.

The nType member holds the ink type of the point. The following ink types are defined in DRAWPAGE.H.

// The types of Ink Data.
  #define INKTYPE_NONE  0
  #define INKTYPE_START 1
  #define INKTYPE_DRAW  2
  #define INKTYPE_STOP  3

The nX and nY members specify the pixel coordinates of the ink point, the nWidth member specifies the ink line width in pixels, and the crColor member specifies the color of the ink point.

The drawing page's persistent properties are also stored in a stream created under the storage branch provided. That stream always has the name "DRAWPROPS". The following is the DRAWPROPS structure from DRAWPAGE.H:

  // Properties of our electronic DrawPage.
  typedef struct _DRAWPROPS
  {
    LONG lInkDataVersion;
    LONG lInkArraySize;
    COLORREF crWinColor;
    RECT WinRect;
    WCHAR wszTitle[PAGE_TITLE_SIZE];
    WCHAR wszAuthor[PAGE_TITLE_SIZE];
    WCHAR wszReserved1[PAGE_TITLE_SIZE];
  } DRAWPROPS;

The lInkDataVersion member holds a version number for the ink data in the drawing page. This allows the first member to be checked to determine possible variations in treatment based on the format version.

The lInkArraySize member holds the current size of the ink data array.

The crWinColor member holds the current background color of the display window. The default for this color is white.

The WinRect member holds the current drawing surface rectangle. This is for future evolution of the application and is not used in this sample. As a persistent property, it can be used to save and restore the display window size.

The wszTitle, wszAuthor, and wszReserved1 members are for future evolution of the application and are not used by PERDRAW or PERCLIEN.

In the division of labor between client and server, CODrawPage does not create the compound file that is used to store drawing page data. To save and load the persistent data of CODrawPage, the client uses the IPersistStorage interface Save and Load methods, passing an IStorage interface pointer for a storage branch located in an existing compound file. CODrawPage then uses the IStorage interface to read data from and write data to the compound file. As mentioned above, CODrawPage creates DRAWDATA and DRAWPROPS streams under the provided storage branch.

The following is the declaration of the CODrawPage COM object from DRAWPAGE.H:

  class CODrawPage : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      CODrawPage(IUnknown* pUnkOuter, CServer* pServer);
      ~CODrawPage(void);

      // A general public method for initializing this newly created
      // object. Creates any subordinate arrays, structures, or objects.
      // Not exposed as part of an interface. Used in Class Factory.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      class CImpIConnectionPointContainer : public IConnectionPointContainer
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIConnectionPointContainer(CODrawPage* pCO, IUnknown* pUnkOuter);
          ~CImpIConnectionPointContainer(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IConnectionPointContainer methods.
          STDMETHODIMP         FindConnectionPoint(REFIID, IConnectionPoint**);
          STDMETHODIMP         EnumConnectionPoints(IEnumConnectionPoints**);

        private:
          // Data private to this interface implementation.
          CODrawPage*   m_pCO;          // Parent COM Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      class CImpIPersistStorage : public IPersistStorage
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIPersistStorage(CODrawPage* pCO, IUnknown* pUnkOuter);
          ~CImpIPersistStorage(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IPersistStorage methods.
          STDMETHODIMP         GetClassID(CLSID* pClassID);
          STDMETHODIMP         IsDirty(void);
          STDMETHODIMP         InitNew(IStorage* pIStorage);
          STDMETHODIMP         Load(IStorage* pIStorage);
          STDMETHODIMP         Save(IStorage* pIStorage, BOOL bSameAsLoad);
          STDMETHODIMP         SaveCompleted(IStorage* pIStorage);
          STDMETHODIMP         HandsOffStorage(void);

        private:
          // Data private to this interface implementation.
          CODrawPage*   m_pCO;            // Parent COM Object back pointer.
          IUnknown*     m_pUnkOuter;      // Outer unknown for Delegation.
          IStorage*     m_pIStorage;      // Main DrawPage storage.
          IStream*      m_pIStream_Props; // Stream for Drawing Properties.
          IStream*      m_pIStream_Data;  // Stream for Drawing Ink Data.
      };

      class CImpIDrawPage : public IDrawPage
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIDrawPage(CODrawPage* pCO, IUnknown* pUnkOuter);
          ~CImpIDrawPage(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IDrawPage methods.
          STDMETHODIMP         InkStart(
                                 SHORT nX,
                                 SHORT nY,
                                 SHORT nWidth,
                                 COLORREF crInkColor);
          STDMETHODIMP         InkDraw(SHORT nX, SHORT nY);
          STDMETHODIMP         InkStop(SHORT nX, SHORT nY);
          STDMETHODIMP         Clear(BOOL bSaveNeeded);
          STDMETHODIMP         Resize(SHORT nWidth, SHORT nHeight);
          STDMETHODIMP         Redraw(void);

        private:
          // Private utility methods of this interface implementation.
          HRESULT NextSlot(void);

          // Data private to this interface implementation of IDrawPage.
          CODrawPage*   m_pCO;          // Parent COM Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested interface implementations
      // friends to COM object instantiations of this CODrawPage
      // COM object class.
      friend CImpIConnectionPointContainer;
      friend CImpIPersistStorage;
      friend CImpIDrawPage;

      // Private Methods of CODrawPage COM objects.

      // Private method of main connectable CODrawPage COM object to
      // broadcast event notifications to all connected listening sinks.
      HRESULT NotifySinks(
                DRAWPAGE_EVENT DrawPageEvent,
                SHORT nX,
                SHORT nY,
                SHORT nInkWidth,
                COLORREF crInkColor);

      // Private data of CODrawPage COM objects.

      // Nested IConnectionPointContainer implementation instantiation.
      CImpIConnectionPointContainer m_ImpIConnectionPointContainer;

      // Nested IPersistStorage implementation instantiation.
      CImpIPersistStorage m_ImpIPersistStorage;

      // Nested IDrawPage implementation instantiation. Custom interface.
      CImpIDrawPage     m_ImpIDrawPage;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;

      // The array of connection points for this connectable COM object.
      IConnectionPoint* m_aConnectionPoints[MAX_CONNECTION_POINTS];

      // The following private data is the working heart of DrawPage objects.
      RECT              m_WinRect;        // Current window rectangle.
      COLORREF          m_crWinColor;     // Current window background color.
      COLORREF          m_crInkColor;     // Current ink color.
      SHORT             m_nInkWidth;      // Current ink width.
      LONG              m_lInkDataEnd;    // Current end of the ink data.
      LONG              m_lInkDataMax;    // Current max end of the ink data.
      INKDATA*          m_paInkData;      // Dynamic Ink data array pointer.
      DRAWPROPS         m_DrawProps;      // Drawing Page properties.
      CLSID             m_ClassID;        // CLSID of this COM Object.
      UINT              m_ClipFmt;        // ClipBoard format.
      PERSTGSTATE       m_StgState;       // Persistent Storage State.
      BOOL              m_bDirty;         // RAM no match file--save needed.
  };

CODrawPage has nested implementations of the IUnknown, IConnectionPointContainer, IPersistStorage, and IDrawPage interfaces. The techniques of these interface implementations follow the pattern used in earlier samples. Implementation of IUnknown was introduced in the COMOBJ sample. IConnectionPointContainer was introduced in the CONSERVE sample. This sample introduces the IPersistStorage standard interface with more detail below. This sample also introduces the IDrawPage custom interface but its implementation is very similar to the custom interface implemementations in earlier samples. For example, see the IPaper interface covered in the STOSERVE sample.

The m_DrawProps member is a DRAWPROPS structure that contains the properties of the drawing page. The m_ClassID holds the CLSID of the CODrawPage component for later use within the method implementations of CODrawPage. The m_ClipFmt member holds an identifier for the clipboard format associated with the ink data in the drawing. (This is for future evolution of the application and would be used if cut and paste edit operations were supported for ink data.) CODrawPage uses the m_bDirty member internally to determine when the resident drawing data has changed relative to the last time it was loaded from or saved to persistent storage.

The m_StgState member holds the current persistent storage state of CODrawPage and is discussed later in this lesson.

CODrawPage keeps the ink data in a RAM-resident array, pointer to which is kept in the m_paInkData variable. This array is initially allocated in the internal CODrawPage::Init method immediately after CODrawPage is created, but it is also deleted and recreated during the IPersistStorage::Load method. The m_lInkDataEnd member holds the array index of the current end of the array. The m_lInkDataMax member holds the current maximum value that m_lInkDataEnd can have. The array grows dynamically within the current upper bound set in m_lInkDataMax. If m_lInkDataEnd reaches m_lInkDataMax, then additional array space is allocated and m_lInkDataMax is assigned a new value.

The following IPersistStorage methods are declared:

  // IPersistStorage methods.
  STDMETHODIMP GetClassID(CLSID* pClassID);
  STDMETHODIMP IsDirty(void);
  STDMETHODIMP InitNew(IStorage* pIStorage);
  STDMETHODIMP Load(IStorage* pIStorage);
  STDMETHODIMP Save(IStorage* pIStorage, BOOL bSameAsLoad);
  STDMETHODIMP SaveCompleted(IStorage* pIStorage);
  STDMETHODIMP HandsOffStorage(void);

Examining these methods in detail shows how the IPersistStorage interface implements persistence for the property and ink data in CODrawPage. The InitNew, Load, Save, and SaveCompleted methods accept an IStorage interface pointer. If you have not used streams (IStream) or storages (IStorage) in structured storage compound files, see the STOSERVE and STOCLIEN lesons for coverage of this topic.

Objects that implement IPersistStorage read and write their state data in a storage branch of a compound file. Like IPersistStreamInit, IPersistStorage supports object initialization with an InitNew method. IPersistStorage also imposes a protocol between client and object that handles situations when the client needs to control the underlying compound file and must tell the object to relinquish control of any open storage or stream elements in may own in the compound file. In conjunction with an internally maintained storage state, the Save, SaveCompleted, and HandsOffStorage methods ensure that the client abides by the protocol.

The protocol is based on certain rules governing transition to and from various object states. Objects that implement their persistence using IPersistStorage must abide these rules. CODrawPage's m_StgState member holds the current storage state of CODrawPage and is assigned values based on the state. The following state values are from APPUTIL.H, which is located in the common INC directory. This enumeration is defined in the common APPUTIL.H because many persistent objects in separate applications might use it.

  // An enumeration of persistent storage states for use in the
  // implementation of IPersistStorage.
  typedef enum
  {
    PERS_UNINIT = 0,  // Uninitialized.
    PERS_SCRIBBLE,    // Write     permitted; Read     permitted.
    PERS_NOSCRIBBLE,  // Write NOT permitted; Read     permitted.
    PERS_HANDSOFF     // Write NOT permitted; Read NOT permitted.
  } PERSTGSTATE;

PERS_UNINIT represents the unitialized state of CODrawPage. PERS_SCRIBBLE represents the normal state of read and write access by CODrawPage to its persistent storage. During this state, CODrawPage can hold open pointers to any elements in its persistent storage. PERS_NOSCRIBBLE represents the state in which CODrawPage can read but not write its persistent storage. This is a 'zombie' state in which CODrawPage's methods cannot perform any incremental writes to storage. This state exists between calls to Save and SaveCompleted. While in this state a client often continues to write to other parts of the compound file. This allows the client to gather several save operations for the compound file--perhaps for several different persistent objects--into one save operation. When done with this operation the client calls SaveCompleted on the appropriate PERS_NOSCRIBBLE objects. PERS_HANDSOFF represents the state in which CODrawPage can neither read nor write to its persistent storage. In this state CODrawPage has no valid interface pointers to its persistent storage.

The InitNew, Load, Save, SaveCompleted, and HandsOffStorage methods manage storage state. Both InitNew and Load change the state from PERS_UNINIT to PERS_SCRIBBLE. Save changes the state from PERS_SCRIBBLE to PERS_NOSCRIBBLE. HandsOffStorage changes the state from PERS_SCRIBBLE to PERS_HANDSOFF or from PERS_NOSCRIBBLE to PERS_HANDSOFF. SaveCompleted changes the state from PERS_NOSCRIBBLE to PERS_SCRIBBLE or from PERS_HANDSOFF to PERS_SCRIBBLE.

The client calls SaveCompleted to enable normal read and write access for an object in either the PERS_NOSCRIBBLE or PERS_HANDSOFF state. The client calls HandsOffStorage to prevent the object from reading or writing to its persistent storage. This would typically occur when the client is performing some action on the underlying compound file and needs to prevent the CODrawPage from reading or writing to a compound file while the file is undergoing global changes. For example, if the client is renaming the file, CODrawPage must be prevented from attempting any modifications to the data in the file during the rename operation. If the client calls HandsOffStorage before calling Save, CODrawPage must release all the interface pointers it holds for storages in the compound file. Since no interface pointers are available after these releases, a PERS_HANDSOFF state that absolutely prevents CODrawPage from accessing its persistent storage is needed. To enable CODrawPage to read and write its persistent storage again, the client passes a fresh IStorage interface in the SaveCompleted method.

The GetClassID method is called by clients of CODrawPage to obtain its component CLSID. This method is actually inherited from the IPersist interface from which IPersistStorage is derived. Here is the implementation of GetClassID from DRAWPAGE.CPP.

  STDMETHODIMP CODrawPage::CImpIPersistStorage::GetClassID(
                 CLSID* pClassID)
  {
    HRESULT hr = E_POINTER;

    if (NULL != pClassID)
    {
      // Use overloaded '=' operator to copy the Class ID to caller.
      *pClassID = m_pCO->m_ClassID;
      hr = NOERROR;
    }

    return hr;
  }

This simple method uses the overloaded "=" operator to copy CODrawPage's main copy of the CLSID to the client's address space using pClassID. The convenient overloading of the "=" operator is provided in the COM and OLE header files that are included at the front of DRAWPAGE.CPP. The CODrawPage constructor initially assigned the value CLSID_DrawPage to m_ClassID. CLSID_DrawPage is defined in the PAGEGUID.H file, which also contains other GUIDs--such as interface IIDs--that are used in the PERSERVE, PERTEXT, PERDRAW, and PERCLIEN samples. PAGEGUID.H is in the common INC directory.

The IsDirty method is called by clients when they need to determine if changes were made to this CODrawPage's persistent data since it was last loaded, initialized, or saved. The following is the implementation of IsDirty from DRAWPAGE.CPP:

  STDMETHODIMP CODrawPage::CImpIPersistStorage::IsDirty(
                 void)
  {
    HRESULT hr;

    hr = m_pCO->m_bDirty ? S_OK : S_FALSE;

    return hr;
  }

This method simply returns the current "dirty" status of the RAM-resident data in terms of the standard HRESULT return codes of S_OK or S_FALSE. S_OK means that the data has changed and needs saving; S_FALSE means the data matches its counterpart in persistent storage.

Clients call the InitNew method to direct CODrawPage to initialize its object state data in RAM for the first time. InitNew is called instead of Load when the newly created object instance must be initialized with new data rather than with persistent data previously saved in a storage. Load and InitNew are never called on the same instance of CODrawPage. The following is the implementation of InitNew from DRAWPAGE.CPP:

  STDMETHODIMP CODrawPage::CImpIPersistStorage::InitNew(
                 IStorage* pIStorage)
  {
    HRESULT hr = E_UNEXPECTED;
    ULONG ulToWrite, ulWritten;

    // There is no previous persistent data for this CODrawPage object.
    // Create and init new persistence data for an empty drawing page.

    // Return E_UNEXPECTED error if not in the UNINIT state.
    if (PERS_UNINIT == m_pCO->m_StgState)
    {
      if (NULL != pIStorage)
      {
        // Create the Properties stream.
        hr = pIStorage->CreateStream(
               WSZ_DRAWPROPS,
               STGM_CREATE | STGM_DIRECT
                 | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
               0,
               0,
               &m_pIStream_Props);
        if (SUCCEEDED(hr))
        {
          // Create the Ink Data stream.
          hr = pIStorage->CreateStream(
                  WSZ_DRAWDATA,
                  STGM_CREATE | STGM_DIRECT
                    | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                  0,
                  0,
                  &m_pIStream_Data);
          if (SUCCEEDED(hr))
          {
            // Write the clipboard format and User Type Name info.
            hr = WriteFmtUserTypeStg(
                   pIStorage,
                   m_pCO->m_ClipFmt,
                   SZ_CLIPUSERTYPE);
          }
        }
      }
      else
        hr = E_POINTER;

      if (SUCCEEDED(hr))
      {
        // Preallocate stream space by writing the DRAWPROPS structure.
        m_pCO->m_DrawProps.lInkArraySize = m_pCO->m_lInkDataEnd+1;
        m_pCO->m_DrawProps.crWinColor = m_pCO->m_crWinColor;
        m_pCO->m_DrawProps.WinRect.right = m_pCO->m_WinRect.right;
        m_pCO->m_DrawProps.WinRect.bottom = m_pCO->m_WinRect.bottom;
        ulToWrite = sizeof(DRAWPROPS);
        hr = m_pIStream_Props->Write(
               &m_pCO->m_DrawProps,
               ulToWrite,
               &ulWritten);
        if (SUCCEEDED(hr) && ulToWrite != ulWritten)
          hr = STG_E_CANTSAVE;
        if (SUCCEEDED(hr))
        {
          // Preallocate stream space by writing the initial Ink Data array.
          ulToWrite = m_pCO->m_DrawProps.lInkArraySize * sizeof(INKDATA);
          hr = m_pIStream_Data->Write(
                 m_pCO->m_paInkData,
                 ulToWrite,
                 &ulWritten);
          if (SUCCEEDED(hr) && ulToWrite != ulWritten)
            hr = STG_E_CANTSAVE;
          if (FAILED(hr))
          {
            // Release ink data stream if error writing.
            m_pIStream_Data->Release();
          }
        }
        else
        {
          // Release property stream if error writing.
          m_pIStream_Props->Release();
        }

        if (SUCCEEDED(hr))
        {
          // Keep open storage pointer around for use in later incremental or
          // low-memory calls of Save. AddRef the interface pointer copy.
          m_pIStorage = pIStorage;
          m_pIStorage->AddRef();

          // Switch the persistent storage state to the scribble state.
          m_pCO->m_StgState = PERS_SCRIBBLE;
        }
      }
    }

    return hr;
  }

InitNew first checks m_StgState and fails with the E_UNEXPECTED error if CODrawPage is already initialized. During InitNew the object should create and open every storage and stream element it expects to access, including any elements it will later require in a low-memory save situation. To help preclude later problems if the storage medium is full, InitNew should also pre-allocate an appropriate amount of stream space. It can do this using IStream::SetSize or by simply writing an initial place-holder object state. The above InitNew writes such a place-holder object state.

InitNew begins by using the methods of the passed IStorage interface to create both the DRAWPROPS and DRAWDATA streams. It then writes the identifier for the clipboard format. If those steps succeed, it pre-allocates stream space by writing appropriate initial data to the newly created DRAWPROPS and DRAWDATA streams. InitNew keeps a copy of the IStorage pointer that was passed into it, which CODrawPage uses later. InitNew also performs an AddRef on the pointer copy. Finally, InitNew changes the CODrawPage storage state from PERS_UNINIT to PERS_SCRIBBLE.

The Load method directs CODrawPage to load its persistent state from an open storage branch whose IStorage interface is passed. Load assumes that the expected storage structure (that is, the particular substorages and streams that contain the entire object state data) are present in the specified storage branch. The following code is the implementation of Load from DRAWPAGE.CPP:

  STDMETHODIMP CODrawPage::CImpIPersistStorage::Load(
                 IStorage* pIStorage)
  {
    HRESULT hr = E_UNEXPECTED;
    ULONG ulToRead, ulReadIn;
    LONG lNewArraySize;
    INKDATA* paInkData;
    DRAWPROPS NewProps;

    // Return E_UNEXPECTED error if not in the UNINIT state.
    if (PERS_UNINIT == m_pCO->m_StgState)
    {
      if (NULL != pIStorage)
      {
        // Open the existing DrawPage properties stream.
        hr = pIStorage->OpenStream(
               WSZ_DRAWPROPS,
               0,
               STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               &m_pIStream_Props);
        if (SUCCEEDED(hr))
        {
          // Open the existing DrawPage ink data stream.
          hr = pIStorage->OpenStream(
                 WSZ_DRAWDATA,
                 0,
                 STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
                 0,
                 &m_pIStream_Data);
          if (SUCCEEDED(hr))
          {
            // Read the DrawPage properties from the their stream.
            ulToRead = sizeof(DRAWPROPS);
            hr = m_pIStream_Props->Read(&NewProps, ulToRead, &ulReadIn);
            if (SUCCEEDED(hr) && ulToRead != ulReadIn)
              hr = STG_E_READFAULT;
            if (SUCCEEDED(hr))
            {
              // Read the Ink Data of the drawing from its stream.
              // Deal with the different versions of ink data format.
              switch (NewProps.lInkDataVersion)
              {
                case INKDATA_VERSION20:
                  // Allocate an ink data array big enough--add some extra.
                  lNewArraySize = NewProps.lInkArraySize + INKDATA_ALLOC;
                  paInkData = new INKDATA[(LONG) lNewArraySize];
                  if (NULL != paInkData)
                  {
                    // Delete the entire old ink data array.
                    delete [] m_pCO->m_paInkData;

                    // Assign the new array.
                    m_pCO->m_paInkData = paInkData;
                    m_pCO->m_lInkDataMax = lNewArraySize;

                    // Now read the complete array of Ink Data.
                    ulToRead = NewProps.lInkArraySize * sizeof(INKDATA);
                    hr = m_pIStream_Data->Read(
                           m_pCO->m_paInkData,
                           ulToRead,
                           &ulReadIn);
                    if (SUCCEEDED(hr) && ulToRead != ulReadIn)
                      hr = STG_E_READFAULT;
                    if (SUCCEEDED(hr))
                    {
                      // Rig CODrawPage to use the DRAWPROPS info.
                      m_pCO->m_lInkDataEnd = NewProps.lInkArraySize-1;
                      m_pCO->m_crWinColor = NewProps.crWinColor;
                      m_pCO->m_WinRect.right = NewProps.WinRect.right;
                      m_pCO->m_WinRect.bottom = NewProps.WinRect.bottom;

                      // Copy the new properties into current properties.
                      memcpy(
                        &m_pCO->m_DrawProps,
                        &NewProps,
                        sizeof(DRAWPROPS));

                      // We are loaded and clean (ie, CODrawPage data
                      // matches file data). Clear dirty flag.
                      m_pCO->m_bDirty = FALSE;
                    }
                    else
                    {
                      // Release data stream if error reading.
                      m_pIStream_Data->Release();
                    }
                  }
                  else
                    hr = E_OUTOFMEMORY;
                  break;
                default:
                  hr = E_FAIL;  // Bad version.
                  break;
              }
            }
            else
            {
              // Release property stream if error reading.
              m_pIStream_Props->Release();
            }
          }
        }

        if (SUCCEEDED(hr))
        {
          // Keep open storage pointer around for use in later incremental
          // or low-memory calls of Save. AddRef the interface pointer copy.
          m_pIStorage = pIStorage;
          m_pIStorage->AddRef();

          // Switch the persistent storage state to the scribble state.
          m_pCO->m_StgState = PERS_SCRIBBLE;
        }
      }
      else
        hr = E_POINTER;
    }

    // Notify all other connected clients that DrawPage is now loaded.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(DRAWPAGE_EVENT_LOADED, 0, 0, 0, 0);

    return hr;
  }

The Load method is called instead of the InitNew method when this CODrawPage object already has a persistent state stored in a storage. A complete copy of that state data is read from the storage into newly allocated, RAM-resident space. Two kinds of state data, a group of object properties from a DRAWPROPS stream and the ink data array from a DRAWDATA stream, are loaded from the storage.

Load begins by checking m_StgState and fails returning E_UNEXPECTED if the storage state of CODrawPage is not PERS_UNINIT. Then, instead of creating streams for DRAWPROPS and DRAWDATA as does the InitNew method, Load opens these two existing streams. Load assumes that the entire set of necessary persistence elements is under the provided storage branch that is accessed by the passed pIStorage interface pointer. If Load opens these two streams successfully, the method reads the DRAWPROPS properties into a temporary NewProps structure. At the end of the successful Load, NewProps is copied to CODrawPage::m_DrawProps to allow reverting to the current CODrawPage state data if the entire load of new data is not successfully completed.

After the DRAWPROPS structure is read in, the lInkDataVersion property is used in a switch statement. This allows for different load behavior to accomodate different data formats based on the version of the data that was stored. Because the version number is the first thing in the stream's data for the object properties, it can be read separately to determine how to subsequently read and deal with the properties data that follows and with the content of the DRAWDATA stream.

The Load method then deletes the existing ink data array and uses the lInkArraySize property to allocate a new ink data array in RAM that is big enough for the drawing data to be read. This allocation includes an additional amount specified by INKDATA_ALLOC for expansion in anticipation of drawing activity in the client. Once the allocation is successful, the ink data array is read in from the DRAWDATA stream. If the read is successful, Load copies the NewProps structure to CODrawPage's m_DrawProps. At this point CODrawPage has been loaded from persistent storage. Since this load has caused the in-RAM data to match the data in the file, the m_bDirty flag is set to FALSE, meaning that a save is not needed to achieve such a match.

If the preceding is succeeds, a copy of the open IStorage interface pointer is kept in CODrawPage's m_pIStorage member for later use, and an appropriate AddRef called on this copied interface. This retained copy of the IStorage pointer is in contrast to the IPersistStream[Init] interfaces which prohibit the keeping of stream pointers in the object. At the end of Load, CODrawPage's storage state is changed from PERS_UNINIT to PERS_SCRIBBLE by assigning the m_StgState member.

If the Load method is finally successful, a call to the internal NotifySinks method notifies all connected clients that the load was completed. This notification to the client makes use of CODrawPage's connectable object features and usually triggers an appropriate display of the newly loaded data.

Clients call the Save method to direct CODrawPage to save its persistent data from RAM to a specified storage in a compound file. As with the Load method, the client supplies a storage interface parameter to the Save method. The following is the implementation of Save from DRAWPAGE.CPP:

  STDMETHODIMP CODrawPage::CImpIPersistStorage::Save(
                 IStorage* pIStorage,
                 BOOL bSameAsLoad)
  {
    HRESULT hr = E_UNEXPECTED;
    IStream* pIStream_Props;
    IStream* pIStream_Data;

    // Return E_UNEXPECTED error if not in the Scribble state.
    if (PERS_SCRIBBLE == m_pCO->m_StgState)
    {
      if (bSameAsLoad)
      {
        LARGE_INTEGER li;

        // If SameAsLoad use copies of the existing open stream pointers.
        pIStream_Props = m_pIStream_Props;
        pIStream_Data = m_pIStream_Data;

        // AddRef these copies; they are released below.
        pIStream_Props->AddRef();
        pIStream_Data->AddRef();

        // We're going to do a fresh save to existing open elements.
        // So recue the stream seek pointers to their start.
        LISet32(li, 0);
        pIStream_Props->Seek(li, STREAM_SEEK_SET, NULL);
        pIStream_Data->Seek(li, STREAM_SEEK_SET, NULL);

        hr = NOERROR;
      }
      else
      {
        // If not SameAsLoad (save to a different storage) then return the
        // E_POINTER error if a NULL pIStroage was passed.
        if (NULL != pIStorage)
        {
          // If we are saving new persistent image of the object to a
          // different storage then create all the necessary persistent
          // storage elements.

          // Create the Properties stream.
          hr = pIStorage->CreateStream(
                 WSZ_DRAWPROPS,
                 STGM_CREATE | STGM_DIRECT
                   | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                 0,
                 0,
                 &pIStream_Props);
          if (SUCCEEDED(hr))
          {
            // Create the Ink Data stream.
            hr = pIStorage->CreateStream(
                    WSZ_DRAWDATA,
                    STGM_CREATE | STGM_DIRECT
                      | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                    0,
                    0,
                    &pIStream_Data);
            if (SUCCEEDED(hr))
            {
              // Write the clipboard format and User Type Name info.
              hr = WriteFmtUserTypeStg(
                     pIStorage,
                     m_pCO->m_ClipFmt,
                     SZ_CLIPUSERTYPE);
            }
          }
        }
        else
          hr = E_POINTER;
      }

      // Now perform the saves of the persistent data elements.
      if (SUCCEEDED(hr))
      {
        ULONG ulToWrite, ulWritten;

        // Save the DrawPage properties in a dedicated stream.
        m_pCO->m_DrawProps.lInkArraySize = m_pCO->m_lInkDataEnd+1;
        m_pCO->m_DrawProps.crWinColor = m_pCO->m_crWinColor;
        m_pCO->m_DrawProps.WinRect.right = m_pCO->m_WinRect.right;
        m_pCO->m_DrawProps.WinRect.bottom = m_pCO->m_WinRect.bottom;
        ulToWrite = sizeof(DRAWPROPS);
        hr = pIStream_Props->Write(&m_pCO->m_DrawProps, ulToWrite, &ulWritten);
        if (SUCCEEDED(hr) && ulToWrite != ulWritten)
          hr = STG_E_CANTSAVE;
        if (SUCCEEDED(hr))
        {
          // Save the DrawPage ink data in a dedicated stream.
          ulToWrite = m_pCO->m_DrawProps.lInkArraySize * sizeof(INKDATA);
          hr = pIStream_Data->Write(m_pCO->m_paInkData, ulToWrite, &ulWritten);
          if (SUCCEEDED(hr) && ulToWrite != ulWritten)
            hr = STG_E_CANTSAVE;
        }

        // Release any temporary streams used.
        pIStream_Props->Release();
        pIStream_Data->Release();

        if (SUCCEEDED(hr))
        {
          // Since the persistent save is now done for this object the
          // file matches the object. So set object's dirty flag to FALSE.
          if (bSameAsLoad)
            m_pCO->m_bDirty = FALSE;

          // Switch the persistent storage state to the NoScribble state.
          // After this Save this object cannot scribble to its persistent
          // storage again until the client grants this freedom by calling
          // SaveCompleted which switches this object to the Scribble state.
          m_pCO->m_StgState = PERS_NOSCRIBBLE;
        }
      }
    }

    // Notify all other connected clients that DrawPage is now saved.
    if (SUCCEEDED(hr))
      m_pCO->NotifySinks(DRAWPAGE_EVENT_SAVED, 0, 0, 0, 0);

    return hr;
  }

Clients pass a bSameAsLoad parameter to determine whether the Save method will save the object state to the object's current storage branch or to a newly specified one. When bSameAsLoad is TRUE, the object state is saved to the currently opened storage (that is, the same storage previously passed to the Load or InitNew methods). This save operation can consist of incremental writes of only the items that need saving in the current storage--which can be all items--without using any extra memory and without opening or creating any storages or streams. Passing bSaveAsLoad as FALSE allows the client to tell the object to save its state to a different storage while keeping its current storage open. The client can use this to perform such actions as writing a copy of an object into a storage other than the current storage that the object was loaded from. All persistent data is written to the newly specified storage. The object's currently open storage remains open and unaffected when bSameAsLoad is FALSE.

The Save method saves the object's persistent state data to the currently open storage or to a newly specified storage depending on the bSameAsLoad parameter. Both during and after Save (and Regardless of the bSameAsLoad value), the object continues to hold its present pointers to opened storage elements. But after Save, the object cannot scribble on its data until the SaveCompleted method is called.

Save must not fail if an out-of-memory condition is encountered. This safety is ensured in InitNew or Load when they pre-open storage elements and hold pointers to any elements later needed by Save.

Save first checks m_StgState and fails by returning E_UNEXPECTED if the storage state of CODrawPage is not PERS_SCRIBBLE. If the bSaveAsLoad parameter is TRUE, The method makes temporary copies of the IStream pointers for DRAWPROPS and DRAWDATA and calls AddRef on the pointer copies, which will be released later in the Save method. Since these are currently open streams they only need to have their seek pointers reset to the start of the streams. If the bSaveAsLoad parameter is FALSE, new DRAWPROPS and DRAWDATA streams are created in the specified new storage. The clipboard format is also written into the new storage.

The Save method then saves the CODrawPage properties and ink data array to their respective DRAWPROPS and DRAWDATA streams. If the save is successful, CODrawPage's m_bDirty flag is cleared to FALSE, but only if bSaveAsLoad is TRUE. A current TRUE value of m_bDirty is not cleared after the save is done to a storage different from the currently open one. This ensures that any needed save to the current storage will also be done later.

If the Save method is finally successful, the method changes CODrawPage's storage state from PERS_SCRIBBLE to PERS_NOSCRIBBLE and calls the internal NotifySinks method to notify all connected clients that the save was completed.

The client calls the SaveCompleted method to tell CODrawPage that the client has completed its overall save operations freeing the object to write (that is, "scribble") to its persistent data elements. The following is the implementation of SaveCompleted from DRAWPAGE.CPP:

  STDMETHODIMP CODrawPage::CImpIPersistStorage::SaveCompleted(
                 IStorage* pIStorage)
  {
    HRESULT hr = E_UNEXPECTED;
    IStream* pIStream_Props;
    IStream* pIStream_Data;
    BOOL bOk;
    PERSTGSTATE StgState = m_pCO->m_StgState;

    // Return E_UNEXPECTED error if storage is NOT in either the
    // No-scribble or Hands-off state.
    bOk = (StgState == PERS_NOSCRIBBLE || StgState == PERS_HANDSOFF);
    if (bOk)
    {
      // Return E_UNEXPECTED if in Hands-off state but pIStorage is NULL.
      if (StgState == PERS_HANDSOFF && NULL == pIStorage)
        bOk = FALSE;
      else
        hr = NOERROR;
    }

    if (bOk)
    {
      if (NULL != pIStorage)
      {
        // If specified storage is non-NULL, release all storage elements
        // and use the specified pIStorage to re-open those elements.
        // If pIStorage == NULL then we need do nothing since we already
        // have all the pointers to storage elements needed for Save.

        // First ensure we can open the property and data streams
        hr = pIStorage->OpenStream(
               WSZ_DRAWPROPS,
               0,
               STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
               0,
               &pIStream_Props);
        if (SUCCEEDED(hr))
        {
          hr = pIStorage->OpenStream(
                 WSZ_DRAWDATA,
                 0,
                 STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                 0,
                 &pIStream_Data);
          if (SUCCEEDED(hr))
          {
            // Release the current stream elements and the main storage.
            RELEASE_INTERFACE(m_pIStream_Props);
            RELEASE_INTERFACE(m_pIStream_Data);
            RELEASE_INTERFACE(m_pIStorage);

            // Reassign the storage and stream interface pointers.
            m_pIStorage = pIStorage;
            m_pIStorage->AddRef();
            m_pIStream_Props = pIStream_Props;
            m_pIStream_Data = pIStream_Data;
          }
        }
      }

      if (SUCCEEDED(hr))
      {
        // Switch the persistent storage state back to the scribble state.
        m_pCO->m_StgState = PERS_SCRIBBLE;

        // Since save is completed, no save is needed. So set object's
        // dirty flag to FALSE.
        m_pCO->m_bDirty = FALSE;
      }
    }

    return hr;
  }

SaveCompleted accepts an IStorage pointer. If this pointer is not NULL, then SaveCompleted must release any interface pointers for storage elements that it currently holds and reopen all those elements within the specified new storage. If the IStorage parameter is NULL then the release and reopen is not needed and the object can once again write to its expected elements in the currently open storage.

SaveCompleted first confirms that CODrawPage's m_bStgState is in neither the PERS_NOSCRIBBLE nor PERS_HANDSOFF state. If it is in either of these states then SaveCompleted fails and returns the E_UNEXPECTED error. This error is also returned if the object is in the PERS_HANDSOFF state and the pIStorage parameter is non-NULL. Clients must not call SaveCompleted when the object is in the PERS_HANDSOFF state.

If the object is in the proper state and if a non-NULL IStorage parameter is passed, SaveCompleted uses the new storage to open the DRAWPROPS and DRAWDATA streams, releasing the previously held pointers for those streams and retaining the new ones. Finally, if SaveCompleted is successful, CODrawPage's storage state is changed to PERS_SCRIBBLE. CODrawPage's m_bDirty flag is also cleared to FALSE to indicate the fact that a save is not needed because the object data in RAM now matches the data saved to storage.

Clients call the HandsOffStorage method to tells the CODrawPage object to release the pointers it holds to all of its persistent data elements and to places the object into the PERS_HANDSOFF state. When in the PERS_HANDSOFF state the object must refrain from writing to its persistent storage until the SaveCompleted method is called to free the object to write to its persistent data elements. The following is the implementation of HandsOffStorage from DRAWPAGE.CPP.

  STDMETHODIMP CODrawPage::CImpIPersistStorage::HandsOffStorage(
                 void)
  {
    HRESULT hr = E_UNEXPECTED;
    PERSTGSTATE StgState = m_pCO->m_StgState;

    // Return E_UNEXPECTED error if NOT in scribble or no-scribble state.
    if (StgState == PERS_NOSCRIBBLE || StgState == PERS_SCRIBBLE)
    {
      // Release the current stream elements and the main storage.
      // This NULLs these interface pointers (via the release macro).
      RELEASE_INTERFACE(m_pIStream_Props);
      RELEASE_INTERFACE(m_pIStream_Data);
      RELEASE_INTERFACE(m_pIStorage);

      // Switch the persistent storage state to the Hands-off state.
      m_pCO->m_StgState = PERS_HANDSOFF;
      hr = NOERROR;
    }

    return hr;
  }

The HandsOffStorage method fails and returns the E_UNEXPECTED error if CODrawPage is in neither the PERS_SCRIBBLE nor the PERS_NOSCRIBBLE storage state. If it is in either of these states the method releases the open DRAWPROPS stream, the open DRAWDATA stream, and the main open storage branch. Finally, it changes the storage state to PERS_HANDSOFF.

CODrawPage exposes the IConnectionPointContainer interface so clients can connect to CODrawPage to receive notifications of certain events that occur in CODrawPage. Exposing this interface makes CODrawPage a connectable object, and a client can call QueryInterface for IConnectionPointContainer and use it to obtain the object's connection points. The client participation in this scheme is covered in the associated PERCLIEN lesson.

The client implements what is called a sink in the form of a sink object with a sink interface. The sink interface receives outgoing event notification calls from CODrawPage after the client connects the sink to a CODrawPage instance. The client makes the connection by using an IConnectionPoint interface on a connection point object that is managed by CODrawPage. There can be numerous connection points on a single connectable COM object. In the PERDRAW sample, CODrawPage has only one connection point to handle draw page events.

Any number of clients can connect to a single connection point. The CONNPOINT_DRAWPAGESINK connection point in CODrawPage maintains a group of connections that can grow dynamically at run time. The full implementation of CODrawPage's connectable object support is coded in files CONNECT.H and CONNECT.CPP and is not be covered here. The construction is very similar to that in the CONSERVE sample.

The PERCLIEN client implements appropriate sink objects for the connection points it expects to find in CODrawPage. From the context of CODrawPage, the single sink object that PERCLIEN implements exposes the IDrawPageSink interface. This is the outgoing interface that CODrawPage uses to notify PERCLIEN of various events in CODrawPage. The following summarizes the methods in IDrawPageSink, which is from IPAGES.H in the common INC directory.

  Loaded
    The DrawPage was loaded from persistent storage.

  Saved
    The DrawPage was saved to persistent storage.

  InkStart
    A client started a color ink drawing sequence. The server echoes
    this event to connected clients in case they wish to track the
    drawing as it occurs.

  InkDraw
    A client is putting ink data points on the drawing surface. The server
    echoes this event to connected clients in case they wish to track the
    drawing as it occurs.

  InkStop
    A client stopped its ink drawing sequence. The server echoes this
    event to connected clients in case they wish to track the drawing as
    it occurs.

  Cleared
    A client has erased/cleared the entire DrawPage.

  Resized
    A client has resized the drawing window.

These methods are largely self-explanatory. The Loaded, Cleared, and Resized methods implemented in the client sink will typically trigger a redisplay of the drawing page. Although the sink must implement all of these methods in some fashion, many are implemented as stubs and are not used in the PERDRAW or PERCLIEN samples.

Top Back to page top

© 1995-1998 Microsoft Corporation


PERCLIEN - Client of Persistent Components - Lesson 28

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

PERCLIEN - Client of Persistent Object Servers

SUMMARY

The PERCLIEN sample completes a sequence of four samples that cover persistent COM objects. The first three samples discuss three COM servers that each house COM objects having a different kind of object persistence. The first sample, PERSERVE, shows how to use the IPersistStream interface to give COPageList COM objects their persistence in a stream of a structured storage compound file. The second sample, PERTEXT, shows how to use the IPersistStreamInit interface is used to give COTextPage COM object their persistence in streams. The IPersistStreamInit::InitNew method is covered in detail. The third sample, PERDRAW, shows how to use the IPersistStorage interface to give CODrawPage COM objects their persistence in substorages of a compound file. The IPersistStorage::HandsOffStorage and IPersistStorage::SaveCompleted methods are covered in detail. This sample, PERCLIEN, integrates the use of the three previous servers into a single COM client application.

The PERCLIEN client uses the components in the PERSERVE, PERTEXT, and PERDRAW to build a document editing application that presents a list of pages to users. The user can add, delete, open, and name the page items in the list. When an item is opened, the content appears in a separate window for editing. The separate edit windows are child windows of PERCLIEN's main window. Two types of pages are supported: text pages and drawing pages.

PERCLIEN provides the graphical user interface for editing the page list as well as for editing the content of the two page types. PERCLIEN's CGuiList C++ object encapsulates the editing of the page list in PERCLIEN's main window. CGuiList controls the server-side COPageList object in PERSERVE. PERCLIEN's CGuiText C++ object encapsulates the editing of text pages in separate child windows. CGuiText controls the server-side COTextPage object in PERTEXT. PERCLIEN's CGuiDraw C++ object encapsulates the editing of drawing pages in separate child windows. CGuiDraw controls the server-side CODrawPage object in PERDRAW.

The separate text page windows of CGuiText provide for the editing of text using the standard Win32 multi-line edit control. The separate drawing page windows of CGuiDraw provide the free-form line drawing functionality that appeared in the STOSERVE and STOCLIEN samples.

The PERCLIEN sample relies on each page's persistent COM object to load and store the page's content. PERCLIEN acquires a pointer to the IPersistStream interface on COPageList to control persistent stream storage of the page list. It acquires a pointer to the IPersistStreamInit interface on COTextPage to control persistent stream storage (with InitNew capability) of a text page. It acquires a pointer to the IPersistStorage interface on CODrawPage to control persistent storage in a substorage (with the HandsOffStorage protocol) of a drawing page.

PERCLIEN controls the use of the compound file and provides the COM objects on the server side with appropriate interface pointers for the storages or streams used to store the data. PERSERVE's COPageList COM object exposes the IPersistStream interface and receives an IStream pointer from PERCLIEN. PERTEXT's COTextPage COM object exposes the IPersistStreamInit interface and receives an IStream pointer by PERCLIEN. PERDRAW's CODrawPage COM object exposes the IPersistStorage interface and receives an IStorage pointer by PERCLIEN. These IStorage and IStream interfaces are not implemented inside PERCLIEN or PERSERVE: They are implemented by the COM libraries. When a pointer to one of these interfaces is obtained, their methods are essentially used as a set of services to operate on areas in a compound file. If you have not used streams (IStream) or storages (IStorage) in structured storage compound files, see the STOSERVE and STOCLIEN samples for coverage of this topic.

PERCLIEN manages its structured storage compound file as a kind of document file where the page list and the pages themselves are stored. PERCLIEN loads and saves its page list, text pages, and drawing pages in the structured storage of this compound file. The compound file has a unique format because of the various streams and storages used. PERCLIEN identifies these compound files as page files with a .PAG file extension.

The connectable-object technology that was shown in the CONSERVE and CONCLIEN samples is used between PERCLIEN and the COM objects in its servers. Thus, PERCLIEN implements custom event sink interfaces for the connectable objects in the servers: IPageListSink, ITextPageSink, and IDrawPageSink.

For functional descriptions and a tutorial code tour of PERCLIEN, see the Code Tour section in PERCLIEN.HTM. For details on the external user operation of PERCLIEN, see both the Usage and Operation sections in PERCLIEN.HTM. To read PERCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the PERCLIEN lesson in the table of lessons. You can also do same thing by double-clicking the PERCLIEN.HTM file after locating it in the main tutorial directory in Windows Explorer. See also PERSERVE.HTM, PERTEXT.HTM, and PERDRAW.HTM in the main tutorial directory for more details on how these servers work and expose their services to PERCLIEN. The makefile for each of the servers automatically registers that server in the system registry, so you must build the PERSERVE, PERTEXT, and PERDRAW servers before you can run PERCLIEN.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the PERCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV PERCLIEN.DSP

You can also simply double-click the PERCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

PERCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. PERCLIEN accepts an optional file name parameter on the command line. For example:

  PERCLIEN c:\pages\mynotes.pag

Where mynotes.pag is a compound file containing pages of text and drawings previously made by PERCLIEN. If no file name argument is specified, PERCLIEN uses the default file name PERCLIEN.PAG and attempts to open it in the same directory as the executing PERCLIEN.EXE.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, PERCLIEN.EXE is the client executable to run for this sample.

OPERATION

The PERCLIEN.EXE application provides the user interface for this sample. It uses the associated but independent PERSERVE.DLL, PERTEXT.DLL, and PERDRAW.DLL in-process servers to demonstrate both client and server use of the persistent objects in these servers. The following summarizes the operation from the standpoint of PERCLIEN.EXE as a COM client of those servers.

Main Menu (Page List)

The PERCLIEN application's main window client area displays the page list for the currently opened page file. PERCLIEN has a main menu helps the user control the page file and items in the page list. The following menu commands are provided.

Menu Selection: File/New
Displays the Save As dialog box to obtain a name and path for a new page file to create. A default .PAG file extension for these files is assumed. The newly created page file becomes the currently open page file. If changes were made to the existing page file before this menu item was chosen, a separate dialog box asks the user if the current page list (and any other open pages) should be saved into its associated compound file.

Menu Selection: File/Open
Displays the Open dialog box to obtain a name and path for an existing page file to open. A default .PAG file extension for these files is assumed. The newly opened page file becomes the currently open page file. If changes were made to the existing page file before this menu item was chosen, a separate dialog box asks the user if the current page list (and any other open pages) should be saved into its associated compound file.

Menu Selection: File/Save
Saves the current drawing into its associated compound file.

Menu Selection: File/Save As
Displays the Save As dialog box to obtain a name and path for a new page file to create. The currently open page list and pages become the saved content of the new file, and the new file becomes the new associated compound file.

Menu Selection: File/Exit
Exits PERCLIEN. If the current page file was changed then a dialog box will ask the user whether a save should be done.

Menu Selection: Page/Open
Opens the page edit window for the page currently selected in the page list.

Menu Selection: Page/Title
Displays a dialog box so the user can edit the list title for the page currently selected in the page list.

Menu Selection: Page/Delete
Deletes the page currently selected in the page list.

Menu Selection: Page/New Text
Creates an empty text page and opens an edit window for it.

Menu Selection: Page/New Drawing
Creates an empty drawing page and opens an edit window for it.

Menu Selection: Help/PERCLIEN Tutorial
Opens the PERCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/PERSERVE Tutorial
Opens the PERSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/PERTEXT Tutorial
Opens the PERTEXT.HTM tutorial file in the Web browser.

Menu Selection: Help/PERDRAW Tutorial
Opens the PERDRAW.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About PERCLIEN
Displays the About dialog box for this application.

Text Page Menu

The PERCLIEN application's text-page window client area displays the currently opened text page. Users edit a the page in a separate window with its own menu that provides for user control of the text page. The following menu commands are available:

Menu Selection: Text/Save
Saves the current text page to the currently open page file.

Menu Selection: Text/Clear
Clears the current text page to a blank text page.

Menu Selection: Text/Exit
Exits the text page. If the text page was changed, a dialog box prompts the user to save the changes.

Menu Selection: Edit/Undo
Undoes the previous editing action.

Menu Selection: Edit/Select All
Selects all text in the text page.

Menu Selection: Edit/Cut
Cuts the currently selected text from the current text page and stores it on the clipboard for later paste.

Menu Selection: Edit/Copy
Copies the currently selected text from the currrent text page and stores it on the clipboard for later paste.

Menu Selection: Edit/Paste
Pastes the text content on the clipboard into the text page at the current cursor.

Menu Selection: Edit/Delete
Deletes the currently selected text from the current text page. This text is not placed on the clipboard.

Menu Selection: Help/PERCLIEN Tutorial
Opens the PERCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/PERTEXT Tutorial
Opens the PERTEXT.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source &File
Displays the Open dialog box so you can use Windows Notepad to open a source file from this or another sample.

Menu Selection: Help/About PERCLIEN
Displays the About dialog box for this application.

Drawing Page Menu

The PERCLIEN application's drawing page window client area displays a currently opened drawing page. Users edit this page is provided in a separate window with its own menu that helps the user control the drawing page. The following menu commands are provided:

Menu Selection: Drawing/Save
Saves the current drawing into the currently open page file.

Menu Selection: Drawing/Clear
Clears the current drawing to a blank drawing page.

Menu Selection: Drawing/Exit
Exits the drawing page. If the drawing page was changed, a dialog box prompts the user to save the file.

Menu Selection: Pen/Color
Displays the Choose Color dialog box to obtain a new pen color for drawing.

Menu Selection: Pen/Thin
Chooses the thin width for drawing. A check mark on this menu choice indicates that thin is the current pen width.

Menu Selection: Pen/Medium
Chooses the medium width for drawing. A check mark on this menu choice indicates that medium is the current pen width.

Menu Selection: Pen/Thick
Chooses the thick width for drawing. A check mark on this menu choice indicates that thick is the current pen width.

Menu Selection: Help/PERCLIEN Tutorial
Opens the PERCLIEN.HTM tutorial file in the Web browser.

Menu Selection: Help/PERDRAW Tutorial
Opens the PERDRAW.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source &File
Displays the Open dialog box so a user can use Windows Notepad to open a source file from this or another lesson.

Menu Selection: Help/About PERCLIEN
Displays the About dialog box for this application.

CODE TOUR

Files          Description
PERCLIEN.TXT   Short description of the sample.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
PERCLIEN.H     The include file for the PERCLIEN application. Contains
               class declarations for main window and dialogs.
PERCLIEN.CPP   The main implementation file for PERCLIEN.EXE. Has WinMain
               and CMainWindow implementation and the main menu dispatching.
PERCLIEN.RC    The application's resource definition file.
PERCLIEN.ICO   The application's main icon resource.
PERCLIEN.PAG   A default page file for the application (with two pages).
RESDEF.H       Resource ID definitions for application menu, strings, etc.
PENCIL.CUR     A pencil image for the drawing page window cursor.
GUILIST.H      The class declaration for the CGuiList C++ class.
GUILIST.CPP    Implementation file for the CGuiList C++ class. Has user
               interface to handle the main page list display window.
LISTWIN.H      The class declaration for the CListWin C++ class.
LISTWIN.CPP    Implementation file for the CListWin C++ class.
               Encapsulates the list box control used to show the page list.
LISTSINK.H     The class declaration for the COPageListSink COM object class.
LISTSINK.CPP   Implementation file for the COPageListSink COM object class.
               Connectable object notifications of page list events are
               handled by COPageListSink.
PAGEFILE.H     The class declaration for the CPageFile C++ class.
PAGEFILE.CPP   Implementation file for the CPageFile C++ class.
               Encapsulates operations on the .PAG page list compound file.
GUITEXT.H      The class declaration for the CGuiText C++ class.
GUITEXT.CPP    Implementaton file for the CGuiText C++ class. Has the
               user interface for the separate editing of text pages.
TEXTWIN.H      The class declaration for the CTextWin C++ class.
TEXTWIN.CPP    Implementation file for the CTextWin C++ class. Encapsulates
               the text edit control used to edit text page.s
TEXTSINK.H     The class declaration for the COTextPageSink COM object class.
TEXTSINK.CPP   Implementation file for the COTextPageSink COM object class.
               Connectable object notifications of text edit events are
               handled by COTextPageSink.
GUIDRAW.H      The class declaration for the CGuiDraw C++ class.
GUIDRAW.CPP    Implementation file for the CGuiDraw C++ class. Has the
               user interface for the separate drawing in draw pages.
DRAWSINK.H     The class declaration for the CODrawPageSink COM object class.
DRAWSINK.CPP   Implementation file for the CODrawPageSink COM object class.
               Connectable object notifications of drawing-related events
               are handled by COTextPageSink.
PERCLIEN.DSP   Microsoft Visual Studio Project file.

PERCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the common APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample focuses mainly on the client's use of the persistence features that are exposed by the persistent objects in the servers. Most of the infrastructure that makes PERCLIEN an application is a straightforward reuse of technology studied in previous samples and is not covered in this lesson. For example, the drawing functionality provided in CGuiDraw windows is very similar to the "scribble" functionality used in the STOCLIEN sample. Other COM-based infastructure is present in PERCLIEN but is not covered. For example, the connectable object sinks in PERCLIEN are very similar to the sink mechanisms studied in several previous samples.

The major topics covered in this code tour are:

  • How CGuiList, CGuiText, and CGuiDraw together form the heart of PERCLIEN.
  • How CGuiList and CPageFile use the IPersistStream exposed by COPageList in the PERSERVE server.
  • How CGuiText uses the IPersistStreamInit exposed by COTextPage in the PERTEXT server.
  • How CGuiDraw uses the IPersistStorage exposed by CODrawPage in the PERDRAW server.

Client Overview

The CGuiList, CGuiText, and CGuiDraw C++ classes all encapsulate the GUI behavior of PERCLIEN. As in previous client samples, PERCLIEN uses a CMainWindow that derives from APPUTIL's CVirWindow abstract class. PERCLIEN's CMainWindow::InitInstance method creates the CGuiList object. This object in turn creates the main application window and attempts to load a default page file. Such a load process will be covered in detail below. With a page file loaded, a page list is displayed to the user as the content of a standard Win32 listbox control. This control is encapsulated in the CListWin C++ object (coded in LISTWIN.H and LISTWIN.CPP). The displayed list shows the page number, page type, and title of all the pages in the page file.

With a page list entry selected, the user can open the corresponding page, which calls CGuiList::PageOpen (in GUILIST.CPP). Using the page number of the page to be opened, PageOpen calls IPageList::Get (implemented by the COPageList object in the PERSERVE server) to obtain the page type, title, and data name of the page. The obtained title is used in the page list display. The page type determines whether CGuiText or CGuiDraw is used to create the window for the new page. Both CGuiText and CGuiDraw are C++ classes derived from APPUTIL's CVirWindow abstract class to facilitate greatly their treatment as separate child windows of PERCLIEN's main window.

Both text page and drawing page windows have their own menus. See PERCLIEN.RC for the resource definitions of the menus for the list, text, and drawing windows. The menu commands are handled by respective DoCommand methods: for the main list window see CMainWindow::DoCommand in PERCLIEN.CPP; for the text window see CGuiText::DoCommand in GUITEXT.CPP; and for the drawing window see CGuiDraw::DoCommand in GUIDRAW.CPP.

Every page has a unique internal data name (for example, Text0001 or Drawing0002) and a counter ensures this uniqueness of data names within the page file. The data name is assigned when the page is first created. It is stored persistently in the page list entry for each page. The data name is also used as the name for the stream or storage located elsewhere in the same compound file in which the page's object data is stored. The page type determines whether a stream or storage branch is used to contain the object's data. For the page list itself, an IStream interface is used by COPageList's implementation of IPersistStream. For a text page, an IStream interface is used by COTextPage's implementation of IPersistStreamInit. For a drawing page, an IStorage interface is used by CODrawPage's implementation of IPersistStorage.

If the page being opened is a text page, then CGuiList::PageOpen creates a new CGuiText C++ object and calls CGuiText::OpenWin (in GUITEXT.CPP) to create a separate text page window that has its own menu. After the window is successfully created as a child window of PERCLIEN's main window, OpenWin calls CGuiText::Load to restore the object's data from its persistent storage. We will cover this load of a text page in more detail below.

If the page being opened is a drawing page, then CGuiList::PageOpen creates a new CGuiDraw C++ object and calls CGuiDraw::OpenWin (in GUIDRAW.CPP) to create a separate drawing page window. This window has its own menu. After the window is successfully created as a child window of PERCLIEN's main window, OpenWin calls CGuiDraw::Load to restore the object's data from its persistent storage. This load of a drawing page is covered in more detail below.

Page List Persistence (IPersistStream)

CGuiList uses the persistence features of the COPageList object in the PERSERVE.DLL server. It does this in CGuiList::New, CGuiList::Load, CGuiList::Save, and CGuiList::SaveAs. These methods rely on an additional level of encapsulation in the CPageFile C++ object. For example, CGuiList::Load calls CPageFile::Load to load the page list. CPageFile is the heart of PERCLIEN's management of the persistence of the page list. The CPageFile object is first created in CGuiList::Init just after CGuiList is created. We will tour CPageFile and see how it uses COPageList's implementation of IPersistStream. The following is CPageFile::New from PAGEFILE.CPP:

  HRESULT CPageFile::New(
            TCHAR* pszFileName,
            IStorage** ppIStorage,
            IPageList** ppIPageList)
  {
    HRESULT hr = E_POINTER;
    BOOL bNewFile = (NULL != pszFileName);
    TCHAR* pszFile;
    IStorage* pIStorage;
    IStream* pIStream;
    IPersistStream* pIPersistStream;
    IPageList* pIPageList;
    ULARGE_INTEGER uliMaxSize;

    if (NULL != ppIStorage && NULL != ppIPageList)
    {
      // Zero the output pointers in case of errror.
      *ppIStorage = NULL;
      *ppIPageList = NULL;

      // If NULL file name passed then use current file name.
      pszFile = bNewFile ? pszFileName : m_szCurFileName;

      // Use COM service to create a new compound file.
      hr = StgCreateDocfile(
             pszFile,
             STGM_CREATE | STGM_DIRECT | STGM_READWRITE
               | STGM_SHARE_EXCLUSIVE,
             0,
             &pIStorage);
      if (SUCCEEDED(hr))
      {
        // We have created a new compound file. Now store the CLSID of
        // COPageList.
        hr = WriteClassStg(pIStorage, m_CidPageList);
        if (SUCCEEDED(hr))
        {
          // Now Create the single 'PageList' stream under the root storage.
          hr = pIStorage->CreateStream(
                 PAGELIST_USTR,
                 STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
                 0,
                 0,
                 &pIStream);
          if (SUCCEEDED(hr))
          {
            // Now use the Class ID to create a COPageList object. Initially
            // ask for the IPageList interface.
            hr = CoCreateInstance(
                   m_CidPageList,
                   NULL,
                   CLSCTX_INPROC_SERVER,
                   IID_IPageList  ,
                   (PPVOID)&pIPageList);
            if (SUCCEEDED(hr))
            {
              // Clear the content of the Page List.
              pIPageList->Clear();

              // Now obtain the IPersistStream interface on the new
              // COPageList object. At this point the client assumes that
              // PageList COM objects support the IPersistStream interface.
              hr = pIPageList->QueryInterface(
                     IID_IPersistStream,
                     (PPVOID)&pIPersistStream);
              if (SUCCEEDED(hr))
              {
                // And as expected by this client, COPageList supports
                // the IPersistStream interface. Now use this interface
                // to ask the COPageList object to save an empty Page List.

                // First ask the COPageList object how much space the
                // maximum save would consume and then pre-allocate that
                // space in the stream.
                hr = pIPersistStream->GetSizeMax(&uliMaxSize);
                if (SUCCEEDED(hr))
                {
                  hr = pIStream->SetSize(uliMaxSize);
                  if (SUCCEEDED(hr))
                  {
                    // Now save the new Page List into the
                    // pre-allocated space.
                    hr = pIPersistStream->Save(pIStream, TRUE);
                  }
                }

                // Done with IPersistStream for now so release it.
                pIPersistStream->Release();
              }

              // Done with the stream for now so release it.
              pIStream->Release();
            }
          }
        }
      }

      if (SUCCEEDED(hr))
      {
        // COPageList is fully created and the page list is
        // loaded. Assign a copy of COPageList's IPageList
        // interface for use by the caller.
        *ppIPageList = pIPageList;

        // For performance, keep the Page List 'Document' file
        // open (ie, don't release IStorage here) and give the
        // caller a copy of the IStorage Pointer.
        *ppIStorage = pIStorage;

        // The page list was loaded and we have a current compound
        // file name. Copy it for later use in saves and loads.
        if (bNewFile)
          lstrcpy(m_szCurFileName, pszFileName);
      }
    }

    return (hr);
  }

The name of the compound file to create is passed to New as the first parameter. StgCreateDocfile expects a Unicode string. When PERCLIEN is compiled for ANSI strings (the UNICODE macro is not defined--which is the default in the makefile), this call actually reduces to a call to an internal APPUTIL function, A_StgCreateDocfile. This function performs the proper conversion of the ANSI pszFile parameter to a Unicode version before calling StgCreateDocfile. For more details, see APPUTIL.H and STOSERVE.HTM.

The New method also receives the addresses of two output pointer variables. When New is done, it assigns an IStorage interface pointer for the root storage of the newly created compound file. New also assigns a pointer to an IPageList interface on the persistent COPageList object that is created to manage the page list data.

The COM standard StgCreateDocfile service function is called to create the compound file in which the page list data will be saved. WriteClassStg stores the CLSID under the root storage for the page list component, making the page list component the main handler of data in .PAG compound files. With a successfully created root storage, a PageList stream is created to hold the persistent object data for COPageList objects. With this stream created, an instance of COPageList is created on the server side by calling CoCreateInstance. The initial interface requested is the native IPageList interface. The COPageList object obtained from the PERSERVE server now saves an empty copy of its persistent object data into the newly created compound file. QueryInterface is called to obtain the IPersistStream interface on COPageList. IPersistStream::GetSize is called to obtain the largest size that the maximum save would consume. IPersistStream::SetSize is then used to reserve that space in the stream. To complete the new persistent COPageList, IPersistStream::Save is called to save an initial copy of the object's persistent data. Since there is no page list yet, no list array is saved, but this does save an initial set of page list properties. Some data must be saved to blank file space even for an "empty" page list. When all this is done successfully, the caller is given a copy of the pointer to the file's root storage and a copy of the pointer to the IPageList interface on the new COPageList object.

If a page list compound file already exists then CPageFile::Load is called to open the root storage of the file, create the COPageList COM object, and load COPageList's persistent data from the storage. The following is CPageFile::Load from PAGEFILE.CPP:

  HRESULT CPageFile::Load(
            TCHAR* pszFileName,
            IStorage** ppIStorage,
            IPageList** ppIPageList)
  {
    HRESULT hr = E_POINTER;
    BOOL bNewFile = (NULL != pszFileName);
    TCHAR* pszFile;
    IStorage* pIStorage;
    IStream* pIStream;
    IPersistStream* pIPersistStream;
    IPageList* pIPageList;
    ULARGE_INTEGER uliMaxSize;

    if (NULL != ppIStorage && NULL != ppIPageList)
    {
      // Zero the output pointers in case of errror.
      *ppIStorage = NULL;
      *ppIPageList = NULL;

      // If NULL file name passed then use current file name.
      pszFile = bNewFile ? pszFileName : m_szCurFileName;

      // Use COM service to check if the file is out there and is actually
      // a valid compound file.
      hr = StgIsStorageFile(pszFile);
      if (SUCCEEDED(hr))
      {
        // We have a compound file. Use COM service to open the compound file
        // and obtain a IStorage interface.
        hr = StgOpenStorage(
               pszFile,
               NULL,
               STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
               NULL,
               0,
               &pIStorage);
        if (SUCCEEDED(hr))
        {
          // We have an IStorage. Now get the ClassID of the COM Server
          // that handles Page Files.
          hr = ReadClassStg(pIStorage, &m_CidPageList);
          if (SUCCEEDED(hr))
          {
            // We have a ClassID for a server. Now Open the single 'PageList'
            // stream under the root storage.
            hr = pIStorage->OpenStream(
                   PAGELIST_USTR,
                   0,
                   STGM_READ | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
                   0,
                   &pIStream);
            if (SUCCEEDED(hr))
            {
              // Now use the previously obtained Class ID to create a
              // COPageList object. Initially ask for IPageList interface.
              hr = CoCreateInstance(
                     m_CidPageList,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IPageList  ,
                     (PPVOID)&pIPageList);
              if (SUCCEEDED(hr))
              {
                // We have a new COPageList object. Now obtain the
                // IPersistStream interface on it. The client assumes at
                // this point that PageList COM objects support only the
                // IPersistStream interface for their persistence.
                hr = pIPageList->QueryInterface(
                       IID_IPersistStream,
                       (PPVOID)&pIPersistStream);
                if (SUCCEEDED(hr))
                {
                  // And as expected by this client, COPageList supports
                  // the IPersistStream interface. Now use this interface
                  // to ask the COPageList object to load the Page List.
                  hr = pIPersistStream->Load(pIStream);

                  // Done with IPersistStream for now so release it.
                  pIPersistStream->Release();
                }
                else
                  pIPageList->Release();
              }

              // Done with the stream for now so release it.
              pIStream->Release();
            }
          }
        }
      }
      else
      {
        // If there was no existing compound file use COM services to
        // create a new compound file.
        hr = StgCreateDocfile(
               pszFile,
               STGM_CREATE | STGM_DIRECT | STGM_READWRITE
                 | STGM_SHARE_EXCLUSIVE,
               0,
               &pIStorage);
        if (SUCCEEDED(hr))
        {
          // We have created a new compound file. Now store the CLSID of
          // COPageList.
          hr = WriteClassStg(pIStorage, m_CidPageList);
          if (SUCCEEDED(hr))
          {
            // Now Create the single 'PageList' stream under the root storage.
            hr = pIStorage->CreateStream(
                   PAGELIST_USTR,
                   STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
                   0,
                   0,
                   &pIStream);
            if (SUCCEEDED(hr))
            {
              // Now use the Class ID to create a COPageList object. Initially
              // ask for the IPageList interface.
              hr = CoCreateInstance(
                     m_CidPageList,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IPageList  ,
                     (PPVOID)&pIPageList);
              if (SUCCEEDED(hr))
              {
                // We have a new COPageList object. Now obtain the
                // IPersistStream interface on it. The client assumes at
                // this point that PageList COM objects support only the
                // IPersistStream interface for their persistence.
                hr = pIPageList->QueryInterface(
                     IID_IPersistStream,
                       (PPVOID)&pIPersistStream);
                if (SUCCEEDED(hr))
                {
                  // And as expected by this client, COPageList supports
                  // the IPersistStream interface. Now use this interface
                  // to ask the COPageList object to save an empty Page List.

                  // First ask the COPageList object how much space the
                  // next save would consume and then pre-allocate that
                  // space in the stream.
                  hr = pIPersistStream->GetSizeMax(&uliMaxSize);
                  if (SUCCEEDED(hr))
                  {
                    hr = pIStream->SetSize(uliMaxSize);
                    if (SUCCEEDED(hr))
                    {
                      // Now save the new Page List into the
                      // pre-allocated space.
                      hr = pIPersistStream->Save(pIStream, TRUE);
                    }
                  }

                  // Done with IPersistStream for now so release it.
                  pIPersistStream->Release();
                }

                // Done with the stream for now so release it.
                pIStream->Release();
              }
            }
          }
        }
      }

      if (SUCCEEDED(hr))
      {
        // COPageList is fully created and the page list is
        // loaded. Assign a copy of COPageList's IPageList
        // interface for use by the caller.
        *ppIPageList = pIPageList;

        // For performance, keep the Page List 'Document' file
        // open (ie, don't release IStorage here) and give the
        // caller a copy of the IStorage Pointer.
        *ppIStorage = pIStorage;

        // The page list was loaded and we have a current compound
        // file name. Copy it for later use in saves and loads.
        if (bNewFile)
          lstrcpy(m_szCurFileName, pszFileName);
      }
    }

    return (hr);
  }

CPageFile::Load does double duty, either creating a new compound file or opening and loading an existing one. The file path name is passed and the StgIsStorageFile is called to determine if the file exists and is a valid compound file. If the file does not exist or is not a valid compound file, Load creates a new file in the same manner as in the New method above. If the file does exist, the StgOpenStorage function opens the compound file and obtain an IStorage interface pointer to the root storage in the file. All the stored elements of a previously saved page list is assumed present in the root storage branch. ReadClassStg is called to obtain the CLSID to use in creating the persistent COPageList object. The PageList stream is opened with the standard IStorage::OpenStream method. CoCreateInstance is called to create the persistent COPageList object. With COPageList created, the IPageList interface is used to QueryInterface for a pointer to the IPersistStream interface. Using the IPersistStream interface on the COPageList object, IPersistStream::Load directs the object to load its persistent object data from the stream.

CPageFile::Save is called in PERCLIEN to save the currently active COPageList persistent object. The following is CPageFile::Save from PAGEFILE.CPP:

  HRESULT CPageFile::Save(
            IStorage* pIStorage,
            IPageList* pIPageList)
  {
    HRESULT hr = E_POINTER;
    IStream* pIStream;
    IPersistStream* pIPersistStream;

    if (NULL != pIStorage && NULL != pIPageList)
    {
      // Obtain the IPersistStream interface on the COPageList
      // COM object.
      hr = pIPageList->QueryInterface(
             IID_IPersistStream,
             (PPVOID)&pIPersistStream);
      if (SUCCEEDED(hr))
      {
        // Save if the data is dirty (ie, doesn't match file data).
        if (S_FALSE != pIPersistStream->IsDirty())
        {
          // Use the existing IStorage to Open the single 'PageList' stream
          // under the root storage.
          hr = pIStorage->OpenStream(
                 PAGELIST_USTR,
                 0,
                 STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
                 0,
                 &pIStream);
          if (SUCCEEDED(hr))
          {
            // Ask the persistent object to save itself in the stream
            // and clear the PageList dirty bit.
            hr = pIPersistStream->Save(pIStream, TRUE);

            // Done with the stream for now so release it.
            pIStream->Release();
          }
        }

        // Done with IPersistStream for now so release it.
        pIPersistStream->Release();
      }
    }

    return (hr);
  }

A pointer to the IStorage interface for the root storage of the currently open page list compound file is passed as well as a pointer to the IPageList interface for the persistent COPageList object. QueryInterface is called on IPageList to obtain a pointer to COPageList's IPersistStream interface. IPersistStream::IsDirty then asks the object if it needs to be saved. ("Dirty" here means that the object data in RAM does not match the data stored on file.) If the object does not need to be saved then Save releases IPersistStream and returns without saving. If the object does need saving then the IStorage interface is used to open the PageList stream. Note that CPageFile holds interface pointers for the storage and COPageList but does not retain any stream interface pointers. CPageFile freshly opens the stream as needed and closes it when done. With the PageList stream open, IPersistStream::Save is called on the COPageList persistent object to direct it to save its object data.

Text Page Persistence (IPersistStreamInit)

You have seen how PERCLIEN uses the IPersistStream interface on the persistent COPageList object to load and save the page list. This section describes how PERCLIEN uses the IPersistStreamInit interface on the persistent COTextPage object to load and save text pages. The following is CGuiText::Load from GUITEXT.CPP:

  HRESULT CGuiText::Load(void)
  {
    HRESULT hr = E_FAIL;
    IStream* pIStream;
    IPersistStreamInit* pIPersistStreamInit;

    // Can't do anything without a root storage.
    if (NULL != m_pIStorage_Root)
    {
      // Use the root IStorage to open the existing stream for
      // this particular Text Page. Load the data for the text page from
      // the stream. This load internally uses the IPersistStreamInit
      // features in a COTextPage object that is created and reconstituted
      // from persistent storage.

      // Open the single stream named by m_wszDataName. It is under
      // the root storage.
      hr = m_pIStorage_Root->OpenStream(
             m_wszDataName,
             0,
             STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
             0,
             &pIStream);
      if (SUCCEEDED(hr))
      {
        // Read the ClassID for the class of Component Objects that can deal
        // with Text Page data.
        hr = ReadClassStm(pIStream, &m_CidTextPage);
        if (SUCCEEDED(hr))
        {
          // Now use the obtained Class ID to create a COTextPage
          // object. Initially ask for the ITextPage interface.
          hr = CoCreateInstance(
                 m_CidTextPage,
                 NULL,
                 CLSCTX_INPROC_SERVER,
                 IID_ITextPage,
                 (PPVOID)&m_pITextPage);
          if (SUCCEEDED(hr))
          {
            // We have a new COTextPage object. Now obtain the
            // IPersistStreamInit interface on it. At this point in the
            // client we are assuming that COTextPage uses only the
            // IPersistStreamInit interface for its persistence.
            hr = m_pITextPage->QueryInterface(
                   IID_IPersistStreamInit,
                   (PPVOID)&pIPersistStreamInit);
            if (SUCCEEDED(hr))
            {
              // And as expected by this client, COTextPage exposes
              // the IPersistStreamInit interface. Now use this interface
              // to ask the COTextPage object to load the Text Page data.
              hr = pIPersistStreamInit->Load(pIStream);

              // Done with IPersistStreamInit for now so release it.
              pIPersistStreamInit->Release();
            }
          }
        }
        // Done with the interface held on the stream.
        pIStream->Release();
      }
      else
      {
        // If there was no existing Stream then create a new one.
        hr = m_pIStorage_Root->CreateStream(
               m_wszDataName,
               STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               0,
               &pIStream);
        if (SUCCEEDED(hr))
        {
          // Write the ClassID of the COTextPage component object class.
          hr = WriteClassStm(pIStream, m_CidTextPage);
          if (SUCCEEDED(hr))
          {
            // Now use the Class ID to create a COTextPage
            // object. Initially ask for the ITextPage interface.
            hr = CoCreateInstance(
                   m_CidTextPage,
                   NULL,
                   CLSCTX_INPROC_SERVER,
                   IID_ITextPage,
                   (PPVOID)&m_pITextPage);
            if (SUCCEEDED(hr))
            {
              // We have a new COTextPage object. Now obtain the
              // IPersistStreamInit interface on it. At this point in the
              // client we are assuming that COTextPage uses only the
              // IPersistStreamInit interface for its persistence.
              hr = m_pITextPage->QueryInterface(
                     IID_IPersistStreamInit,
                     (PPVOID)&pIPersistStreamInit);
              if (SUCCEEDED(hr))
              {
                // And as expected by this client, COTextPage supports
                // the IPersistStreamInit interface. Now use this interface
                // to ask the COTextPage object to initialize a new empty
                // text page. Then save it into the stream.
                hr = pIPersistStreamInit->InitNew();
                if (SUCCEEDED(hr))
                  hr = pIPersistStreamInit->Save(pIStream, TRUE);

                // Done with IPersistStreamInit for now so release it.
                pIPersistStreamInit->Release();
              }
            }
          }

          // Done with the interface held on the stream.
          pIStream->Release();

          if (FAILED(hr))
            m_pIStorage_Root->DestroyElement(m_wszDataName);
        }
      }
    }

    return hr;
  }

Like CPageFile::Load discussed in the preceding section, CGuiText::Load does double duty by either creating a new stream for the text page or opening and loading an existing one. If the text page stream is present it opens and loads from that stream. If the stream is not present, the stream is created and an empty text page is written into the stream. Load assumes the compound file is already opened and that CGuiText::m_pIStorage_Root has the IStorage interface for the file's root storage. Load also assumes that when CGuiText was initialized it was given the data name of the stream where the text page data is stored. This name is kept in CGuiText::m_wszDataName. This name is used to open or create the stream for the text page.

IStorage::OpenStream is called and if the stream exists, the COM ReadClassStm function is called to obtain the CLSID for the server object that can handle text page data. This CLSID is then used in a call to the COM CoCreateInstance function to create the COTextPage COM object in the PERTEXT server. A pointer to the ITextPage interface is initially requested. QueryInterface is also called to obtain a pointer to the IPersistStreamInit interface on the new COTextPage object. IPersistStreamInit::Load is then directs COTextPage to load its persistent object data from the opened stream.

If IStorage::OpenStream indicates that there is no existing stream with the name in m_wszDataName, then IStorage::CreateStream creates a new stream of this name. Because this is a new stream, the WriteClassStm COM function is called to store the CLSID for the class of components that can handle text page data. The COM CoCreateInstance function is called to create the COTextPage object in the PERTEXT server. A pointer to the ITextPage interface is initially requested. QueryInterface is also called to obtain a pointer to the IPersistStreamInit interface on the new COTextPage. Because this is an entirely new instance of COTextPage the IPersistStreamInit::InitNew method is called to initialize the new instance in RAM. With the new instance created, IPersistStreamInit::Save is finally used to save an empty text page into the stream. In contrast to the IPersistStream implementation used for page lists (described above), the InitNew method in IPersistStreamInit adds convenient provisions for initializing the new instance of COTextPage.

CGuiText::Save is called in PERCLIEN to save a currently active COTextPage persistent object. The following is CGuiText::Save from GUITEXT.CPP.

  HRESULT CGuiText::Save(void)
  {
    HRESULT hr = E_FAIL;
    HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
    HCURSOR hCurPrev;
    INT iTextLength;
    WCHAR* pwszText;
    LARGE_INTEGER liSeek;
    IStream* pIStream;
    IPersistStreamInit* pIPersistStreamInit;

    if (NULL != m_pIStorage_Root && NULL != m_pITextPage)
    {
      // Change cursor to the hour glass.
      hCurPrev = SetCursor(hCurWait);

      // Open the single stream named by m_wszDataName. It is under
      // the root storage.
      hr = m_pIStorage_Root->OpenStream(
             m_wszDataName,
             0,
             STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
             0,
             &pIStream);
      if (SUCCEEDED(hr))
      {
        // First ask the edit control how long its text data is.
        hr = m_pTextWin->GetLength(&iTextLength);
        if (SUCCEEDED(hr))
        {
          // Allocate some temporary space for the text.
          pwszText = new WCHAR[iTextLength+4];
          if (NULL != pwszText)
          {
            // Zero the text string.
            memset(pwszText, 0, (iTextLength+2) * sizeof(WCHAR));

            // Get the text from the the edit control.
            hr = m_pTextWin->GetText(pwszText);
            if (SUCCEEDED(hr))
            {
              // Put the text into the COTextPage object.
              hr = m_pITextPage->PutText(pwszText, iTextLength);
              if (SUCCEEDED(hr))
              {
                // Clear the TextWin changed flag.
                m_bChanged = FALSE;

                // Tell the COTextPage object to save itself
                // (via its IPersistStreamInit interface).
                hr = m_pITextPage->QueryInterface(
                       IID_IPersistStreamInit,
                       (PPVOID)&pIPersistStreamInit);
                if (SUCCEEDED(hr))
                {
                  // Recue Stream to start of page text data.
                  LISet32(liSeek, sizeof(CLSID));
                  hr = pIStream->Seek(liSeek, STREAM_SEEK_SET, NULL);
                  if (SUCCEEDED(hr))
                    hr = pIPersistStreamInit->Save(pIStream, TRUE);

                  // Done with IPersistStreamInit for now so release it.
                  pIPersistStreamInit->Release();
                }
              }
            }
            // Delete the temporary text buffer.
            delete [] pwszText;
          }
          else
            hr = E_OUTOFMEMORY;
        }
        // Done with the interface held on the stream.
        pIStream->Release();
      }

      // Set Cursor back to what it was.
      SetCursor(hCurPrev);
    }

    return hr;
  }

The stream whose name is kept in CGuiText::m_wszDataName is freshly opened and is released at the end. The streams for text pages are not kept open but are opened and closed as needed by CGuiText. With the existing stream open, CGuiText::Save works with the multi-line edit control to obtain a copy of the text of the current text page. The edit control is encapsulated in the CTextWin C++ object. A pointer to this object is kept in CGuiText::m_pTextWin. The text is obtained from CTextWin and ITextPage::PutText is called to put the text into the COTextPage object. COTextPage now has the text in RAM. QueryInterface is called to obtain a pointer to the IPersistStreamInit interface on the COTextPage object. IStream::Seek is called to reset the seek pointer of the stream to its start. Finally, IPersistStreamInit::Save is called to direct the COTextPage object to save its persistent object data to the stream.

Drawing Page Persistence (IPersistStorage)

This lesson has now explained how PERCLIEN uses the IPersistStream interface on the persistent COPageList object to load and save the page list, and how PERCLIEN uses the IPersistStreamInit interface on the persistent COTextPage object to load and save text pages. This section describes how PERCLIEN uses the IPersistStorage interface on the persistent CODrawPage object to load and save drawing pages. The following is CGuiDraw::Load from GUIDRAW.CPP:

  HRESULT CGuiDraw::Load(void)
  {
    HRESULT hr = E_FAIL;
    IPersistStorage* pIPersistStorage;

    // Can't do anything without a root storage.
    if (NULL != m_pIStorage_Root)
    {
      // Use the root IStorage to open the nested substorage for this
      // particular DrawPage. Load the drawing data from the substorage.
      // This load internally uses the IPersistStorage features in a
      // created CODrawPage object to reconstitute the object from
      // persistent storage. CODrawPage handles its own persistence that
      // is client-controlled via its exposed IPersistStorage interface.

      // Open the single storage named by m_wszDataName. It is under
      // the root storage. The client here is in charge of the underlying
      // storage that the client asks CODrawPage to use.
      hr = m_pIStorage_Root->OpenStorage(
             m_wszDataName,
             NULL,
             STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
             NULL,
             0,
             &m_pIStorage_Page);
      if (SUCCEEDED(hr))
      {
        // If the storage already exists, read the ClassID for the class of
        // component objects that can deal with DrawPage data.
        hr = ReadClassStg(m_pIStorage_Page, &m_CidDrawPage);
        if (SUCCEEDED(hr))
        {
          // Now use the obtained Class ID to create a CODrawPage
          // object. Initially ask for the IDrawPage interface.
          hr = CoCreateInstance(
                 m_CidDrawPage,
                 NULL,
                 CLSCTX_INPROC_SERVER,
                 IID_IDrawPage,
                 (PPVOID)&m_pIDrawPage);
          if (SUCCEEDED(hr))
          {
            // We have a new CODrawPage object. Now obtain the
            // IPersistStorage interface on it. At this point in the
            // client we are assuming that CODrawPage objects use only
            // the IPersistStorage interface for their persistence.
            hr = m_pIDrawPage->QueryInterface(
                   IID_IPersistStorage,
                   (PPVOID)&pIPersistStorage);
            if (SUCCEEDED(hr))
            {
              // And as expected by this client, CODrawPage exposes
              // the IPersistStorage interface. Now use this interface
              // to ask the CODrawPage object to load the drawing data.
              hr = pIPersistStorage->Load(m_pIStorage_Page);

              // Done with IPersistStorage for now so release it.
              pIPersistStorage->Release();
            }
          }
        }
      }
      else
      {
        // If there was no existing page substorage then create a new one.
        hr = m_pIStorage_Root->CreateStorage(
               m_wszDataName,
               STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               0,
               &m_pIStorage_Page);
        if (SUCCEEDED(hr))
        {
          // Write the ClassID of the CODrawPage component object class.
          hr = WriteClassStg(m_pIStorage_Page, m_CidDrawPage);
          if (SUCCEEDED(hr))
          {
            // Now use the Class ID to create a CODrawPage
            // object. Initially ask for the IDrawPage interface.
            hr = CoCreateInstance(
                   m_CidDrawPage,
                   NULL,
                   CLSCTX_INPROC_SERVER,
                   IID_IDrawPage,
                   (PPVOID)&m_pIDrawPage);
            if (SUCCEEDED(hr))
            {
              // We have a new CODrawPage object. Now obtain the
              // IPersistStorage interface on it. At this point in the
              // client we are assuming that CODrawPage objects use only
              // the IPersistStorage interface for their persistence.
              hr = m_pIDrawPage->QueryInterface(
                     IID_IPersistStorage,
                     (PPVOID)&pIPersistStorage);
              if (SUCCEEDED(hr))
              {
                // And as expected by this client, CODrawPage supports
                // the IPersistStorage interface. Now use this interface
                // to ask the CODrawPage object to initialize a new empty
                // drawing page and save it into the substorage.
                hr = pIPersistStorage->InitNew(m_pIStorage_Page);

                // Done with IPersistStorage for now so release it.
                pIPersistStorage->Release();
              }
            }
          }

          if (FAILED(hr))
            m_pIStorage_Page->DestroyElement(m_wszDataName);
        }
      }
    }

    return hr;
  }

Like the other load methods above, CGuiDraw::Load does double duty. It either creates a new storage for the drawing page or it opens and loads an existing one. If the drawing page storage is present, it opens and loads from that storage branch. If the storage is not present, the storage is created and an empty drawing page is written into the storage. Load assumes the compound file is already opened and that CGuiDraw::m_pIStorage_Root has the IStorage interface for the file's root storage. Load also assumes that when CGuiDraw was initialized it was given the data name of the storage where the drawing page data is stored. This name is kept in CGuiDraw::m_wszDataName. This name is used to open or create the storage for the drawing page.

IStorage::OpenStorage is called and if the storage exists, the COM ReadClassStg function obtains the CLSID for the server object that can handle drawing page data. This CLSID is then used in a call to the COM CoCreateInstance function to create the CODrawPage COM object in the PERDRAW server. A pointer to the IDrawPage interface is initially requested. QueryInterface is also called to obtain a pointer to the IPersistStorage interface on the new CODrawPage. IPersistStorage::Load is then used to direct CODrawPage to load its persistent object data from the opened storage.

If IStorage::OpenStorage indicates that there is no existing storage with the name in m_wszDataName, then a new storage of this name is created. IStorage::CreateStorage is called to perform the creation. Because this is a new sub-storage, the WriteClassStg COM function is called to store the CLSID for the class of components that can handle drawing page data. The COM CoCreateInstance function is called to create the CODrawPage object in the PERDRAW server. A pointer to the IDrawPage interface is initially requested. QueryInterface is also called to obtain a pointer to the IPersistStorage interface on the new CODrawPage. Since this is an entirely new instance of CODrawPage the IPersistStorage::InitNew is called to initialize the new CODrawPage persistent object. InitNew initializes CODrawPage's object data in RAM and also saves this empty drawing to the storage.

CGuiDraw::Save is called in PERCLIEN to save a currently active CODrawPage persistent object. The followin is CGuiDraw::Save from GUIDRAW.CPP:

  HRESULT CGuiDraw::Save(
            void)
  {
    HRESULT hr = E_FAIL;
    HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
    HCURSOR hCurPrev;
    IPersistStorage* pIPersistStorage;

    if (NULL != m_pIStorage_Page && NULL != m_pIDrawPage)
    {
      // Change cursor to the hour glass.
      hCurPrev = SetCursor(hCurWait);

      // Tell the CODrawPage object to save itself
      // (via its IPersistStorage interface).
      hr = m_pIDrawPage->QueryInterface(
             IID_IPersistStorage,
             (PPVOID)&pIPersistStorage);
      if (SUCCEEDED(hr))
      {
        // Perform the save with fSameAsLoad==TRUE.
        hr = pIPersistStorage->Save(m_pIStorage_Page, TRUE);
        if (SUCCEEDED(hr))
        {
          // This is the general save by the client. In the case of
          // this application the client does not need to do any other
          // intermediate save or write operations to the compound file
          // prior to informing the server that client save operations
          // are completed.
          hr = pIPersistStorage->SaveCompleted(NULL);
        }

        // Done with IPersistStorage for now so release it.
        pIPersistStorage->Release();
      }

      // Set Cursor back to what it was.
      SetCursor(hCurPrev);
    }

    return hr;
  }

The CGuiDraw object uses the copy of the IDrawPage interface pointer it keeps in member m_pIDrawPage to QueryInterface for a pointer to the IPersistStorage interface on the CODrawPage object. IPersistStorage::Save is called to direct CODrawPage to save its persistent object data to the already opened storage (m_pIStorage_Page). Finally, IPersistStorage::SaveCompleted is called to complete the save operation. Other intervening save or write actions could be performed between the Save and the SaveCompleted but are not needed in this application.

Top Back to page top

© 1995-1998 Microsoft Corporation


DCDMARSH - Standard Marshaling for DCOM - Lesson 29

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DCDMARSH - Standard Marshaling for DCOM

SUMMARY

The DCDMARSH sample builds the DCDMARSH.DLL proxy/stub server. DCDMARSH.DLL works with DCDSERVE.EXE and DCOMDRAW.EXE to provide standard marshaling of the ISharePaper and IPaperSink custom interfaces across process and machine boundaries. In this set of samples, DCDMARSH.DLL is the marshaling server, DCDSERVE.EXE is the out-of-process component server, and DCOMDRAW.EXE is the client. As a Distributed COM (DCOM) sample, DCOMDRAW.EXE creates and uses components housed in the DCDSERVE.EXE server. Multiple DCOMDRAW clients running on multiple computers connected to a network can access a shared single COPaper object housed in the DCDSERVE.EXE server. Compared to previous samples requiring marshaling across process or machine boundaries, DCDMARSH requires no significant differences in how you provide standard marshaling for your custom interfaces. This lesson is therefore almost completely based on the standard marshaling techniques that were used in the MARSHAL sample. See the MARSHAL lesson for more details on these techniques.

The Microsoft Interface Definition Language (MIDL) compiler is used to compile the interface specifications (expressed using the MIDL language in the PAPINT.IDL file). Microsoft provides the MIDL.EXE as part of the Platform SDK. The MIDL compilation of PAPINT.IDL generates the PAPINT.H, PAPINT_I.C, PAPINT_P.C, and DLLDATA.C source files. The primary source for the DCDMARSH sample is in the PAPINT.IDL file. It contains the IDL specifications for the interfaces that DCDSERVE.DLL marshals. After the makefile copies the MIDL-generated PAPINT.H file to the common INC directory, PAPINT.H is available for use by such samples as DCDMARSH and DCOMDRAW.

For functional descriptions and a tutorial code tour of the DCDMARSH sample, see the Code Tour section in DCDMARSH.HTM. For details on setting building and registering DCDMARSH.DLL, see the Usage section in DCDMARSH.HTM. To read DCDMARSH.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DCDMARSH lesson in the table of lessons. You can do the same thing by double-clicking the DCDMARSH.HTM file after locating the main tutorial directory in the Windows Explorer. See also DCDSERVE.HTM and DCOMDRAW.HTM in the main tutorial directory for more details on this client and server and how DCDMARSH.DLL supports their operation. Because those code samples rely on DCDMARSH.DLL, you must build it before building or running DCOMDRAW and DCDSERVE. DCDMARSH's makefile automatically registers DCDMARSH's proxy and stub interface marshalers in the system registry. This registration must be done before these interfaces can be used by COM clients or servers in the DCDSERVE and DCOMDRAW samples.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DCDMARSH sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DCDMARSH.DSP

You can also simply double-click the DCDMARSH.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

DCDMARSH.DLL is built solely as a marshaling DLL for certain interfaces. Although it can be implicitly loaded by linking to its associated .LIB file, it is normally used on behalf of a COM client using the interfaces across apartment, process, or machine boundaries. In these cases COM loads this DLL when needed. Before COM can find and load DCDMARSH.DLL to provide marshaling for its supported interfaces, the DCDMARSH server must be registered in the registry as the marshaling server for those interfaces. DCDMARSH is a self-registering in-process server. It exports the standard DllRegisterServer and DllUnregisterServer functions.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the DCDMARSH directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the DCDMARSH directory.

  ..\register\register.exe dcdmarsh.dll

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of DCDMARSH.DLL.

In this series, the makefiles use the REGISTER.EXE utility from the REGISTER sample. Recent releases of the Platform SDK and Visual C++® include a utility, REGSVR32.EXE, that can be used in a similar fashion to register in-process servers and marshaling DLLs.

CODE TOUR

Files        Description
DCDMARSH.TXT Short description of the sample.
MAKEFILE     The generic makefile for building the code sample.
DCDMARSH.DEF The module definition file. Exports server housing functions.
PAPINT.IDL   The MIDL interface specifications for ISharePaper and
             IPaperSink.
PAPINT.H     Generated by compiling PAPINT.IDL. The interface include
             file for the specified interfaces. Included by other modules.
PAPINT_I.C   Generated by compiling PAPINT.IDL. The data definitions
             of the GUIDs for the marshaled interfaces.
PAPINT_P.C   Generated by compiling PAPINT.IDL. The actual proxy and
             stub functions for the interface methods.
DLLDATA.C    Generated by compiling PAPINT.IDL. DLL data routines for
             the proxies and default definitions for the DllMain,
             DllRegisterServer, and DllUnregisterServer functions.
DCDMARSH.DSP Microsoft Visual Studio Project file.

PAPINT.IDL contains the principal source code for this sample. Here are the IDL interface specifications from PAPINT.IDL for the ISharePaper and IPaperSink interfaces.

  [uuid(0002da31-0000-0000-c000-000000000046),
      object
  ]
  interface ISharePaper : IUnknown
  {
    import "unknwn.idl";

    HRESULT InitPaper(
              [in,out] RECT* pWinRect,
              [out]    BOOL* pbFirst);
    HRESULT Lock(
              [in] BOOL bLock);
    HRESULT Load(
              [out] RECT* pWinRect);
    HRESULT Save(
              void);
    HRESULT InkStart(
              [in] SHORT nX,
              [in] SHORT nY,
              [in] SHORT nInkWidth,
              [in] COLORREF crInkColor);
    HRESULT InkDraw(
              [in] SHORT nX,
              [in] SHORT nY);
    HRESULT InkStop(
              [in] SHORT nX,
              [in] SHORT nY);
    HRESULT GetInk(
              [in] LONG lIndex,
              [out] SHORT* pnInkType,
              [out] SHORT* pnX,
              [out] SHORT* pnY,
              [out] SHORT* pnInkWidth,
              [out] COLORREF* pcrInkColor);
    HRESULT Erase(
              void);
    HRESULT Resize(
              [in] LONG lWidth,
              [in] LONG lHeight);
  }

  [uuid(0002da34-0000-0000-c000-000000000046),
      object
  ]
  interface IPaperSink : IUnknown
  {
    import "unknwn.idl";

    HRESULT Locked(
              void);
    HRESULT Unlocked(
              void);
    HRESULT Loaded(
              void);
    HRESULT Saved(
              void);
    HRESULT InkStart(
              [in] SHORT nX,
              [in] SHORT nY,
              [in] SHORT nInkWidth,
              [in] COLORREF crInkColor);
    HRESULT InkDraw(
              [in] SHORT nX,
              [in] SHORT nY);
    HRESULT InkStop(
              [in] SHORT nX,
              [in] SHORT nY);
    HRESULT Erased(
              void);
    HRESULT Resized(
              [in] LONG lWidth,
              [in] LONG lHeight);
  }

  cpp_quote ("// The types of Ink Data.")
  cpp_quote ("#define INKTYPE_NONE  0")
  cpp_quote ("#define INKTYPE_START 1")
  cpp_quote ("#define INKTYPE_DRAW  2")
  cpp_quote ("#define INKTYPE_STOP  3")

The custom IPaperSink interface is the same interface used in the connectable-object features of the STOSERVE, STOCLIEN, PERDRAW, and PERCLIEN samples. In those samples the IPaperSink interface required no marshaling because the servers were all in-process servers. Now, for the first time in the series, connectable-object technology is shown operating across both process and machine boundaries in DCDSERVE and DCOMDRAW. Thus, the custom IPaperSink now requires the standard marshaling support provided by the DCDMARSH.DLL of this sample. The COPaper object in the DCDSERVE server calls methods in IPaperSink to make event notifications to the connection sink in the DCOMDRAW client. See such earlier samples as STOSERVE and STOCLIEN for more details on how the IPaperSink interface works as a connectable-object interface.

The custom ISharePaper interface is similar to the IPaper and IDrawPage interfaces used in the STOSERVE, STOCLIEN, PERDRAW, and PERCLIEN samples. These interfaces are implemented to expose a set of features for free-form drawing on an electronic paper surface using "ink" of specified color and width. This functionality is outwardly similar to the "scribble" tutorial samples in many versions of the Microsoft Visual C++ product. See earlier samples like STOSERVE and STOCLIEN for more details on how the ISharePaper interface works as a drawing interface.

The "cpp_quote" directives shown above simply pass the quoted text through the MIDL compilation process to the output PAPINT.H file. They generate #define macros for use during C++ compilation. For the modules that include PAPINT.H at C++ compilation time, these macros specify the possible ink data types used in the drawing process. This illustrates an additional way to use the .IDL file as a primary repository of specification for important aspects of the interfaces to be marshaled.

Top Back to page top

© 1995-1998 Microsoft Corporation


DCDSERVE - DCOM Server Using Security - Lesson 30

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List 		Next Lesson
Next Lesson

DCDSERVE - DCOM Server Using Security

SUMMARY

The DCOMDRAW, DCDSERVE, and DCDMARSH samples together form a distributed drawing application. Using Distributed COM (DCOM) technology, they allow users on different client machines in a network to interactively draw on a shared drawing. All DCOMDRAW clients see the same drawing and any client can take ownership of the pen to draw. The user of DCOMDRAW can use a mouse or tablet device to draw in the client window and can choose the color and width of the electronic ink. As clients draw, the application echoes the drawing activity from DCDSERVE to all connected clients using DCOM over the network. Although these samples do not build an optimized and complete application, the functionality is suggestive of the shared whiteboards offered by some workgroup applications.

In addition to providing a standard way to construct applications from re-usable software components, COM technology fosters a strong architectural separation between client and server. This separation has been the repeated pattern in previous samples of this series. For example, the STOSERVE server houses a COPaper COM object that is used by the STOCLIEN client. Although they are a client/server pair, the STOCLIEN and STOSERVE samples use an in-process server for the COPaper COM object. With the DCOMDRAW and DCDSERVE client/server samples the architectural separation between client and server is retained but the COPaper COM object is housed in an out-of-process server, DCDSERVE. COPaper implements a custom ISharePaper interface to permit multiple clients to use the COPaper object simultaneously. The application also requires a DCDMARSH server to provide standard marshaling for the custom interfaces used across process and machine boundaries.

Although DCOMDRAW, DCDSERVE, and DCDMARSH can work on the same machine across process boundaries, the main goal is to provide a distributed application that enables multiple DCOMDRAW clients on different machines to access a shared single drawing object housed in a DCDSERVE server running on a common server machine. A typical scenario would have numerous client machines hooked to an intranet consisting of a Windows NT network domain or peer-to-peer workgroup. DCOMDRAW can then run on any of these client machines. DCDSERVE is installed and runs on a common server machine in the domain. The user of the DCOMDRAW client can interactively choose the remote server machine to determine which DCDSERVE COM server to use for the shared drawing.

Like the STOSERVE sample, which provided the CLSID_DllPaper component, DCDSERVE provides the CLSID_SharePaper component. Using this component, DCDSERVE uses the SharePaper component to manage a shared, single COPaper COM object that models a sheet of white drawing paper. Interface methods on COPaper objects enable free-form drawing on the paper surface using "ink" of specified color and width. This functionality is outwardly similar to the "scribble" tutorial samples in many versions of the Microsoft Visual C++® product. The electronic drawing paper features of COPaper objects are available to clients through a custom ISharePaper interface. COPaper implements the ISharePaper interface. A clear architectural distinction is kept between client and server. No graphical user interface (GUI) is provided by COPaper. Instead, the COPaper object relies on the client for all GUI behavior. COPaper encapsulates only the server-based capture and storage of the drawn ink data.

The ink data that is drawn on the COPaper surface can be stored in and loaded from compound files. The ISharePaper::Save and ISharePaper::Load method implementations in COPaper use structured storage to store the current data of the shared drawing in a compound file kept with DCDSERVE on the machine acting as the common server.

The primary focus of this sample is how to construct a single, shared, and thread-safe COM object on the server side and how to take care of COM security in the networked context of multiple clients of that shared object. The sample covers both process and activation COM security. A simple shared drawing sample is used to study these issues in a Distributed COM client/server application.

COPaper is housed in an out-of-process server and is made publicly available as a custom COM component. Like all other servers in this tutorial series, DCDSERVE.EXE is a self-registering COM server. It makes the COPaper object type available to clients as the SharePaper component using a CLSID_SharePaper registration in the Registry.

As was the case in the previous STOSERVE server, the COPaper object in DCDSERVE supports connectable-object features. COPaper exposes the IConnectionPointContainer interface and implements an appropriate connection point. In this context, an outgoing custom IPaperSink interface is declared for use in sending notifications to the DCOMDRAW client. The use of COM connection-point technology in this sample is significant because it is used in a distributed application that crosses machine boundaries. The connection points are used to echo highly interactive mouse-motion events across the network to DCOMDRAW clients that are connected to the same COPaper drawing. Despite the overhead of DCOM, RPC, and marshaling, the DCDSERVE and DCOMDRAW samples demonstrate that COM's connectable-object technology can play a role in distributed COM-based applications.

The two ISharePaper and IPaperSink custom interfaces are declared in PAPINT.H, which is located in the common INC directory. PAPINT.H is automatically generated in the DCDMARSH sample. The GUIDs for the interfaces and objects are defined in PAPGUIDS.H located in that same directory.

The CThreaded facility in APPUTIL is used by DCDSERVE to achieve thread safety, as it was in the FRESERVE sample. COPaper objects are derived from the CThreaded class and inherit its OwnThis and UnOwnThis methods. These methods allow only one thread at a time to have access to the DCDSERVE server and to the shared single COPaper object managed by the server. Because the Single Threaded Apartment (STA) model is in effect for both DCDSERVE and DCOMDRAW, this thread safety is not actually needed for COPaper but is still appropriate for the DCDSERVE server housing.

Because client and server run in separate processes--usually on different machines--both DCDSERVE and DCOMDRAW rely on standard marshaling for the ISharePaper and IPaperSink custom interfaces. This support is provided by the DCDMARSH code sample, so you must also build (or otherwise register) DCDMARSH.DLL on both machines prior to building and running DCDSERVE and DCOMDRAW across machines.

To set up for the proper operation of a DCOMDRAW client on one machine to control the common DCDSERVE server on another, both machines must have DCOM (Distributed COM) installed. DCOM is included in Windows NT 4.0 or later and in Windows 98. For Windows 95 you must install the DCOM95 add on. The multiple machines must be connected in a properly configured network. For details on setting up a network, see your Windows NT product documentation or the Windows NT Resource Kit. For more details on machine and network setup for running with DCOM, see also the "Network and Setup Issues" section at the end of the REMCLIEN lesson. Note that the less restrictive security in Windows 95 and Windows 98 prevents the SCM under DCOM from automatically launching DCDSERVE on behalf of a remote DCOMDRAW client. You must manually pre-launch DCDSERVE on these operating systems if you are attempting a remote load of DCDSERVE from a DCOMDRAW running on another machine. The DCDSERVE and DCOMDRAW lessons assume that you install DCDSERVE.EXE on a machine running Windows NT Server or Workstation.

For functional descriptions and a tutorial code tour of DCDSERVE, see the Code Tour section in DCDSERVE.HTM. For details on setting up the programmatic usage of DCDSERVE, see the Usage section in DCDSERVE.HTM. To read DCDSERVE.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DCDSERVE lesson in the table of lessons. You can do the same thing in the Windows Explorer by double-clicking the DCDSERVE.HTM file after locating the main tutorial directory. For more details on the DCOMDRAW client and how it works with DCDSERVE.DLL, see DCOMDRAW.HTM in the main tutorial directory. You must build DCDSERVE.DLL before building or running DCOMDRAW.

DCDSERVE's makefile automatically registers DCDSERVE's SharePaper COM component in the registry. This component must be registered before DCDSERVE is available to outside COM clients as a server for that component. To register the SharePaper component, the makefile uses the REGISTER.EXE utility built in the REGISTER sample. Therefore you must build the REGISTER sample before you can build or run DCDSERVE.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DCDSERVE sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DCDSERVE.DSP

You can also simply double-click the DCDSERVE.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

The DCDSERVE executable is meant to be used as an out-of-process COM server. Out-of-process servers like DCDSERVE are registered in the system registry, and DCDSERVE has built-in support for registering its components. It accepts the following command line switches to register and unregister:

  -RegServer or /RegServer to register
  -UnregServer or /UnregServer to unregister

String matches on these switches are case-insensitive. DCDSERVE also recognizes the standard -Embedding or /Embedding switch, which directs it to run as an out-of-process server. This switch normally means that the server remains hidden when COM runs it on behalf of a client. As an out-of-process server, DCDSERVE is not designed to run as a stand-alone application with a visable GUI. If you attempt to run DCDSERVE as a stand-alone application with no command line switches, it will exit with an error.

The makefile that builds this sample automatically registers the server in the registry. You can manually initiate its self-registration by issuing the following command at the command prompt in the DCDSERVE directory:

  nmake register

This assumes that you have a compilation environment set up. If not, you can also directly invoke the REGISTER.EXE command at the command prompt while in the DCDSERVE directory.

  ..\register\register.exe dcdserve.exe

These registration commands require a prior build of the REGISTER sample in this series, as well as a prior build of DCDSERVE.EXE.

Run the Sample

The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.

If you have already built the appropriate samples, DCOMDRAW.EXE is the client executable to run for this sample.

CODE TOUR

Files          Description
DCDSERVE.TXT Short description of sample.
MAKEFILE     The generic makefile for building the DCDSERVE.EXE
             code sample of this tutorial lesson.
DCDSERVE.H   The include file for the DCDSERVE application. Contains
             class declarations, function prototypes, and resource
             identifiers.
DCDSERVE.CPP The main implementation file for DCDSERVE.EXE. Has WinMain
             and CMainWindow implementation, as well as the main menu
             dispatching.
DCDSERVE.RC  The resource definition file for the executable.
DCDSERVE.PAP A default paper drawing file for the application.
DCDSERVE.ICO The icon resource for the executable.
SERVER.H     The include file for the server control C++ object. Also
             used for DCDSERVE externs.
SERVER.CPP   The implementation file for the server control object.
             Manages object counts, server lifetime, and the creation
             of class factories.
FACTORY.H    The include file for the server's class factory COM objects.
FACTORY.CPP  The implementation file for the server's class factories.
CONNECT.H    The include file for the connection point enumerator,
             connection point, and connection enumerator classes.
CONNECT.CPP  The implementation file for the connection point enumerator,
             connection point, and connection enumerator objects.
PAPER.H      The include file for the COPaper COM object class.
PAPER.CPP    The implementation file for the COPaper COM object class
             and the connection points.
DCDSERVE.DSP Microsoft Visual Studio Project file.

DCDSERVE uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library's source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

This sample is part of a graduated series of tutorial samples. This tour assumes that you have some exposure to those previous samples. It does not revisit basic interface implementation techniques, COM object construction, out-of-process server construction, class factory construction, and connectable object construction. For information on these topics, study the earlier tutorial samples.

The major topics covered in this code tour are:

  • an overview of how DCDSERVE works
  • thread-safe mechanisms in DCDSERVE
  • shared single object construction
  • process security in DCDSERVE
  • activation security in DCDSERVE
  • installing DCDSERVE on the server machine

DCDSERVE: A Distributed Shared Drawing Server

The COPaper COM object is the single object type managed by the DCDSERVE out-of-process server. COPaper is constructed as an aggregatable COM object with a native implementation of the ISharePaper interface. COPaper exposes the ISharePaper interface to allow clients to perform a small set of drawing operations on a COPaper instance. The COPaper object encapsulates data that defines a single drawing surface that models a white sheet of paper. This lesson does not cover ISharePaper methods in detail here because they are very similar to methods in the IPaper and IDrawPage interfaces studied in the STOSERVE, STOCLIEN, PERDRAW, and PERCLIEN samples.

ISharePaper does have Load and Save methods clients can use to direct COPaper to save or load the current shared drawing to or from compound file storage. Much like the STOSERVE sample, the drawing consists of a dynamic array of "ink" data which COPaper manages. Unlike the STOCLIEN and STOSERVE samples which kept the compound file with the client, the compound file for the drawing in DCDSERVE and DCOMDRAW samples is kept with the server. This is because the drawing data is shared by clients on different machines on the network. The default compound file is DCDSERVE.PAP and it is located with DCDSERVE. If no default file is there then a new one of that name is created with a blank drawing in it.

A client uses the ISharePaper interface to deposit ink into DCDSERVE's current drawing. As a DCOMDRAW client sends these ink data points to COPaper, they are retained and sent to all connected clients using COM connectable-object technology. A custom IPaperSink interface is used to call back from COPaper to the client's event sinks. Although it is largely transparent, the calls to and from COPaper that underlie the drawing behavior rely on the DCOM infastructure to conduct interface calls between client and server across the network.

On the client side in DCOMDRAW a master/slave relationship is maintained. Slave clients do not have ownership of the drawing pen and are passively "listening" for new drawing data. They immediately display any new ink data they receive. A master client has ownership of the drawing pen and can drive the drawing process. As drawing occurs in the master client it is immediately displayed in that client as well as sent to the connected slave clients. The master client also currently owns the load and save operations. The first client that loads the remote DCDSERVE drawing becomes the master until it relinquishes that role by the Give Pen menu choice. Other instances of DCOMDRAW that connect to this drawing will become slave clients to that drawing's contents. If no master client currently owns the pen then ownership can be acquired by the Take Pen menu choice an any of the other slave clients. When a slave client takes the pen it becomes a master client.

Thread Safety in DCDSERVE

The master/slave logic supported by COPaper in DCDSERVE helps preclude conflicting access to the shared drawing by multiple threads. However, all clients may also ask for drawing data during asynchronous re-painting. Such a re-paint might occur when a DCOMDRAW client window is topped after being covered. Thus, it is likely that while a master client is actively drawing to the surface, other clients could be asynchronously requesting ink data for their own re-paints of the current drawing. Because both the DCDSERVE server and the DCOMDRAW client use the Single Threaded Apartment (STA) model, this mutually exclusive access to COPaper is ensured by COM.

Constructing a Shared Single Object Server

The STOSERVE server (in the earlier STOSERVE sample) houses a COPaper COM object very similar to the COPaper object housed in this DCDSERVE server. Although their C++ class names are the same, they are distinctly different COM objects because they are different COM components--an instance of one created using CLSID_DllPaper, an instance of the other using CLSID_SharePaper. When a STOCLIEN client requested a new COPaper object using CLSID_DllPaper, the STOSERVE server's class factory creates a separate new instance of COPaper. This is the behavior usually needed in COM-based applications. However, in distributed applications often multiple clients need to access the same instance of a shared COM object. DCDSERVE provides a server housing that supports this. When a client requests a new COPaper object using CLSID_SharePaper, DCDSERVE's class factory creates a new one if it does not yet exist. But after the first instance of COPaper exists, any subsequent create calls by clients return the existing COPaper. This is sometimes called a "singleton" COM component. Only one instance is ever created and that instance is shared by all clients.

This section describes one way to construct the class factory to manufacture a shared single COM object.

To determine if a COPaper object has been created, a pointer to the new object's principal IUnknown is kept in the CServer control object as member m_pCOPaper. Initialized to NULL in the CServer constructor, it is assigned after the first object instance is created. It is subsequently used in any new requests for object creation. A non-NULL value of this variable can be used as a flag to determine if an attempt to create a new COPaper object is the first. Here is the ObjectFirst method of CServer from DCDSERVE's SERVER.CPP:

  BOOL CServer::ObjectFirst(void)
  {
    BOOL bFirst = TRUE;

    if (OwnThis())
    {
      bFirst = (NULL == m_pCOPaper);

      UnOwnThis();
    }

    return bFirst;
  }

This method is called by the class factory to determine if this is the first object creation, and if it is, the method returns TRUE. Since the m_pCOPaper variable is in the global server housing it needs protection by an OwnThis/UnOwnThis pair.

When the first object is created, the ObjectSet method of CServer is called to set the m_pCOPaper IUnknown pointer variable to point the principal IUnknown of the new COPaper object. This assignment also tells the server and the class factory that the initial instance of COPaper now exists. Here is ObjectSet from SERVER.CPP:

  HRESULT CServer::ObjectSet(IUnknown* pCob)
  {
    HRESULT hr = NOERROR;

    if (OwnThis())
    {
      m_pCOPaper = pCob;

      UnOwnThis();
    }

    return hr;
  }

To create a COPaper object, a client typically calls CoCreateInstance or calls CoGetClassObject followed by a call to the CreateInstance method of the obtained class factory. In both cases the CreateInstance call requests an initial interface on the new COM object. This is done internally using a QueryInterface call on the object to return the desired interface. To support this on the shared single instance of COPaper, the ObjectQI method is implemented in CServer. Here is ObjectQI from SERVER.CPP:

  HRESULT CServer::ObjectQI(REFIID riid, PPVOID ppv)
  {
    HRESULT hr = E_FAIL;

    if (OwnThis())
    {
      hr = m_pCOPaper->QueryInterface(riid, ppv);

      UnOwnThis();
    }

    return hr;
  }

The m_pCOPaper pointer to the single COPaper object is used to perform a call to COPaper's QueryInterface.

With this thread-safe support in the global CServer object, the class factory's CreateInstance method can now determine if it is doing the first create or a subsequent one. Here is CFPaper's CreateInstance method from DCDSERVE's FACTORY.CPP:

  STDMETHODIMP CFPaper::CImpIClassFactory::CreateInstance(
                 IUnknown* pUnkOuter,
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_POINTER;
    COPaper* pCob = NULL;

    if (NULL != ppv)
    {
      // NULL the output pointer.
      *ppv = NULL;
      hr = E_FAIL;

      // If the creation call is requesting aggregation (pUnkOuter != NULL),
      // the COM rules state the IUnknown interface MUST also be concomitantly
      // be requested.  If it is not so requested (riid != IID_IUnknown) then
      // an error must be returned indicating that no aggregate creation of
      // the CFPaper COM Object can be performed.
      if (NULL != pUnkOuter && riid != IID_IUnknown)
        hr = CLASS_E_NOAGGREGATION;
      else
      {
        if (m_pServer->ObjectFirst())
        {
          // Instantiate a COPaper COM Object.
          pCob = new COPaper(pUnkOuter, m_pServer);
          if (NULL != pCob)
          {
            // Create and initialize any subordinate objects.
            hr = pCob->Init();
            if (SUCCEEDED(hr))
            {
              // We successfully created the new COM object so tell the
              // server to increment its global server object count.
              m_pServer->ObjectsUp();

              // We QueryInterface this new COM Object not only to deposit the
              // main interface pointer to the caller's pointer variable, but
              // to also automatically bump the Reference Count on the new COM
              // Object after handing out this reference to it.
              hr = pCob->QueryInterface(riid, (PPVOID)ppv);
              if (SUCCEEDED(hr))
              {
                // Rig so that any subsequent creates get the same shared
                // single object.
                m_pServer->ObjectSet(pCob);
              }
              else
              {
                delete pCob;
                m_pServer->ObjectsDown();
              }

            }
            else
              delete pCob;
          }
          else
            hr = E_OUTOFMEMORY;
        }
        else
          hr = m_pServer->ObjectQI(riid, ppv);
      }
    }

    return hr;
  }

The code calls CServer::ObjectFirst and if ObjectFirst returns TRUE then the single COPaper object is created and initialized. After successful creation, a pointer to the object is passed to CServer::ObjectSet to assign CServer's internal, protected m_pCOPaper pointer. This is a thread-safe call and after it returns, any subsequent calls to CServer::ObjectFirst will return FALSE. CServer's ObjectsUp method is called to increment the object count. This count will never exceed 1 reflecting the single COPaper object in existence. As seen many times in this sample series, server lifetime is linked to object lifetimes. There is simply only one living object. The COPaper object's lifetime is determined by reference counts. When the last client calls Release on the ISharePaper interface that it holds on COPaper, the object terminates. During this termination COPaper deletes itself and calls CServer's ObjectDown method to decrement the server object count from 1 to 0 causing the exit of the DCDSERVE server. This is the familiar logic governing server and object lifetime seen in earlier servers in the COM tutorial series. The only difference is that DCDSERVE's server object count never exceeds 1.

Note that this means that when new clients request the creation of COPaper, the object count is not increased even though CreateInstance completed successfully and the client was given the requested interface to an object. From the client's perspective, a new object was created. In a way the client is fooled into accepting an existing object as a new object.

As in previous samples, the QueryInterface method of the object's principal IUnknown is used to return the requested interface pointer to the calling client. When the above call to CServer::ObjectFirst returns FALSE, the single COPaper object already exists. In this case, all that is needed is a call to CServer::ObjectQI to return to the client the requested interface on the existing object.

Process Security in DCDSERVE

COM-based client/server applications that run on the same machine usually run under the authority of a single currently-logged-on user. However, when the client and server run on different machines that are separated by a network, it is very likely that different users will be logged on to those machines. In fact, the machine where the COM server runs may not have any user logged on. To launch and access an out-of-process server across the network in a COM application, both client and server must fulfill certain security requirements.

There are various levels of security that can be established in both the client and the server. For example, on the server side, specific security can be established for the entire server process, for each object instance, or for each method call. There are also different techniques for establishing these levels of security. For example, named values in the registry entries for the server can determine who can access the server after it is launched. Or helper functions can be called at server run-time to establish security for the server process. There are often multiple techniques to achieve the same control over the security context.

The server's settings in the system registry can be used to determine security for the server. Such registry settings are valuable for legacy COM-based applications that have no built-in code to establish their security context at runtime. COM, however, does support coding this security within the server. This sample demonstrates only some of the many possible ways to establish security in the server. The DCOMDRAW client must also participate with a compatible client security context. For coverage of how the client handles COM security, see the DCOMDRAW lesson.

For client calls to ISharePaper methods across the network to act upon COPaper, the call must be acting under an identity that is both authenticated and authorized to perform the action. Authentication is the recognition of the calling client (that is, the calling agent is recognized to be who it claims to be). Authorization is the confirmation that the calling client has permission to perform the action.

DCDSERVE uses a call to CoInitializeSecurity to establish a security context for the entire server process. Here is that call in a fragment from the WinMain function in DCDSERVE.CPP:

  ...
  ...
  // If we were compiled for UNICODE and the platform seems OK with this
  // then proceed.  Else we error and exit the app.
  if (UnicodeOk())
  {
    // Ensure that DCOM (Distributed COM) is installed.
    if (DComOk())
    {
      // Call to initialize the COM Library.  Use the SUCCEEDED macro
      // to detect success.  If fail then exit app with error message.
      if (SUCCEEDED(CoInitialize(NULL)))
      {
        // Initialize for server security.
        hr = CoInitializeSecurity(
               NULL,                        //Points to security descriptor
               -1,                          //Count of entries in asAuthSvc
               NULL,                        //Array of names to register
               NULL,                        //Reserved for future use
               RPC_C_AUTHN_LEVEL_NONE,      //Default authentication level
                                            // for proxies
               RPC_C_IMP_LEVEL_IMPERSONATE, //Default impersonation level
                                            // for proxies
               NULL,                        //Reserved; must be set to NULL
               EOAC_NONE,                   //Additional client or
                                            // server-side capabilities
               NULL);                       //Reserved for future use
        if (SUCCEEDED(hr))
        {
          // If we succeeded in initializing the COM Library we proceed to
          // initialize the application.  If we can't init the application
          // then we signal shut down with an error message exit.
          iRun = InitApplication(hInstance);
          if (iRun)
          {
            ...
            Run the server
            ...
          }
        }
        else
          Error: Could not initialize security.
      }
      else
        Error: Could not initialize COM.
    }
    else
      Error: DCOM is not available.
  }
  else
    Error: Platform can not accept Unicode.
  ...
  ...

This CoInitializeSecurity call is made after the CoInitialize call that initializes COM. Here is the prototype for CoInitializeSecurity with the parameters values passed in the above call:

  HRESULT CoInitializeSecurity(

            PSECURITY_DESCRIPTOR pSD,
              // NULL: Points to security descriptor that can contain
              // DACLs (Discretionary ACLs) that grant or deny access
              // to specific users. The SACL in the SD must be NULL.
              // If pSD is NULL then no ACL checking will be done.
              // DCOM will abide by the AccessPermission value that
              // exists for the server's AppID key in the registry.

            DWORD cAuthSvc,
              // -1: Count of entries in asAuthSvc. If 0, register no
              // services. If -1, COM picks the number of authentication
              // services.

            SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
              // NULL: Array of authentication/authorization principal
              // service names to register. NULL means that the default
              // authentication/authorization principal for each proxy
              // will be negotiated.

            void* pReserved1,
              // NULL: Reserved for future use; must be NULL.

            DWORD dwAuthnLevel,
              // RPC_C_AUTHN_LEVEL_NONE: Default authentication level
              // for proxies. RPC_C_AUTHN_LEVEL_NONE means that no
              // authentication is used in this process.

            DWORD dwImpLevel,
              // RPC_C_IMP_LEVEL_IMPERSONATE: Default impersonation level
              // for proxies. RPC_C_IMP_LEVEL_IMPERSONATE means that the
              // server can impersonate the client identity.

            RPC_AUTH_IDENTITY_HANDLE pAuthInfo,
              // NULL: Reserved for future use; must be NULL.

            DWORD dwCapabilities,
              // EOAC_NONE: Additional client or server-side capabilities.
              // EOAC_NONE means no additional capabilities are used.

            void* pReserved2);
              // NULL: Reserved for future use; must be NULL.

The RPC_C_AUTHN_LEVEL_NONE value for the dwAuthnLevel parameter and the RPC_C_IMP_LEVEL_IMPERSONATE value for dwImpLevel parameter are the only significant values in DCDSERVE's use of this call. With these values, the CoInitializeSecurity call essentially opens the security door for clients of the DCDSERVE process.

Activation Security in DCDSERVE

With the above run-time process security in effect in DCDSERVE, the DCOMDRAW client can freely call the COPaper object in the server once DCDSERVE is running. But there are additional security issues involved with launching DCDSERVE in the first place. When the DCOMDRAW client calls to create the first remote COPaper, COM will check DCDSERVE's security attributes before launching it. If no such attributes are registered then a default set of attributes will be used. Two of these attributes are kept in the system registry. These are the DefaultLaunchPermission and DefaultAccessPermission named values of the following key in the registry:

  HKEY_LOCAL_MACHINE\Software\Microsoft\OLE

These named values contain a binary ACL (Access Control List) specifying the system default set of users that have permission to launch or access objects. On a newly installed Windows NT system these default users include Administrators, SYSTEM, and INTERACTIVE user groups. You can view and change these default values using the DCOMCNFG.EXE utility that is included with Windows NT version 4 or later. These values are the default values that are used when no others are specifically set up by servers and clients. Because security can be set appropriately for individual servers and clients, changing the system default values is usually not recommended.

For a server like DCDSERVE, another registry key is used to store the security attributes for the server and its components. This is the AppID key for the server. We saw this AppID scheme in the LOCSERVE and APTSERVE samples. Like CLSIDs, AppIDs are GUIDs (Globally Unique Identifiers). AppIDs provide a way to collect servers and components into one group each member of which has the same security attributes. When COM is presented with a CLSID to create a new object, it can consult the AppID stored in the registry on the CLSID to determine the security attributes for the server and its components. These attributes tell COM how the server can be activated, who can activate it, and who can later access it. This AppID information is was introduced with Windows NT 4.0. If no AppID is present for an out-of-process server, the system's default security attributes are transparently used. In DCDSERVE, an explicit AppID value is added to the registry to explicitly determine the necessary security attributes for the server. DCDSERVE's self-registration code registers an AppID for the server's CLSID_SharePaper component. Here is the DCDSERVE's RegisterServer method from DCDSERVE.CPP:

  BOOL CMainWindow::RegisterServer(void)
  {
    BOOL     bOk = TRUE;
    TCHAR    szID[GUID_SIZE+1];
    TCHAR    szCLSID[GUID_SIZE+32];
    TCHAR    szAPPID[GUID_SIZE+32];
    TCHAR    szModulePath[MAX_PATH];
    TCHAR    szExeName[MAX_PATH];

    // Be safe with null strings in these stack-allocated strings.
    szID[0] = 0;
    szCLSID[0] = 0;
    szAPPID[0] = 0;
    szModulePath[0] = 0;
    szExeName[0] = 0;

    // Obtain the path to this module's executable file for later use.
    // GetModuleFileName is an APPUTIL convenience function.
    GetModuleFileName(
      g_pServer->m_hInstServer,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    // Obtain the file name of this module's executable for later use.
    // GetExeName is an APPUTIL convenience function.
    GetExeName(
      g_pServer->m_hInstServer,
      szExeName);

    /*---------------------------------------------------------------------
      Create registry entries for the SharePaper Component.
    ---------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(CLSID_SharePaper, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);
    // Use the ClassID of this first component as the AppID
    // for all of the server's components.
    lstrcpy(szAPPID, TEXT("AppID\\"));
    lstrcat(szAPPID, szID);

    /*---------------------------------------------------------------------
      Create ProgID keys.
    ---------------------------------------------------------------------*/
    SetRegKeyValue(
      TEXT("CTS.SharePaper.1"),
      NULL,
      TEXT("SharePaper Component - DCDSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("CTS.SharePaper.1"),
      TEXT("CLSID"),
      szID);

    /*---------------------------------------------------------------------
      Create VersionIndependentProgID keys.
    ---------------------------------------------------------------------*/
    SetRegKeyValue(
      TEXT("CTS.SharePaper"),
      NULL,
      TEXT("SharePaper Component - DCDSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("CTS.SharePaper"),
      TEXT("CurVer"),
      TEXT("CTS.SharePaper.1"));
    SetRegKeyValue(
      TEXT("CTS.SharePaper"),
      TEXT("CLSID"),
      szID);

    /*---------------------------------------------------------------------
      Create entries under CLSID.
    ---------------------------------------------------------------------*/
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("SharePaper Component - DCDSERVE Code Sample"));
    SetRegKeyValue(
      szCLSID,
      TEXT("ProgID"),
      TEXT("CTS.SharePaper.1"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("CTS.SharePaper"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("LocalServer32"),
      szModulePath);
    AddRegNamedValue(
      szCLSID,
      NULL,
      TEXT("AppID"),
      szID);

    /*---------------------------------------------------------------------
      Create Server AppID entries.
    ---------------------------------------------------------------------*/
    SetRegKeyValue(
      szAPPID,
      NULL,
      TEXT("CTS.SharePaper.1"));
    SetRegKeyValue(
      TEXT("AppID"),
      szExeName,
      NULL);
    AddRegNamedValue(
      TEXT("AppID"),
      szExeName,
      TEXT("AppID"),
      szID);

    return bOk;
  }

Note that the keys created here are all under the main HKEY_CLASSES_ROOT branch of the registry. The HKEY_LOCAL_MACHINE\SOFTWARE\Classes branch could alternately be used because it is functionally identical to the HKEY_CLASSES_ROOT branch. As seen in previous server samples in this series, the usual ProgID, VersionIndependentProgID, and CLSID keys are stored in the registry. The AppID scheme that RegisterServer sets up is as follows. A named value, "AppID", is added to the CLSID key for the CTS.SharePaper component. The following is the location of that CLSID key:

  HKEY_CLASSES_ROOT\CLSID\{0002DA32-0000-0000-C000-000000000046}
To this key the following named value (of string type, REG_SZ) is added:

  Name       Value
  ----       --------------------------------------
  AppID      {0002DA32-0000-0000-C000-000000000046}

Similar to the CLSID branch, the AppIDs are stored in an AppID branch in the registry. Thus, the following key is stored as the AppID entry for DCDSERVE and its CTS.SharePaper component.

  HKEY_CLASSES_ROOT\AppID\{0002DA32-0000-0000-C000-000000000046}

In this sample the CLSID GUID string is used as the AppID GUID string as well. This AppID key in the registry is where the named security values are assigned. For example, the following named values are assigned to this key in the DCDSERVE sample when DCDSERVE is installed on the server machine.

  Name              Value
  ----              -------------------------------------------------------
  LaunchPermission  ACL in binary form (type REG_BINARY).
  AccessPermission  ACL in binary form (type REG_BINARY).
  RunAs             User account of the form: Domain\User
                    When specified, this is the account under which the
                    DCDSERVE server and its SharePaper component are run.
                    A hidden password corresponding to this account is
                    also stored.

These security values are not assigned in the RegisterServer method above. These values could be assembled programatically using the Windows NT Win32 security functions and then assigned in the registry using functions like RegSetValueEx. RegisterServer could be expanded to perform this activity at self-registration time. However, in this lesson, the DCOMCNFG utility is used to manually "install" DCDSERVE's SharePaper component (see details in the "Installing DCDSERVE" section below). For more details on how to programmatically assign these security values see the DCOMPERM sample in the Platform SDK (currently located at \MSSDK\SAMPLES\COM\DCOM\DCOMPERM in the installed SDK).

To complete the registration of the AppID a module entry is also required. The following key is stored:

  HKEY_CLASSES_ROOT\AppID\DCDSERVE.EXE

This key in turn is assigned the following named value (of type REG_SZ):

  Name       Value
  ----       --------------------------------------
  AppID      {0002DA32-0000-0000-C000-000000000046}

Given the module name, the server's AppID entry can be located in the registry. Likewise, given the CLSID for a component, the appropriate AppID entry can be located. Thus, there are several avenues in the registry back to the AppID entry containing the security attributes.

Installing DCDSERVE

This lesson assumes that the DCDSERVE will be installed on a Windows NT (Server or Workstation) machine. This is the machine on the network that is intended to store the shared drawing. In this Distributed COM sample, the DCOMDRAW client can run on a variety of other machines (for example, running Windows 95, Windows NT Workstation, Windows NT Server, or Windows 98). One easy way to get started is to compile the complete sample set on the intended server machine and then copy the samples (with compiled executables) over the network to the client machines. On the client machines, the copies of the samples must have the servers registered on that machine as well. To do this run the REGALL.BAT file in the main tutorial directory. You can compile the entire sample set by running the LOGMALL.BAT batch file in the main tutorial directory. This batch file assumes that you have your development environment set up properly for Win32 and COM development. For more details, see Building the Code Samples.

Installing DCDSERVE on the intended server machine first requires that DCDSERVE be directed to register itself. Compiling the sample or running REGALL.BAT will do this. After the registration, the DCOMCNFG utility is used to manually perform additional security configuration. Follow these steps to use DCOMCNFG to set the security for DCDSERVE:

  1. Login on the common server machine intended for DCDSERVE. This must be an account that has administrator privileges.
  2. Make sure DCDSERVE is compiled and/or registered on the intended common server machine.
  3. Add the dedicated user account under which DCDSERVE and the SharePaper component will run.
    1. Choose the "User Manager" choice on the "Administrative Tools" menu.
    2. In the "User Manager" choose the "New User" choice on the "User" menu.
    3. In the "New User" dialog, enter the following values (within the square brackets):
      • "User Name:" [DCOMDraw]
      • "Full Name:" [DCOM Draw Code Sample]
      • "Description:" [COM Tutorial Sample]
      • "Password:" [public]
      • "Confirm Password:" [public]
    4. In the "New User" dialog:
      1. Check (or uncheck) boxes so that the following boxes are checked: "User Cannot Change Password" and "Password Never Expires"
      2. Click the "Groups" button and in the "Group Memberships" dialog add the DCOMDraw account to all Administrator, User, and Guest groups.
      3. Click the "Add" button to add the new "DCOMDraw" user.
      4. Click the "Cancel" button to exit the "New User" dialog.
    5. Back in the "User Manager":
      1. Double click the "Administrators" entry in the "Groups" column.
      2. In the "Local Group Properties" dialog click the "Add" button.
      3. In the "Add Users and Groups" dialog:
        1. Select the network domain (in the top drop-down list) that your intended DCOMDRAW client users will logging into.
        2. Select the DCOMDraw entry in the "Names" list box.
        3. Click the "Add" button. An entry should appear in the "Add Names" box.
        4. Click the "OK" button to add the new domain login local administrator account.
      4. In the "Local Group Properties" dialog click the "OK" button.
    6. Exit the "User Manager" by choosing "Exit" on the "User" menu.
  4. Run the DCOMCNFG utility by choosing the "Run" choice on the desktop "Start" menu--just enter DCOMCNFG. DCOMCNFG.EXE is located in the \WINNT\SYSTEM32 directory.
    1. Select the "SharePaper Component" in the "Applications" list.
    2. Click the "Properties" button.
    3. Click the "Security" tab.
    4. Select the "Use custom access permissions" radio button and click the "Edit" button for it.
    5. In the "Registry Value Permissions" dialog click the "Add" button.
    6. In the "Add Users and Group" dialog select the "Everyone" entry in the "Names" list and click the "Add" button.
    7. Click the "OK" button in the "Registry Value Permissions" dialog. This assigns the AppID's "AccessPermissions" value in the registry.
    8. Back in the "Security" tab, select the "Use custom launch permissions" radio button and click the "Edit" button for it.
    9. In the "Registry Value Permissions" dialog click the "Add" button.
    10. In the "Add Users and Groups" dialog select the "Everone" entry in the "Names" list and click the "Add" button.
    11. Click the "OK" button in the "Registry Value Permissions" dialog. This assigns the AppID's "LaunchPermissions" value in the registry.
    12. Click the "Identity" tab and select the "This user" radio button.
    13. Enter the following user values (within the square brackets). If you are operating in a network domain click the "Browse" button to select the appropriate MYDOMAIN\DCOMDraw user account (where MYDOMAIN is the network domain in which client users will be logged in).
      • "User:" [MYDOMAIN\DCOMDraw]
      • "Password:" [public]
      • "Confirm Password:" [public]
    14. Click the "OK" button to complete the properties for the SharePaper component. This assigns the AppID's "RunAs" value in the registry.
    15. Finally, click the "OK" button to close the DCOMCNFG utility.

At this point DCOMDRAW clients should be able to launch and access the SharePaper component in DCDSERVE. The security scheme above uses a dedicated DCOMDraw account on the server machine. If DCOMCNFG has been used to set the "Launching User" as the identity under which DCDSERVE is launched then COM would launch separate instances of DCDSERVE because the remote client logins are likely different within the network domain or workgroup. By launching and accessing the SharePaper component under the same identity, COM will use the same instance of DCDSERVE if it is already running. Of course, this is required if the same COPaper instance is to be shared by all DCOMDRAW clients.

Note that to run DCOMDRAW on a client machine to access the above installation of DCDSERVE on a server machine requires that DCDSERVE (and DCDMARSH) be registered on the client machine as well. This is because DCOMDRAW initially loads a COPaper drawing from the locally registered DCDSERVE on the client machine. See the DCOMDRAW lesson for more details.

Top Back to page top

© 1995-1998 Microsoft Corporation


DCOMDRAW - DCOM Client Using Security - Lesson 31

COM Logo COM Tutorial Samples Tutorial Home
Tutorial Home 		Previous Lesson
Previous Lesson 		Lesson List
Lesson List

DCOMDRAW - DCOM Client Using Security

SUMMARY

The DCOMDRAW, DCDSERVE, and DCDMARSH samples together form a distributed drawing application. Using Distributed COM (DCOM) technology, they allow users on different client machines in a network to interactively draw on a shared drawing. All DCOMDRAW clients see the same drawing and any client can take ownership of the pen to draw. The user of DCOMDRAW can use a mouse or tablet device to draw in the client window and can choose the color and width of the electronic ink. As clients draw, the application echoes the drawing activity from DCDSERVE to all connected clients using DCOM over the network. Although these samples do not build an optimized and complete application, the functionality is suggestive of the shared whiteboards offered by some workgroup applications.

Although DCOMDRAW, DCDSERVE, and DCDMARSH work on the same machine across process boundaries, the main goal is to provide a distributed application that enables multiple DCOMDRAW clients on different machines to access a single shared drawing object housed in a DCDSERVE server running on a common server machine. A typical scenario would have numerous client machines hooked to an intranet consisting of a Windows NT network domain or peer-to-peer workgroup. DCOMDRAW can run on any of these client machines. DCDSERVE is installed and runs on a common server machine in the domain. The user of the DCOMDRAW client can interactively choose the remote server machine to determine which DCDSERVE COM server to use for the shared drawing.

DCDSERVE manages a shared, single COPaper COM object that models a sheet of white drawing paper. Clients can use the methods of the COPaper object to draw on the paper surface using "ink" of specified color and width. This functionality is outwardly similar to the "scribble" tutorial samples in many versions of the Microsoft Visual C++® product. The drawing paper features of COPaper objects are exposed to DCOMDRAW clients by a custom ISharePaper interface. COPaper implements the ISharePaper interface. A clear architectural distinction is kept between client and server. No graphical user interface (GUI) is provided by the COPaper object. It relies on the DCOMDRAW client for all GUI behavior. COPaper encapsulates only the server-based capture and storage of the drawn ink data.

The ink data that is drawn on the COPaper surface can be stored in and loaded from compound files. The ISharePaper::Save and ISharePaper::Load method implementations in COPaper use structured storage to store the current data of the shared drawing in a compound file kept with DCDSERVE.EXE on the machine acting as the common server.

The DCOMDRAW sample creates and uses the connectable COPaper COM object that is provided as the CLSID_SharePaper component in the DCDSERVE server. The DCOMDRAW client creates a COPaper object and controls it using the methods of the ISharePaper interface. DCOMDRAW obtains drawing data from the user and displays that data in a window that it manages. DCOMDRAW uses COPaper's ISharePaper interface to save the drawing data in COPaper and to direct file-storage operations on this data.

COPaper only manages the drawing data; it performs no GUI actions. DCOMDRAW provides the GUI for the drawing application by encapsulating this functionality in a central CGuiPaper C++ object.

DCOMDRAW also implements the custom IPaperSink interface on a COPaperSink COM object and connects this interface to an appropriate connection point on the COPaper object in DCDSERVE. COPaper uses the connected IPaperSink interface to send notifications back to the COPaperSink in DCOMDRAW. The normal GUI repainting of COPaper's drawing data is done in DCOMDRAW using COPaper's connectable object technology. Thus in this sample, COM's connectable-object technology uses DCOM to function transparently across the network. In earlier samples such as STOCLIEN and STOSERVE, COM's connectable-object technology was shown operating only in-process.

The two ISharePaper and IPaperSink custom interfaces are declared in PAPINT.H, which is located in the common INC directory. PAPINT.H is automatically generated in the DCDMARSH sample. The GUIDs for the interfaces and objects are defined in PAPGUIDS.H located in that same directory.

This lesson focuses primarily on how to take care of COM process security on the client side and how to load a remote object using DCOM. Using CoInitializeSecurity to set process security is covered. CoCreateInstanceEx using the MULTI_QI structure is covered.

Because client and server run in separate processes--usually on different computers--both DCDSERVE and DCOMDRAW rely on standard marshaling for the ISharePaper and IPaperSink custom interfaces. The DCDMARSH sample provides this support, so you must build (or otherwise register) DCDMARSH.DLL on both machines prior to building and running DCDSERVE and DCOMDRAW across machines.

To set a DCOMDRAW client on one machine to control the common DCDSERVE server on another, both machines must have DCOM (Distributed COM) installed. DCOM is included in Windows NT 4.0 or above and in Windows 98. If a computer is running Windows 95, you must install the DCOM95 add on. DCOM95 can currently be obtained by download from Microsoft's world wide Web site at: http://www.microsoft.com/com/.

The multiple computers must be connected in a properly configured network. For details on setting up a network, see your Windows NT product documentation or the Windows NT Resource Kit. For more details on computer and network setup for running with DCOM, see the "Network and Setup Issues" section at the end of the REMCLIEN lesson. Note that the less restrictive security in Windows 95 and Windows 98 prevents the SCM under DCOM from automatically launching DCDSERVE on behalf of a remote DCOMDRAW client. You must manually pre-launch DCDSERVE on these operating systems if you attempt a remote load of DCDSERVE from a DCOMDRAW running on another machine. The DCDSERVE and DCOMDRAW lessons assume that you install DCDSERVE on a machine running Windows NT Server or Workstation.

For functional descriptions and a tutorial code tour of DCOMDRAW, see the Code Tour section in DCOMDRAW.HTM. For details on the external user operation of DCOMDRAW, see both the Usage and Operation sections in DCOMDRAW.HTM. To read DCOMDRAW.HTM, run TUTORIAL.EXE in the main tutorial directory and click the DCOMDRAW lesson in the table of lessons. You can do the same thing by locating the main tutorial directory in the Windows Explorer and double-clicking the DCOMDRAW.HTM file. For more details on how DCDSERVE works and exposes its services to DCOMDRAW, see DCDSERVE.HTM in the main tutorial directory. The makefile for DCDSERVE automatically registers that server in the registry of the host machine, so you must build DCDSERVE on the remote machine before attempting to run DCOMDRAW.

For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.

For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the DCOMDRAW sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:

    MSDEV DCOMDRAW.DSP

You can also simply double-click the DCOMDRAW.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.

Usage

DCOMDRAW is an application that you can execute directly from Windows or from the command-prompt window. No command-line parameters are recognized by DCOMDRAW. DCOMDRAW currently runs on the Windows 95 operating system with the DCOM95 update for Windows 95 installed. It will run on Windows 98 which includes DCOM. It will also run under version 4 or later of Windows NT Workstation or Windows NT Server.

OPERATION

The DCOMDRAW.EXE application provides the user interface for this sample application. It uses the functionality of the associated, but independent, DCDSERVE.EXE out-of-process server. Here is a summary of operation from the standpoint of DCOMDRAW.EXE as a client of DCDSERVE.EXE.

Since two machines are involved in this lesson, this lesson refers to the machine where DCDSERVE runs as Machine-S (server) and to the machine where DCOMDRAW runs as Machine-C (client).

The are many ways to set up to build and run DCOMDRAW for this tutorial. Here are two alternate ways to get started:

  1. Set up Machine-S (running Windows NT 4.0) to compile Win32 applications. Run MAKEALL.BAT to compile all of the COM tutorial code samples there. Among other things, this build process registers the necessary interfaces in the registry on Machine-S for standard marshaling using the DCDMARSH marshaling server on that machine. Using the drive share predefined as C$ on Machine-S, copy a complete copy of the compiled \MSSDK\SAMPLES\COM\TUTSAMP branch across the network from Machine-S to Machine-C. With all the sample executables on Machine-C (running Windows NT 4.0), run the REGALL batch file in the TUTSAMP directory on Machine-C. This registers the DCDSERVE server and its interfaces in the registry on Machine-C. The DCDMARSH standard marshaling server located on that machine is used to register the interfaces there.
  2. Set up both Machine-S and Machine-C (perhaps with Machine-S running Windows NT and Machine-C running Windows 98). Copy the uncompiled source of the samples branch from Machine-S to Machine-C. Then run MAKEALL.BAT on both machines. This will perform a complete build of all the executables and automatically register the DCDSERVE and DCDMARSH servers on both machines.

Once registered, DCDSERVE server requires manual security configuration using the DCOMCNFG utility. For more details, see "Installing DCDSERVE" in the DCDSERVE.HTM file.

With the executables present and the servers properly registered on both machines, you can run the DCOMDRAW application on Machine-C, relying on COM to locate and load the remote DCDSERVE server on Machine-S. DCOMDRAW provides a Load from Remote Server dialog box in which you can enter the name of the remote machine on which the DCDSERVE server runs.

DCOMDRAW.EXE provides menus for loading and saving the shared drawing and for controlling the drawing pen color and width. The following menus are provided:

Menu Selection: File/Load Remote...
Displays the Load from Remote Server dialog box, in which you specify the network machine name of the computer where the remote DCDSERVE server is located and registered. DCOM recognizes machine names that are in a valid UNC format (for example, Machine-S or \\Machine-S), a DNS format (for example, www.mymachine.com), or an IP address format (for example, 123.45.6.7).

Menu Selection: File/Load Local
Loads (or reloads) the drawing from a DCDSERVE registered on the local machine (that is, the machine where this client is running).

Menu Selection: File/Save
Saves the current drawing. Beeps if this DCOMDRAW does not own the pen.

Menu Selection: File/Exit
Exits DCOMDRAW.

Menu Selection: Draw/Take Pen
Takes ownership of the pen to permit this DCOMDRAW to draw on the shared drawing. Beeps if another client already owns the pen.

Menu Selection: Draw/Give Pen
Relinquishes ownership of the pen to permit any other DCOMDRAW client to take the pen draw on the shared drawing.

Menu Selection: Draw/Redraw
Forces a redraw of the current DCOMDRAW client's display of the shared drawing.

Menu Selection: Draw/Erase
Erases all content of the currently shared drawing. Beeps if this DCOMDRAW client does not own the pen.

Menu Selection: Color
Shows the Pick Color dialog allowing you to choose the current color of the drawing pen. Beeps if this DCOMDRAW client does not own the pen.

Menu Selection: Thin
Sets current pen width to Thin. Beeps if this DCOMDRAW client does not own the pen.

Menu Selection: Medium
Sets current pen width to Medium. Beeps if this DCOMDRAW client does not own the pen.

Menu Selection: Fat
Sets current pen width to Fat. Beeps if this DCOMDRAW client does not own the pen.

Menu Selection: Help/DCOMDRAW Tutorial
Opens the DCOMDRAW.HTM tutorial file in the Web browser.

Menu Selection: Help/DCDSERVE Tutorial
Opens the DCDSERVE.HTM tutorial file in the Web browser.

Menu Selection: Help/DCDMARSH Tutorial
Opens the DCDMARSH.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this lesson or another one in the Windows Notepad.

Menu Selection: Help/About DCOMDRAW
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to use the CAboutBox class provided by APPUTIL.LIB.

When first run on a machine, DCOMDRAW loads a shared drawing from the DCDSERVE registered on the local machine. That first instance of DCOMDRAW is given "master" ownership of the drawing pen. This means that it can immediately make drawing changes to the drawing. Any subsequent instances of DCOMDRAW on that same machine will access the same locally shared drawing, but will not own the pen. This means that they cannot draw and may merely see drawing activity as it is done in the DCOMDRAW client that owns the pen. The pen cursor changes to an 'X' indicating that the pen is not owned and that drawing is off. When drawing is on, the pen cursor size changes (that is, a thin, medium, or fat pen) to indicate the chosen pen width. The DCOMDRAW title bar also has an indicator of pen ownership. If DCOMDRAW owns the pen, "***TALK***" appears in the title bar. If the client does not own the pen, "LISTEN" appears in the title bar. The usage model here assumes that while users are sharing the drawing they are also talking over some audio channel (possibly telephone). Typically, when a client user is drawing they are talking to explain it; otherwise the client user con only watch another client's drawing activity.

When you own the pen in DCOMDRAW, you press and hold the left mouse button as you move the mouse to draw ink.

The File/Load Remote menu choice allows you to load or connect to a shared drawing maintained on a remote machine. If multiple clients on different client machines load from that same remote machine then they will all share the same drawing maintained by the DCDSERVE registered on that server machine.

CODE TOUR

Files          Description
DCOMDRAW.TXT   Short description of the sample.
MAKEFILE       The generic Win32 makefile for building the code sample
               application of this tutorial lesson.
DCOMDRAW.H     The include file for the DCOMDRAW application. Contains
               class declarations, function prototypes, and resource
               identifiers.
DCOMDRAW.CPP   The main implementation file for DCOMDRAW.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
DCOMDRAW.RC    The application resource definition file.
DCOMDRAW.ICO   The application icon resource.
PENCURT.CUR    Pen cursor for the Thin pen width.
PENCURM.CUR    Pen cursor for the Medium pen width.
PENCURF.CUR    Pen cursor for the Fat pen width.
PENCURN.CUR    Pen cursor for when drawing is off (pen is not owned).
GUIPAPER.H     The class declaration for the CGuiPaper C++ class.
GUIPAPER.CPP   Implementation file for the CGuiPaper C++ class.
SINK.H         The class declaration for the COPaperSink COM object class.
SINK.CPP       Implementation file for the COPaperSink COM object class.
DCOMDRAW.DSP   Microsoft Visual Studio Project file.

Like all code samples in the series, DCOMDRAW uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the APPUTIL directory and the lesson in the APPUTIL.HTM file in the main tutorial directory.

DCOMDRAW requires the DCOM facility in COM. Early in its WinMain function, DCOMDRAW calls APPUTIL's DComOk function to confirm that DCOM is installed on the system. DComOk does this by ensuring that the CoInitializeEx function is exported from the OLE32.DLL system library. CoInitializeEx was initially supplied with the DCOM functionality in Windows NT 4.0 and its presence is thus a simple way to confirm that DCOM is present. Similarly, DComOk will return FALSE on Windows 95 if the DCOM95 add-on is not installed.

DCOMDRAW and DCDSERVE borrow much from the STOCLIEN and STOSERVE samples. As a result, this lesson does not discuss in detail how CGuiPaper encapsulates the GUI behavior of drawing paper, how the interactive mouse activity is captured and displayed, how COPaper records the drawing data, how IPaperSink and the connectable-object technology works, and how the drawing data is actually stored in the structured storage of a compound file. For more details on these topics, see the STOCLIEN/STOSERVE lessons.

The major topics covered in this code tour are:

  • How DCOMDRAW establishes its process security context to use DCOM with a remote server.
  • How DCOMDRAW loads the remote COPaper drawing.
  • How DCOMDRAW paints the remote COPaper drawing.

The DCOMDRAW sample relies heavily on COM's connectable-object technology for communication from COPaper back to the client. Interactive drawing as well as on-demand re-painting use this technology. In particular, COPaper calls the methods of an IPaperSink interface on a COPaperSink object in DCOMDRAW. For COM to carry these calls across the network you must take care of some security issues. We saw in the DCDSERVE lesson that its process is acting under the identity of a DCOMDraw user. We explicitly created that user account when installing DCDSERVE. Unless we create an account for this specific user on every client machine with the proper permissions, this DCOMDraw identity in the server will not have permissions to call back into (or otherwise access) DCOMDRAW on the client side. So a technique is needed to permit this access into the client from an outside identity.

A call to CoInitializeSecurity grants this access for the entire DCOMDRAW client process. The call establishes a security context in the client that permits COPaper on the server side to call back into DCOMDRAW on the client side. Here is such a CoInitializeSecurity call in a code fragment from the WinMain function in DCOMDRAW.CPP:

  ...
  ...
  // If we were compiled for UNICODE and the platform seems OK with this
  // then proceed.  Else we error and exit the app.
  if (UnicodeOk())
  {
    // Call to initialize the COM Library.  Use the SUCCEEDED macro
    // to detect success.  If fail then exit app with error message.
    // Tell COM that this client process will use the Single Threaded
    // Apartment model.
    if (SUCCEEDED(CoInitialize(NULL)))
    {
      // Ensure that DCOM (Distributed COM) is installed.
      if (DComOk())
      {
        // Initialize for Client process security. Allow COPaper to
        // call back into the client (ie, the client can impersonate
        // the server identity).
        hr = CoInitializeSecurity(
               NULL,                        //Points to security descriptor
               -1,                          //Count of entries in asAuthSvc
               NULL,                        //Array of names to register
               NULL,                        //Reserved for future use
               RPC_C_AUTHN_LEVEL_NONE,      //Default authentication level
                                            // for proxies
               RPC_C_IMP_LEVEL_IMPERSONATE, //Default impersonation level
                                            // for proxies
               NULL,                        //Reserved; must be set to NULL
               EOAC_NONE,                   //Additional client or
                                            // server-side capabilities
               NULL);                       //Reserved for future use
        if (SUCCEEDED(hr))
        {
          ...
          ...
          Initialize the client app and run the main message loop.
          ...
          ...
        }
        else
          Error: Can't initialize security.
      }
      else
        Error: DCOM is not installed.

      // We're exiting this app (either normally or by init failure) so
      // shut down the COM Library.
      CoUninitialize();
    }
    else
      Error: Can't initialize COM.
  }
  else
    Error: Platform Can't handle UNICODE.
  ...
  ...

The above CoInitializeSecurity call should look familiar because it is identical to the one used in the DCDSERVE sample. See DCDSERVE.HTM where this call is covered in detail. In DCOMDRAW the call to CoInitializeSecurity shown above establishes a process security context in which call backs are not authenticated (that is, they are allowed to pass) and the calling identity (the DCOMDraw user) is impersonated in the client. This essentially opens the security door in this sample client. You can also use CoInitializeSecurity to create a more restrictive arrangement. For example, a non-NULL security descriptor could be passed as the first parameter. It could include a DACL (Discretionary Access Control List) that specifically grants permission to only the DCOMDraw user. Although this sample does not use them, there are also techniques whereby restricted security contexts can be established for the duration of individual interface method calls.

When you run DCOMDRAW it initially loads the drawing from a COPaper that is available from a DCDSERVE server that is registered on the local machine. At this point DCOMDRAW is a simple local drawing application. The drawing you make is stored in the DCDSERVE.PAP compound file located with the DCDSERVE on the local machine. If you run multiple instances of DCOMDRAW on this machine, they will all share that local drawing.

By choosing the File/Load Remote command you can enter the machine name for the remote drawing to be shared. The existing local drawing is then closed and a drawing is loaded into a COPaper running on the remote server machine. (Of course, this assumes that the remote machine is set up with DCDSERVE installed as described in the DCDSERVE lesson.) The REMCLIEN lesson also covered using CoCreateInstanceEx with a COSERVERINFO structure. DCOMDRAW uses the same technique. Here is the CGuiPaper::LoadRemote method from GUIPAPER.CPP:

  HRESULT CGuiPaper::LoadRemote(void)
  {
    HRESULT hr = NOERROR;
    BOOL bFirst;
    COPaperSink* pCobSink = NULL;
    HCURSOR hCurPrev;
    HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
    COSERVERINFO* pServerInfo = &g_ServerInfo;
    MULTI_QI qiRes;
    int iAns;
    CDlgLoadRemote dlgLoadRemote;

    // Ask user for the remote machine name. Cancel this whole load if
    // he cancels the dialog.
    iAns = dlgLoadRemote.ShowDialog(
             m_hInst,
             MAKEINTRESOURCE(IDD_LOAD_REMOTE),
             m_hWnd);
    // Ensure a remote machine name was specified by user.
    if (IDOK == iAns && pServerInfo->pwszName[0])
    {
      hr = E_FAIL;
      // Unload the existing COPaper object.
      if (m_pISharePaper)
      {
        // Ask User if save of current drawing is desired.
        AskSave();

        // Change cursor to the hour glass.
        hCurPrev = SetCursor(hCurWait);

        // Clear the window of the previous drawing.
        ClearWin();

        // Make sure we unlock the paper object.
        Lock(FALSE);

        // Disconnect all Sinks--currently only one: PaperSink. This
        // officially stops all PaperSink notifications.
        hr = DisconnectPaperSink();

        // Release the main interface pointer copy held in CGuiPaper.
        // This causes destruction (in the server) of the current
        // COPaper object.
        RELEASE_INTERFACE(m_pISharePaper);
      }

      // Load a new COPaper from the server on a user-specifed remote machine.

      // Call COM service to create an instance of the remote COPaper
      // COM object. We are not aggregating this new object (viz, the
      // NULL parameter), so we ask for its IShareDraw interface directly.
      qiRes.pIID = &IID_ISharePaper;
      qiRes.pItf = NULL;
      qiRes.hr = 0;
      hr = CoCreateInstanceEx(
             CLSID_SharePaper,
             NULL,
             CLSCTX_REMOTE_SERVER,
             pServerInfo,
             1,
             &qiRes);
      if (SUCCEEDED(hr))
      {
        hr = qiRes.hr;
        if (SUCCEEDED(hr))
        {
          // Grab our copy of the returned interface pointer. An AddRef was
          // done by CoCreateInstanceEx on this interface pointer.
          m_pISharePaper = (ISharePaper*)qiRes.pItf;

          // Init the newly created COPaper object.
          GetClientRect(m_hWnd, &m_WinRect);
          hr = m_pISharePaper->InitPaper(&m_WinRect, &bFirst);
          if (SUCCEEDED(hr))
          {
            // Reconnect all Sinks--currently only one: PaperSink.
            // This restores all PaperSink notifications.
            hr = ConnectPaperSink();
            if (SUCCEEDED(hr))
            {
              if (bFirst)
              {
                // Lock and load.
                Lock(TRUE);
                hr = Load();
              }
              else
              {
                Lock(FALSE);
                // If this is not first init then resize this client's
                // window to match what the remote COPaper object is
                // using for its window size.
                hr = ResizeWin(m_WinRect.right, m_WinRect.bottom);
              }
            }
          }
        }
      }

      if (FAILED(hr))
      {
        HrMsg(m_hWnd, TEXT(REMOTE_CREATE_ERR_STR), hr);

        // If error with remote load then restore local drawing.
        hr = LoadLocal();
      }

      // Set Cursor back to what it was.
      SetCursor(hCurPrev);
    }

    return hr;
  }

The CoCreateInstanceEx call uses the MULTI_QI structure even though only one interface is requested. This facility is especially useful in the context of distributed COM where numerous interfaces on a remote object are needed by a client. Rather than suffer the overhead of separate QueryInterface round-trips across the network, this facility allows more than one interface to be obtained in one CoCreateInstanceEx call.

After obtaining the m_pISharePaper interface pointer, it is used to call the InitPaper method. This call returns the window rectangle of the remote drawing and also a flag that indicates whether this is the first client to gain access to the newly created COPaper.

If this is the first client then an initial lock on the shared COPaper drawing is requested. This lock means that this first client owns the pen. COPaper is then asked to load the drawing content from its associated compound file. This is the DCDSERVE.PAP file located on the remote machine with DCDSERVE.

If this client is not the first client, then it does not request a lock on COPaper because it is assumed that an earlier client owns the pen. Since a COPaper object already exists and is loaded, this client resizes its window to match the existing drawing used by the other clients. In addition to drawing something, one of these earlier clients may have resized the drawing window. Finally, the drawing is painted in the client's window whether this client is the first or not.

Since the drawing data for the shared drawing is managed by a single instance of COPaper on the server side, DCOMDRAW must obtain this data from COPaper to display the drawing. Here is the CGuiPaper::PaintWin method from GUIPAPER.CPP.

  HRESULT CGuiPaper::PaintWin(void)
  {
    HRESULT hr = E_FAIL;
    COLORREF crInkColorTmp;
    SHORT nInkWidthTmp;
    BOOL bInkSavingTmp;
    LONG i;
    SHORT nInkType;
    SHORT nX;
    SHORT nY;
    SHORT nInkWidth;
    COLORREF crInkColor;

    if (m_pISharePaper && !m_bPainting && !m_bInking)
    {
      hr = NOERROR;
      m_bPainting = TRUE;
      // Save and restore ink color and width since redraw otherwise
      // ends up changing these values in CGuiPaper.
      crInkColorTmp = m_crInkColor;
      nInkWidthTmp = m_nInkWidth;
      bInkSavingTmp = m_bInkSaving;
      m_bInkSaving = FALSE;
      for (i = 0; SUCCEEDED(hr); i++)
      {
        hr = m_pISharePaper->GetInk(
               i,
               &nInkType,
               &nX,
               &nY,
               &nInkWidth,
               &crInkColor);
        if (SUCCEEDED(hr))
        {
          switch (nInkType)
          {
            case INKTYPE_START:
              m_nInkWidth = nInkWidth;
              m_crInkColor = crInkColor;
              InkStart(nX, nY);
              break;
            case INKTYPE_DRAW:
              InkDraw(nX, nY);
              break;
            case INKTYPE_STOP:
              InkStop(nX, nY);
              break;
            default:
              break;
          }
        }
      }
      m_nInkWidth = nInkWidthTmp;
      m_crInkColor = crInkColorTmp;
      m_bInkSaving = bInkSavingTmp;
      m_bPainting = FALSE;
      hr = NOERROR;
    }

    return hr;
  }

The DCOMDRAW client plays an active role in getting the ink data for painting. Compare this to the repainting in the STOCLIEN sample. In that sample the client would request a redraw of the COPaper object. COPaper would then loop to send the data back to a sink in the client. But in this DCOMDRAW distributed drawing sample, the clients can all concurrently loop to obtain the data for their own separate repaints. Because both the DCDSERVE server and the DCOMDRAW client initialize COM in a Single Threaded Apartment (STA), COM ensures that multiple client calls to the shared COPaper are serialized on the same thread in DCDSERVE. This arrangement is convenient to program but can tax performance. For better performance, COPaper can be coded as a free-threaded object residing in the Multi-Threaded Apartment (MTA) of DCDSERVE.

Note that performance of the drawing and repainting in DCOMDRAW can vary depending on the complexity of your network. For example, during repaints COPaper in DCDSERVE may be calling COPaperSink in DCOMDRAW through intermediary servers or from trusted domains to trusting domains. DCOMDRAW and DCDSERVE are not optimized for maximum performance in these cases. One worthy optimization would cache individual ink points into collections called strokes. Each stroke would be a variant blob beginning with an InkStart and ending with an InkStop. Rather than each individual ink point being sent separately, strokes would be sent. This would replace the InkDraw calls that make up the bulk of the repaint traffic with a greatly reduced number of InkStroke calls. The drawing behavior would look less smooth but a repaint would be faster.

Top Back to page top

© 1995-1998 Microsoft Corporation


Glossary

A

Activation

The process of loading an object in memory, which puts it into its running state. See also Binding.

Active state

A COM object that is in running state and has a visible user interface. See also Loaded state, Object state, Passive state, and Running state.

Absolute moniker

A moniker that specifies the absolute location of an object. An absolute moniker is analogous to a full path. See also Moniker.

Advisory holder

A COM object that caches, manages, and sends notifications of changes to container applications' advisory sinks. See also Advisory sink.

Advisory sink

A COM object that can receive notifications of changes in an embedded object or linked object because it implements the IAdviseSink or IAdviseSink2 interface. Containers that need to be notified of changes in objects implement an advisory sink. Notifications originate in the server, which uses an advisory holder object to cache and manage notifications to containers. See also Advisory holder, Container application, and Object handler.

Aggregate object

A COM object that is made up of one or more other COM objects. One object in the aggregate is designated the controlling object, which controls which interfaces in the aggregate are exposed and which are private. This controlling object has a special implementation of IUnknown called the controlling IUnknown. All objects in the aggregate must pass calls to IUnknown methods through the controlling IUnknown. See also Aggregation.

Aggregation

A composition technique for implementing COM objects. It allows you to build a new object by reusing one or more existing objects' interface implementations. The aggregate object chooses which interfaces to expose to clients, and the interfaces are exposed as if they were implemented by the aggregate object. Clients of the aggregate object communicate only with the aggregate object. See also Aggregate object. Contrast with Containment.

Ambient property

A run-time property that is managed and exposed by the container. Typically, an ambient property represents a characteristic of a form, such as background color, that needs to be communicated to a control so that the control can assume the look and feel of its surrounding environment. See also Run-time property.

Anti-moniker

The inverse of a file, item, or pointer moniker. An anti-moniker is added to the end of a file, item, or pointer moniker to nullify it. Anti-monikers are used in the construction of relative monikers. See also Relative Moniker.

Artificial reference counting

A technique used to safeguard an object before calling a function or method that could prematurely destroy it. A program calls IUnknown::AddRef to increment the object's reference count before making the call that could free the object. After the function returns, the program calls IUnknown::Release to decrement the count.

Asynchronous binding

A type of binding in which it is necessary for the process to occur asynchronously to avoid performance degradation for the end user. Typically, asynchronous binding is used in distributed environments such as the World Wide Web. OLE supports asynchronous moniker classes and callback mechanisms that allow the process of locating and initializing an object in a distributed environment to occur while other operations are being carried out. See also Asynchronous moniker, and Binding.

Asynchronous call

A call to a function that is executed separately so that the caller can continue processing instructions without waiting for the function to return. Contrast with Synchronous call.

Asynchronous moniker

A moniker that supports asynchronous binding. For example, instances of the system-supplied URL moniker class are asynchronous monikers. See also Asynchronous binding, Moniker, and URL moniker.

Automation

A way to manipulate an application's objects from outside the application. Automation is typically used to create applications that expose objects to programming tools and macro languages, create and manipulate one application's objects from another applications, or to create tools for accessing and manipulating objects.

B

Bind context

A COM object that implements the IBindCtx interface. Bind contexts are used in moniker operations to hold references to the objects activated when a moniker is bound. The bind context contains parameters that apply to all operations during the binding of a generic composite moniker and provides the moniker implementation with access to information about its environment. See also Binding, Generic composite moniker, and Moniker.

Binding

Associating a name with its referent. Specifically, locating the object named by a moniker, putting it into its running state if it isn't already, and returning an interface pointer to it. Objects can be bound at run time (also called late binding or dynamic binding) or at compile time (also called static binding). See also Moniker and Running state.

C

Cache

A (usually temporary) local store of information. In OLE, a cache contains information that defines the presentation of a linked or embedded object when the container is opened. See also Container, Embedded object, and Linked object.

Cache initialization

Filling a linked or embedded object's cache with presentation data. The IOleCache interface provides methods that a container can call to control the data that gets cached for linked or embedded objects. See also Container, Embedded object, and Linked object.

Class

The definition of an object in code. In C++, the class of an object is defined as a data type, but this is not the case in other languages. Because OLE can be coded in any language, class is used to refer to the general object definition. See also Class factory.

Class factory

A COM object that implements the IClassFactory interface and that creates one or more instances of an object identified by a given class identifier(CLSID). See also Class identifier.

Class identifier (CLSID)

A globally unique identifier (GUID) associated with an OLE class object. If a class object will be used to create more than one instance of an object, the associated server application should register its CLSID in the system registry so that clients can locate and load the executable code associated with the object(s). Every OLE server or container that allows linking to its embedded objects must register a CLSID for each supported object definition. See also Class and Class factory.

Class object

In object-oriented programming, an object whose state is shared by all the objects in a class and whose behavior acts on that classwide state data. In COM, class objects are called class factories, and typically have no behavior except to create new instances of the class. See also Class factory.

Client

A COM object that requests services from another object. See also Container.

Client site

The display site for an embedded or linked object within a compound document. The client site is the principal means by which an object requests services from its container. See also Compound document and Container.

CLSID

See Class identifier.

Commit

To persistently save any changes made to a storage or stream object since it was opened or changes were last saved. See also Revert.

Component

An object that encapsulates both data and code, and provides a well-specified set of publicly available services.

Component Object Model (COM)

The OLE object-oriented programming model that defines how objects interact within a single process or between processes. In COM, clients have access to an object through interfaces implemented on the object. See also Interface.

Composite menu bar

A shared menu bar composed of menu groups from both a container application and an in-place-activated server application. See also In-place activation.

Composite moniker

A moniker that consists of two or more monikers that are treated as a unit. A composite moniker can be non-generic, meaning that its component monikers have special knowledge of each other, or generic, meaning that its component monikers know nothing about each other except that they are monikers. See also Generic composite moniker.

Compound document

A document that includes linked or embedded objects, as well as its own native data. See also Embedded object and Linked object.

Compound File

An OLE-provided Structured Storage implementation that includes the IStorage, IStream, and ILockBytes interfaces. The StgXxx helper functions create and manipulate compound files. See also Structured Storage.

COM object

An object that conforms to the OLE Component Object Model (COM). A COM object is an instance of an object definition, which specifies the object's data and one or more implementations of interfaces on the object. Clients interact with a COM object only through its interfaces. See also Component Object Model and Interface.

Connectable object

A COM object that implements, at a minimum, the IConnectionPointContainer interface, for the management of connection point objects. Connectable objects support communication from the server to the client. A connectable object creates and manages one or more connection point subobjects, which receive events from interfaces implemented on other objects and send them on to the client. See also Connection point object and Advisory sink.

Connection point object

A COM object that is managed by a connectable object and that implements the IConnectionPoint interface. One or more connection point objects can be created and managed by a connectable object. Each connection point object manages incoming events from a specific interface on another object and sends those events on to the client. See also Connectable object, Advisory sink.

Container

See Container application.

Container application

An application that supports compound documents. The container application provides storage for an embedded or linked object, a site for its display, access to the display site, and an advisory sink for receiving notifications of changes in the object. See also Advisory sink, Compound document, Client site, Embedded object, and Linked object.

Containment

A composition technique for implementing COM objects. It allows one object to reuse some or all of the interface implementations of one or more other objects. The outer object acts as a client to the other objects, delegating implementation when it wishes to use the services of one of the contained objects. Contrast with Aggregation.

Control

An embeddable, reusable COM object that supports, at a minimum, the IOleControl interface. Controls are typically associated with the user interface. They also support communication with a container and can be reused by multiple clients, depending upon licensing criteria. See also Licensing.

Control container

An application that supports embedding of controls by implementing the IOleControlSite interface. See also Control.

Control property

A run-time property that is exposed and managed by the control itself. For example, the font and text size used by the control are control properties. See also Run-time property.

Controlling object

The object within an aggregate object that controls which interfaces within the aggregate object are exposed and which are private. The IUnknown interface of the controlling object is called the controlling IUnknown. Calls to IUnknown methods of other objects in the aggregate must be passed to the controlling IUnknown. See also Aggregate object.

Control site

A structure implemented by a control container for managing the display and storage of a control. Within a given container, each control has a corresponding control site. See also Control and Control container.

D

Data transfer object

An object that implements the IDataObject interface and contains data to be transferred from one object to another through either the Clipboard or drag-and-drop operations.

Default object handler

A DLL provided with OLE that acts as a surrogate in the processing space of the container application for the real object.

With the default object handler, it is possible to look at an object's stored data without actually activating the object. The default object handler performs other tasks, such as rendering an object from its cached state when the object is loaded into memory.

Dependent object

A COM object that is typically initialized by another object (the host object). Although the dependent object's lifetime may only make sense during the lifetime of the host object, the host object does not function as the controlling IUnknown for the dependent object. In contrast, an object is an aggregated object when its lifetime (by means of its reference count) is completely controlled by the managing object. See also Host object. Contrast with Aggregation and Containment.

Direct access mode

One of two access modes in which a storage object can be opened. In direct mode, all changes are immediately committed to the root storage object. See also Transacted access mode.

Document object

An OLE document that can display one or more in-place activated views of its data within a native or foreign frame, such as a browser, while retaining full control over its user interface. In addition to implementing the usual OLE document and in-place activation interfaces, a document object must implement IOleDocument, and each of its views (in the form of a document view object) must implement IOleDocumentView. See also Document view, Document view object, and Frame.

Document object container

A container application capable of displaying one or more views of one or more document objects and of managing all contained document objects within a file. Each document object is associated with a document site, and each document site contains one or more document view sites corresponding to the views supported by the document object. A document object container also includes a container frame, which handles menu and toolbar negotiation and the enumeration of contained objects. See also Document object, Document site, Document view, Document view site, and Frame.

Document object server

A server application capable of providing document objects. See also Document object and Document object container.

Document site

A client site implemented by a document object container for managing the display and storage of a document object. Each document object in a container has a corresponding document site. See also Document object and Document object container.

Document site object

A COM object that implements the IOleDocumentSite interface, in addition to the usual client-site interfaces (such as IOleClientSite). See also Document site.

Document view

A particular presentation of a document's data. A single document object can have one or more views, but a single document view can belong to one and only one document object. See also Document object.

Document view object

A COM object that implements the IOleDocumentView interface and corresponds to a particular document view. An object with multiple document views aggregates a separate document view object for each view. See also Document view.

Document view site

An object aggregated by a document site object for managing the display space for a particular view of a document object. Within a given document site, each document view has a corresponding document view site. See also Document object, Document site object, and Document view.

Document view site object

A COM object that is aggregated in a document site object and implements the IOleInPlaceSite interface and, optionally, the IContinueCallback interface. See also Document site object.

Drag and drop

An operation in which the end user uses the mouse or other pointing device to move data to another location in the same window or another window.

E

Embed

To insert an object into a compound document in such a way as to preserve the data formats native to that object, and to enable it to be edited from within its container using tools exposed by its server.

Embedded object

An object whose data is stored in a compound document, but the object runs in the process space of its server. The default object handler provides a surrogate in the processing space of the container application for the real object. See also Default object handler, Compound document, and Container application.

.EXE server

See Out-of-process server.

Extended property

A run-time property, such as a control's position and size, that a user would assume to be exposed by the control but is exposed and managed by the container. See also Run-time property.

F

File moniker

A moniker based on a path in the file system. File monikers can be used to identify objects that are saved in their own files. A file moniker is a COM object that supports the system-provided implementation of the IMoniker interface for the file moniker class. See also Item moniker, Generic composite moniker, and Moniker.

Font object

A COM object that provides access to Graphics Device Interface (GDI) fonts by implementing the IFont interface.

Format identifier

A GUID that identifies a persistent property set. Also referred to as FMTID. See also Property set.

Frame

The part of a container application responsible for negotiating menus, accelerator keys, toolbars, and other shared user-interface elements with an embedded COM object or a document object. See also Document object and Embedded object.

Frame object

A COM object that implements the IOleInPlaceFrame interface and, optionally, the IOleCommandTarget interface.

G

Generic composite moniker

A sequenced collection of monikers, starting with a file moniker to provide the document-level path and continuing with one or more item monikers that, taken as a whole, uniquely identifies an object. See also Composite moniker, Item moniker, and File moniker.

H

Helper function

A function that encapsulates calls to other functions and interface methods publicly available in the OLE SDK. Helper functions are a convenient way to call frequently used sequences of function and method calls that accomplish common tasks.

Host object

A COM object that forms a hierarchical relationship with one or more other COM objects, known as the dependent objects. Typically, the host object instantiates the dependent objects, and their existence only makes sense within the lifetime of the host object. However, the host object does not act as the controlling IUnknown for the dependent objects, nor does it directly delegate to the interface implementations of those objects. See also Dependent object.

HRESULT

An opaque result handle defined to be zero for a successful return from a function and nonzero if error or status information is returned. To convert an HRESULT into the more detailed SCODE, applications call GetScode(). See also SCODE.

Hyperlink object

A COM object that implements, at a minimum, the IHlink interface and acts as a link to an object at another location (the target). A hyperlink is made up of four parts: a moniker that identifies the target's location; a string for the location within the target; a friendly, or displayable, name for the target; and a string that can contain additional parameters.

Hyperlink browse context

A COM object that implements the IHlinkBrowseContext interface and maintains the hyperlink navigation stack. The browse context object manages the hyperlink frame window and hyperlink target object's window. See also Hyperlink target.

Hyperlink container

A container application that supports hyperlinks by implementing the IHlinkSite interface and, if the container's objects can be targets of other hyperlinks, the IHlinkTarget interface. See also Container.

Hyperlink frame object

A COM object that implements the IHlinkFrame interface and controls the top-level navigation and display of hyperlinks for the frame's container and the hyperlink target's server.

Hyperlink site object

A COM object that implements the IHlinkSite interface and supplies either the moniker or interface identifier of its hyperlink container. One hyperlink site can serve multiple hyperlinks. See also Hyperlink and Hyperlink container.

Hyperlink target object

A COM object that implements the IHlinkTarget interface and supplies its moniker, friendly name, and other information that other hyperlink objects will use to navigate to it.

I

In parameter

A parameter that is allocated, set, and freed by the caller of a function or interface method. An In parameter is not modified by the called function. See also In/Out parameter and Out parameter.

In/Out parameter

A parameter that is initially allocated by the caller of a function or interface method, and set, freed, and reallocated, if necessary, by the process that is called. See also In parameter and Out parameter.

In-place activation

Editing an embedded object within the window of its container, using tools provided by the server. Linked objects do not support in-place activation; they are always edited in the window of the server. See also Embedded object and Linked object.

In-process server

A server implemented as a DLL that runs in the process space of the client. See also Out-of-process server, Local server, and Remote server.

Instance

An object for which memory is allocated or which is persistent.

Interface

A group of semantically related functions that provide access to a COM object. Each OLE interface defines a contract that allows objects to interact according to the Component Object Model (COM). While OLE provides many interface implementations, most interfaces can also be implemented by developers designing OLE applications. See also Component Object Model and COM object.

Interface identifier (IID)

A globally unique identifier (GUID) associated with an interface. Some functions take IIDs as parameters to allow the caller to specify which interface pointer should be returned.

Item moniker

A moniker based on a string that identifies an object in a container. Item monikers can identify objects smaller than a file, including embedded objects in a compound document, or a pseudo-object (like a range of cells in a spreadsheet). See also File moniker, Generic composite moniker, Moniker, and Pseudo-object.

L

Licensing

A feature of COM that provides control over object creation. Licensed objects can be created only by clients that are authorized to use them. Licensing is implemented in COM through the IClassFactory2 interface and by support for a license key that can be passed at run time.

Link object

A COM object that is created when a linked COM object is created or loaded. The link object is provided by OLE and implements the IOleLink interface.

Linked object

A COM object whose source data physically resides where it was initially created. Only a moniker that represents the source data and the appropriate presentation data are kept with the compound document. Changes made to the link source are automatically reflected in the linked object. See also Link source.

Link source

The data that is the source of a linked object. A link source may be a file or a portion of a file, such as a selected range of cells within a file (also called a pseudo object). See also Linked object.

Loaded state

The state of an object after its data structures have been loaded into memory and are accessible to the client process. See also Active state, Passive state, and Running state.

Lcal server

An out-of-process server implemented as an .EXE application running on the same machine as its client application. See also In-process server, Out-of-process server, and Remote server.

Lock

A pointer held to-and possibly, a reference count incremented on-a running object. OLE defines two types of locks that can be held on an object: strong and weak. To implement a strong lock, a server must maintain both a pointer and a reference count, so that the object will remain "locked" in memory at least until the server calls Release. To implement a weak lock, the server maintains only a pointer to the object, so that the object can be destroyed by another process.

M

Marshaling

Packaging and sending interface method calls across thread or process boundaries.

Media type

An extension of MIME that allows data format negotiation between a client and an object. See also Multipurpose Internet Mail Extension (MIME).

MIME

See Multipurpose Internet Mail Extension.

MIME content type

See Media type.

Multipurpose Internet Mail Extension (MIME)

An Internet protocol originally developed to allow exchange of electronic mail messages with rich content across heterogeneous network, machine, and e-mail environments. In practice, MIME has also been adopted and extended by non-mail applications.

Moniker

An object that implements the IMoniker interface. A moniker acts as a name that uniquely identifies a COM object. In the same way that a path identifies a file in the file system, a moniker identifies a COM object in the directory namespace. See also Binding.

Moniker class

An implementation of the IMoniker interface. System-supplied moniker classes include file monikers, item monikers, generic composite monikers, anti-monikers, pointer monikers, and URL monikers.

Moniker client

An application that uses monikers to acquire interface pointers to objects managed by another application.

Moniker provider

An application that makes available monikers that identify the objects it manages, so that the objects are accessible to other applications.

N

Namespace extension

An in-process COM object that implements IShellFolder, IPersistFolder, and IShellView, which are sometimes referred to as the namespace extension interfaces. A namespace extension is used either to extend the shell's namespace or to create a separate namespace. Primary users are the Windows Explorer and common file dialog boxes.

Native data

The data used by an OLE server application when editing an embedded object. See also Presentation data.

O

Object

In OLE, a programming structure encapsulating both data and functionality that are defined and allocated as a single unit and for which the only public access is through the programming structure's interfaces. A COM object must support, at a minimum, the IUnknown interface, which maintains the object's existence while it is being used and provides access to the object's other interfaces. See also COM and Interface.

Object handler

See Default object handler.

Object state

The relationship between a compound document object in its container and the application responsible for the object's creation: active, passive, loaded, or running. Passive objects are stored on disk or in a database, and the object is not selected or active. In the loaded state, the object's data structures have been loaded into memory, but they are not available for operations such as editing. Running objects are both loaded and available for all operations. Active objects are running objects that have a visible user interface.

Object type name

A unique identification string that is stored as part of the information available for an object in the registration database.

OLE

Microsoft's object-based technology for sharing information and services across process and machine boundaries.

OLE Automation

See Automation.

OLE control

See Control.

Out-of-process server

A server, implemented as an .EXE application, which runs outside the process of its client, either on the same machine or a remote machine. See also Local server and Remote server.

Out parameter

An OUT parameter is allocated by the function being called, and freed by caller. See also In parameter and In/Out parameter.

P

Passive state

The state of a COM object when it is stored (on disk or in a database). The object is not selected or active. See also Active state, Loaded state, Object state, and Running state.

Persistent properties

Information that can be stored persistently as part of a storage object such as a file or directory. Persistent properties are grouped into property sets, which can be displayed and edited.

Persistent properties are different from the run-time properties of objects created with OLE Controls and Automation technologies, which can be used to affect system behavior. The PROPVARIANT structure defines all valid types of persistent properties, whereas the VARIANT structure defines all valid types of run-time properties. See also Compound files, Property, and Property sets.

Persistent storage

Storage of a file or object in a medium such as a file system or database so that the object and its data persist when the file is closed and then re-opened at a later time.

Picture object

A COM object that provides access to GDI images by implementing the IPicture interface.

Pointer moniker

A moniker that maps an interface pointer to an object in memory. Whereas most monikers identify objects that can be persistently stored, pointer monikers identify objects that cannot. They allow such objects to participate in a moniker binding operation.

Presentation data

The data used by a container to display embedded or linked objects. See also Native data.

Primary verb

The action associated with the most common or preferred operation users perform on an object. The primary verb is always defined as verb zero in the system registration database. An object's primary verb is executed by double-clicking on the object.

Property

Information that is associated with an object. In OLE, properties fall into two categories: run-time properties and persistent properties. Run-time properties are typically associated with control objects or their containers. For example, background color is a run-time property set by a control's container. Persistent properties are associated with stored objects. See also Persistent properties and Run-time properties.

Property frame

The user interface mechanism that displays one or more property pages for a control. The OLE Controls run-time system provides a standard implementation of a property frame that can be accessed by using the OleCreatePropertyFrame helper funtion. See also Control and Property page.

Property identifier

A four-byte signed integer that identifies a persistent property within a property set. See also Persistent property and Property set.

Property page

A COM object with its own CLSID that is part of a user interface, implemented by a control, and allows the control's properties to be viewed and set. Property page objects implement the IPropertyPage interface. See also CLSID, Control.

Property page site

The location within a property frame where a property page is displayed. The property frame implements the IPropertyPageSite interface, which contains methods to manage the sites of each of the property pages supplied by a control. See also Property frame.

Property set

A logically related group of properties that is associated with a persistently stored object. To create, open, delete, or enumerate one or more property sets, implement the IPropertySetStorage interface. If you are using compound files, you can use OLE's implementation of this interface rather than implementing your own. See also Persistent properties.

Property set storage

A COM storage object that holds a property set. A property set storage is a dependent object associated with and managed by a storage object. See also Dependent object, Property set.

Property sheet

A set of property pages for one or more objects. See also Property page.

Proxy

An interface-specific object that packages parameters for that interface in preparation for a remote method call. A proxy runs in the address space of the sender and communicates with a corresponding stub in the receiver's address space. See also Stub, Marshaling, and Unmarshaling.

Proxy manager

In standard marshaling, a proxy that manages all the interface proxies for a single object. See also Marshaling, Proxy.

Pseudo-object

A portion of a document or embedded object, such as a range of cells in a spreadsheet, that can be the source for a COM object.

R

Redistributable files

For more information on redistributable files, see the readme file for this service pack and the file redist.txt in the license directory.

Reference counting

Keeping a count of each interface pointer held on an object to ensure that the object is not destroyed before all references to it are released. See also Lock.

Relative moniker

A moniker that specifies the location of an object relative to the location of another object. A relative moniker is analogous to a relative path, such as ..\backup\report.old. See also Moniker.

Remote Server

A server application, implemented as an EXE, running on a different machine from the client application using it. See also In-process server, Local server, and Out-of-process server.

Revert

To discard any changes made to an object since the last time the changes were committed or the object's storage was opened. See also Commit and Transacted access mode.

Root storage object

The outermost storage object in a document. A root storage object can contain other nested storage and stream objects. For example, a compound document is saved on disk as a series of storage and stream objects within a root storage object. See also Compound document, Storage object, and Stream object.

Running state

The state of a COM object when its server application is running and it is possible to access its interfaces and receive notification of changes. See also Active state, Loaded state, Passive state.

Running Object Table (ROT)

A globally accessible table on each computer that keeps track of all COM objects in the running state that can be identified by a moniker. Moniker providers register an object in the table, which increments the object's reference count. Before the object can be destroyed, its moniker must be released from the table. See also Running state.

Run-time property

Discrete state information associated with a control object or its container. There are three types of run-time properties: ambient properties, control properties, and extended properties. See also Ambient property, Control property, and Extended property. Contrast with Persistent property.

S

SCODE

A DWORD value that is used to return detailed information to the caller of an interface method or function. See also HRESULT.

Self-registration

The process by which a server can perform its own registry operations.

Server application

An application that can create COM objects. Container applications can then embed or link to these objects. See also Container application.

Sink

See Advisory sink.

State

See Active state, Loaded state, Object state, Passive state, and Running state.

Static object

An object that contains only a presentation, with no native data. A container can treat a static object as though it were a linked or embedded object, except that it is not possible to edit a static object.

A static object can result, for example, from the breaking of a link on a linked object-that is, the server application is unavailable, or the user doesn't want the linked object to be updated anymore. See also Native data.

Storage object

A COM object that implements the IStorage interface. A storage object contains nested storage objects or stream objects, resulting in the equivalent of a directory/file structure within a single file. See also Root storage object and Stream object.

Stream object

A COM object that implements the IStream interface. A stream object is analogous to a file in a directory/file system. See also Storage object.

Strong lock

See Lock.

Structured Storage

OLE's technology for storing compound files in native file systems. See also Compound file, Storage object, and Stream object.

Stub

When a function's or interface method's parameters are marshaled across a process boundary, the stub is an interface-specific object that unpackages the marshaled parameters and calls the required method. The stub runs in the receiver's address space and communicates with a corresponding proxy in the sender's address space. See also Proxy, Marshaling, and Unmarshaling.

Stub manager

Manages all of the interface stubs for a single object.

Subobject

See Dependent object.

Synchronous call

A function call that does not allow further instructions in the calling process to be executed until the function returns. See also Asychronous call.

System registry

A system-wide repository of information supported by Windows, which contains information about the system and its applications, including OLE clients and servers.

T

Transacted access mode

One of two access modes in which a storage object can be opened. When opened in transacted mode, changes are stored in buffers until the root storage object commits its changes. See also Direct access mode, Commit, Revert, and Root storage object.

Type information

Information about an object's class provided by a type library. To provide type information, a COM object implements the IProvideClassInfo interface.

U

Uniform data transfer

A model for transferring data via the Clipboard, drag and drop, or Automation. Objects conforming to this model implement the IDataObject interface. This model replaces DDE (dynamic data exchange). See also Data transfer object.

Unmarshaling

Unpacking parameters that have been sent to a proxy across process boundaries.

Universal resource locator (URL)

The identifier used by the World Wide Web for the names and locations of objects on the Internet. OLE provides a moniker class, URL moniker, whose implementation can be used to bind a client to objects identified by a URL. See also URL moniker.

URL moniker

A moniker based on a universal resource locator (URL). A client can use URL monikers to bind to objects that reside on the Internet. The system-supplied URL moniker class supports both synchronous and asynchronous binding. See also Asynchronous binding.

V

Virtual Table (VTBL)

An array of pointers to interface method implementations. See also Interface.

Visual Editing

A term in end-user documents that refers to the user's ability to interact with a compound-document object in the context of its container. The term most often used by developers is in-place activation.

W

Weak lock

See Lock.


COM Language Translations

Components created using the Component Object Model (COM) can be reused in applications written in any COM-supporting programming language. This is because COM is a binary standard and, as such, is language-independent.

In this software development kit (SDK), COM objects are documented in the most relevant programming language or languages. For example, objects that are created for use in Web pages are typically documented in the Microsoft® Visual Basic® development system, whereas operating system–level objects are typically documented in C++.

But because COM is language-neutral, you are not constrained to use the object in the same language in which it is written or documented. For example, you can write a Java application that uses a control created in C++ and documented in Visual Basic.

The following topics discuss the differences between programming languages and describe how to translate COM object syntax from one language to another. Additional topics describe how to use COM objects in various scripting languages and environments.

The intent of this document is to address the most common language translation issues that arise when using COM objects. Because scripting languages and programming languages represent different programming paradigms, translation between scripting languages and programming languages is not addressed.

Note The programming languages discussed in the following topics are Visual Basic, C++, and Java. The scripting languages discussed are Microsoft® Visual Basic Scripting Edition (VBScript), Microsoft® JScript® development software, and JavaScript. The techniques and principles described, however, apply to any programming or scripting language that supports COM.


Syntax Differences

The most apparent change as you move between programming languages is the change in syntax. For example, consider the EnhEvents object's Add method, shown as it is declared in three different languages.

Visual Basic

object.Add( Time As Double, Name As String ) As Variant

C++

HRESULT Add(
  double Time,
  BSTR Name,
  VARIANT* pVal
);

Java

public com.ms.com.Variant Add(
  double Time,
  java.lang.String Name
);

Although the syntax of each language expresses the method differently, the functionality is the same. In each language, the Add method takes the parameters Time and Name and returns an EnhEvent object. In the C++ example, the method returns the object by using a third, output parameter, pVal.

Typically, the functionality of a COM object is the same across programming languages. Because of this, SDK documentation for a COM object is useful even if the object is documented in another programming language than the one you are using. The descriptions of the object's functionality, parameters, and return values are, with few exceptions, valid for all languages.

For information about how to convert the syntax of a COM object to another programming language, see Translating COM Object Syntax for Programming Languages.

The syntax differences among the scripting languages JavaScript, JScript, and VBScript are less pronounced than the syntax differences among the programming languages shown preceding. For example, consider the square function as it is implemented in each of these three scripting languages:

VBScript

Function square(x)
  square = x*x
End Function

JavaScript

function square(x){ return x*x; }

JScript

function square(x){ return x*x; }

Notice that the scripting languages, unlike the programming languages, are weakly typed. In other words, you do not have to specify the data type of a parameter or return value when you declare a function. Instead, the variables are automatically cast to the appropriate data type. In the case of VBScript, all variables are of the same data type, Variant.

The JavaScript and JScript syntax for square is the same. JScript is largely compatible with JavaScript. However, JScript includes some objects not currently supported by JavaScript, such as ActiveXObject, Enumerator, Error, Global, and VBArray. For more information about these objects, see the JScript Language Reference section of the Platform SDK.

On the surface, JavaScript and JScript syntax resembles Java syntax. This similarity is only superficial. The Java language was developed independently from both JavasSript and Jscript and is not related to either.

VBScript, on the other hand, is a subset of the Visual Basic programming language. Because of this, VBScript syntax is a subset of Visual Basic syntax and is often interchangeable with Visual Basic syntax.

For information on using COM objects in scripting languages, see Scripting with COM Objects.


Data Type Conversions

Each programming language defines certain types and containers for data. Most of these data types, especially the primitives, map easily to other programming languages. Some data types, however, have no equivalent in another language and cannot be converted.

For specific information about data types not recognized by your programming language, see the translation topics for C++, Visual Basic, or Java.

The following table lists conversions between programming languages for common data types. The Contains column includes the size and type of each data type.

C++ | Visual Basic | Java | Contains
signed char | Not supported | byte | 1-byte signed integer (VT_I1, [T])
unsigned char | Byte | Not supported | 1-byte unsigned integer (VT_UI1, [V][T][P][S])
unsigned char | Character | char | 2-byte Unicode character (VT_UI2, [T][P])
short | Integer | short | 2-byte signed integer (VT_I2, [V][T][P][S])
unsigned short | Not supported | Not supported | 2-byte unsigned integer (VT_UI2, [T][P])
int | Long | int | 4-byte signed integer (VT_I4, [V][T][P][S])
unsigned int | Not supported | Not supported | 4-byte unsigned integer (VT_UI4, [T][P])
__int64 | Not supported | long | 8-byte signed integer (VT_I8, [T][P])
unsigned __int64 | Not supported | Not supported | 8-byte unsigned integer (VT_UI8, [T][P])
float | Single | float | 4-byte floating point number (VT_R4, [V][T][P][S])
double | Double | double | 8-byte floating point number (VT_R8, [V][T][P][S])
BSTR | String | java.lang.String | Automation string (VT_BSTR, [V][T][P][S])
BOOL | Boolean | boolean | Boolean (VT_BOOL, [V][T][P][S])
VARIANT | Variant | com.ms.com.Variant | VARIANT FAR* (VT_VARIANT, [V][T][P][S])
IUnknown | object | com.ms.com.IUnknown | IDispatch interface pointer (VT_DISPATCH, [V][T][P][S])
DATE | Date | com.ms.com.Variant | Date (VT_DATE, [V][T][P][S])
CURRENCY | Currency | com.ms.com.Variant | Currency (VT_CY, [V][T][P][S] or VT_DECIMAL, [V][T][S])

The Contains column also lists each data type's VARTYPE value and allowed usage. VARTYPE is an enumeration that lists the Automation variant types. The characters in square brackets ([]) after each VARTYPE value describe the allowed usage of that VARTYPE, as follows:

  • [V] indicates that the data type may be used in a VARIANT.
  • [T] indicates that the data type may be used in a TYPEDESC.
  • [P] indicates that the data type may be used in a Automation property set.
  • [S] indicates that the data type may be used in a SAFEARRAY.

For more information about VARTYPE values and how to use them, see the topic IDispatch Data Types and Structures in the Platform SDK documentation.

The data type conversions between scripting languages are simpler than those for programming languages. JScript and JavaScript both support the same data types, and Visual Basic supports only a single data type, Variant. Thus, all JScript and JavaScript data types become Variant types when converted to VBScript. When you convert VBScript to JScript or JavaScript, the Variant types become numbers, strings, Boolean values, and so on.


IDL Files

COM uses the Microsoft® Interface Definition Language (MIDL) to describe COM objects. MIDL is an extension of the IDL for distributed computing environments defined by the Open Software Foundation, which was developed to define interfaces for remote procedure calls in traditional client/server applications. MIDL includes most of the attributes and statements of Object Definition Language (ODL), the language originally used to generate type libraries for OLE Automation.

In C++ and Java, a developer building a COM object creates an IDL file that the MIDL compiler then processes to create a type library or header and proxy files, or both. A type library is a binary file that describes the COM object or COM interfaces, or both. A type library is the compiled version of the IDL file. However, type libraries support ODL semantics only. In particular, they cannot represent all the information from an IDL file related to IDL attributes like size_is(). You need to create and use proxy files for IDL files affected by information loss in the type library.

In Visual Basic, a developer creating a COM object does not create an IDL file. Instead, Visual Basic gathers information using class and project properties and directly creates the type library.


Translating COM Object Syntax for Programming Languages

To call a COM object from an application written in a programming language other than the one used to write the COM object, you must first translate the object's syntax to your programming language. This can be done using the following steps:

  1. View the COM object's type library in your programming language's syntax. Doing this provides you with descriptions of the object's classes, interfaces, methods, properties and events in the language syntax you use.

    Microsoft developer products provide several tools to assist you in viewing and converting type libraries. For more information, see Type Library Viewers and Conversion Tools.

    Once you can view the object's type library in your preferred programming language, you can compare its syntax to that in the SDK documentation for the object. If the object is documented in a programming language other than the one you are using, the data types and syntax may differ, but descriptions of parameters, return values, and the object's functionality should be the same.

  2. Take into account any special considerations for translating to your programming language.

    Because each programming language defines concepts that may not have an equivalent in other languages, some of an object's functionality may work differently in another language, or not be available at all.

    For example, the Visual Basic programming language does not recognize C++ unsigned data types, such as unsigned long. An application written in Visual Basic cannot use COM methods that accept or return unsigned data type variables.

  3. Add the COM object's compiled code to your project. The compiled code is typically contained in a .dll or .ocx file. This step is necessary for the compiler to recognize the COM object's classes. After you add the COM object, your application can use its classes and interfaces.

The following topics describe how to translate and use COM objects in a variety of programming languages:

  • Translating to Java
  • Translating to Visual Basic
  • Translating to C++

Note

The following topics describe conversion tools and processes provided by Microsoft developer products. For instructions on how to program COM objects using development tools created by other companies, see those development tools' documentation.


Type Library Viewers and Conversion Tools

Type libraries contain the specification for one or more COM elements, including classes, interfaces, enumerations, and more. These files are stored in a standard binary format. A type library can be a stand-alone file with the .tlb file name extension, or it can be stored as a resource in an executable file, which can have an .ocx, .dll, or .exe file name extension. The type library viewers and conversion tools described following read this format to gain information about the COM elements in the library.

Before you can program an object in a particular programming language, you must be able to view its type library in that language. Doing this provides you with the proper syntax for the classes, interfaces, methods, properties, and events of the COM object.

Microsoft development products provide the following tools that you can use to generate, extract, and view type library information.

Visual Basic

  • Visual Basic Object Browser

C++

  • OLE-COM Object Viewer
  • MIDL compiler
  • MkTypLib command-line tool

Java

  • JActiveX command-line tool
  • Java Type Library Wizard
  • JavaTLB command-line tool

For an overview of how the preceding tools interact with type libraries, see How Developer Tools Use Type Libraries.


How Developer Tools Use Type Libraries

The following diagram illustrates how the various development tools interact with a COM object's type library. Each type library exposes standard programmatic interfaces that tools can call to get information about the elements described in that type library. In this diagram, GUID stands for globally unique identifier and RPC for remote procedure call.

In the preceding diagram, the C++ conversion tools, such as the MIDL compiler and the wizards provided by the Microsoft® Visual C++® development system, generate header and stub files. You can add these files to your project in order to use the COM object described by the type library.

Similarly, in Java the developer tools generate Java class and source files, which you can then import into your application.

In Visual Basic, the scenario is somewhat simpler. You do not need to generate additional files. The Visual Basic environment provides dialog boxes listing the COM objects currently installed on your computer. You select the component you want to call from your application, and it is added to your project, either as a component or a reference.

The OLE-COM viewer reads a type library, generates a temporary IDL file based on the type library, and displays it to users. The OLE-COM viewer also displays the C++ syntax for the COM elements listed in the type library.

For more information about type libraries, see Type Libraries and the Object Description Language in the Platform SDK.


Translating to Visual Basic

You can add a COM object to your Visual Basic project either as a reference or a component. Once the object is added to your project, your application can access its classes and interfaces. You can then use the Visual Basic Object Browser to view the object's type library information in Visual Basic syntax.

Typically, controls are added to a project as a components and noncontrols are added as references. When a COM object is added as a component, it appears in the Visual Basic toolbox. New instances of that object are created by dragging the object icon from the toolbox onto a Visual Basic form or other type of container. New instances of COM objects added as references are created using the new keyword.

The distinction between using a class as a reference versus a component is subtle but important. When you add an object as a reference, you can use only the type library that the control provides, or the "raw" type library.

If you add a control as a component, Visual Basic merges the extender properties and methods of the form in which the control is embedded with the control's type library, thus providing a wrapped, extended version of the type library. With this version of the type library, you can use extender properties such as Top and Left as if they were part of the control, instead of the control's container.

Note Visual Basic does not currently support multiple type libraries built into a single .dll file. If you run into a DLL that incorporates multiple type libraries, you should get stand-alone copies of the type libraries from the source that supplied the object in order to use the object with Visual Basic.

The following topics describe issues you should consider when translating a COM object's syntax to Visual Basic from C++ or Java:

  • Translating to Visual Basic from C++
  • Translating to Visual Basic from Java

For guidelines on how to develop COM objects that are fully compatible with Visual Basic, see the "Building COM Components That Take Full Advantage of Visual Basic and Scripting" article. This document can be located on the MSDN™ developer library CD, or on the MSDN web site at http://msdn.microsoft.com/library/.


Translating to Visual Basic from C++

Using the C++ programming language, developers can directly access the memory that stores a particular variable. Memory pointers provide this direct access. In Visual Basic, pointers are handled for you. For example, a parameter declared as a pointer to an int in C++ is equivalent to a parameter declared in Visual Basic as a ByRef Integer.

A parameter that is declared As String in Visual Basic is declared as a pointer to a BSTR in C++. Setting a string pointer to NULL in C++ is equivalent to setting the string to the vbNullString constant in Visual Basic. Passing in a zero-length string ("") to a function designed to receive NULL does not work, as this passes a pointer to a zero-length string instead of a zero pointer.

C++ supports data containers, namely structures and unions, that have no equivalent in Visual Basic. For this reason, COM objects typically wrap information that usually is stored in structures and unions in object classes. Some COM objects, however, may contain structures, causing portions of the object's methods or functionality to be inaccessible to Visual Basic.

Note

Future versions of Visual Basic, version 6.0 and beyond, will support structures.

Some C++ data types are not supported in Visual Basic, for example unsigned types and HWND types. Methods that accept or return these data types are not available in Visual Basic.

Visual Basic uses Automation-compatible data types as its internal data types. Thus, C++ data types that are Automation-compatible are also compatible with Visual Basic. Data types that are not Automation-compatible may not be able to be converted to Visual Basic. For more information about Automation data types, see the Automation documentation in the Platform SDK.

The following table lists the data types are supported by Visual Basic and their VARTYPE equivalents. VARTYPE is an enumeration that lists the Automation variant types.

Visual Basic data type | VARTYPE equivalent
Integer | 16 bit, signed, VT_I2
Long | 32 bit, signed, VT_I4
Date | VT_DATE
Currency | VT_CY
Object | *VT_DISPATCH
String | VT_BSTR
Boolean | VT_BOOL
Currency | VT_DECIMAL
Single | VT_R4
Double | VT_R8
Decimal | VT_DECIMAL
Byte | VT_DECIMAL
Variant | VT_VARIANT

All parameters in Visual Basic, unless labeled with the keyword ByVal, are passed by reference (as pointers) instead of by value.

C++ and Visual Basic differ slightly in how they represent properties. In C++, properties are represented as a set of accessor functions, one that sets the property value and one that retrieves the property value. In Visual Basic, properties are represented as a single item that can be used to retrieve or set the property value.


Translating to Visual Basic from Java

Visual Basic uses Automation-compatible data types as its internal data types. Thus, Java data types that are Automation-compatible are also compatible with Visual Basic. Data types that are not Automation-compatible may not be able to be converted to Visual Basic. For more information about Automation data types, see the Automation documentation in the Platform SDK.

Java and Visual Basic differ slightly in how they represent properties. In Java, properties are represented as a set of accessor functions, one that sets the property value and one that retrieves the property value. In Visual Basic, properties are represented as a single item that can be used to retrieve or set the property value.


Adding a Component to a Visual Basic Project

The following procedure describes how to add a COM object as a component to a Visual Basic project. Your application can then use the classes of that object.

Adding a control as a component exposes the Visual Basic extender properties and methods as if they were part of the control. In contrast, when you add an object as a reference you can only use the type library provided by the control, or the "raw" type library.

To insert a control into a Visual Basic project

  1. On the Project menu, click Components.
  2. Click to select the check box next to the component you want to add. If the component is not listed, locate the .dll or .ocx file using Browse.
  3. Click OK.

The component is now part of the project and appears in the object toolbar. Your application can create instances of the control by dragging the control from the toolbar and dropping it onto a form or dialog box.


Adding a Reference to a Visual Basic Project

The following procedure describes how to add a reference to an COM object to a Visual Basic project. Your application can then use the classes of that object.

When you add an object as a reference, you can only use the type library provided by the control, or the "raw" type library. In contrast, adding a control as a component also exposes the Visual Basic extender properties and methods as if they were part of the control.

To make a Visual Basic reference to a component

  1. On the Project menu, click References.
  2. Click to select the check box next to the component you want to add. If the component is not listed, locate the .dll or .ocx file using Browse.
  3. Click OK.

The component is now part of the project. Your application can create class instances using the New keyword.


Visual Basic Object Browser

Once a COM object has been added to your project as a reference or component, you can use the Visual Basic Object Browser to view type library information for that object in Visual Basic.

To view a COM object in the Object Browser

  • On the View menu, click Object Browser.

The left side of the Object Browser window displays all of the classes of references and components added to the current project. The right side contains the methods, properties, and events of those classes.

You can filter which COM object classes are displayed by selecting an object library from the library selection drop-down list.

For more information, see "Browsing ActiveX Component Type Libraries" in the Visual Basic documentation.

Note

In some cases, the Visual Basic Object Browser appears not to show a return type for a function or property. No return type appears when the return type is the default type, Variant. If the description pane of the Object Browser describes the element as a function or property and a return type is not shown, the return type statement is "As Variant".


Translating to Java

The Microsoft® Visual J++™ development system for Java provides two tools that you can use to generate type library information for a COM object in Java syntax, the Java Type Library Wizard and the JActiveX command-line tool. Using either of these tools, you can generate Java class files for a COM object.

Note

In Visual J++ version 6.0, the JActiveX tool replaces the JavaTLB command-line tool. For more information, see the Visual J++ documentation.

If you are using a Java development environment other than Visual J++, consult that product's documentation for information on COM conversion tools.

Once you have generated the class files for a component, you can add the component classes to your Java application using the import statement. For example, to import the classes stored in the Stream.dll library you use the following code:

import stream.*;

You can then create and use the object's classes in your Java application. For more information about the Java import statement, see a reference on the Java programming language.

The following topics describe issues you should consider when translating a COM object's syntax to Java from C++ or Visual Basic:

  • Translating to Java from C++
  • Translating to Java from Visual Basic


Translating to Java from C++

Using the C++ programming language, developers can directly access the memory that stores a particular variable. Memory pointers provide this direct access. In Java, pointers are handled for you.

In Java, struct, union, and typedef composite data types are handled exclusively through the use of classes. For example, the C++ VARIANT data type maps to com.ms.com.Variant in Java.

In C++, strings are an array of characters. In Java, strings are objects. Methods that act on strings treat the string as a complete object.

COM methods return a value known as a HRESULT, which is a 32-bit error code. The Java support for Microsoft® Internet Explorer defines a class, com.ms.com.ComException, which wraps the HRESULT error code. For more information about com.ms.com.ComException, see the Visual J++ documentation.

Java does not support unsigned data types except for char, which is a 16-bit unsigned integer. Methods that accept or return other unsigned data types cannot be used from Java.

Java does not support multidimensional arrays. Methods that accept or return multidimensional arrays are not available from Java.

Boolean values in Java cannot be cast to 0 and 1.


Translating to Java from Visual Basic

By default, Visual Basic passes parameters by reference. Parameters that are meant to be passed by value only are specified by the keyword ByVal.

Java does not support multidimensional arrays. Methods that accept or return multidimensional arrays are not available from Java.

Java and Visual Basic differ slightly in how they represent properties. In Java, properties are represented as a set of accessor functions, one that sets the property value and one that retrieves the property value. In Visual Basic, properties are represented as a single item that can be used to retrieve or set the property value.


JActiveX Command-Line Tool

The JActiveX command-line application is a component of Visual J++ 6.0. It reads the type library of a Microsoft® ActiveX® control and generates the corresponding Java source files. You can then compile these source files and import them into your Java application.

To generate Java source files for a COM object

• From the command prompt, run

jactivex filename

where filename is the name of the file that contains the type library. This file may have any of the following file name extensions: .tlb, .olb, .ocx, .dll, or .exe.

For more information about the optional parameters you can set in JActiveX, see the Visual J++ documentation, or type the following command line:

jactivex /?

Note

Do not use Visual J++ compilers prior to version 1.02.3920 to compile the files generated by JActiveX.exe. This is because the generated source files must be compiled with a @com-aware compiler. Only Visual J++ compilers later than version 1.02.3920 are @com-aware.


Java Type Library Wizard

The Java Type Library Wizard is a component of Visual J++ 5.0. This wizard generates class files and Java type library information for a COM object.

To generate Java class files and type library information

  1. On the Tools menu, click Java Type Library Wizard.

    A list of the installed COM components appears.

  2. Click to select the check boxes next to the items you want to convert.
  3. Click OK.

    The type library wizard generates class files for the selected components. It prints the import statement required to insert each component into a Java application or applet and a summary text file that contains the Java type library information for the object.

For more information, see "Running the Java Type Library Wizard" in the Visual J++ documentation.


JavaTLB Command-Line Tool

JavaTLB is a component of Visual J++ 5.0. JavaTLB is a command-line application that generates class files for a COM object. Methods that contain data types that cannot be accurately and safely represented in Java are not converted.

Unlike the Java Type Library Wizard, JavaTLB does not generate a summary file containing the Java type library information.

To generate Java class files for a single COM object

• From the command prompt, run

javatlb filename

where filename is the name of the file that contains the type library. This file may have any of the following file name extensions: .tlb, .olb, .ocx, .dll, or .exe.

To generate Java class files for multiple COM objects

• From the command prompt, run

javatlb @TextFile

where TextFile is the name of a text file that contains a list of the files containing the COM object's type libraries.

For more information, see "Using the JavaTLB Command-Line Tool" in the Visual J++ documentation.

Note

In Visual J++ 6.0, the JavaTLB command-line tool is replaced by the JActiveX tool. For more information, see the Visual J++ 6.0 documentation.


Translating to C++

Visual C++ provides several tools to assist you in adding COM objects to your project. You can add an ActiveX control to your project, use the MFC Class Wizard, or run the MIDL compiler on an IDL file.

If you are using a C++ development environment other than Visual C++, consult that product's documentation for information on COM conversion tools.

Once you have added the COM object's include or stub files to your project, your application can use the COM object's classes.

Visual C++ also provides the OLE-COM Object Viewer, which you can use to view compiled type library information. The object viewer presents the COM classes in C++ syntax.

For more information about using COM objects in Visual C++ projects, see the Visual C++ documentation.

The following topics describe issues you should consider when translating a COM object's syntax to C++ from Java or Visual Basic:

  • Translating to C++ from Visual Basic
  • Translating to C++ from Java

Note

The MkTypLib command-line tool, Mktyplib.exe, is obsolete and has been replaced by the MIDL compiler, Midl.exe.


Translating to C++ from Visual Basic

Visual Basic handles pointers implicitly. In C++, your application is responsible for performing any necessary pointer arithmetic.

By default, Visual Basic passes parameters by reference (as pointers). Parameters that are meant to be passed by value only are specified by the keyword ByVal. For example, a ByVal Integer parameter in Visual Basic is equivalent to an short parameter in C++, whereas a ByRef Integer parameter in Visual Basic is equivalent to an short* parameter.

A parameter that is declared As String in Visual Basic is declared as a pointer to a BSTR in C++. Setting a string pointer to NULL in C++ is equivalent to setting the string to the vbNullString constant in Visual Basic. Passing a zero-length string ("") to a function designed to receive NULL does not work, as this passes a pointer to a zero-length string instead of a zero pointer.

C++ and Visual Basic differ slightly in how they represent properties. In C++, properties are represented as a set of accessor functions, one that sets the property value and one that retrieves the property value. In Visual Basic, properties are represented as a single item that can be used to retrieve or set the property value.


Translating to C++ from Java

Java handles pointers and memory management implicitly. In C++, your application is responsible for allocating and deallocating memory, and for performing any necessary pointer arithmetic.


Adding an ActiveX Control to a Visual C++ Project

The following procedure adds dispatch class and header files for an ActiveX control to a Visual C++ project.

To add an ActiveX control to a Visual C++ project

  1. On the Project menu, click Add to Project. A shortcut menu appears.
  2. Click Components and Controls. The Components and Controls Gallery dialog box appears.
  3. Click the component to add to your project. Visual C++ displays a dialog box from which you can select a subset of the component's classes to add to your project.
  4. Click to select the check boxes of the classes you want to add, and click OK.

Visual C++ generates dispatch class and header files for the component and adds them to your project.


MFC Class Wizard

The MFC Class Wizard is a component of Visual C++ that reads a type library file and creates dispatch classes for the objects described therein. The MFC Class Wizard writes header and implementation code for your dispatch class and adds the .cpp file to your project. This tool only works with applications that use Microsoft® Foundation Classes (MFC).

You can run the class wizard on type library files created by the MIDL compiler.

To generate dispatch classes from a type library file

  1. On the View menu, click ClassWizard. The MFC Class Wizard dialog box appears.
  2. On the Add Class menu, click From a Type Library. The Import From a Type Library dialog box appears.
  3. Type the name of the type library file in the dialog box. The Confirm Classes dialog box appears.
  4. Click OK.

Visual C++ generates dispatch class and header files for the component and adds them to your project.

For more information, see "Automation Clients: Using Type Libraries" in the Visual C++ documentation.


MIDL Compiler

The MIDL compiler processes an IDL file to generate a type library and output files. The type of output files generated by the MIDL compiler depends on the attributes specified in the IDL file's interface attribute list.

If the attribute list contains the object keyword, the MIDL compiler generates COM interface output files: an interface proxy file, an interface header file, and a globally unique identifier (GUID) file for the interface. If the IDL file contains a library statement, MIDL generates a type library file with the .tlb file name extension. If there are any interfaces in the IDL file that do not have the object keyword and are not enclosed in a library statement, the MIDL compiler generates interface output files appropriate for remote procedure calls (RPCs): a client stub file, a server stub file, and a header file. For more information, see the topic Interface Definitions and Type Libraries and Generating a Type Library with MIDL in the MIDL Programmer's Guide and Language Reference in the Platform SDK.

To generate a type library and output files from an IDL file

• From the command prompt, run

midl filename

where filename is the name of the IDL file.

The MIDL compiler also supports several optional parameters. For a complete list, see "MIDL Command-Line Reference" in the Visual C++ documentation, or run the following command line:

midl /?

For more information about using the MIDL compiler, see the Visual C++ documentation.


MkTypLib Command-Line Tool

MkTypLib is a command-line application that processes an IDL file to produce a type library. It can also be used to generate a C or C++ header file.

Note

This tool is obsolete. You should instead use the MIDL compiler. If you need backward compatibility with old Automation programs, use the MIDL compiler with the /mktyplib203 option.

The type library generated by MkTypLib can then be brought into your Visual C++ project by using the MFC Class Wizard.

To generate a type library from an ODL file

• From the command prompt, run

mktyplib filename

where filename is the name of the ODL file.

MkTypLib also supports several optional parameters. For a complete list, run the following command line:

mktyplib /?

Because MkTypLib is an obsolete application, it cannot parse IDL files or files with interfaces defined outside of a library statement. For more information, see Differences Between MIDL and MKTYPLIB in the MIDL Programmer's Reference in the Platform SDK.

For more information about MkTypLib, see the Visual C++ documentation.


OLE-COM Object Viewer

The OLE-COM object viewer is an application supplied with Visual C++ that displays the COM objects installed on your computer and the interfaces they support. You can use this object viewer to view type libraries.

To view a COM object's type library

  1. On the object viewer File menu, click View TypeLib.

    An Open dialog box appears.

  2. Specify the type library file you want to open, and click OK.

The object viewer displays the object's interfaces.

For more information, see the Visual C++ documentation.


Scripting with COM Objects

A scripting language is a programming language that is parsed at run time by a scripting engine, a component that translates scripts written in that language into machine code. Each scripting engine translates a specific scripting language. A scripting host is an application, such as a Web browser, that hosts a scripting engine to run scripts. If your scripting host supports COM, you can write scripts that use COM objects. The following topics describe scripting hosts that support COM objects, common scripting languages, and how to translate between scripting languages.

A scripting language differs from a compiled language in that it is translated into machine code at run time. This means that each time you run a script, the scripting engine first parses the code and then runs it. In contrast, compiled languages, such as C++, are translated to machine code once, during compilation. When you run a compiled application, the operating system simply runs the precompiled code.

Because a scripting engine must reparse a script each time it runs, scripting languages are typically slower and less efficient than their precompiled counterparts. The advantage of scripts, however, is they are easy to write and maintain. Scripting languages are usually simpler than precompiled languages, and when a script changes, it does not need to be recompiled. For lightweight and rapidly changing applications, such as Web pages, scripting languages are ideal.

There are several host environments in which you can write scripts that use COM objects, as described following:

  • Embedding COM Objects in Web Pages
  • Using COM Objects in Active Server Pages
  • Using COM Objects in Windows Scripting Host
  • Scripting COM Objects in Custom Applications

In each of the host environments mentioned preceding, a scripting engine parses and runs the script. Because the engine for each scripting language is a separate component, you can add a new scripting language to an environment by adding a new engine.

The most commonly used scripting languages are:

  • Microsoft Visual Basic Scripting Edition (VBScript), a subset of Visual Basic.
  • JavaScript, the Netscape scripting language, formerly known as LiveScript.
  • Microsoft® JScript® development software, the Microsoft implementation of the ECMA 262 language specification.

Microsoft provides scripting engines for JScript and VBScript. Other software companies provide ActiveX scripting engines for languages such as PerlScript, PScript, Python, and others.

For more information about creating or hosting scripting engines, see "ActiveX Scripting" in the Internet Client SDK section of the Platform SDK. For more information about the ECMA 262 language specification, see http://www.ecma.ch/stand/ecma-262.htm.

Note

Most scripting languages, such as VBScript and JScript, cannot access or modify files. This inability prevents the script from altering data on client computers. COM objects, however, have no such limitations. Once they are downloaded and installed on client computers, they can perform any standard application action. Thus, users should only download and run ActiveX controls from trusted sources.

For information on translating between scripting languages, see the following topics:

  • Translating to VBScript
  • Translating to JavaScript
  • Translating to JScript


Embedding COM Objects in Web Pages

You can use COM objects in Web pages. To do this, first create an instance of that COM object. Once an object instance is created, you can use it in the following scripts on that Web page.

To create an COM object instance in a Web page, you can use an OBJECT tag. Alternatively, if your scripting language provides a native way to create COM objects, you can create an object instance using script.

Note that embedding COM objects in Web pages only works with browsers that support ActiveX and COM, for example Internet Explorer.

The following example illustrates using the OBJECT tag to embed a COM object in a Web page:

<OBJECT
  ID = vid
  CLASSID = "clsid:31263EC0-2957-11CF-A1E5-00AA9EC79700"
  BORDER = 0
  VSPACE = 0
  HSPACE = 0
  ALIGN = TOP
  HEIGHT = 100%
  WIDTH = 100%
>
</OBJECT>

You can also create a COM object instance in script, if your scripting language provides a way to create COM objects. For example, VBScript provides the CreateObject method and JScript provides the ActiveXObject object. Creating objects in script is illustrated in the following examples.

<SCRIPT LANGUAGE = "VBScript">
  Dim objXL
  Set objXL = CreateObject("Excel.Application")
</SCRIPT>

<SCRIPT LANGUAGE = "JScript">
  var objXL = new ActiveXObject("Excel.Application");
</SCRIPT>

In addition to the CreateObject method and the ActiveXObject object, both VBScript and JScript provide the method GetObject, which returns an object instance. For more information about GetObject, see the reference documentation for your scripting language.

Once a COM object has been created, you can reference it in subsequent scripts by using the identifier specified in the OBJECT tag's ID attribute. In the preceding example, this identifier was specified as "vid." Note that the script that uses the COM object must appear after the OBJECT tag or script that creates the object instance; otherwise, the object identifier is undefined. The following script uses the objXL object to display the version information for Microsoft® Excel.

<SCRIPT LANGUAGE = "VBScript">
  Msgbox objXL.Version
</SCRIPT>

For more information about the OBJECT and SCRIPT tags and how to use them, consult an HTML reference, such as the HTML Reference available in the MSDN library.

If you are writing scripts embedded in a Web page, the browser also exposes an object model that your scripts can access. Internet Explorer and Netscape Navigator use similar object models. The model used by Internet Explorer conforms to the Document Object Model (DOM) proposed by the World Wide Web Consortium (W3C). In the future, the Netscape Navigator object model will also conform to this standard.

For more information about the objects, methods, events, and properties provided by Internet Explorer, see the Internet Client SDK section of the Platform SDK.

Note

Netscape Navigator does not currently support ActiveX technologies and the OBJECT tag. If your script needs to work in Netscape Navigator, it should not rely on COM objects.


Using COM Objects in Active Server Pages

You can script COM objects in Active Server Pages (ASP) applications. To do so, you must first create an instance of the object either by using the OBJECT tag or by calling the CreateObject method of the ASP Server object. Once a COM object has been created, you can use it in following scripts on the ASP page.

Using ASP, you can work with many different types of scripting engines, each of which supports a different scripting language. ASP comes with VBScript and JScript scripting engines. You can also plug in scripting engines developed by other companies to support languages such as PerlScript, PScript, Python, and others.

If you do not set the scripting language for an ASP page, VBScript is the default. To specify a scripting language other than VBScript, include a line such as the following at the top of each ASP page:

<%@ LANGUAGE=JScript %>

To use a COM object in an ASP page, you must first create an instance of that object. You do this by using the OBJECT tag and specifying the value "SERVER" for the RUNAT attribute, as shown in the following example. By default, the OBJECT tag creates an instance of the object on the client. Setting the RUNAT attribute to SERVER causes the object to be created on the server. The object must run on the server in order to be used by ASP.

<OBJECT
RUNAT=SERVER
ID=MyAds
CLASSID="Clsid:1621F7C0-60AC-11CF-9427-444553540000">
</OBJECT>

You can also create an instance of a COM object on an ASP page by calling the CreateObject method of the ASP Server object. Using Server.CreateObject is slower than creating the object using an OBJECT tag, but it is slightly more readable because it specifies the programmatic identifier instead of the class identifier of the COM object. The Server object is exposed by ASP and does not need to be created. How to call Server.CreateObject is illustrated in the following examples. The first example is VBScript:

<%
  Set MyAds = Server.CreateObject("MSWC.AdRotator")
%>

The next example is JScript:

<%
  var MyAds = Server.CreateObject("MSWC.AdRotator")
%>

Note

Calling CreateObject is slower than using the OBJECT tag to create a COM object. In applications where performance is critical, you should use the OBJECT tag.

Once you have created an instance of the COM object, you can use it in scripts. Doing this is illustrated in the VBScript example following, which sets the value of the COM object's Border property.

<% MyAds.Border = 0 %>

For more information about ASP and scripting COM objects in Active Server Pages, see the Active Server Pages section of the Platform SDK.


Using COM Objects in Windows Scripting Host

Microsoft® Windows® Scripting Host is a scripting utility you can use to run scripts within the base operating system. Windows Scripting Host is supported by all Windows 32-bit operating systems. You can use Windows Scripting Host to automate common tasks and to create powerful macros and logon scripts. Windows Scripting Host comes with VBScript and JScript ActiveX scripting engines. Other software companies provide ActiveX scripting engines for languages such as PerlScript, PScript, Python, and others.

To use a COM object in a script run by Windows Scripting Host, you must first create an instance of the object. Once a COM object has been created, you can then use it in scripts.

Windows Scripting Host consists of two applications. One runs scripts from the Windows desktop (WScript.exe); the other runs scripts from the command prompt (CScript.exe).

To run a script from the desktop, simply double-click on a script file. Script files are text files. By convention, VBScript files have the extension .vbs and JScript files .js.

To run a script from the command prompt, run the Cscript.exe application with a command line such as the following

cscript c:\"sample scripts"\chart.vbs

where c:\"sample scripts"\chart.vbs is the path to the file containing the script.

You can print out a list of the parameters supported by Cscript.exe by entering the following command line:

call cscript //?

To use a COM object in a script run by Windows Scripting Host, you must first create an instance of the object. You can do this by calling the CreateObject method (in VBScript) or the ActiveXObject object (in JScript). The following example illustrates calling CreateObject using VBScript:

Dim objXL
Set objXL = CreateObject("Excel.Application")

The following example illustrates creating an ActiveXObject object using JScript:

var objXL = new ActiveXObject("Excel.Application");

Once you have created an instance of the COM object, you can write script that uses the object, for example:

objXL.Visible = true;

In addition to the CreateObject method and ActiveXObject object, both VBScript and JScript provide the method GetObject, which returns an object instance. For more information about GetObject, see the reference documentation for your scripting language.

For more information about Windows Scripting Host, see the Windows Scripting Host Programmer's Reference section of the Platform SDK.


Scripting COM Objects in Custom Applications

Microsoft provides several environments for scripting COM objects: Active Server Pages, Windows Scripting Host, and so on. Independent software vendors (ISVs) can also license Microsoft scripting engines for use in their applications. For example, an ISV creating a word processor might want to add a macro language to the application so users could automate simple tasks. By licensing a scripting engine, the ISV can use a language such as VBScript or JScript as the application's macro language.

In addition to licensing VBScript and JScript scripting engines, ISVs can write their own ActiveX scripting engines. These scripting engines can then be plugged into any host environment that supports the ActiveX scripting engine standard. For example, an ISV might write a PerlScript scripting engine and install it on an ASP server, thus enabling the ASP server to process PerlScript scripts.

For more information about how to write or license an ActiveX scripting engine, see "ActiveX Scripting" in the Internet Client SDK section of the Platform SDK.


Translating to VBScript

VBScript is a subset of the Microsoft® Visual Basic for Applications language. Because VBScript is intended to be safe for use in client-side applications such as Web pages, it does not provide file input and output or direct access to the underlying operating system. This prevents a Web-based script from deleting or altering files on your computer. For a complete list of Visual Basic features that are not supported in VBScript, see the Visual Basic for Applications documentation.

In addition to the language features provided by VBScript, the scripting host may also expose a VBScript object model that your script can access. For example, Internet Explorer exposes an object model that enables scripts to interact with and programmatically control the browser. For more information about working with such object models, see the documentation on your scripting host.

For more information about VBScript, see the Visual Basic for Applications documentation.

The following topics describe issues you should consider when translating a script to VBScript from JavaScript or JScript:

  • Translating to VBScript from JavaScript
  • Translating to VBScript from JScript


Translating to VBScript from JavaScript

In JavaScript, there are several data types, such as numbers, strings, Booleans, objects, and the null attribute. VBScript only uses one data type, Variant, which can be subtyped to represent strings, numbers, Booleans, and so on.

In JavaScript, arrays can be expanded dynamically by setting a new value for the array's length property. In VBScript, arrays cannot be enlarged; they must be redimensioned using the redim statement.

Both VBScript and JavaScript support functions. VBScript, however, also supports subroutines. Subroutines are similar to functions, except they do not return a value.

JavaScript is case-sensitive. VBScript is not.

JavaScript is supported by a wide variety of Web browsers, including both Internet Explorer and Netscape Navigator. Netscape Navigator does not support VBScript.

VBScript supports error handling through its Err object. JavaScript does not currently provide a means of trapping and handling errors.


Translating to VBScript from JScript

In VBScript, the For...Each loop enumerates the members of a collection; in JScript, the for...in loop enumerates the members of a JScript object or array. To enumerate a collection in JScript, use an Enumerator object.

JScript provides the Error object, which can be used to trap and handle errors. The Error object is analogous to the VBScript Err object.

In JScript, there are several data types such as numbers, strings, Booleans, objects, and the null attribute. VBScript only uses one data type, Variant, which can be subtyped to represent strings, numbers, Booleans, and so on.

In JScript, arrays can be expanded dynamically by setting a new value for the array's length property. In VBScript, arrays cannot be enlarged; they must be redimensioned using the redim statement.

Both VBScript and JScript support functions. VBScript, however, also supports subroutines. Subroutines are similar to functions but do not return a value.

JScript is case-sensitive. VBScript is not.

JScript is supported by a wide variety of Web browsers, including both Internet Explorer and Netscape Navigator. Netscape Navigator does not support VBScript.

JScript arrays are not arrays of the variable type VARIANT SAFEARRAY. A JScript script must use a VBArray object to access the VARIANT SAFEARRAY variable. VBScript scripts can access VARIANT SAFEARRAY variables directly.


Translating to JavaScript

JavaScript is a scripting language that was created by Netscape. It was originally called LiveScript. Despite the similarities in name and syntax, JavaScript is not related to the Java programming language. Java is a full-featured programming language developed by Sun Microsystems, Inc.

JavaScript is supported by a wide range of Web browsers, including both Internet Explorer and Netscape Navigator.

In addition to the language features provided by JavaScript, the scripting host may also expose an object model that your script can access. For example, Internet Explorer exposes an object model that enables scripts to interact with and programmatically control the browser. For more information about working with such object models, see the documentation on your scripting host.

For more information about JavaScript, see a JavaScript reference.

The following topics describe issues you should consider when translating a script to JavaScript from VBScript or JScript:

  • Translating to JavaScript from VBScript
  • Translating to JavaScript from JScript


Translating to JavaScript from VBScript

In JavaScript, there are several data types such as numbers, strings, Booleans, objects, and the null attribute. VBScript only uses one data type, Variant, which can be subtyped to represent strings, numbers, Booleans, and so on.

In JavaScript, arrays can be expanded dynamically by setting a new value for the array's length property. In VBScript, arrays cannot be enlarged; they must be redimensioned using the redim statement.

Both VBScript and JavaScript support functions. VBScript, however, also supports subroutines. Subroutines are similar to functions but do not return a value.

JavaScript is case-sensitive. VBScript is not.

JavaScript is supported by a wide variety of Web browsers, including both Internet Explorer and Netscape Navigator. VBScript is only supported by Internet Explorer.

VBScript provides error handling support through its Err object. JavaScript does not currently provide a means of trapping and handling errors.


Translating to JavaScript from JScript

JScript is largely compatible with JavaScript. However, JScript includes some objects not currently supported by JavaScript, such as ActiveXObject, Enumerator, Error, Global, and VBArray. For more information about these objects, see the JScript Language Reference section of the Platform SDK.

JScript version 5.0 supports exception handling through try...catch statements. JavaScript does not currently provide an error-handing mechanism.

When working with either JScript or JavaScript, there are some subtle differences between the object model implementations supported by various Web browsers. To write script that runs on both Internet Explorer and Netscape Navigator, limit the features used by your scripts to those specified in the World Wide Web Consortium (W3C) standard for HTML version 3.2. For more information about this standard, see http://www.w3.org/TR/REC-html32.html.


Translating to JScript

JScript is the Microsoft implementation of the ECMA 262 language specification. It is supported by both Internet Explorer and Netscape Navigator. JScript is the first scripting language to conform to the ECMA 262 standard.

JScript is supported by a wide range of Web browsers, including both Internet Explorer and Navigator.

In addition to the language features provided by JScript, the scripting host may also expose an object model that your script can access. For example, Internet Explorer exposes an object model that enables scripts to interact with and programmatically control the browser. For more information about working with such object models, see the documentation for your scripting host.

For more information about JScript, see the JScript Language Reference section of the Platform SDK. For more information about the ECMA 262 language specification, see http://www.ecma.ch/stand/ecma-262.htm.

The following topics describe issues you should consider when translating a script to JScript from JavaScript or VBScript:

  • Translating to JScript from JavaScript
  • Translating to JScript from VBScript


Translating to JScript from JavaScript

JScript is largely compatible with JavaScript. However, JScript version 5.0 includes some objects not currently supported by JavaScript, such as ActiveXObject, Enumerator, Error, Global, and VBArray. For more information about these objects, see the JScript Language Reference section of the Platform SDK.

JScript 5.0 supports exception handling through try...catch statements. JavaScript does not currently provide an error-handing mechanism.

When working with either JScript or JavaScript, there are some subtle differences between the object model implementations supported by various Web browsers. To write script that runs on both Internet Explorer and Netscape Navigator, limit the features used by your scripts to those specified in the World Wide Web Consortium (W3C) standard for HTML version 3.2. For more information about this standard, see http://www.w3.org/TR/REC-html32.html.


Translating to JScript from VBScript

In VBScript, the For...Each loop enumerates the members of a collection; in JScript, the for...in loop enumerates the members of a JScript object or array. To enumerate a collection in JScript, use an Enumerator object.

In JScript, there are several data types such as numbers, strings, Booleans, objects, and the null attribute. VBScript only uses one data type, Variant, which can be subtyped to represent strings, numbers, Booleans, and so on.

In JScript, arrays can be expanded dynamically by setting a new value for the array's length property. In VBScript, arrays cannot be enlarged; they must be redimensioned using the redim statement.

Both VBScript and JScript support functions. VBScript, however, also supports subroutines. Subroutines are similar to functions but do not return a value.

JScript is case-sensitive. VBScript is not.

JScript is supported by both Internet Explorer and Netscape Navigator. Netscape Navigator does not support VBScript.

JScript provides the Error object, which can be used to trap and handle errors. The Error object is analogous to the VBScript Err object.

JScript arrays are not arrays of the variable type VARIANT SAFEARRAY. If your script receives a VARIANT SAFEARRAY variable from a COM object or VBScript script, it must use a VBArray object to access the VARIANT SAFEARRAY variable.


Topic Not Found

No matching topic was found. Some possible reasons and remedies are as follows:

  • The file (.chm) that contains the destination topic is not in the expected location.

    Remedy: Rerun the MSDN Library Setup.

  • The file (.chm) that contains the current topic or destination topic is out-of-date and the link is no longer valid.

    Remedy: Install the latest version of the MSDN Library.

  • The MSDN Library CD is not in the CD-ROM drive.

    Remedy: Insert the MSDN Library CD into the CD-ROM drive.