Copyright: OLE Online Help, Beta Version 1.00.151


Overview

What is OLE 2?

Component Object Model

Data Transfer Model

Structured Storage Model

Compound Document Management

OLE Programming in a 32-bit Environment


What is OLE?

OLE is designed to assist you in creating sophisticated and extensible applications that operate across multiple platforms and conform to the programming model for future versions of Windows. OLE for 16-bit applications was the first step toward presenting applications as a collection of independently installable components. The 32-bit version of OLE is actually incorporated as part of the operating system, representing another move toward meeting that goal.

Note  If you are an experienced OLE programmer, begin by reading OLE Programming in a 32-bit Environment. It highlights differences between 16- and 32-bit OLE, discusses new material, and provides cross references to more detailed information.

OLE lets users manipulate information in an intuitive manner, using an environment that is more "document-centric" and less "application-centric". Users can create compound documents with data, or objects, of different formats, and focus directly on the data rather than on the applications responsible for the data. The data can be embedded within the document, or linked to it, so that only a reference to the data is stored in the document.

OLE facilitates application integration by defining a set of standard interfaces, groupings of semantically related functions through which one application accesses the services of another. The way OLE exposes functionality through interfaces makes it an open, extensible system. It is open in the sense that anyone can provide an implementation of a defined interface and anyone can develop an application that uses it. It is extensible because new or extended interfaces can be defined and integrated into existing applications without requiring changes to existing code. Applications that can implement or access the new interfaces can exploit them while continuing to operate with other applications through the older interfaces. Interfaces can be implemented within a single process in Dynamic Linked Library (DLL) objects or across processes in executable (EXE) objects. Applications can take advantage of built-in functionality provided by OLE, or they can add to it or fully replace it as best suits their needs.

OLE services are organized in a two-tier hierarchy. The lower level contains the infrastructure services; basic services that provide the means by which features can be implemented and used. These include interface negotiation, memory management, error and status reporting, interprocess communication, structured storage, and data transfer.

The upper level of the OLE service hierarchy provides application features; those services that benefit the end user. Some of the features currently available include compound-document management, in-place activation, OLE automation, and drag-and-drop operations. Because OLE is open and extensible, new features will be added in the future.

The following figure illustrates the relationship between the infrastructure services and the features. Each service and feature represents an interface (or more than one interface), made up of semantically related functions.

OLE as a layered architecture.

Much of the remainder of this section provides an overview of the OLE architectural model. It begins with a discussion of the Component Object Model, the object-oriented programming model that is the basis for the OLE infrastructure. A brief description of the infrastructure services follows, and then an introduction to compound documents and topics associated with them. These include the differences between linked and embedded objects, the various roles an application can play in compound-document creation and management, and the components that provide communication between applications. The concluding sections discuss differences between 16- and 32-bit OLE, including a list of items that are new in 32-bit OLE.


Component Object Model

The Component Object Model specifies how objects interact within a single application or between applications. A component object conforms to this model, implementing and using the interfaces that support object interaction. Implementing component objects is a matter of style. They can be implemented using C++ classes, for example, or C structures. Component objects can be independent, stand-alone entities or be composed of other objects. Using a composition technique known as aggregation, you can build a new object using one or more existing objects that support some or all of the new object's required interfaces. This technique makes the new object appear as a seamless whole rather than as a collection of independent parts. Aggregation is explained in detail in Programming Considerations.

The Component Object Model defines the following:

•	the concept of an interface by which a client of a service communicates with the provider of that service. The service provider, whether it is implemented in a DLL or an EXE, is referred to as the object.

•	an architecture by which objects can support multiple interfaces, providing a way for potential clients to query an object about support for a specific interface.

•	a reference-counting model for object management that permits simultaneous use of objects by multiple clients. It also provides a way to determine when an object is no longer in use and can be safely destroyed.

•	a mechanism by which memory passed between clients and objects can be allocated and freed.

•	a model for reporting error and status information.

•	a mechanism for allowing objects to communicate transparently across process boundaries.

•	a mechanism by which a specific object implementation (DLL or EXE) can be identified and dynamically loaded into the running system. The implementation may be local or remote and the difference is handled transparently.

The Component Object Model is the key to OLE's extensible architecture, providing the foundation on which the rest of OLE is built. Each of the mechanisms described above is supported by one or more interfaces. The figure below provides an alternate way of looking at The previous figure. The infrastructure is divided into the services supported by Component Object Model (interface negotiation, memory management, error and status reporting, and interprocess communication) and other basic services built on the model. The feature set is divided into two groups: those that are currently provided and those that might be available in the future.

Component Object Model as OLE's foundation layer.


Interfaces

Interfaces are the binary standard for component object interaction. Each interface contains a set of functions that defines a contract between the object implementing the interface and the client using it. The contract includes the name of the interface, the name of each function in the set, and each function's parameter names and types. Under this contract, the object must implement all of the functions defined for that interface and the implementations must conform to the contract.

Clients use pointers to reference interface instances, obtaining them either when the object is instantiated or by querying the object. An interface pointer points to a pointer to an array of function pointers known as a virtual table (VTBL). The functions that are pointed to by the members of the VTBL are called the methods, or member functions, of the interface.

The next figure shows the run-time representation of an interface instance. A variable supplied by the client of the component object points to the object's VTBL pointer that points to the VTBL instance. The component object contains, in addition to the VTBL pointer, private data that cannot be used by the object's client. Each function pointer in the VTBL points to the actual method implementation.

OLE interface model.

Interfaces, using C++ terminology, are abstract base classes that specify behavior in a general manner with no implementation. Interfaces are defined by OLE as "pure virtual", which means without implementation specified here. However, OLE does provide implementations for the interfaces that support the Component Object Model and other pieces of the infrastructure, as well as for some of the interfaces that support application features. Applications typically implement some of the infrastructure interfaces, such as those that support data transfer, and some of the feature-specific interfaces. Also, if an OLE implementation of a particular interface is somehow unsuitable, an application can provide its own unique implementation, either adding onto what OLE has provided or completely replacing it.

The next figure shows all interfaces available to OLE applications by functional area. The Component Object Model interfaces are in the center of the diagram because they provide the foundation on which all other interfaces are built. The Component Object Model interfaces support the services described above in the Component Object Model section. For example, IUnknown supports reference-counting and IMalloc supports memory management.

All interface names are prefixed with either "IOle" or "I." Interfaces that begin with "IOle" provide services relating to compound-document management; those that begin with "I" provide more general services. For example, IOleObject contains methods used by a client of an embedded or linked compound-document object. IOleObject is implemented and used only by applications that participate in compound-document management. IDataObject, however, contains methods typically used by all applications. These methods provide the means by which data of any type is transferred.

Functional area view of OLE.


Interface Negotiation

Objects use interfaces through a mechanism known as interface negotiation. Each interface has a globally unique interface identifier (IID) by which it is known at run time. The IID allows a potential client to determine the capabilities (that is, supported interfaces) of objects dynamically by means of a call to the object's implementation of IUnknown::QueryInterface.

IUnknown::QueryInterface allows clients of objects to query for and obtain pointers to needed interfaces. IUnknown is implemented by all component objects because it is the base interface from which all other OLE interfaces are derived. Given the interface pointer pInterface1, for example, a caller can invoke pInterface1->QueryInterface to get pointers to other interface implementations that are supported by the object. The caller passes the IID of the desired interface to IUnknown::QueryInterface, which then returns either a NULL pointer, signifying that the interface is not supported, or a valid pointer to the interface.

Note  Even though IUnknown is the OLE base interface, you can implement or use an interface that does not derive directly from it. If an interface is implemented in an object containing an implementation of another interface that does derive from IUnknown, it is possible to request the new interface using QueryInterface on the interface that does inherit from IUnknown.

There are two strategies for obtaining interface pointers in OLE applications. Some applications acquire all the interface pointers they will use over the lifetime of the application at initialization time. These applications typically store copies of the pointers with the objects that make calls to the interfaces' methods. Other applications postpone the acquisition of interface pointers until specific interfaces are needed. These applications do not store pointer copies. The interface pointer is used and then released.


Reference-Counting

Reference-counting is keeping a count of each instance of a pointer to an interface that is derived from IUnknown. Reference-counting ensures that an object is not destroyed before all references to it are released. Objects are kept alive if there are one or more references to one or more of their interfaces. Under no circumstances is an object to be deleted when its reference count is not zero. The reference-counting mechanism enables independent components to obtain and release pointers to a single object with no coordination required.

The rules for accurate reference-counting and suggestions for optimization are described in Component Object Interfaces and Functions.


Memory Management

All task memory allocated by the 32-bit OLE libraries and by object handlers is allocated using either an application-supplied allocator or a default allocator provided by OLE when the application does not provide its own. If an application-supplied allocator is provided, the application controls how memory is actually allocated. This is more efficient because the application does not have to allocate copies of data to pass as parameters to OLE functions.

Task memory is not shared between processes. Although 32-bit OLE does not support it, shared memory is sometimes used in 16-bit OLE applications. It is used much less frequently than task memory in OLE, but remains relevant for those who intend to port their 16-bit applications to a 32-bit platform. For more information, see the reference material for CoGetMalloc in Component Object Interfaces and Functions.

The reference-counting mechanism manages memory allocated for pointers to interfaces. Managing the memory for parameters to functions or methods that are passed between processes is handled by a universal convention adopted by OLE, whereby the caller and callee agree on the allocation and deallocation of memory for the function parameter. OLE parameters to functions are defined as either in parameters, out parameters, or in-out parameters. For each parameter type, the responsibility for allocating and freeing of resources is as follows:

Parameter Type	How It Is Allocated and Freed
In Parameter	In parameters are both allocated and freed by the caller. The task allocator can be used to allocate the parameter.
Out Parameter	Out parameters are allocated by the callee using only the task allocator; they are freed by the caller. Since the Out parameter is memory allocated using the standard OLE allocators, it must be freed using IMalloc::Free.
In/Out Parameter	In/Out parameters are initially allocated by the caller using only the task allocator; they are freed and reallocated by the callee if necessary. As with out parameters, the caller is responsible for freeing the final returned value.
Note  These memory management conventions apply only to public interfaces and functions, not necessarily to memory allocation internal to applications.

Methods that need to hold onto data longer than a call's duration need to either make a copy of that data or increment the reference counter for the interface pointer.

Regarding error handling, if a function containing an out parameter returns an error result, the out parameter must be set to a value that can be cleaned up without any effort by the caller. Pointer out parameters must be explicitly set to NULL. In-out parameters should be left untouched when the function returns an error, thereby remaining in the state set by the caller.


Error and Status Reporting

In 16-bit OLE, most interface methods and functions return result handles, HRESULTs, that comprise a severity code, context information, a facility code, and a status code. The returned HRESULT can be interpreted at two levels. At the simplest level, zero indicates success and nonzero indicates failure or nonsuccess. At a more complex level, you can examine the nonzero value to determine a more detailed reason for the failure.

HRESULTs are not implemented in the current release of 32-bit OLE 2, but they do appear throughout this document. An HRESULT is now equal to an SCODE, on which all HRESULTs are based, and the two terms are now synonymous. (This change occurred too late to completely change this document.)

The tables listing interface method return values in the following reference sections specify status code constants that incorporate information about the facility, severity, and status. 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.

Success codes are defined when the interface's designer ships that interface (that is, makes it public). Although success codes cannot be altered, error codes can be changed by the designer of the interface. By design, the error codes of Microsoft interfaces do not overlap, and it is vital that they not be changed. So far, most interfaces have been designed by Microsoft, but in the future, other developers will design and ship interfaces.

More details about the SCODE and HRESULT structure and strategies and suggestions for error handling can be found in Component Object Interfaces and Functions.


Interprocess Communication

Communication across process boundaries in 32-bit OLE is handled by Remote Procedure Call (RPC), a widely used standard defined by the Open Software Foundation (OSF) for distributed computing. Microsoft's RPC implementation adheres to this standard and is compatible with the OSF DCE version. Although now OLE's interprocess communication is limited to processes on a single machine, eventually Microsoft RPC will provide transparent communication across networks. Clients will be able to communicate to objects on different servers without altering their code. Clients will be able to disregard the physical location of the server.

RPC applications describe the interfaces they expose by using the Microsoft RPC Interface Definition Language (IDL). These descriptions are then compiled, producing source code to handle the transmission of interface method calls across process boundaries. For more information about interprocess communication and the component objects involved, see the section that addresses the architecture of compound-document objects.

The sending and receiving of interface parameters across process boundaries, referred to as marshalling and unmarshalling, is the job of proxy and stub component objects. Every interface that is transmitted across a process boundary (remoted) will have its own proxy object that can package method parameters for that interface. On the receiving side, there is an interface-specific stub object that unpackages the parameters and makes the required method call. A proxy manager exists to establish connections to the RPC channel and to load and unload proxy objects as needed. In most cases, callers of interface methods are not affected when calls cross a process boundary.

Several component object interfaces manage the marshalling and unmarshalling process. For more information about this process and how it works, see Component Object Interfaces and Functions.


Dynamic Loading of Object Implementations

One of the key features of the Component Object Model is that a client of an object can locate the code responsible for the object and dynamically load it into a running system. Once the code is loaded, OLE can use class identifiers (CLSIDs) to forward client requests transparently to the object's interface methods. A CLSID is assigned to each type of object; which allows OLE to match an object type with its associated code. Object implementors store their CLSID information in the system registry. For more information, see OLE Registry Information.


Data Transfer Model

OLE's data transfer model lets users transfer data uniformly with a drag-and-drop operation, a copy/paste operation, or programmatically. The data transfer model is supported by the IDataObject interface. By implementing IDataObject, an application can provide data to be pasted or dropped in a variety of formats across a range of mediums. By obtaining a pointer to an IDataObject implementation, an application can call IDataObject methods to query for appropriate formats and mediums, set up data advises, and send or receive the actual data.

OLE associates two types of data with a compound-document object: presentation data and native data. The presentation data is needed to render the object on an output device, while the native data is needed for editing. There are data formats used to describe both types of data. Applications that copy to the Clipboard or that can act as drag sources publish formats that best describe the source data. These can be standard formats (such as CF_METAFILEPICT), private formats, or OLE formats. OLE formats support the creation of compound-document objects so one or more of these formats must be available to create a linked or embedded object.

Non-OLE applications that copy data to the Clipboard do not offer OLE data formats; data transfers remain unchanged. Likewise, OLE 1 applications offer the OLE 1 set of Clipboard formats that can be understood by OLE 2 applications. OLE 2 sources offer OLE 2 Clipboard formats and the OLE system then synthesizes additional corresponding OLE 1 formats. Some source applications might not follow the rules of offering preferred formats first. A target application pasting from such a source might end up with a non-typical result.


Structured Storage Model

OLE's structured storage model specifies how data is saved and retrieved from storage. In this model, multiple streams are multiplexed onto one underlying file (or byte array) so this layer looks like a file system directory tree; a file system within a file. There are two types of objects in a file system: directories and files. The structured storage model has two equivalent types of objects: storages and streams. A storage object is analogous to a directory in that each storage may contain nested storages and streams. Stream objects are analogous to files. The structured storage model lets objects control their own storage, loading directly from and saving directly to disk.

Storage objects provide binary compatibility across all OLE-supported development platforms. OLE writes information to an object's storage so that the information can be read on other platforms. All supporting data that OLE stores about an object is converted as needed on each platform.

The next figure shows the hierarchical nature of the structured storage model. The document's outermost storage object is referred to as the root storage object, and it is created by the container. Root storage objects, like all storage objects, can be marshalled from one process to another. The root storage object in the following figure contains a stream for OLE data and one or more streams for native data. The two embedded objects have storage objects with their own streams for OLE and native data.

Hierarchy of the OLE structured storage model.


Compound Document Management

A compound document is a document within a container application that seamlessly integrates data of different formats, such as sound clips, spreadsheets, text, and bitmaps. Each piece of integrated data, referred to as a compound-document object, is created and maintained by its object application. End users feel as though they are using a single application, with all the functionality of each of the object applications. Therefore, rather than being concerned with managing multiple object applications, they focus solely on the compound document and the task being performed.

A compound-document object is a specific type of component object. In addition to using the component object interfaces, container and object applications that deal with compound-document objects implement and use a set of interfaces for creating and managing compound documents. There are various types of container and object applications and the type relates to the features offered by the application. The type determines the specific set of interfaces to be implemented. For example, container applications can either be pure containers, providing sites for compound-document objects, or linking containers that allow other containers to link to their embedded objects. Object applications can support linking to file-based objects only or linking to portions of their file-based objects, referred to as pseudo objects.

The following table describes the types of OLE applications:

Application Types	Description
Pure Container	Contains linked and embedded objects within its documents. Does not allow other applications to link to its data.
Link Container	Contains linked and embedded objects within its documents. Allows other applications to link to its embedded objects.
Simple Object Application	Creates object(s) that can be embedded or linked. Allows linking only to the whole object.
Pseudo Object Application	Creates object(s) that can be embedded or linked. Allows linking to whole object and to selections of data.
Link Object Application	Cannot create object(s) to be embedded in a container (is not insertable). Acts as a link source only.
Container/Object Application	Creates object(s) that can be embedded or linked and can embed or link objects within its documents.


Linking vs. 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 actual source data comprising the object is stored. This, in turn, affects the object's portability, its method(s) of activation, and the size of the compound document.

When an object is linked, the source data, or link source, continues to reside wherever it was initially created, either at another point within the document or within a different document altogether. Only a reference, or link, to the object and appropriate presentation data is kept with the compound document. Linked objects cannot "travel" with documents to another machine; they must remain within the local file system or be copied explicitly.

Linking is efficient and keeps the size of the compound document small. Users may choose to link when the data source is owned or maintained by someone else and must be shared among many users. A single instance of the object's data can serve many documents. Changes made to the source object are automatically reflected in any compound documents that have a link. From the user's point of view, a linked object appears to be wholly contained within the document. In addition to creating simple links, users can nest links and combine linked and embedded objects to create complex documents.

In the case of an embedded object, a copy of the original object is physically stored in the compound document, as is all the information needed to manage the object. As a result, the object becomes a part of the document. A compound document containing embedded objects will be larger than one containing the same objects as linked objects. However, embedding offers several advantages that might outweigh the disadvantages of the extra storage overhead. For example, users can transfer compound documents with embedded objects to another computer and edit them there. The new user of the document need not touch the original data because a copy of the object's source (native) data travels with the compound document.

Embedded objects can be edited, or activated, in place. The user can maintain the object without leaving the compound document. Because each user has a copy of the object's source data, changes one user makes to an embedded object will not effect other compound documents containing an embedding of the same original object. However, if there are links to this object, changes to it will be reflected in each document containing a link.


Architecture

Container and object applications interact through a piece of object-specific code that is loaded into the container's process space called the object handler, or handler. There is a one-to-one relationship between an object class instance and its handler, although the default handler provided by OLE has an n to 1 relationship with object classes.

The handler provides a partial implementation of an object class so that for some container requests it can take the place of the object application. Communication is by function call using the handler rather than by remote procedure call. When the object application is implemented as a DLL within the container's process and all calls are local, there is no need for a handler.

The handler is a composite, or aggregate, object that is made up of several components. The type of components and their structures differ slightly, depending on whether the object is an embedded or a linked compound-document object. Each component implements one or more interfaces that are used by the container, object application, or one of the components responsible for interprocess communication. OLE provides a default handler that most object applications use. However, if an object application requires special or additional behavior that is not available through the default handler, the application may implement its own custom handler. The following section describes the various types of handlers in more detail.

Compound document interaction involves data flowing both from the container to the object and from the object to the container. For linked compound-document objects, data always flows through the handler. For embedded compound-document objects, however, there are two possible routes when the data originates with the object. Data can either flow directly to the container or indirectly through the object handler. As with linked objects, data flowing from the container is always transferred via the handler.

Two interface-specific components are used to transfer, or marshal, method call parameters between container and object. The proxy, instantiated in the caller's process space, packages the parameters in preparation for sending. On the receiving side, the stub unpackages the parameters and forwards the method call.

The diagram below shows the components involved for a typical embedded object. The object and container applications are in different processes and the handler in use is the default handler provided by OLE. The proxies and stubs are represented by the RPC remoting components. The handler is made up of three pieces: a remoting piece, a cache, and a controlling piece that provides coordination. The embedded object's structured storage can be accessed by the container, the object, and the handler.

 Data flow for an embedded object.

The diagram below illustrates the flow of a linked compound-document object. Again, the container and the object application are in separate processes. The main difference between linked and embedded objects is in the structure of the handler. The linked object's handler is made up of two main components: a component that contains the control and remoting pieces and an additional component called the link. The link has its own controlling piece that supports link source naming and binding, the process of locating and running the link source. The link also contains the cache and provides access to the structured storage.

 Data flow for a linked object.

Both of the preceding illustrations describe the compound-document architecture when a container application and object application reside in separate processes. You can write an object application either as a separate task in a separate process space (.EXE), providing services to containers remotely, or as a dynamic linked library in the container process space (.DLL), providing services to containers through local function calls. Object application executables that run in a separate process space are referred to as local servers. Object applications that run in the container's process space are referred to as in-process servers.

When an object application is implemented as an in-process server, it replaces most of the functionality provided by the object handler. Implementing the object application in-process does not preclude it from using some or all of the services provided by the default object handler. The remoting object may or may not exist. However, if there is a remoting object, OLE is not affected by its existence.

The diagram below shows the flow for in-process servers and container applications.

 Data flow for an in-process server.


Object Handlers

The object handler provides a partial implementation of a particular object class. Object handlers are dynamically loaded into a container's address space so that communication is by function call rather than remote messaging.

The object handler is an aggregate object, a composite made up of a remoting piece for marshalling and unmarshalling interface parameters across the process boundary, the cache, an optional link object, and a controlling object that coordinates between the pieces. There is a one-to-one relationship between an object class and its handler. An object handler is unique to an object class. Once it is instantiated, a handler cannot be interchanged or shared between object classes. When used for a compound document, the object handler implements the container-side data structures when objects of a particular class are accessed remotely.

Handler Structures

The object handler can be structured in one of the four ways shown in the following diagram.

 Structures of object 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 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.

The diagram below shows how objects communicate across process boundaries using the default and remoting handlers. Object A is the container for Object B. The default handler is instantiated in Object A's process space. When Object A makes calls to Object B, the default handler either processes the request (if possible) or forwards it on to Object B. The remoting piece that receives the call is the stub discussed earlier. Because Object A is a container without an assigned CLSID, there is no need for a default or custom handler. The remoting handler is sufficient.

 Component objects using interfaces.

Handler Components

The default handler and all custom handlers include a control and the cache. The control facilitates communication within the handler itself, while the cache is responsible for transferring data from structured storage to memory and back to storage. The cache stores data in formats needed by the handler to satisfy container drawing requests.

When a linked object is initially created or loaded from storage, the link component is instantiated with the handler. The link exposes interfaces that manage the naming and tracking of link sources. Link sources are named with a mechanism known as a moniker. Monikers are conceptual handles that can be stored persistently with the container. Similar to pointers in programming languages, monikers can be bound, or dereferenced, to locate and load objects.

The important properties of a moniker are:

•	A moniker's representation is opaque to its users. Linked object containers are not informed of the structure or type of the moniker(s) used to represent their link sources.

•	Applications can compose (concatenate) and decompose a moniker without being informed of its syntax. The link is responsible for this process.

•	OLE defines a set of moniker classes that provide standard ways for naming objects. The main classes are the file moniker, item moniker, and composite moniker. The file moniker is used to name files, the item moniker is used to name parts of files, or objects within a file, and the composite moniker combines one or more other monikers. OLE provides an implementation of each of these moniker types.


Link Objects

The link object is a component object that is instantiated whenever a linked compound-document object is created or loaded. The link object manages the naming of linked objects and the tracking of link sources.

When the link is created, the instantiated link object acts like an in-process server. For example, when the user of the container activates the linked object with a double-click, the link object locates and launches the appropriate object application so that editing (or other supported verbs) can be initiated.


Cache

The cache is responsible for transferring an embedded object's data from storage to memory and back to storage, where it is saved persistently. 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.


Container Applications

Container applications are the compound-document object's consumer, providing storage, a place for object display, and access to this display site, referred to as the client site. Containers can be pure containers that simply hold embedded and linked objects, or more sophisticated containers that allow other containers to link to their embedded objects. Combination container/object applications can be used to embed and link to objects created by other applications as well as to create and service their own class(es) of objects.

The following table lists the interfaces that are implemented by container applications of various types. The interfaces that support drag-and-drop operations and in-place activation are included. If an application does not support these features, the interfaces need not be implemented. Only two interfaces are required: IOleClientSite and IAdviseSink. IOleClientSite is the primary interface by which a container provides services to the object. IAdviseSink supports the flow of notifications between an object and its container. Other interfaces are implemented to provide additional functionality. The table includes only derived interfaces but implies that all inherited interfaces are used or implemented along with those that are derived.

Pure Container	Link Container	Container/Object
IOleClientSite	IOleClientSite	IOleClientSite
IAdviseSink	IAdviseSink	IAdviseSink
IAdviseSink2	IAdviseSink2	IAdviseSink2
IMessageFilter	IMessageFilter	IMessageFilter
IOleInPlaceSite	IOleInPlaceSite	IOleInPlaceSite
IOleInPlaceFrame	IOleInPlaceFrame	IOleInPlaceFrame
IDataObject	IDataObject	IDataObject
IDropSource	IOleItemContainer	IOleItemContainer
IDropTarget	IPersistFile	IPersistFile
	IClassFactory	IClassFactory
	IDropSource	IDropSource
	IDropTarget	IDropTarget
		IPersistStorage
		IOleObject
Container applications are typically structured so that particular interfaces are grouped together. These groupings are shown in the following figure. The IClassFactory and IMessageFilter interfaces are each implemented separately and instantiated once per application instance. To support linking, each document object exposes IPersistFile and IOleItemContainer. The document also exposes IDropTarget if it allows data to be dropped onto its window. Another document grouping contains IDataObject and IDropSource for data transfer support. The container site object contains the required interfaces, IOleClientSite and IAdviseSink, and IOleInPlaceSite, for in-place activation.

 Interface groupings for a container.


Object Applications

Object applications create and service embedded or linked objects of a specific class. Some object applications also provide container capabilities. Applications also vary in the number of objects they can create and service. Microsoft Paintbrush, for example, is a single-object application; Microsoft Excel is an example of a multiple-object application, providing spreadsheet and chart objects.

Object applications must provide a persistent representation of each object and one or more presentations for the container's use. Object applications also supply user interface and editing capabilities.

Object applications can allow linking to file-based documents or to pieces of data within those documents. Applications that allow linking to pieces of data are said to support pseudo objects. These pseudo objects implement the same interfaces as do the registered objects that are supported.

The following table lists the interfaces that object applications of various types typically implement.

Simple Object Application	Pseudo Object Application	Link Object Application
IOleObject	IOleObject	IOleObject
IClassFactory	IClassFactory	IClassFactory
IDataObject	IDataObject	IDataObject
IMessageFilter	IMessageFilter	IMessageFilter
IPersistFile	IPersistFile	IPersistFile
IOleInPlaceObject	IOleInPlaceObject	IOleInPlaceObject
IOleInPlaceActiveObject	IOleInPlaceActiveObject	IOleInPlaceActiveObject
IDropSource	IDropSource	IDropSource
IDropTarget	IDropTarget	IDropTarget
IPersistStorage	IPersistStorage	IOleItemContainer
	IOleItemContainer
Some object applications are referred to as mini servers. A mini server is one that cannot run as a stand-alone application — it is always run from another application. Mini servers, with their simplified user interface, can only support embedded objects and they cannot open or save files independently. Microsoft Draw is an example of a mini server. A mini server is useful when limited functionality is required or when there is a predefined partnership with specific containers.

Object applications can support either one document per application instance or multiple documents per application instance. When a single document interface (SDI) application is running and a user tries to create an object, a new instance must be launched to service the request. When a multiple document interface (MDI) application is running and a user tries to create an object, the application creates a new document within the instance that is already running. MDI applications are capable of servicing both a container and a user simultaneously.

Object applications, like container applications, structure their implemented interfaces so the ones that make sense grouped are implemented together. The diagram below shows the typical structure of an object application. As with container applications, there are two types of document groupings: one for the user document and one for data transfer, which exposes IDataObject and IDropSource. However, in object applications, the user document also implements IDataObject and a variety of other interfaces. The pseudo object is implemented only if the object application supports linking to selections of data.

 Interface groupings for an object application.


Object States
Object States: Compound-document objects

The states of a compound-document object apply to the object once it has been created by an object application and embedded or linked in a container. A compound-document object's state describes the relationship between the object in its container and the application responsible for its creation. Compound-document objects have three states: passive, loaded, and running. The loaded and running states are referred to as active. These object states do not correlate with the end user object states described in User Interface Guidelines.The compound-document object states are described as follows:

Object State	Description
Passive	The compound-document object is not selected or active. It is in its stored state (on disk or in a database).
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.
Running	The object application is running. It is possible to edit the object, access the object's interfaces, and receive notification of changes. The objects that control remoting have been created. The object handler delegates calls to the real object (in the object application).
The diagram below shows the three states and the method calls that cause an object to change from one state to another.

 States of an embedded object.

The next figure shows the relationship between a container for an embedded object, its object handler, and its executable object application as the object makes the transition to each of the three states. In-process servers have no object handler in the container process; it is up to the server to keep track of its state (loaded vs. running).

 States of an embedded 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 application must be located and run so its services can be utilized. Embedded objects are placed in the running state either explicitly through a request by the container, such as when the need to draw a format not presently cached occurs, or implicitly by OLE as a side effect of invoking an operation, as when the 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. Binding involves locating the object data, which must be done before the object application can be located and run.

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:

•	OLE allows a link to refer to objects (and possibly parts of objects) that are embedded in other containers. This implies a potential for nested embeddings. The link must include sufficient information to allow it to locate the object inside the other container(s). This information is maintained through the use of monikers, the naming mechanism described earlier. Typically, an object's moniker is part of a filename (path) concatenated with a series of item name parts. During binding, names are reduced before the steps in binding take place, which allows for important functionality, such as link-tracking and user-aliasing, to be implemented transparently.

•	Because linking permits multiple references to a given object, it is possible that the object may already be running, in which case OLE binds to the running version rather than running another copy. At any point in the containment hierarchy, OLE might be able to bind to an already running object.

•	Running an object requires accessing the storage area for the object. When an embedded object is run, OLE is given a pointer to the storage during the load process, which it passes on to the object application. However, for linked objects, there is no standard interface for accessing storage. The object application may use the file system interface or another mechanism.

OLE manages the binding process for applications. For more information, see Linking Interfaces and Functions.


Entering the Passive State

Object closure forces an embedded or linked object into the passive state. It is normally initiated from the object application's user interface, such as when the user selects the File Close command. In this case, the object 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 object 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. Containers may be designed 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.


OLE Programming in a 32-Bit Environment

Note  This topic is intended for programmers who are already familiar with the terms and concepts used in OLE. If you are new to OLE, it may be easier to go on to other material and periodically return to obtain more specific information.

Developing 32-bit OLE applications is not significantly different from developing 16-bit OLE applications. Most aspects of 32-bit OLE are not merely similar to their 16-bit counterparts, they are exactly the same. Generally, 32-bit OLE is 16-bit functionality ported to the 32-bit environment. For example, the specification for 16- and 32-bit OLE is the same document. User interface guidelines are virtually the same and sample code is practically unchanged. In effect, 32-bit OLE is an extension of its 16-bit counterpart, although it does not expand functionality to a significant degree at this time.

The following information is set forth in two categories. The first summarizes the major differences between 16- and 32-bit OLE. The second category includes Win32 issues of which developers should be aware before spending extensive resources in the development process.


Differences Between 16- and 32-Bit OLE

Differences that do exist between 16- and 32-bit OLE are often not of direct significance to application development. Many of the issues are simply artifacts of developing and running in the 32-bit environment. Although you should be aware of them, most will not require changes to your code. In addition, because most of the issues associated with moving into the 32-bit environment are not specific to OLE, the Win32 SDK is frequently the best source of information on 32-bit development issues. OLE was developed using the Win32 SDK.


API Functions and Interface Methods

Some functions and interface methods implemented for 16-bit OLE are not used in 32-bit OLE. They are included in this documentation but are clearly identified as limited to 16-bit OLE functionality.


Parameter Change in OleInitialize and CoInitialize

In 16-bit OLE, the only parameter used in each of these functions points to the memory allocator to be used for task memory by the libraries and by object handlers. In both cases, it is named pMalloc and its data type is LPMALLOC.

In 32-bit OLE, the parameter's name is pvReserved and its type is LPVOID in both functions. This parameter must be NULL. 32-bit OLE does not support applications replacing OLE's allocator and, if the parameter is not NULL, both of these functions will return E_INVALIDARG.


CoGetMalloc and Shared Memory

As pointed out in the Memory Management section, 32-bit OLE does not use shared memory. This does not mean that consideration of the issue of memory management need not include it. If you are porting 16-bit applications to a 32-bit environment, the absence of the shared memory allocator is of obvious importance.

As a result of this change, calling CoGetMalloc(MEMCTX_SHARED, ...) will return E_INVALIDARG. Other changes have also been made. This API is documented in Component Object Interfaces and Functions.


Determining the CLSID of a Handler

IPersist is no longer used to determine a handler's CLSID; IStdMarshalInfo::GetClassForHandler is the only mechanism provided for this task. For more information about IStdMarshalInfo::GetClassForHandler, see Component Object Interfaces and Functions.


Version Checking

Windows 3.1 does not ship with 16-bit OLE 2 support so 16-bit applications must install OLE 2 as part of their setup process. If OLE components already exist on the target machine, any OLE 2 application being installed must do version checking. If the OLE components already on the machine have a version number greater than or equal to those supplied with the application, the application must not install 16-bit OLE components; instead it would have to install the complete set of OLE components. This is documented in Component Object Interfaces and Functions.In 32-bit OLE, version checking is eliminated because OLE is part of the operating system (OS). Support for both 16- and 32-bit OLE is provided when the OS is installed and applications must never install any OLE components. Changes to OLE components will only occur with revisions to the operating system. Any 16-bit applications that are still shipping will have lower version numbers than the system-supplied components so, as discussed in the preceding paragraph. Therefore, their OLE components will not be installed. This also means that CoBuildVersion and OleBuildVersion functions are obsolete. For more information, see OLE Library Functions.


Registering Object Applications

In Windows 3.1, object applications are registered using the reg.dat file. In 32-bit OLE, they are entered directly into the system registry. This is a relatively simple process that is explained further in OLE Registry Information.


HRESULTs and SCODES

As discussed earlier in Error and Status Reporting, most return values in 16-bit OLE are HRESULTS. This is a stylistic convention that is not supported in 32-bit OLE. The actual return type is now SCODE, and HRESULT is a synonym for it.

The values of some SCODEs have been changed in 32-bit OLE 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. Following is a list of their 32-bit values:

SCODE	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
For more information, see Handling Error Information in Component Object Interfaces and Functions.


Interprocess Communication

As noted earlier, remote procedure calling (RPC) has been implemented in 32-bit OLE. In 16-bit OLE, the lightweight remote procedure calling (LRPC) mechanism is used because communication is limited to a single machine. Although the single machine limitation continues to exist, the mechanisms to provide network implementation of OLE are now in place. RPC is of vital importance to this effort because it is an actual communications protocol. It also permits OLE developers to design their own custom interfaces easily, as explained in OLE: Custom Interfaces. Although LRPC serves the purpose of communicating between processes on single machines, enabling communication between machines and facilitating custom interface design are not part of that purpose.

Generally, the change to RPC does not affect application development, except that you can now define your own interfaces with relative ease. Implementing the sending and receiving of interface parameters across process boundaries, referred to as marshalling and unmarshalling, is virtually unchanged; proxy and stub component objects are responsible for the movement of data across process boundaries.

If you are familiar with interprocess communication in 16-bit OLE, you will have little extra to learn. Because programming with this release of OLE requires no specific knowledge of RPC, information about the operation of RPC, its error codes, and related topics are not documented here. For more information about RPC, see the RPC Programmer's Guide and Reference in the Win32 SDK. This is also the best source of information about the interface definition language (IDL) used to write header files and custom interfaces.


Application Message Queue Sizing

In 32-bit OLE, it is not necessary to enlarge the application's message queue to a minimum size value of 96 messages. It is dynamically sized and requires no special treatment.


32-Bit OLE Filenames

The 32-bit version of OLE supports filenames of 260 characters or less in length based on MAX_PATHLEN, as defined in the OLE 2 header files.


32-Bit OLE and Window Focus Management

Win32 handles window focus management differently than 16-bit versions of Windows. If you want a window to act as a foreground window, you must call the Win32 function, SetForegroundWindow, in addition to the typical SetFocus or SetActiveWindow calls. For more detailed information, see the documentation for these functions in the Win32 SDK. For additional OLE-specific information, see IOleObject::DoVerb and IOleClientSite::OnShowWindow.


Running in a Preemptive Environment
Running in a Pre-emptive Environment

Windows NT is a pre-emptive multitasking operating system. Windows 3.1, which relies on message-driven multitasking, is not. This difference can cause compatibility problems when a 16-bit application is run on a 32-bit platform, for example. Performance in other environments cannot be determined solely by the application's performance in Windows NT, so proper testing of OLE applications in other environments is required to be certain they will run.


32-Bit Addressing

Because the 32-bit environment means a 32-bit address space, all pointers are 32 bits, including pointers in the local heap. There will be no more 16-bit pointers, no more segments, and pointers will no longer wrap at a 64K boundary. However, you will see formal parameter names and data types throughout this documentation that use the prefix LP, which usually identifies a long pointer, such as LPSTORAGE. This notation is used to maintain consistency with 16-bit OLE even though there are no long pointers in 32-bit OLE. This usage is also present in the OLE header files that are shipped as part of the Win32 SDK. Mechanisms such as

#typedef IStorage *LPSTORAGE

ensure that programs will compile no matter which data type is used.


Monikers (16- and 32-Bit)

Composing two antimonikers together in 16-bit OLE 2 created a third antimoniker with an internal count of two. In the 32-bit OLE, this is no longer true. For more information, see Linking Interfaces and Functions.


OLE 1 Clipboard Formats

In 16-bit OLE, applications could use either OLE 1 or OLE 2 Clipboard formats. However, 32-bit OLE does not recognize OLE 1 Clipboard formats. As a result, 16-bit applications being ported to run on 32-bit platforms must use the OLE 2 API functions rather than getting and setting Clipboard formats directly. If an application does not use the OLE 2 API, putting data on the Clipboard will not be possible on some platforms. Accordingly, 16-bit code should be examined and changed, if necessary. For more information about Clipboard formats, see Data Transfer/Caching Interfaces and Functions.


Interoperability (16- and 32-Bit)

Interoperability between 16-bit and 32-bit OLE 2 applications is supported by 32-bit OLE. For more information, see 16:32 Bit Interoperability.


OLE 1/OLE 2 Compatibility

Compatibility is now available. For more information, see Compatibility with OLE 1.


Win32-Specific Issues

The following items represent issues and areas of functionality that relate primarily to Win32. Some of them require no more than an awareness on the developer's part.


Unicode and ANSI

The 32-bit version of OLE is Unicode specific. The impact of this change on OLE applications is discussed in Why Unicode?. Complete documentation of Unicode is included in the Win32 SDK.


New 32-Bit OLE APIs

The 32-bit version of OLE includes new APIs:

CoGetMarshalSizeMax

CoTaskMemAlloc

CoTaskMemFree

CoTaskMemRealloc

For complete documentation of these APIs, see Component Object Interfaces and Functions.


The Windows NT Desynchronized Messaging Model

The 16-bit environment of Windows 3.1 prevents the user from switching to an alternative application to continue working when the current one stops responding. Windows NT remedied this situation by providing the desynchronized message model that features independent message queues. Although this is generally the case for any 32-bit application running on Windows NT, OLE does provide an exception.

During the processing of in-place activation sessions, OLE synchronizes the message queues for the two processes involved in that in-place activation session. When the in-place activation session ends, desynchronized messaging is resumed.


Custom Interfaces

With the introduction of RPC, designing and implementing your own interfaces is relatively simple. This feature of 32-bit OLE provides exceptional flexibility for developers of OLE applications. For more information, see 16:32-Bit Interoperability and OLE: Custom Interfaces.


Security

Security is one of the most important features of Windows NT and is generally available to Windows-based applications automatically. However, 32-bit OLE neither uses it nor interferes with the Windows NT security subsystem. Security can, however, become an issue when a certain set of conditions exist.

Part of the security provisions for each securable object is an access control list (ACL) that specifies groups and users as well as the type of access to the object each is granted or denied. The entities within an application that can be protected are objects, although not necessarily OLE objects. Access control lists (ACLs) might exist on files containing linked data and this can cause OLE-related problems.

If the user who is currently logged-on:

•	does not have the proper permission to modify those files

•	has the containing document open, and

•	double-clicks on a linked object,

the operating system is likely to return File Not Found or Access denied. Applications need to be able to handle this case.

A caller to the running object table that runs in a specific account can only see objects running in that account. If a container application running in one account asks for a local server already running in another account, a copy of the server is started in the first account to serve the request.

Another security-related item requiring attention is that applications running on NT have a specific way of compartmentalizing their protectable entities. They may expose ways of putting ACLs or permissions on those protectable entities. It is important to be aware that protectable entities are not necessarily OLE objects. Although they might be the same, they may not be. For more information about Windows NT security and related topics, see the Win32 Programmer's Reference.


Structured Exception Handling

An exception is an event that occurs during the execution of a program. It can be generated by either hardware or software and when an exception occurs, it requires special handling. Although the Win32 application programming interface (API) supports a mechanism for this purpose called structured exception handling, OLE does not support it. This is because it is illegal to throw exceptions across process boundaries. For more information about structured exception handling, see the Win32 SDK.


Structures (16- and 32-Bit)

In terms of cross-platform file compatibility, when you save structures of data as object data in compound files, those structures should be of a fixed size. That is, you want to use fixed-sized structures on both platforms. Give particular attention to structures that have fields that widen when you move to 32 bits. It is important to examine 16-bit structures carefully to determine, for example, if an int is 16 or 32 bits.

It is important to determine a fixed format and follow it. Although structure packing may appear to solve the problem discussed above, it does not perform the same on all machines. The potential penalty is the loss of your application's extensibility.


Message Parameter Repacking

On 32-bit platforms, the parameters of some Windows messages have been repackaged. This was done primarily because some of the data elements within some of the message parameters were stretched to 32 bits, most notably handles. This affects only about two dozen APIs. For OLE development, this is mainly an issue in areas of coding around in-place activation scenarios; in particular, the WM_COMMAND message. On Windows 3.1 platforms, parameters were set up so that the hwnd and cmd parameters were passed in the lparam field and the id was passed in the wparam field. Because handles have been stretched to 32 bits for Windows NT, it took the entire lparam field to store the handle to the window and the id and cmd parameters had to be shared in the wparam field. This is one example of how a message and its parameters have been repackaged and the care you need to take in dealing with these. The others are documented in the Win32 SDK.


Shared and Separate Memory Space

Windows NT 3.5 supports running 16-bit Windows applications in either a shared memory space or a separate memory space for each application. When OLE starts 16-bit OLE applications, they are always started in the shared memory space. This provides for OLE behavior in Windows NT 3.5 that is the same as on Windows 3.1.

In addition to the information discussed in the preceding paragraph, consider the following:

•	Objects and containers can be running in separate memory space.

•	Applications run by the logged-on user, such as Microsoft Excel, can be run in a separate space. An OLE application (container or object application) started in a separate memory space can work with other OLE applications running in shared memory space.

•	All object applications started by OLE on behalf of the logged-on user run in the shared memory space.

•	Object applications started by OLE, on behalf of any user other than the logged-on user are always run in a separate memory space for each 16-bit object application process.

For more information about shared and separate memory space, see the Win32 SDK.


Multithreaded Applications

Although Windows NT is a multithreaded environment, support for multithreading is not implemented in this release of OLE. All OLE calls must be made from the thread in your application that calls OleInitialize (or CoInitialize). For more information about OleInitialize and CoInitialize, see Component Object Interfaces and Functions.


Programming Considerations

While detailed application-specific information is beyond the scope of this Help, this section contains some helpful guidelines for developing OLE applications and provides information on variety of subjects that pertain to the implementation of OLE in a general sense. Most of the section discusses implementation issues more specific to applications that act as containers dealing with compound documents.


Designing Component Objects

Component objects contain data that is 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 can be accessed through pointers.

Because interfaces are a binary standard, interface implementation is language independent. However, 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, as is summarized below:

•	VTBLs must be initialized explicitly either at compile or run time and should not be changed. 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 choose to 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. With C++, all members are implicitly dereferenced off the this pointer. With 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:

IUnknown_Doc_QueryInterface (LPUNKNOWN pUnk, REFIID riid,

LPVOID * ppvObj);

The following sections demonstrate how to declare a component object in a few typical ways: using C nested data structures, C++ nested classes, and C++ multiple inheritance. The demonstration object, called CObj, derives from IUnknown and supports two interfaces that also derive from IUnknown, InterfaceA and InterfaceB. CObj'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). The order of the members in the interface structures is identical, to facilitate code sharing.

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;


Component Objects: C++ Nested Classes

The next example shows how the same object is declared and initialized using the C++ nested class approach. As in the C example, 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 lie 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;


Multiple Inheritance

The next example illustrates the use of 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 Reference Counting. 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 IUnkown 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);

};


Aggregation

An aggregate is an object like any other object in that it implements one or more interfaces. Internal to the aggregate, the implementation of certain interfaces is actually provided by one or more contained objects. Users of the object are unaware of this internal structure. They cannot tell and do not care that the object is an aggregate: aggregation is purely an implementation technique.

The figure below shows an aggregate object consisting of a control object that implements Interface A and Interface B, and a noncontrol object that implements Interface C. All these interface implementations are exposed publicly, as indicated by the line and circle extending to the outside of the aggregate.

Aggregate Containing Two Internal Objects.

In the aggregation model, the control object determines how an aggregate behaves and operates, making decisions about which interfaces are exposed outside of the object and which interfaces remain private. The control object has a special instance of the IUnknown interface known as the controlling unknown. The controlling unknown must always be implemented as part of the new code written when the aggregate is put together from other objects. However, when the controlling unknown is passed in, it does not increment the reference count; instead it is a non-incrementing pointer.

The other objects can be implemented at any time. These noncontrol objects can be instantiated separately or as part of the aggregate. However, if these objects are to be capable of aggregation, they must be written to cooperate with the control object by forwarding their QueryInterface, AddRef, and Release calls to the controlling unknown. A reference count for the aggregate as a whole is maintained by the control object so that it is kept alive if there are one or more references to any of the interfaces supported by either the control or noncontrol objects.

It is possible for an object to call a method that may cause the object to be released. A technique known as "artificial reference counting" can be used to guard against this untimely release. The object calls IUnknown::AddRef before the potentially destructive method call and IUnknown::Release after it. If the object in question can be aggregated, it must call the controlling unknown's implementations of AddRef and Release (pUnkOuter->AddRef and pUnkOuter->Release) to artificially increment the reference count rather than its own implementations. For more information about reference counting, see Component Object Interfaces and Functions.

The next figure shows how the coordination between the control and noncontrol object works. The control object exposes the controlling unknown, Interface A and Interface B. The noncontrol object supports IUnknown and Interface C. All three interfaces derive from IUnknown.

The control object holds a pointer to the noncontrol object's IUnknown implementation so it can call the noncontrol methods when appropriate. The noncontrol object holds a pointer to the controlling unknown for the same reason. The pointer to the controlling unknown is supplied when the noncontrol object is instantiated. When the controlling unknown pointer is saved by the noncontrol object, AddRef is not called to increment the reference count as you might expect. This is due to the reference-counting cycle it would create. Instead, the noncontrol object just keeps a copy of the pointer and delegates calls to it as described in this section.

Referring again to the next figure, whereas the controlling unknown is available to the outside, as is indicated by the line and circle identifying it extending past the bounds of the aggregate, the noncontrol object's IUnknown implementation works locally and can't be obtained from outside the aggregate. It is called solely by the controlling unknown to obtain instances of the noncontrol interfaces, such as Interface C, to expose to the outside.

Internal Structure of an Aggregate.

Aggregation and Cached Interface Pointers

Interface-based reference counting has generally shown itself to be a preferable way of keeping OLE objects stable, although 16-bit aggregates have proven troublesome. However, 32-bit OLE 2 applications can overcome this difficulty with relative ease. For example, when a control object holds an interface pointer to a noncontrol object and the reference count has not been incremented, the pointer is said to be cached. In 16-bit OLE applications, this is achieved by calling QueryInterface to get the cached pointer and then releasing it in the following manner:

// 16-bit method for caching pointers to interfaces on noncontrol

// objects

IInterface1 *m_pi1;

m_pUnkInner->QueryInterface(IID_Interface1, &m_pi1);

m_pi1->Release();

To facilitate the use of per-interface reference counting in 32-bit OLE, the method for caching interface pointers has changed, as shown in the following:

// 32-bit method for caching pointers to interfaces on noncontrol

// objects.

IInterface1 *m_pi1;

m_pUnkInner->QueryInterface(IID_Interface1, &m_pi1);

m_pUnkOuter->Release();	\\ Uses the same m_pUnkOuter given to 	\\ the aggregated object.

The difference is in the third line. Furthermore, when a cached pointer is released, the following lines should be included (nothing is done for 16-bit applications at the same point):

m_pUnkOuter->Addref();

m_pi1->Release();

These two approaches are not compatible; all 16-bit aggregate control objects must use the first method and all 32-bit aggregate control objects must use the second one.

Note that interface-based reference counting works best when control objects delay the acquisition of cached pointers and release them when there is no possible use for them.

During the destruction of any aggregate, even those in which the control object does not cache pointers, the control object must guard the destructor by means of artifical reference counting, as shown below:

ULONG Release_ObjectXXX(ObjectXXX *this)

{

if(- -this->m_refs;

{

++this->m_refs;

...destroy the instance..

return 0;

}

return this->m_refs;

}

This code must be included because the sequence m_pUnkOuter->AddRef() followed by m_pi1->Release increments and decrements the reference count of the aggregate as a whole. Since this may occur in the destruction of the aggregate itself, the destructor must be guarded.

Note  This technique cannot be used by noncontrol objects to hold cached pointers to objects higher up in the aggregate. The noncontrol object has no way of knowing whether it has the same pUnkOuter as any other object in the aggregate. Although this is usually the case, it cannot be guaranteed.

Aggregation is entirely optional, and you should decide whether to use it as you design each object. However, providing support for aggregation costs little in time and complexity. The reward is larger than the cost in that aggregation enables the effective re-use of interface implementations. The key in implementing an aggregate object is making the correct delegation decision in both the aggregated and non-aggregated situations.

The following code example is an aggregate object implemented using C++ nested classes. The constructor takes as an argument the pointer to the control object's IUnknown implementation, or controlling unknown. If this pointer is NULL, indicating that the new object is not being aggregated, the m_pUnkOuter member is set to the address of m_Unknown, the local implementation of IUnknown. If pUnkOuter is not NULL, indicating that the new object is being aggregated, m_pUnkOuter is set to pUnkOuter. The InterfaceA implementations of the IUnknown methods forward their calls to the controlling unknown implementations. The controlling unknown IUnknown implementation manages queries for interfaces for the aggregate and maintains a master reference count.

class CAggregateObj

{

public:

static HRESULT CreateObj(LPUNKNOWN pUnkOuter, REFIID riid,

LPVOID  * ppvObj)

{

HRESULT hr;

CAggregateObj *pObj = new CAggregateObj(pUnkOuter);

if (pObj == NULL)

{

*ppvObj = NULL;

return E_OUTOFMEMORY;

}

if ((hr = pObj->m_Unknown.QueryInterface(riid, ppvObj)) != 	NOERROR)

// QI call above will set *ppvObj on success

// and NULL it on error

delete pObj;

return hr;

}

private:

CAggregateObj(LPUNKNOWN pUnkOuter)

: m_Unknown(this), m_InterfaceA(this)

{

if (pUnkOuter == NULL)

// not aggregated; make like the

// aggregated case

pUnkOuter = &m_Unknown;

m_pUnkOuter = pUnkOuter;

m_cRefs = 0;

}

~CAggregateObj();

struct CUnknown : IUnknown

{

CUnknown(CAggregateObj  * pAgg)

{ m_pAgg = pAgg; }

HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj);

ULONG AddRef(void)

{ return ++m_pAgg->m_ObjRefCount; }

ULONG Release(void)

{

if (--m_pAgg->m_cRefs  == 0)

{

// don't need guard here since this object

// doesn't aggregate another one

delete m_pAgg;

return 0;

}

return m_pAgg->m_cRefs;

}

  private:

CAggregateObj  * m_pAgg;

}

friend CUnknown;

CUnknown m_Unknown;

struct CInterfaceA : InterfaceA

{

CInterfaceA(CAggregateObj  * pAgg)

{ m_pAgg = pAgg; }

HRESULT QueryInterface(REFIID riid, LPVOID  * ppvObj)

{ return m_pAgg->m_pUnkOuter->QueryInterface(riid, ppvObj); }

ULONG AddRef(void)

{ return m_pAgg->m_pUnkOuter->AddRef(); }

ULONG Release(void)

{ return m_pAgg->m_pUnkOuter->Release(); }

HRESULT MethodA1(LPVOID  * ppvObj);

HRESULT MethodA2(DWORD dwArg);

  private:

CAggregateObj  * m_pAgg;

}

friend CInterfaceA;

CInterfaceA m_InterfaceA;

private:

ULONG	m_cRefs;

LPUNKNOWN	m_pUnkOuter;

}


Grouping Interfaces

Interface relationships are based on the ability to successfully obtain interface pointers by using IUnknown::QueryInterface. Although interface implementors are advised to design objects in such a way that interfaces are accessible from many other interfaces, there is generally no guarantee to a user that a specific interface is accessible from another interface. Implementations of QueryInterface are application specific so it usually cannot be assumed that it returns a pointer to another specific interface.

Two interfaces can be related in one of three possible ways. Each of these is set forth in the following list, using Interfaces named A and B as examples. Notice the importance of reference counting in managing the three types of relationships.

1.	Given a pointer to Interface A, it should be possible to obtain a pointer to Interface B. Interface A and Interface B are managed by a single reference count. OLE defines the following interfaces that fall in this category:

•	IOleObject, IDataObject, IViewObject, IOleCache, IOleInPlaceObject, and IPersistStorage. If the object is a link, IOleLink should also be available.

•	IPersistFile and IOleItemContainer

•	IOleClientSite and IOleInPlaceSite

2.	Given a pointer to Interface A, it must not be possible to obtain a pointer to Interface B. Interface A and Interface B can be managed by either a single reference count or separate reference counts. IOleInPlaceActiveObject and IOleInPlaceObject fall in this category. IStorage and IStream, typically implemented by OLE, also fall in this category, simply because they point to different objects.

3.	Given a pointer to Interface A, it might be possible to obtain a pointer to Interface B, depending on the implementing application. Interface A and Interface B can be managed by a single reference count. Clients of the interfaces cannot assume it is always possible to obtain the same set of interfaces in this category from all implementing applications. The decision to group these interfaces is up to the implementor, and there is no guarantee they will be implemented in the same manner by all applications.

All OLE interfaces not specifically mentioned as belonging to the first or second groups belong to the third type. These interfaces can be grouped in a manner that works best for the application. It is wise to implement interfaces together whenever possible, because doing so leads to greater flexibility and interconnectivity. Implementing interfaces together implies they are within a single object supported by one piece of memory. The interfaces share one IUnknown::QueryInterface implementation that recognizes all related interfaces. Pointers to the interfaces are managed by a single reference count.

An object that has multiple groups of interfaces, each managed by its own reference count, is kept alive if there is at least one reference to any interface in one of the groups.

There are two possible approaches to interfaces that cannot be implemented together. They can be implemented either in separate objects, or in a single object that manages the interfaces independently. In the single object approach, there can be one IUnknown::QueryInterface implementation or several, depending on the number of interface groups in the object. In the single QueryInterface implementation, explicit steps must be taken to return pointers only to related interfaces.


Common Reference Counters, Pointers, and Flags

The following tables include suggestions for data members for applications, documents, and site objects that might be included in most container or object applications. Only the more widely used members are listed; applications that support custom features such as in-place activation may have additional data members. Also not included in these tables are pointers to interfaces that may be used either temporarily or throughout the lifetime of the application.

The following table lists data members that may be useful for an application-level object.

Data Member	Type	Application Type	Use
m_cRef	ULONG	All	Reference count  for application.
m_cDoc	ULONG	All	Total count of open documents (documents that are visible to the user and invisible data transfer documents).
m_fUserCtrl	BOOL	All	Flag to control lifetime of application on behalf of user.
m_dwRefClassFac	DWORD	Object applications and containers that support linking	Value returned by CoRegisterClassObject
m_fOleInitialized	BOOL	All	Indicates whether OleInitialize was called.
m_cfFormat1, 2, ...	UINT	All	OLE 2 Clipboard formats.
m_arrFormatetc	FORMATETC	All	Array of formats offered in Clipboard copy and supported in IDataObject::GetData.
The following table contains suggestions for document level object data members:

Data Member	Type	Application Type	Use
m_cRef	ULONG	All	Reference count  for document.
m_cLock	ULONG	All	Lock count for  document; useful for debugging. These locks are not strong enough to warrant reference counts, because they can be arbitrarily broken by the user closing the application (File Exit).
m_fObjClosing	BOOL	All	Flag to guard against recursive close call.
m_dwDocType	DWORD	All	Type of document; for example, uninitialized, embedded, etc.
m_fObjDestroying	BOOL	All	Flag to guard against recursive destroy call.
m_dwRegROT	DWORD	All	Key if document is registered as running.
m_nNextObjNo	ULONG	Containers	Next available object number for storage name.
m_fShowObject	BOOL	Containers	Indicates whether to show object.
m_cPseudoObj	ULONG	Object applications	Count of pseudo objects.
m_dwStorageMode	DWORD	Object applications	Indicates state during save.
m_szAppName	LPOLESTR	Object applications	Name of application.
m_szObjName	LPOLESTR	Object applications	Name of object in container.
m_clsidTreatAs	CLSID	Object applications	CLSID to be treated as.
m_typeTreatAs	LPOLESTR	Object applications	User type to be treated as.
The following table contains suggestions for site objects in containers.

Data Member	Type	Use
m_cRef	ULONG	Reference count for site.
m_szStgName	LPOLESTR	Name of storage object.
m_fObjWinOpen	BOOL	Indicates whether object window is open. If open, need to shade object.
m_fMoniker	BOOL	Indicates if a moniker is currently assigned.
m_dwDrawAspect	DWORD	Current display aspect.
m_fDoGetExtent	BOOL	Indicates extents may have changed.
m_fDoSetExtent	BOOL	Indicates object was resized when not running.
m_sizeInHimetric	SIZEL	Extents of object in himetric units.
m_dwLinkType	DWORD	Indicates type of link: automatic or manual.
m_lpszShortType	LPOLESTR	Short type name of OLE object.


Implementing OLE Applications

This section focuses on the implementation tasks that pertain to OLE container and object applications. Where implementation differs depending on the type of application, these differences are described. The implementation tasks can be grouped as follows:

•	Tasks that apply to all applications.

•	Tasks that apply to applications serving a specific role.

•	Tasks that apply to applications supporting a specific feature.

The following table lists these tasks with the action(s) that typically cause various states to be entered:

Application Task	User Action
Starting an application	User launches application directly or by double-clicking an object in a container.
Opening a document	User selects the File New or Open command.
Activating a linked or embedded object (open mode or in place)	User double-clicks object or selects a verb from the object's Edit menu.
Creating a linked or embedded object	1. User selects the Insert Object command. 2. User copies data to the Clipboard and     selects the Paste or Paste Link command. 3. User drags and drops data.
Saving a linked or embedded object	User selects the File Save, Save As, Close, or Exit command.
Closing a document	User selects the File Close or Exit command.
Closing an application	User selects the File Exit command.


Starting OLE Applications

The following tasks are performed by OLE applications before they display their main window and enter the message-processing loop. These tasks are described in greater detail in the following sections.

•	Verify that the correct information is in the registry. (Containers that do not support linking to embedded objects do not need to do this.)

•	Check the application's compatibility with the OLE libraries.

•	Initialize the OLE libraries.

•	C-based applications only: Initialize the OLE interface VTBL data structures.

•	Register the IMessageFilter interface implementation to manage concurrency.

•	Register the Clipboard data formats.

•	Determine how the application was started.

•	Register the application's class object(s), if required.

•	Initialize any flags and variables used to maintain the state of OLE objects and/or interfaces.


Verifying Registry Entries

On start up, a stand-alone object application should verify that the path for its EXE and handler are correctly specified in the registry. If the information is incorrect or not present, the object application should automatically update it; otherwise the application will not be found should a user try to activate an object belonging to that application. In addition, an EXE should also check the paths for its own handlers and in-process object application DLLs (a DLL installed with the application). Mini object applications should take these same steps before they exit.

For more information on registering object applications, see OLE Registry Information.


Verifying Build Version Compatibility of OLE Libraries

This is not necessary in 32-bit OLE but must be done by 16-bit applications. Refer to OleBuildVersion and CoBuildVersion.


Initializing the OLE Libraries

Refer to OleInitialize.


Initializing the Interface VTBL Data StructuresC Only
Initializing the Interface VTBL Data Structures—C Only

A C-based OLE application typically implements its interfaces as nested data structures. Interface data structure initialization involves allocating memory and setting each member to an initial value. These structures can be allocated and initialized in any order, but the IUnknown and IClassFactory interface structures must be initialized before they can register a class of object(s).

Applications written in C must explicitly initialize each interface VTBL. Typically the VTBLs are initialized as part of creating the application object's frame window, before the window is shown to the user. Initializing the VTBLs involves setting each of the VTBL pointers to the appropriate method implementation.

The following code sample illustrates how a C program might initialize the IUnknown VTBL. The IUnknown interface is implemented in a data structure nested in MyObject. The members of the VTBL are set to the application's unique implementations of the IUnknown methods, IUnknown_MyObj_QueryInterface, IUnknown_MyObj_AddRef, and IUnknown_MyObj_Release.

myUnknownVtbl.QueryInterface = IUnknown_MyObj_QueryInterface;

myUnknownVtbl.AddRef = IUnknown_MyObj_AddRef;

myUnknownVtbl.Release = IUnknown_MyObj_Release;

pMyObject->m_Unknown->pVtbl = &myUnknownVtbl;


Determining the Start-Up Method

An application's set of initialization tasks is affected by its method of start-up; accordingly, an OLE application must determine whether it was started by a user or as part of activating a linked or embedded object. To determine how it was started, an application parses the command line to see whether the /Embedding option has been specified. Its presence indicates that OLE has started the application on behalf of a container of an embedded or linked object. The absence of the /Embedding option indicates that the user is in control.

Note  Applications should check for both /Embedding and -Embedding whenever they check for the command-line options. Although this documentation refers to /Embedding, either option is valid.

Although OLE is Unicode, the command-line string (including /Embedding) is and will be ANSI. This means that Unicode applications must either call the GetCommandLine function to get the Unicode command line instead of using the ANSI lCommandLine parameter passed in WinMain or convert the lpCommandLine parameter to Unicode before passing it to OLE. For more information about other alternatives, see the Win32 SDK.

How an application initializes itself depends on whether /Embedding [filename] is present on the command line:

•	A multiple-use application started with the /Embedding option on the command line registers its class object(s), providing OLE with a pointer to the IClassFactory interface; single-use applications do not register their class object. The application frame window is kept hidden until either IOleObject::DoVerb (with an iVerb parameter value of OLEIVERB_SHOW) is called for an object application or IOleClientSite::ShowObject is called for a container.

The presence of /Embedding filename indicates the application is being started to service a file-based linked object and the filename references the linked file. In this case, the application should load the file and register it in the running object table — but not make it visible. An application started with /Embedding filename might or might not need to register its class object(s), depending on the type of class object. Single use class objects should not be registered; multiple use class objects must be registered.

•	An application started by the user creates either an untitled document or, if a filename is specified, a document initialized to the contents of the specified file. The application window is made visible immediately. Because an SDI application can only work with one user document at a time, it does not register its class object(s). An MDI application registers its class object(s), indicating that multiple objects can be handled or created.

For more information on creating and opening documents, see Opening Compound Documents.


Registering Class Objects and Object Classes

Refer to IClassFactory::CreateInstance.


Locking an Application in Memory

Refer to CoLockObjectExternal.


Opening Compound Documents

Note  The information presented in this section assumes an application is saving its native data using the OLE structured-storage model. While there is no prerequisite for using structured storage, using it can simplify solutions to storage needs.

For an overview of the OLE storage model, see Persistent Storage Interfaces and Functions.

A user typically initiates the opening of a compound document by choosing the File New command for new documents or the File Open command for existing documents. The tasks an application has to perform to open existing documents and new ones are the same, except for the added step of loading an existing document into memory.

Before making an open compound document visible, container applications must complete the following tasks:

•	Allocate memory for the document object, using a class or a structure, and initialize its members.

•	Lock the application object in memory.

•	Create and register a file moniker, if linking to the file is supported.

•	Open an IStorage object.

•	Load the data (for existing documents only).

•	Change the user interface as appropriate.

•	Register as a drop target (if applicable).

•	Show the document to the user and set the document's dirty flag, as appropriate.

•	If the document was opened by the user, lock the document in memory.


Initializing the Document Structure

Before allocating memory for a new document object, SDI applications check to see whether there is a document already open that needs to be saved. If there is and the document has changes, the application should prompt the user to save the document. An MDI application can open a new document while others are open. For more information on closing a document, see Closing OLE Compound Documents.

The type of document being opened determines the user interface changes to be made, if any. For example, when the user opens a new untitled document in an MDI object application started with /Embedding, the application must change its user interface menus. When saving a document, applications check the document type in order to call the correct save routine. For information on saving OLE documents, see Saving OLE Compound Documents.

To help keep track of the document type and how it is being initialized, applications can define and use constants similar to the following:

Document Type	Description
DOCTYPE_UNKNOWN	A newly allocated document object exists but has not been initialized with application data yet.
DOCTYPE_NEW	A new document was created by the user by means of the File New command. The document is initialized as an untitled document. The usual user interface for user documents is displayed.
DOCTYPE_FROMFILE	The document was initialized from an existing file using the File Open command (or by using IPersistFile::Load being called when binding a link source).The normal user interface for user documents is displayed.
DOCTYPE_EMBEDDED	The document was initialized as an embedded compound-document object using the IStorage opened by its container. The usual user interface is replaced with the modified user interface for an embedded object.
DOCTYPE_FROMSTG	The document is initialized using data from the IStorage provided by using IPersistStorage::Load. The usual user interface for user documents is displayed.
The document type is initially set to DOCTYPE_UNKNOWN because the actual document type is not known until the document object is initialized with data.

An application should keep track of references to its document object in a data member of the object. To guard this object from accidental destruction while it is being initialized, the reference counter should be intentionally incremented at the beginning of the initialization process. When initialization is complete, the counter can be decremented. This technique is known as artificial reference counting and is explained more fully in Component Object Interfaces and Functions.

This document-level reference counter can work in conjunction with a corresponding counter in the application object. To guarantee the application is not accidentally closed while the document is open, it is wise to increment both counters.


Creating and Registering File Monikers

Applications that allow containers to link to their file-based documents must create a file moniker to represent the open document and register the moniker in the running object table. The file moniker is created by calling CreateFileMoniker, passing in the document's complete path. To register the moniker, make the following two calls: GetRunningObjectTable to retrieve a pointer to the running object table and IRunningObjectTable::Register to complete the registration. For more information about monikers and the running object table (ROT), see Linking Interfaces and Functions.


Opening IStorage Objects

Refer to IStorage Interface.


Loading Objects

Refer to IStorage Interface and OleLoad.


Changing the User Interface

An application sets the title of the document window either to the name of the file from which the document was initialized or to an untitled document name.

If an SDI object application started with /Embedding opens a document, the File New and File Open commands are disabled and the File Save and File Exit commands are replaced with the File Save Copy As and File Exit and Return to <container document> commands. The File Save Copy As command lets a user save an embedded object in a document other than its containing document.

An MDI object application started with /Embedding makes the same user-interface changes as does an SDI object application, except these changes are made when the document window changes from a user-opened document to one needed to service an embedded object. Also, an MDI object application does not disable the File Open and File New menu items.


Registering a Document for Drag and Drop

An application window that can accept dropped data must register as a valid drop target by calling RegisterDragDrop, passing a pointer to its IDropTarget implementation. Drop targets can be registered whenever the window handle exists, although doing so while the window is visible is most common. This is not always the case, such as when multiple windows and drop targets exist for each object.


Showing the Document and Setting the Dirty Flag

The application must make the document visible to the user and set a flag to indicate whether data has changed since it was last saved to storage. For a document opened with /Embedding, the document dirty flag should be set to TRUE to ensure the container always gets the most up-to-date representation of the object.

The next figure shows the state of an MDI document object opened by OLE using a moniker bind operation, so the dirty flag is set to TRUE. This document object is the data source for a linked object in a container. The reference counts for the objects are stored in the data member cRef. As shown, the document and application objects have a reference count of one, reflecting the external reference held by the stub managers. The stub managers keep track of the total number of external references (leftmost number) to the objects and how many of those references are strong (rightmost number).

The class object has a reference count of two: the one that OLE holds on the object on behalf of the class factory table registration, and the pointer reference to the object's IClassFactory interface.

Document object created during a file moniker bind operation.

Documents created or opened by the user should set their dirty flag to FALSE. To lock the document in memory explicitly on behalf of the user, the application calls CoLockObjectExternal (pUnk, TRUE, NULL).


Using the Insert Object Dialog

The Insert Object dialog box, opened from the Edit menu, allows a user to create a new or embedded object from either a default object-application object or a file.

The Insert Object dialog box provides two options for creating objects: Create New and Create From File. The Create New option creates an embedded object from a list of registered object-application object types. When the Create New option is selected, a list of currently registered objects is displayed from which the user can choose. The helper function OleUIInsertObject from the sample user-interface library can be called to display the dialog box, accept the user's selection, and optionally go on to create the necessary object.

The Insert Object dialog box is displayed in the figure below. The Create From File option, when selected, creates either a new embedded object from a file or a link to a file if the Link check box is chosen. The Display as Icon check box allows the object to be inserted as an icon.

Insert Object dialog for creating an object from a file.

The implementation of the three options is identical from the container's point of view, with one exception. A different OLE creation function is called for each option. The container performs the following steps:

1.	Display the Insert Object dialog box and allow the user to make a selection.

2.	Allocate a storage object and allocate and initialize the container site.

3.	Depending on the user's selection, call the appropriate OLE creation function.

4.	Set up advisory connections between the object application and the newly initialized container site.

5.	Integrate the object into the container document.

6.	Make the object visible.

7.	Mark the document as changed.


Calling OLE to Create the Object

OLE provides several helper functions for creating embedded and linked objects. As noted above, different OLE creation functions are called for different options selected by the user.

The following table lists the OLE creation functions used with the Insert Object dialog and provides a brief description:

OLE Object Creation Function	Type of Object Created
OleCreate	New embedded object.
OleCreateFromFile	Embedded object from a file.
OleCreateLinkToFile	Linked object to a file.
The OLE creation functions take several parameters, among them the CLSID of the object being created, the name of the file (if appropriate), the interface ID to be returned, the render option for cache initialization, and a pointer to the container's IOleClientSite interface. The typical container sets the render option to OLERENDER_DRAW, indicating that the compound-document object should determine the formats to cache.

Containers can be designed to implement the functionality provided in the creation helper functions. However, most developers will want to use these functions to minimize errors and speed implementation.

Containers can also implement this functionality in a different way. The function calls made in OleCreate and their purposes are listed in the following table in the order in which they are made:

Object Creation Function Called	Purpose of Call
CoCreateInstance	Creates a class object for the object to be created and returns a pointer to that class object (IClassFactory interface).
IClassFactory::CreateInstance	Allocates and initializes the new object.
IOleObject::SetClientSite	Passes a pointer to the container's IOleClientSite interface to the object.
IPersistStorage::InitNew	Passes a pointer to the storage object.
OleRun	Runs the object and initializes the cache if a cache has been requested.


Setting Up Advisory Connections

When the OLE creation function returns, the container registers to receive either OLE notifications and data or view notifications. Typical compound document containers that rely on OLE to draw their embedded and linked objects register for OLE and view notifications. Containers handling the caching of their own presentation data can register for data notifications rather than view notifications. These containers pass OLERENDER_NONE in the render option in the call to the OLE creation function and then call IUnknown::QueryInterface asking for the IOleCache interface, creating the cache(s) needed with the IOleCache methods.

The following table indicates the interface methods that should be called to set up notification registration and the type of notifications handled by each method:

Interface Method Called	Type of Notification Registered For
IViewObject::SetAdvise	Registers for view notifications.
IOleObject::Advise	Registers for OLE notifications.
IDataObject::DAdvise	Registers for data change notifications.
When containers are finished registering, they call IOleObject::SetHostNames to inform the object of the name in the container. The object is made visible with a call to IOleObject::DoVerb.


Activating Objects

Activation refers to invoking a particular operation, or verb, available for a compound-document object when it is selected in its container. The operations can be invoked on both embedded and linked objects. Activation requires that the compound-document object be in the running state so the object application is available to supply the requested operation. The process of getting the object into the running state is known as binding. Because binding is handled by OLE, it is transparent to containers and object applications. For more information about binding, see Linking Interfaces and Functions.

An embedded object is activated when the user selects a verb from the Edit or popup menus or double-clicks the object. The container makes a call to IOleObject::DoVerb, and the object application either activates the object in place or in a separate window in the object application process space, as is appropriate. Binding an embedded object involves locating and running the object application with the object data passed from the container.

From the container's perspective, activating a linked object is the same as activating an embedded object. However, the internal process required to activate the link source is much different from the process for activating an embedded object. When a linked object is bound, each of the pieces that make up the link source's moniker, typically a generic composite moniker, is evaluated. When the binding process is complete, the link source's application, if not already running, is forced into the running state.

Linked objects must be edited in their original source application. This usually means the linked object must be edited in a separate window rather than in place. The use of the separate window makes it clear that an edit process is occurring, which is important because changes made to a linked object might affect other objects. However, if the link is to an embedded object whose object application supports in-place editing, the embedded object can be edited in place. Other types of activities, such as play or rewind, can also be done in place.


Activating an Embedded Object

Binding an embedded object involves locating and running the object's object application using the unique CLSID stored with the object. The object application is invoked with /Embedding, indicating it is being launched to support an OLE operation. For information on initializing an object application for OLE operations, see Starting OLE Applications.

the next figure shows what happens after a container makes a call to IOleObject::DoVerb to activate an embedded object. The site for the embedded object in the container has a pointer to one of the object's interfaces in the object handler, typically IOleObject. The object handler is responsible for locating and starting the object application. A pointer to the object in the object application is stored in the object handler.

Relationship between an embedded object, the object handler, and the object application.

An object application's implementation of IOleObject::DoVerb for activating in open mode is quite simple: IOleObject::DoVerb determines which of the supported verbs have been requested and then initiates the action required. The object returns OLEOBJ_E_INVALIDVERB if the verb is invalid.


Activating a Linked Object

The link source for a linked object can be a file-based document, a selection of data within a document, an embedded object, or a selection of data within an embedded object. Each of these link sources is represented by a moniker that is itself made up of one or more monikers of the following types:

•	item moniker: a wrapper for a null-terminated string for naming an item or object.

•	file moniker: a wrapper for the path in the native file system (always the leftmost piece in a generic composite moniker).

•	generic composite moniker: a concatenation of two or more monikers. A generic composite moniker is made up of one file moniker and one or more item monikers.

The following table briefly describes the types of monikers typically assigned to link source data:

Link Source Type	Type of Moniker
Document stored in a disk file.	File moniker.
Range of data within a document that might or might not be explicitly named by the user (pseudo object).	Generic composite moniker comprised of a file moniker and an item moniker to represent the pseudo object.
Part or all of an embedded object (either within the same container or in a different container).	Generic composite moniker comprised of a file moniker and two item monikers, one for the object in the container and one for the data in the object application.
A pseudo object is created when a link is made to a selection of data within a document. Pseudo objects are not associated with an identifiable storage location. The pseudo object is assigned a generic composite moniker consisting of the file moniker for its document and an item moniker for the data range. The following figure shows the typical components of a generic composite moniker.

Components of a Generic Composite Moniker.

The next figure shows what happens after a container makes a call to IOleObject::DoVerb to activate a linked object. Except for the insertion of the OLE link object between the container site and the object handler, the flow of control for linked objects is identical to the flow of control for embedded objects. The OLE link object's implementation of IOleObject::DoVerb initiates the moniker binding process.

Relationship between a linked object, the OLE link, the class handler, and the object application.

The next figure illustrates how editing a link to an embedded object that is typically edited in place might appear to the user. Two containers are involved, one to hold the linked object and the other to hold the embedded object. When the user double-clicks the linked object, the moniker for its link source is bound. This starts the object application for the embedded object. Because the object application's IOleObject::DoVerb implementation supports in-place editing, the link source is edited in place within its embedding container.

Editing a link to an in-place embedded object.


Binding a File Moniker

When a file moniker is bound, the running object table is checked first to see whether the object is already running. If it is, the file moniker then binds to the running object. If the object is not currently running, the OLE function GetClassFile is called to determine the object class associated with the file. GetClassFile uses three strategies to determine the object class. If the file is a compound file, the class is read directly from the compound file. If compound files are not used, GetClassFile checks the registry and compares the registered file type patterns against the data stored in the file. If these two strategies fail, the class is determined from the file's extension.

GetClassFile runs the appropriate object application, if it is not already running, and then calls IPersistFile::Load to open the file and IClassFactory::CreateInstance to instantiate the object.


Binding an Item Moniker

An item moniker requires the IOleItemContainer interface of the object to its left to bind. IOleItemContainer is implemented by all object applications that allow linking to pseudo objects and all containers that allow linking to embedded objects. When the item moniker gains access to the interface, IOleItemContainer::GetObject is called to locate and run the object represented by the item name and to return a pointer to one of the object's interfaces. The caller passes the item name, a value that indicates the amount of time the caller is willing to wait for the object, an interface identifier, and a place to return the object. The object application's implementation of IOleItemContainer::GetObject returns the pointer to an active pseudo object. The container's implementation of GetObject returns a pointer to an interface belonging to the running object. GetObject cannot be recursive; it must get the object and then return.


Binding a Generic Composite Moniker

Binding a generic composite moniker involves binding all its pieces. The process starts with the rightmost piece, typically an item moniker. OLE locates and loads the object represented by the item moniker by calling IOleItemContainer::GetObject, and then initiates binding on the piece to the left. This process is repeated until the binding of the leftmost moniker, a file moniker, causes the object application to be run (if it is not already running). The file containing the link source is opened and loaded, and the link source is placed in the running state.


Verifying Links

IOleItemContainer::IsRunning provides an application with a way to determine whether an object identified by a specific name is running. Determining the state of a link source is important for maintaining active link connections.

Containers call IOleObject::IsUpToDate to find out whether they have the most up-to-date data. Linked objects can be outdated if the document containing the link source has changed. If IOleObject::IsUpToDate returns FALSE, containers of manual links that are connected can get updated data by calling IOleLink::GetBoundSource to get a pointer to the link source. The container can then call QueryInterface on the returned pointer asking for a pointer to IDataObject. IDataObject:GetData can then be called to get the most up-to-date data.


Using an Objects Verbs
Using an Object's Verbs

Verbs are the set of operations supported by a particular object. The set of object-specific verbs can differ for each object type because different verbs make sense for different objects. For example, a sound object's verbs displayed to the user might include play, rewind, and edit while a text object might only support edit. An object application stores a set of object-specific verbs in the registry for each type of object supported. These user-selectable verbs are assigned sequentially positive values, beginning with 0 for the primary verb (in order to maintain compatibility with OLE 1 containers).

Compound-document objects must also register and support a set of special OLE verbs. (These verbs are not to be confused with those that appear on the Edit and popup menus for the user to select.) The OLE verbs have a predefined meaning and value for each supported object. The OLE verbs include OLEIVERB_PRIMARY, OLEIVERB_SHOW, OLEIVERB_OPEN, OLEIVERB_HIDE, and a few other verbs that apply only to in-place activation. For more information, see IOleObject::DoVerb.OLE verbs are typically invoked programmatically and are stored in the registry. OLEIVERB_PRIMARY has a value of 0; the other OLE verbs have negative values in sequential order beginning with -1. Refer also to Registering Object Applications.


Managing Notifications

Notifications are callbacks generated by an object when it detects a change. Containers and other clients of an object register to receive notifications of interest by setting up an advisory connection to the object. Through this connection, the object undergoing the change notifies the advisory sink. Some of the changes that can cause a notification to be sent include renaming of the object, saving the object to persistent storage, closing the object, changing its view, and so on. Notifications ensure that a container has the most up-to-date information about its embedded and linked objects.

There are three types of notifications: OLE (or compound document), data, and view. The OLE notifications include OnRename, OnSave, OnLinkSrcChange, and OnClose. Containers and other objects receive OLE notifications by calling the object's IOleObject::Advise method, passing it a pointer to its IAdviseSink interface implementation. (Advise connections are not saved with objects.) The IAdviseSink interface is implemented by all objects that may require notifications of any type. An extension of IAdviseSink, IAdviseSink2, is implemented by objects that may required OnLinkSrcChange notifications. Each type of notification maps to one of the methods in IAdviseSink or IAdviseSink2. For example, when an object application is saving an embedded object, it generates an OnClose notification that causes the container's IAdviseSink::OnClose method to be called.

Notification registration is handled by methods in three different interfaces. To register for OLE notifications, an object calls IOleObject::Advise. To register for data change notifications, an object calls IDataObject::DAdvise. To register for view notifications, an object calls IViewObject::SetAdvise. IDataObject and IOleObject are implemented by the object application and object handler; IViewObject is implemented by the cache object.

The five types of notifications are described below:

Notification	Description
OnRename	Sent when the object application's full moniker is initially set or changed. (OLEWHICKMK_OBJFULL is passed as a parameter to IOleObject::SetMoniker). Tells the OLE link object to update its moniker. Containers can ignore this notification.
OnSave	Sent when the object application saves the object. If the ADVF flag ADVFCACHE_ONSAVE was specified, tells the OLE link object or object handler to update its cache.
OnClose	Sent when the object application is closing the object. Tells an OLE link object to release its pointer to the bound link source. Containers can revert the storage to ensure that uncommitted changes are flushed.
OnViewChange	Sent when the object's view (presentation) has changed. Containers need to redraw. Can be sent when the object is in either the running or loaded state.
OnDataChange	Sent when the object's data has changed. Cache might need updating. Linked objects with manual links do not update the cache with OnDataChange.
OnLinkSrcChange	Sent by the OLE link object when it receives the OnRename notification from the link source (object) application. The link object updates its moniker and calls IAdviseSink2::OnLinkSrcChange
There are a few other methods related to notification registration. IDataObject::DUnadvise and IOleObject::Unadvise remove a registration; IDataObject::EnumDAdvise, IOleObject::EnumAdvise, and IViewObject::GetAdvise list the registrations currently in effect for the object.


Managing Notification Registration

An object application must manage registration requests, keeping track of who is interested in which notifications and sending those notifications when appropriate. OLE provides two component objects to simplify this task: the OleAdviseHolder for OLE notifications and the DataAdviseHolder for data notifications.

The OleAdviseHolder, created with a call to the OLE function CreateOleAdviseHolder, is an object provided by OLE that implements the IOleAdviseHolder interface. IOleAdviseHolder contains methods for managing registration requests and sending notifications to IAdviseSink sites.

The DataAdviseHolder is an object provided by OLE that implements the IDataAdviseHolder interface and is used to manage data notifications. Applications can call the OLE function CreateDataAdviseHolder to instantiate a DataAdviseHolder.

When an object application uses one of the advise holder objects, a call to one of the registration methods in IOleObject or IDataObject results in the corresponding function being called in the advise holder object. For example, the following implementation of IDataObject::DAdvise illustrates the use of the DataAdviseHolder to handle registering data-change notification:

HRESULT IDataObject_DAdvise(

        LPDATAOBJECT        pThis,

        FORMATETC *      pFormatetc,

        DWORD               advf,

        LPADVISESINK        pAdvise,

        DWORD *          pdwConnection )

{

HRESULT hrErr;

LPDOC pDoc=((struct CDocDataObjectImpl  *)pThis)->pDoc;

*pdwConnection = NULL;

if (pDoc->m_pDataAdviseHldr == NULL && CreateDataAdviseHolder

 (&pDoc->m_pDataAdviseHldr) != NOERROR)

return ResultFromScode(E_OUTOFMEMORY);

else  {

hrErr = pDoc->m_pDataAdviseHldr->pVtbl->DAdvise(

pDoc->m_pDataAdviseHldr,

(LPDATAOBJECT)&pDoc->m_pDataObject,

pFormatetc,

advf,

pAdvise,

pdwConnection   );

return hrErr;

}

Among the parameters passed to IDataObject::DAdvise is a group of flags, the advf parameter in the above example, that controls the advisory connection. These flags are also used with the IOleCache methods. The value for the parameter is taken from the enumeration ADVF; one or more of these values (OR-ed together) can be used.

When a caller specifies ADVF_PRIMEFIRST as the advf parameter, DAdvise immediately sends an OnDataChange notification, thus enabling the caller to get the data immediately.

A possible scenario for setting these flags involves a user selecting the Display as Icon check box in the Paste Special dialog. The object is requested with the DVASPECT_ICON aspect value that causes an icon to be placed in the cache. When a data change notification is set up, the caller could specify ADVF_NODATA | ADVF_ONLYONCE | ADVF_PRIMEFIRST in its call to IDataObject::DAdvise. The result would be that the only one data-change notification would ever be sent and no data would be sent with it. The ADVF_NODATA value can be used whenever the caller does not want OLE to update the cache.


Notification Flow

Notifications originate in the object application and flow to the container by using 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. A special purpose container, such as one that renders the data itself (instead of calling the OLE helper-function OleDraw), 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 call IDataObject::GetData to get the new tabular data.

The next figure shows the notification flow from an object application to an embedded object container that has registered for OLE and view notifications. The solid lines represent memory pointers; the dotted lines represent notifications. Note that the notifications go directly to the IAdviseSink interface. Data notifications sent to the object handler are forwarded to the appropriate cache element in the object handler's cache object. If a container has registered for view notifications rather than data notifications, the cache generates an OnViewChange notification it sends to the container site.

Notification flow for an embedded object.

For a linked object, the flow is slightly different. Instead of holding a pointer to the object handler, the container points to the OLE link object. The OLE link object points to its delegate, the object handler. There is no pointer from the object application back to the container site. The object application, or link source, sends data and OLE notifications to the object handler which in turn sends them on to the OLE link object. The OLE link object generates a view notification it sends to the container, if it has registered for view notifications, and a link-source change notification, if appropriate. The following figure shows the flow for a linked object.

Notification flow for a linked object.

The next figure shows how notifications are sent to container sites in separate container applications. The container sites contain linked objects that are linked to the same link source. Note that each container has its own OLE link object and object handler within its process space. The following figure introduces the advise holders to keep track of the advisory connections. The OleAdviseHolder manages OLE notifications; the DataAdviseHolder manages data notifications.

Notification flow to separate container processes.

Notifications can be synchronous or asynchronous. With synchronous notifications, the remoting object responsible for marshalling the data across the process boundary waits for a response from the remoting object on the other side of the boundary before returning control to the object that sent the notification. With asynchronous notifications, the remoting object does not wait for a response before returning. For more information about the types of method calls in OLE, see Concurrency Management.


Sending 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.

Each notification maps to one or more conditions. The following table lists the common conditions that generate each notification. The view and link source change notifications are not included because object applications do not send them.

Condition in Object application	Notification Generated
User selects Close	OnClose
User selects Save or Save As	OnSave
Document or Pseudo Object name first assigned or changed	OnRename
Object's data changed	OnDataChange
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.

Object applications that support linking to ranges must consider how pseudo objects are to be informed of the need to send notifications. For example, when a document is saved, the OnSave notification must be sent to both the container sites of the standard objects and the container sites of the pseudo objects. Two approaches can be considered:

•	Any change in the document causes a notification to be sent to all container sites for all pseudo objects in the document.

•	A mechanism can be implemented for determining the specific pseudo object affected by a change. Notification is sent only to the container sites of the those affected.

Some object applications disable the sending of notifications for an extended period of time so certain operations are not interrupted. Notification requests are placed in a queue, and when the operation completes, these pending notifications are sent. For example, a graphic object application can ensure that the repainting of its image is not interrupted; notifications are not sent until the repaint operation has been completed.

There are a few considerations for object applications that implement pseudo objects to support linking to ranges. If such an object application supports the queueing of notifications, a mechanism to queue pseudo-object notifications must be implemented. Notifications must be sent to all pseudo-object container sites when the OnRename and OnSave conditions arise.


Receiving Notifications

A container is responsible for registering for notifications and responding if necessary after a notification is received. Containers register to receive notifications when a new embedded or linked object is created and when an object is loaded.

The IAdviseSink interface implementation dictates the necessary response for each notification. IAdviseSink is implemented by containers, the OLE link object, and the object handler; and each implementation is different. For example, the OLE link object's implementation of OnRename updates the moniker for a linked object, but the object handler and container versions of OnRename do nothing.

A container that does not use OLE's caching support registers for data-change notifications. If a container receives data notifications, its implementation of IAdviseSink::OnDataChange could post an internal message or invalidate the rectangle for the changed data by calling the Windows function InvalidateRect and then waiting for a WM_PAINT message. It is not valid to call IDataObject::GetData within OnDataChange. Calling GetData would result in a synchronous call nested inside of an asynchronous call.

OnViewChange handles view and extent changes. To handle extent changes, set a flag in OnViewChange called fDoGetExtent to TRUE and then call InvalidateRect to force repainting. In the drawing code, insert the following functionality:

if (fDoGetExtent)  {

fDoGetExtent = FALSE;

GetExtent(&newExtents);

if (newExtents are different)

relayout the contents.

}

draw the object

Containers can postpone receiving view notifications so an operation, such as printing, can continue uninterrupted. A call to IViewObject::Freeze causes view notifications to be queued until IViewObject::Unfreeze is called.


Saving OLE Compound Documents

Users typically initiate a document save by selecting the File Save or File Save As commands. An SDI object application opened with /Embedding also offers the File Save Copy As command, which is used to save the embedded object in a file that is separate from its containing document's file. Saving an object with the File Save Copy As command does not break the connection to the object's container application.

Note  Unlike OLE 1 object applications, OLE 2 object applications do not reuire a File Update command to update changes made to an open OLE 2 embedded object. The changes are automatically reflected in the object's container document. However, if the object application is going to support OLE 1 objects, it must support the Update command. For more information on OLE user interface recommendations, see User Interface Guidelines.

When saving untitled documents, OLE applications must create a new file moniker for the document and register it in the running object table. If the untitled document had a temporary file moniker registered in the running object table, this temporary moniker must be revoked by calling IRunningObjectTable::Revoke.


File Save As Command

To save a document using the File Save As command, OLE applications perform the following tasks:

•	Prompt the user for the filename in which to save the document. If the user cancels the save operation, leave the document object initialized in the state it was in before the user chose the save operation.

•	Set the document's title to that of the filename specified by the user.

•	If linking is supported, create and register a new file moniker for the document in the running object table. Destroy and revoke the registration of the document's temporary file moniker (if it exists).

•	Object application only: Notify all linking containers and/or pseudo objects that the document has been renamed by calling IAdviseSink::OnRename. Direct all pseudo objects to notify their registered linking clients. Each pseudo object sends a notification in a similar fashion by calling IAdviseSink::OnRename for each of its registered sinks or IOleAdviseHolder::SendOnRename.

•	Container only: Notify all compound-document objects that have monikers assigned that the document's moniker has changed by calling IOleObject::SetMoniker. If the container is a top-level container, this is done when the container's filename changes.

•	Create the specified file in which to save the document as an OLE compound file by calling StgCreateDocfile.

•	Open a stream in the open IStorage object and save the object's CLSID by calling WriteClassStg. Save the object's user type name and Clipboard data format by calling WriteFmtUserTypeStg. (When saving an embedded object, the object application does not call WriteClassStg; it is called automatically when the container calls OleSave to save the object.)

•	Open as many streams as necessary to write the document's native data.

•	Container only: If the document contains objects, save them as child IStorage objects of the opened IStorage instance.

•	Object application only: If the object application loads all of its native data into memory, it can release the pointer to its open IStorage object. However, a container, even if it loads all of its native data into memory, cannot release its IStorage pointer because it must keep the storage object open at all times so its loaded embedded objects have access to their storage.

•	Reset the document's dirty flag.


Setting the Filename of the Document

An OLE object application started with /Embedding does not have a File Save As command; instead, the File Save As command is replaced with the File Save Copy As command. In a File Save Copy As operation, the application does not reinitialize the document object to the file in which the document is being saved, because this would break the connection with the object's container application. This model is different from OLE 1.


Opening an IStorage Object

IStorage objects can be opened in one of two access modes: direct or transacted. In direct mode, the changes to the IStorage are committed immediately with no chance of undoing the changes. In transacted mode, the IStorage is opened in a buffered state, whereby changes are saved to temporary files until they are committed with a call to IStorage::Commit.

The access mode can be combined with the ability to read and write to the IStorage, and to deny others the right to do the same.

StgCreateDocfile uses its grfMode parameter (a set of flags) to control how the IStorage object is created and subsequently accessed. Valid values for grfMode are taken from the following constants:

#define STGM_DIRECT	0x00000000L

#define STGM_TRANSACTED	0x00010000L

#define STGM_READ	0x00000000L

#define STGM_WRITE	0x00000001L

#define STGM_READWRITE	0x00000002L

#define STGM_SHARE_DENY_NONE	0x00000040L

#define STGM_SHARE_DENY_READ	0x00000030L

#define STGM_SHARE_DENY_WRITE	0x00000020L

#define STGM_SHARE_EXCLUSIVE	0x00000010L

#define STGM_PRIORITY	0x00040000L

#define STGM_DELETEONRELEASE	0x04000000L

#define STGM_CREATE	0x00001000L

#define STGM_CONVERT	0x00020000L

#define STGM_FAILIFTHERE	0x00000000L

The grfMode parameter values are meant to be used together. Only one value from each of the five groups of flags can be specified at one time. As an example, the following combination of flag values opens the IStorage in transacted mode, with read and write access granted to the caller and write access denied to all other objects:

STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_DENY_WRITE

Except for the root IStorage, each IStorage object and its contained IStream object(s) are always opened in the context of some other open IStorage object. Child IStorage objects inherit the access mode of the parent; thus, the child IStorage object can only be opened in a more restrictive mode.

Writing OLE Data to IStorage

OLE uses one or more IStream objects for saving information about the document in the root IStorage object. This information includes the object's CLSID, which is written to the root IStorage object using WriteClassStg. Note that an object application launched to service an embedded object does not save the object's CLSID. The object's container saves this in an IStream of the object's IStorage element. OLE also saves the moniker of a linked object, the Clipboard data format of the document's native data, and the user-type name by calling the WriteFmtUserTypeStg function.

Writing Native Data to IStorage

Before it can write its native data to an IStream instance in the root IStorage object, an application must create a stream by calling the IStorage::CreateStream method. After creating the IStream object, the application calls the IStream::Write method to write its data to the IStream instance.

Saving Compound-Document Objects to IStorage

Consider the compound document illustrated in the following figure. The root IStorage, pStg1 contains nested embedded objects, which also contain nested embeddings. For simplicity, the streams are represented by the small circles.

Saving a compound-document object.

To save its contained embeddings, the top-level container must do the following tasks:

1.	Call OleSave on the IStorage object of each immediate embedded object, passing the IStorage object a pointer to where the object is to be saved. The object then recursively calls OleSave on any of its nested embeddings.

OleSave first calls WriteClassStg to save the object's CLSID before calling the object handler's IPersistStorage::Save method. (Usually, the object handler is the OLE-provided default handler.) The default handler's implementation of IPersistStorage::Save first saves the object's presentation cache and then calls IPersistStorage::Save on the running object to save the native data.

Finally, OleSave calls IStorage::Commit on the IStorage object to commit the changes back one level to the transaction state of the root IStorage object.

2.	After the return from OleSave, the top-level container must call IPersistStorage::SaveCompleted on the immediate embedded objects to return them and any of their nested embeddings to the normal storage mode. Unless this is done, the object is left in the no-scribble mode it was put in when OleSave called IPersistStorage::Save. Thus, SaveCompleted should be called immediately on the object after the return from OleSave to put the object and any of its nested embeddings back in the normal storage mode with full read and write access.

Note  IPersistStorage::SaveCompleted is always called on the IStorage object that contains the object; it is a call that recurses to all currently loaded or running embedded objects. Should SaveCompleted fail, the object(s) does not return to normal storage mode. In this case the container should remove any changes that have been committed and instead try to save the object following the recommendations for saving an object in low-memory situations. See Saving Objects in Low Memory.

Saving a linked object is no different from saving an embedded object. To save a linked object that is not loaded or running, you can use IStorage::CopyTo. When an object is in the loaded or running state, calling OleSave saves all changes.

OleSave and IPersistStorage::Save use the fSameAsLoad flag to determine whether the storage object that the object is being saved to is the same one from which the object was loaded. The value of fSameAsLoad can be combined with the value of the pStg passed to these functions to determine the save routine being used to save the compound document, as shown in the following table:

Value of fSameAsLoad	Value of pStg passed to IPersistStorage:: SaveCompleted	Save Routine to call
TRUE	NULL	File Save
FALSE	pStg	File Save As
FALSE	NULL	File Save Copy As
A container should save both the document's native data stream and any information on behalf of the compound-document object, including the following data:

Data Saved on Behalf of a Compound-document object	Description
m_szStgName	Stores the name of object's persistent IStorage element.
m_fMonikerAssigned	Keeps track of whether or not a moniker was assigned to the compound-document object.
m_dwAspect	Determines how to display the object when it is later loaded into memory: as an embedded object, a thumbnail representation (appropriate for browsing tools), an iconic representation, or as a sequence of pages (as if the object had been printed).


Handling Monikers

Saving an untitled document requires that the application revoke the temporary file moniker created and registered for the untitled document, if any.

If a linking container established links to an object application's untitled document and then shuts down before the object application saves the untitled document, the links to the untitled document are broken. To repair the links, the user needs to use the Edit Links dialog. For this reason, some applications might not allow linking to untitled documents.

Notifying Linking Clients of File Moniker Changes—Object application Only

When the file moniker to a document changes, OLE object applications need to notify all registered linking clients. A linking client must have previously registered to receive change notifications by calling the IOleObject::Advise and/or IDataObject::DAdvise methods, as appropriate. To notify the linking clients, the object application calls the OLE-provided IOleAdviseHolder::SendOnRename method.

Notifying Pseudo Objects of Moniker Change—Object Application Only

Just as they notify linking clients of a change in the document's file moniker, OLE object applications that support linking to pseudo objects must notify the registered linking clients that the pseudo object's file moniker has changed. Again, the application can use the OLE-provided implementation of the IOleAdviseHolder and IDataAdviseHolder interface to notify the linking clients of the file moniker change.

Notifying Compound-Document Objects of File Moniker Change—Containers only

A container that supports linking to its embedded objects must notify each of its linking containers that the file moniker used to represent its document has changed, either when the document is saved for the first time or when it is saved to a new file. Otherwise, all links to the container's embedded objects are broken.

To notify an embedded object of its document's moniker change, containers call the IOleObject::SetMoniker method, passing in the new moniker and a flag that specifies the name of the object's container has changed (OLEWHICHMK_CONTAINER). Calling IOleObject::SetMoniker on a running object forces the object to revoke its currently registered moniker from the running-object table and register the new moniker.


Resetting the Documents Dirty Flag
Resetting the Document's Dirty Flag

After saving the document, an application resets its dirty flag on the document to FALSE, unless the document is being saved as the result of a File Save Copy As operation, the flag is not reset.


File Save Command

Saving a document with the File Save command follows the same logic as File Save As. Except when saving documents with File Save, the application does not need to prompt the user for the name of the file or call the routines that create and register new monikers.

To save a document using the File Save command, OLE applications perform the following tasks. For a discussion of these tasks, see File Save As Command.

•	Open the specified file in which to save the document as a compound file.

•	Open as many IStream instances as needed to save the OLE information to the document's root IStorage object, including the CLSID, user-type name, and Clipboard data format.

•	Save the document's native data to the root IStorage object.

•	If the document contains objects, save them as child IStorage instances to the open root IStorage object. After saving all objects, call IPersistStorage::SaveCompleted on each IStorage object to return it and any of its nested embeddings to the normal storage mode.

•	Reset the document's dirty flag.


Saving to Temporary Files

A document saved to a compound file can implement its save functionality by doing a complete save to a temporary file residing in the same directory as the original. After the temporary file has been created, the original file is deleted, and the temporary one is renamed using the name of the original. As far as the object is concerned, the process of saving documents this way works in three steps:

1.	The object's container calls IPersistStorage::Save. This call temporarily removes the object's right to scribble into its storage; putting the object into the no-scribble storage mode. While in this mode, the object cannot write to its storage object, although it can read from it. Thus, the duration of the no-scribble storage mode should be kept to the absolute minimum.

2.	The container does whatever it needs to do to safely retain the changes the object saved. Usually, containers call IStorage::Commit.

3.	The container returns the object to normal storage mode by calling IPersistStorage::SaveCompleted.

Notice that in either normal or no-scribble storage mode an object can hold on to its open IStorage instance. With OLE compound files, the object application also has the actual disk file open. Unfortunately, under MS-DOS, a file cannot be deleted or renamed while is open, which prevents the application from doing a full save operation. To solve this problem, an object can be put into the hands-off storage mode, which is similar to but somewhat more severe than the no scribble storage mode. In this mode, the object must release the IStorage instance it owns. If the object is also an OLE container, it recursively causes any of its loaded or running objects to enter the hands-off storage mode. When the object returns to the normal storage mode, its container gives the object back its storage pointer.

To put an object into the hands-off storage mode, the container calls the IPersistStorage::HandsOffStorage method. To return the object to its normal storage mode, the IPersistStorage::SaveCompleted method is called with a pStg to the object's IStorage instance. The contents of the just-passed pStg are guaranteed to be identical to the contents of the pStg that the object had but which were revoked in the call to IPersistStorage::HandsOffStorage. Thus, the object does not have to do a full reload; it only needs to re-open any substreams in the normal storage mode.

Note  Calling IPersistStorage::Save always causes the object to enter the no-scribble storage mode. This guarantees that low-memory saves work while ensuring efficiency in the high-memory cases. Thus, every call to IPersistStorage::Save must be followed by a call to IPersistStorage::SaveCompleted to return the object and any of its child IStorage objects to the normal storage mode. In addition, the container can make an intervening call to IPersistStorage::HandsOffStorage, which is useful for implementing the application's File Save As routine.

From the perspective of a client of an object, the hands-off storage mode is very similar to the no scribble storage mode. In neither mode can the client expect anything at all to work on the object. From the object's perspective, in no scribble storage mode the object can read but not write to its IStorage instance; in hands-off storage mode, it clearly can no longer read from the IStorage.


Saving Objects in Low Memory

Often an object is asked to save itself in low-memory situations. This can be thought of as doing a File Save As operation. For example, suppose there is a root document in a file and all the changes to its embedded objects have been saved, but not yet committed at the document level, as shown in the following figure:

State of an unsaved object in a low-memory situation.

Here, the uncommitted changes are simply those written to the compound file that have yet to be committed. In low-memory situations, an application must be guaranteed a way to save its compound document to a new base file without consuming any new memory, as shown in the next figure.

Saving objects to a new base file in low-memory situations.

The new base file is a copy of the original file. The uncommitted compound file changes are now associated with the new file; the old file is inaccessible. If IStorage::Commit is now called, the changes go to the new file. In this situation, IStorage::Commit is guaranteed to work without consuming additional memory.

When you are saving compound documents in this manner, it is recommended that any loaded or running embedded objects be in the hands-off storage mode, which simplifies the implementation of this low-memory save operation in compound files.

When saving an object in low-memory situations, the following interface methods are guaranteed to work:

IStream::Read
IStream::Write

IStream::SetSize
IStream::Seek

IStream::Commit
IStream::Revert

IStream::AddRef
IStream::Release

IStorage::QueryInterface
IStorage::SetClass

IStorage::SetStateBits
IStorage::Commit

IStorage::Revert
IStorage::AddRef

IStorage::Release
IRootStorage::QueryInterface

IRootStorage::AddRef
IRootStorage::Release

IRootStorage::SwitchToFile


Full Save Operations

To do a full save of a document, an application completes the following steps:

1.	The object creates and opens a temporary destination file in direct mode.

2.	The container then creates the appropriate IStorage instances for each object, also using direct mode for these storage objects.

3.	The container then asks each loaded or running embedding to save to the new storage by calling IPersistStorage::Save (pStg, FALSE).

4.	If the object's IStorage instance was opened in transacted mode, the container then writes the root level document's data to the compound file and flushes the root storage object by calling IStorage::Commit.

If any of these steps fail for low-memory reasons, the container should back each object out by calling IPersistStorage::SaveCompleted (NULL) and try to save the document using the sequence of steps for saving in low-memory situations.

5.	Once the data is in the new file, the container calls IPersistStorage::HandsOffStorage on each object and releases the IStorage instance that the root document is holding on to. This then allows the container to delete the original file and to rename the new one to be the old name.

6.	The container reloads each object by calling IPersistStorage::SaveCompleted (pStgNew).

If any errors occur after the data is saved in the new file but before completion, error handlers need to note that some objects are either in the no-scribble or hands-off storage mode and need to be returned to the normal storage mode eventually.


Doing a Full Save in Low Memory

To do a full save in low memory, a container does the following steps:

1.	Ask each object to save to its current IStorage by calling IPersistStorage::Save(pStg, TRUE). Objects are responsible for ensuring that this operation works without consuming additional memory.

2.	Commit the storage of each of the objects by calling IStorage::Commit to percolate the changes out to the next transaction level.

3.	Write any changes made to the root-level document to the appropriate streams immediately contained within the root IStorage instance.

4.	Put each object in the hands-off storage mode by calling IPersistStorage::HandsOffStorage on the object.

5.	Close any streams the root-level document might be holding on to.

6.	Call IRootStorage::SwitchToFile(lpszNewFile) to switch the files. At this point, the original file is closed because the caller has switched files.

7.	Call IStorage::Commit on the root IStorage instance to commit the new file. This Commit can safely be done using STGC_OVERWRITE, which reduces the size of the final file.

8.	After the data is in the new file, delete the original file before renaming the new one to the old name. Then reattach the objects by calling IPersistStorage::SaveCompleted(pStgNew).

9.	Reopen any root-level streams.

The error handling cautions for doing a full save in high memory apply to the low-memory case as well.

The Save As case is the same, except the container need not bother with deleting the original file and renaming the new one.


Backing Out of a Save As

In the File Save As sequence, a container recursively calls IPersistStorage::Save followed by a call to IPersistStorage::SaveCompleted on its contained embedded objects. However, suppose the Save As operation cannot be completed. In this case, some of the objects will be holding on to a storage object in the new location, while some are holding on to the old location. There are two things a container can do to get the objects that have been saved to back out:

1.	The container can ask these objects to "Save As" back to their original location. This is slow, but might be an acceptable solution for the container, especially if this scenario happens infrequently.

2.	The container can close and reload the object from the original location. If the object is loaded, this has no effect visible to the user; however, if the object is running, it closes the object on the user's screen.


Incremental Save Operations

Incremental saves are somewhat less complicated. The presence of transactioning and not needing a new temporary file makes this type of save easier. Incremental saving assumes that the root IStorage object is open in transacted mode.

To do an incremental save, containers do the following tasks:

1.	Ask each object to save to its current IStorage by calling IPersistStorage::Save (pStg, TRUE). This is guaranteed by contract with the object not to fail for lack of memory.

2.	Commit the storage of each of the objects by calling IStorage::Commit to percolate the changes out to the next transaction level; compound files are guaranteed that this works in low memory.

3.	Write any changes made to the root-level document to the root IStorage instance. As with objects, the root document ensures this works in low memory.

4.	Call IStorage::Commit on the root IStorage instance to commit the compound file.

5.	Return the object to its normal storage mode by calling IPersistStorage::SaveCompleted (NULL).


Supporting Link Updating

Applications that act as file-level link sources can better handle all cases of manual links and nearly all cases of automatic links by setting the change time in the running object table to the time of the file's last save. The following pseudo code can be implemented after the save:

if registered in the running object table

get time of saved file in FILETIME format

get a pointer to the running object table (pROT)

call pROT->NoteChangeTime(dwRegister, &filetype)

call pROT->Release()

To get the time of the saved file:

hFile = CreateFile(<filename>, GENERIC_READ,

FILE_SHARE_READ|FILE_SHARE_WRITE,

NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, NULL)

GetFileTime(hFile, NULL, NULL, ptfLastWrite);


Close Handle Saving Data to Compound Files Using Transacted Mode

When an application is repeatedly saving (writing then committing) data to compound files in transacted mode, the size of the resulting file may not reflect the actual amount of data written to the file. This is because in transacted mode, the new data does not immediately overwrite the old data in the file in order to allow changes to be discarded and reverted back to the original, intact file, according to the transactioning definition.

This concept is illustrated by a simple example. Consider a document being saved to a compound file; after the save operation, the file size is approximately 50K, as shown below:

The black and white blocks represent the internal structures of the compound file which help support transacted mode. these blocks form a chain that refers to the most recently committed data in the file.

If the document is now rewritten into the same file, the new data will be written at the end of the file along with a new reference block (small white block, below); a final commit operation will automatically update the references in the master reference block (black block, below) to point to the new data as well as to the now unused area where the previously saved data lay. This file is now about 100K, of which only 50K is used for the document's data.

Now, consider a third save operation on the same file where the data is again completely rewritten. This time, the write operation will reclaim the unused area of the compound file (below, A), and shrink the size of the file by discarding the unused portions at the end of the file (below, B). At the end of this save, the size of the file is again 50K, just as it was when it was originally saved.

To avoid problems like this, file formats should be designed so that the application only has to write the data that has changed; transactioning will take care of the robustness issues and will also prevent the file from growing in size. If an application must rewrite the entire contents of the file, it should just save to another file and not use transactioning (saving to a new file guarantees the smallest file possible). Note that using CopyTo to copy an existing root storage in direct mode results in the most compact file possible.


Closing OLE Compound Documents

On shutdown, an SDI application will have only one open document to close while an MDI application might have many documents to close. To close a document, OLE applications do the following tasks:

•	Check the open document(s) for changes. If a file-based document has changed, prompt the user to save the document; otherwise, do nothing. If an embedded object has changed, tell the object's container to save it.

•	Safeguard both the application and document objects from premature closing by incrementing the objects' reference counters.

•	Revoke the registration of the document's moniker from the running object table.

•	If the document is the source for the data on the Clipboard, flush the Clipboard.

•	Hide the document window and unlock the document on behalf of the user. If the application supports drag-and-drop operations, revoke the document as a drop target.

•	Object application only: Force all pseudo objects to close.

•	Object application only: Send a final OnDataChange to all clients that have registered to receive data notifications when the object stops running (ADVF_DATAONSTOP). After sending the notification, release the DataAdviseHolder.

•	Object application only: Send an OnClose advisory notification to all registered containers; after sending it, release the OleAdviseHolder.

•	Container only: Force all loaded embedded and linked objects to close.

•	To guarantee the document object has been freed of all external and pointer references, call CoDisconnectObject.

•	Release the intentional reference count on the document and application objects. When the document object's reference count goes to zero, destroy the object.


Checking Documents for Changes

When closing documents, an application should ensure that its application and document objects are prevented from accidentally closing. To guard the objects, an application increments the reference counts on these objects. The reference counts remain until all other external memory pointers have been released.

Applications begin shutting down by first checking all open documents to see whether there are changes to be saved. If the application was started by the user, it should prompt the user to save any changes made to the document. This model differs from OLE 1, where the application prompted the user to save the object in its container.

If the document is dirty, the object application should check to see whether or not OLE opened the document. If the document was opened by OLE, the object application should call the container's IOleClientSite::SaveObject method to save the object. Otherwise, the object application should prompt the user to save the changes made to the document.


Closing Pseudo ObjectsObject Application Only
Closing Pseudo Objects—Object Application Only

Object applications that support linking to pseudo objects must close the pseudo objects as part of closing the open document(s). Object applications also must send an OnClose notification to all registered linking containers of the pseudo objects, telling them the pseudo objects are being closed. Upon receipt of the OnClose notification, the linking container is automatically disconnected from the pseudo object. The OLE Link object, which is part of the container's object handler, performs the disconnection. To forcefully close any connections to its pseudo object, the object application can call the CoDisconnectObject function.


Closing Loaded ObjectsContainer Only
Closing Loaded Objects—Container Only

To force-close all loaded objects—including links and embeddings—containers can call CoDisconnectObject. The container passes to CoDisconnectObject a pointer to one of the interfaces being maintained on behalf of the object. Usually, this is a pointer to the IUnknown interface.

Note  Care should be taken when calling CoDisconnectObject because it force-closes all objects. An application should not call CoDisconnectObject to routinely disconnect and release an interface pointer to an object; instead, it should call the object's IUnknown::Release method. CoDisconnectObject should be called only by the process that manages the object and only when that process is supposed to exit.


Hiding the Document Window

As the last part of closing a document, the application hides the document's window from the user. At the time of hiding the window, the application needs to revoke the document's registered file moniker from the running object table and take control of the document away from the user. (If the application supports drag-and-drop operations, it also unregisters itself as a drop target.)

As was discussed in Opening Compound Documents, applications that support linking to pseudo objects create and register a file moniker for every document they open. Registering the file moniker places a weak reference lock on the document object on behalf of the registered moniker. Before the document can be closed, this moniker should be revoked from the running object table by calling the IRunningObjectTable::Revoke method, passing it the dwRegister token returned by the IRunningObjectTable::Register method when the moniker was registered.

Note

Note  Registering a moniker in the running object table places a weak reference lock on the document. This lock is not strong enough to keep the document object alive should all the other external connections be released. Still, the application should release the moniker from the table before closing the document.

If the object application activated the object in a window in the object application process space, it must call IOleClientSite::OnShowWindow (FALSE) to tell the container to remove the hatching around the open object.

When no more documents are visible to the user and the application is not under user control, the application should hide its application window. This leaves the application in a hidden, running state. Only when the application object's last external reference is freed does the application actually shut down. This step is critical for MDI applications.


Taking Control Away from the User

If the document was visible to the user when closing on shutdown, the application takes control of the document away from the user. To free this external user reference, the application calls CoLockObjectExternal (pDoc, FALSE, TRUE).


Sending Close NotificationsObject Applications Only
Sending Close Notifications—Object Applications Only

When closing a document, an object application sends a final OnDataChange notification to all clients registered with the ADVF_DATAONSTOP flag, telling them the object has stopped running. After sending the notification, the object application releases its IDataAdviseHolder pointer.

The object application also sends an OnClose advisory notification to all registered containers telling them it is shutting down. The object application then releases its IOleAdviseHolder interface pointer.

For more information on sending notifications, see Managing Notifications.


Releasing Reference Locks

Just as CoLockObjectExternal (pDoc, FALSE, TRUE) was called to release the external reference on behalf of the user, the application must call CoLockObjectExternal (pApp, FALSE, TRUE) to release the lock on the application made when the document was first created.


Closing Scenarios

How an OLE container or object application was started and subsequently used affects the closing of the application. There are three scenarios in which applications can be closed:

•	The user started the container or object application and initiated application shutdown by choosing the File Exit command or the system Close command.

•	The object application was started by means of \Embedding to service a linked or embedded object. Application shutdown was initiated by the container calling the IOleObject::Close method.

•	The object application was started to service a linked or embedded object. Application shutdown was initiated by the user choosing the File Exit command or the system Close command.

The procedure for closing an application started by the user is the same for both object application and container applications; the sequence of shutdown tasks begin with the application object. However, for an object application started with /Embedding, shutdown is initiated by the object's container application and begins with the document object.

The following sections describe each of these scenarios in greater detail.


Closing an Application With the File Exit Command

The next figure shows the state of an MDI object application at the time the user initiated program shutdown by choosing the File Exit command (the application was started explicitly by the user).

Note  While the following figure is representative of an MDI object application, the scenario is also applicable to SDI object applications, except that SDI object applications do not register their class of object(s) in the class factory table.

MDI object application shutdown initiated by user choosing File Exit command.

As shown in the next figure, the application object's reference count is one, which reflects the reference on behalf of the stub manager. The document object also has a reference count of one on behalf of the user opening the document. (If a data transfer object had been created during a Copy/Paste operation, the Clipboard object would also maintain similar reference counts).

Closing an application with the File Exit command starts a sequence of shutdown events beginning with the application object. Upon receiving the WM_CLOSE command, the application object begins shutdown by first closing the open document. After the document has been saved, the document's moniker is revoked from the running object table, the document window is hidden, the document is unregistered as a drop target, and control of the document is taken away from the user. These events cause the document object's reference counter to decrement to zero. Only when the reference count reaches zero can the document be safely destroyed.

After destroying the document object, the application object's reference count maintained on behalf of the document object is released, leaving the object application in the state shown in the next figure.

Note  The scenario described in the next figure reflects an MDI object application with one open document but it is possible there could be multiple document objects to close. Each open document in an MDI application is reflected in the application object's application and document reference counts.

MDI object application after closing open document.

After all documents have been closed, the application object removes control of the application from the user and hides the application frame window. The following figure shows the state of the application immediately after control is removed from the user.

Object application after removing user control.

After calling CoLockObjectExternal (pApp, FALSE, TRUE) to remove control from the user, the application object's reference count is generally zero. When this count reaches zero, the application calls its destructor routine. Because CoLockObjectExternal was called with fLastUnlockReleases set to TRUE, the application object is freed when control is removed from the user and there are no outstanding external references. As part of its destructor routine, the application should call CoRevokeClassObject to release the class object from the class factory table and then release the IClassFactory interface pointer. This destroys the IClassFactory object.


Closing an Embedded Object from its Container

The next figure shows the state of an SDI object application started to service an embedded object. Because the object is visible, the user controls the lifetime of the running embedded object but not the application itself. The embedded object, in turn, holds the application alive.

Application shutdown is forced by the object's container calling IOleObject::Close on the loaded object. (If the embedding container were to simply release the object, the application would not necessarily shut down.)

SDI object application at the time the object's container called IOleObject::Close.

As shown, the document object has a cRef of one on behalf of the document being visible to the user. The application object has a cRef of one on behalf of the open document. Because the user is not in control of the application or document, the fUserCtrl flag is FALSE on the application object.

The embedding container initiates shutdown by calling the IOleObject::Close method on the running OLE object. Closing begins with the open document object instead of with the application object as is done when the user explicitly initiates shutdown by using the File Exit command.

As part of closing, the document object closes all open pseudo and embedded objects before revoking the document's registered moniker from the running object table. Only when all external connections are disconnected does the document object receive its final release. To release the reference, the document object sends an OnClose notification to the container, telling it the document is being closed. The OnClose notification is first received by the object handler in the container's process. Upon receipt of the notification, the handler disconnects its RPC connection to the running object application document, and then forwards the notification on to advisory sink in the container. On receiving the OnClose advisory, the container should revert the OLE object's IStorage to flush any uncommitted changes.

In conjunction with sending the OnClose notification, the document object calls CoDisconnectObject to forcefully disconnect any existing external connections to the document. Once the document object's reference count goes to zero, the document object's destructor routine is called and the document is destroyed.

Note  Because any changes made to the open document would have already been saved by the time of sending the OnClose notification, an object application can safely call the CoDisconnectObject function to release all references to the object.

After the document object has been destroyed, the application object's cDoc reference count maintained on behalf of the document object is released. If this was the only open document and the user was not in control of the application, the application would be in a state similar to that shown in the figure in Converting Mapping Modes , with one cRef reference count remaining on behalf of the registered class object. To release its class object, the object application calls the CoRevokeClassObject function as part of calling its destructor routine.


MDI Object Application Considerations

If the user creates a new untitled document or opens a file-based document in an MDI application started to service a linked or embedded object, the application object sets its fUserCtrl flag to TRUE. On a shutdown initiated by the container application calling IOleObject::Close, the loaded OLE object would first be closed. After the document object's destructor routine is called, control of the shutdown process is passed to the application object. This leaves the application in a state similar to that of an application started by the user . Because the user is in control, the application won't shut down until the user chooses the File Exit or system Close commands.


Closing Data Transfer Objects

How the data transfer object is destroyed depends on how application shutdown was initiated: by the user choosing the File Exit command or by the container calling IOleObject::Close on its loaded object. If the object is being closed by the user, the object closes the Clipboard data object and flushes it of any data pertaining to the application.

When a container calls IOleObject::Close to initiate shutdown on an object application document that is the source for a data transfer object, the object application's document object flushes the Clipboard of data pertaining to the document.

To empty the Clipboard, an application calls OleFlushClipboard if it is desirable to have all hGlobal-based formats and OLE 1 compatibility formats remain on the Clipboard. Otherwise, OleSetClipboard (NULL) can be called.


Closing OLE Applications

As part of application shutdown, OLE containers and object applications do the following tasks. These tasks are described in the following sections.

•	Safeguard the application against premature shutdown by incrementing the reference count on the application object.

•	Close all open documents. For more information on closing open compound-document objects, see Closing OLE Compound Documents.

•	Flush the Clipboard of any data object pertaining to the application. (Object applications usually flush the Clipboard when closing the document that is the source of the Clipboard data.)

•	If the user had control, release the external reference lock on behalf of the user and hide the application frame window.

•	 To ensure that all references to the application have been freed, call CoDisconnectObject on the application object.

•	When the reference count on the application object reaches zero, destroy the application object, freeing all application-specific structures from memory.

•	Close the OLE libraries.


Safeguarding the Application Object

On initiating application shutdown, applications they are not exited until all shutdown procedures have been done. To safeguard itself, the application can increment cRef, which controls the lifetime of the application. Only after all other references have been released does the application release the reference count.


Hiding the Application Frame Window

If the user has control of the application after the document and Clipboard data transfer objects have been closed, release user control of the application by calling CoLockObjectExternal (pApp, FALSE, TRUE) on behalf of the user. After releasing this last external reference on behalf of the user, hide the application frame window from the user for the remainder of the shutdown process.

When CoLockObjectExternal is finished, the application's destructor routine is called. As part of its destructor's routine, the application should revoke the registered class factory object from the class factory table. This is done by calling CoRevokeClassObject, passing in the dwRegister token returned by CoRegisterClassObject.


Closing OLE Libraries

After the application has exited its main GetMessage loop, the final step in the shutdown process is to close the OLE libraries initialized at program startup.

To close the libraries, an application calls the OleUninitialize function.

To guard against OleUninitialize being called if OleInitialize was never called, applications can set a flag as part of initializing the libraries. Before calling OleUninitialize, the application checks the flag to ensure that the libraries were properly initialized.


Converting Mapping Modes

In Windows, mapping modes define how numbers relating to object sizes are to be passed and interpreted. The 32-bit version of OLE uses only one mapping mode, MM_HIMETRIC. The other Win32 mapping modes include MM_HIENGLISH, MM_LOENGLISH, MM_LOMETRIC, TWIPS, and PIXEL Modes (MM_ISOTROPIC and MM_ANISOTROPIC being PIXEL modes). When working with the visual presentation of OLE data, it is important to be aware of the mapping mode that OLE uses and how this mapping mode affects an application.

The mapping modes communicate physical sizes. For example, if an application using MM_HIMETRIC is to display a line ten centimeters long, the number of units would be 10,000. However, the line drawn on the screen would be ten centimeters long, regardless of the size of the video display area. The printed output would also be a line ten centimeters long.

Note  For those applications that use a mapping mode other than MM_HIMETRIC, the sample user interface library provides some functions that can be used to convert objects to and from MM_HIMETRIC units.

Because people read display screens from a greater distance than they do printed copy, most applications written for Windows display text in a larger size than they print it, using what is commonly referred to as logical resolution. For example, a ten-point font is easy enough to read on the printed page, but generally appears too small on a screen for comfortable reading. To afford more comfortable viewing, applications typically expand the size of the displayed text to some logical size. Using this approach, a column of text that is physically six inches wide might be eight inches wide on the screen, yet still print as a six-inch column.

While this display-enlargement scheme works well from the user's point of view, a problem can occur when pasting objects into container documents. It is possible to lose the correct size ratio between the pasted object and the text owned and displayed by the container. The result is that the container's text is scaled up for readability but the pasted object might not be. Consequently, applications must preserve the relative size and position of text and objects, meaning that if text uses logical resolution, it should scale objects accordingly.

In the examples shown in the figure below, a chart object has been pasted from a source application that uses physical size into documents of two different containers that use logical resolution for display of the text. The container displaying the object on the left has scaled up the chart object to the logical size of the adjacent text to maintain the object/text size ratio. That is, it has been enlarged from its physical size by an amount that maintains its proportion to the text of the container document. In the document on the right, the application displays the chart object at its physical size, with no scaling to logical resolution for display. Both documents print with the correct object-to-text-size ratio.

Object sized in logical and physical resolution.

In OLE, the units for specifying the size of drawn objects is MM_HIMETRIC, which means object sizes are in physical units. However, containers need not use the MM_HIMETRIC mapping mode to draw pasted objects to the display, but should rather map objects to the screen in the same manner as text. That is, if the container application displays text to the screen using a mapping mode that enlarges it, objects should be mapped to the display in the same manner. Using the same mapping mode for both the text and objects is required to establish the correct object-to-text ratio as shown in the document on the left side of the preceding figure. Because most Windows applications use logical resolution for this type of display mapping, it is suggested that OLE containers also use logical resolution and set up their mapping modes and coordinate transforms accordingly. This allows objects to be moved from one application to another without changing their displayed size.


OLE: Custom Interfaces

If you are an OLE developer, you have already implemented and used objects that communicate through Component Object Model (COM) interfaces. If you are new to OLE, you are about to discover an exciting and useful technology. Calls to these COM interfaces can cross process boundaries as long as both processes are running on the same machine. These interfaces can be the standard ones provided by OLE or new ones you design and implement for your specific application.

The Custom Interface technology lets you create new, unique interfaces. Where standard COM interfaces specify a contract between the interface implementor and its user that applies to every component object, custom interfaces let you define a contract tailored to fulfill specific needs. Custom interfaces are extensions to the COM standard; they let you extend the standard behavior and still take advantage of the services provided by the base interface. Anyone can write a custom interface, as long as it inherits from IUnknown and does not include asynchronous methods. Once the interface is written, anyone can use it.

This section introduces the Custom Interface technology and explains how to write custom interfaces. OLE experience is a prerequisite, as is a basic knowledge of RPC. For more information about OLE and COM technology, see Inside OLE 2, a detailed guide to writing OLE applications. The latter also contains complete descriptions of the standard COM interfaces and related functions. Refer to the RPC Programmer's Guide and Reference for information about the RPC programming environment.


How Do Client/Server Objects Work?

When a client connects to a server object, the name of the server is stored in the system registry. In 16-bit OLE, the server is either an in-process DLL or a local executable. A new component, the Service Control Manager (SCM), now locates the server and runs it. Connecting to the Server, explains the role of the SCM in greater depth.

Making a call to an interface method in an object in another process involves the cooperation of several components. The 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 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 server. It also packages reply information to send back to the client.

Note  Readers more familiar with RPC than OLE may be used to seeing the terms client stub and server stub. These terms are synonymous with 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. Note that the channel is part of the COM library. The channel sends the buffer containing the marshalled 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.

Components of interprocess communications.

The following sections discuss some of the issues involved in Custom Interface technology and describe these components in greater detail.


Connecting to the Server

The SCM ensures that when a client request is made, the appropriate server is connected and ready to receive the request. The SCM keeps a database of class information based on the system registry that the client caches locally through the COM library.

Depending on the type and location of the server, the SCM plays a slightly different role. When the server is an in-process DLL, the SCM simply passes the name of the DLL to the COM library. The COM library then loads the server and makes the method call. When the server is an executable in another process, the SCM either creates the server process and starts the server or contacts it directly if it is already running.


Marshaling
Marshalling

Marshalling is the process of packaging and unpackaging parameters so that a remote procedure call can take place. Different parameter types are marshalled in different ways. For example, marshalling an integer parameter involves simply copying the value into the message buffer. Marshalling 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 marshalled, the data that the pointer is pointing to is copied. Unique functions exist to handle the marshalling of each parameter type.

For interface pointers, there are two types of marshalling: standard and custom. The server object selects the marshalling method for its set of interfaces as a whole; it cannot custom-marshal some and standard-marshal others. Standard marshalling uses proxies and stubs either provided by OLE, as is the case for many standard OLE interfaces, or generated by the interface designer, as is the case for many custom interfaces. If the server object uses standard marshalling, the proxies and stubs are loaded from a proxy/stub DLL. The appropriate DLL is located via the system registry. Standard interfaces use the standard proxy/stub DLL, OLE2PRX32.DLL. Custom interfaces use other proxy/stub DLLs

Custom marshalling is outside the scope of this section so this discussion is brief. Custom marshalling uses proxies and stubs implemented by the server object. Server objects that choose to custom-marshal must implement the IMarshal interface, whereas server objects that support standard marshalling do not. For complete documentation of IMarshal, see Component Object Interfaces and Functions.Standard marshalling is the default method for interface pointers. However, because multiple objects may implement a single interface any number of ways, standard marshalling may not be sufficient for a particular implementation. Therefore, any object may override the default and provide custom marshalling for its interfaces. Other implementations of the interface are not affected.

Marshalling support is provided for all the standard COM interfaces. If you decide to write a custom interface, you must provide marshalling support for it. You can, however, use standard marshalling. This allows you to use tools contained in the Win32 SDK CD to create the proxy/stub code and the proxy/stub DLL.


Proxy

The proxy's role is to package the interface parameters for its server interfaces into a message buffer and pass the buffer onto the channel, which handles its transport between processes. The proxy is implemented as an aggregate, or composite, object. It contains a manager piece called the proxy manager and one or more interface-specific pieces called interface proxies. The number of interface proxies equals the number of server object interfaces that can be transported. To the client, the proxy appears to be the real server object.

Each interface proxy implements one of the server object's interfaces and IRpcProxyBuffer. Whereas the server interface implementation 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 IUnknown. Each interface proxy can exist in a separate DLL that is loaded when the interface it supports is transported.

The following diagram shows the structure of a proxy that supports the  marshalling of parameters belonging to two interfaces: IFoo1 and IFoo2. Each interface proxy implements IRpcProxyBuffer. When the proxy is ready to pass its marshalled 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 server.

Structure of the Proxy.


Stub

There are a number of similarities between the stub and the proxy. The stub, like the proxy, is made up of one or more interface pieces and a manager. Each interface stub has an implementation of one of the server's supported interfaces and an interface for internal communication. The stub manager keeps track of the available interface stubs.

There are also many differences between the stub and the proxy. The most important difference is that the stub is instantiated in the server process rather than in the client process. The stub is not implemented as an aggregate object; each piece in the stub is a separate component. The server object interface implementations are private, rather than public. The interface stubs implement IRpcStubBuffer, not IRpcProxyBuffer. And last, instead of packaging parameters to be marshalled, the stub unpackages them after they have been marshalled and then packages the reply.

The following diagram shows the structure of the stub. Each interface stub is connected to an interface on the server 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.


Microsoft RPC

Microsoft's implementation of RPC is compatible with the Open Software Foundation (OSF) Distributed Computing Environment (DCE) version. You can configure RPC to use one or more transports, one or more name services, and one or more security servers. 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. Refer to Writing a Custom Interface 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 Building a Proxy/Stub DLL.


Writing a Custom Interface

In order to perform the tasks discussed in the following section, you will need to obtain a Microsoft Win32 Software Development Kit (SDK). The Win32 SDK CD contains a toolkit and all of the libraries you need for building a custom interface project. A sample code project shows how to write a custom interface using the MIDL compiler. Also included in the project is the client code that uses each custom interface, as well as the server code that implements it. The SAMPLE1 server displays its time and date, and the version of ICustomInterface in SAMPLE1 derives directly from IUnknown.

After installing the toolkit, you will design and write your custom interface using the MIDL compiler, and build the proxy/stub DLL for your interface from the files generated by the compiler. You will then install the DLL in the system registry.


Designing Efficient Interfaces

There are some special considerations to keep in mind when designing a custom interface. If you follow these guidelines, your marshalling code can be used across the network when support for remotable objects is provided in a future release. First of all, because data is shipped across address spaces, using architecture-dependent types such as int may prohibit the data from being re-created correctly in the server's address space. Specifying data types unambiguously to the MIDL compiler will allow your interface to be more easily transferred to remote machines with different architectures and addressing schemes.

The datatype int should not be used for structure data members or interface method parameters. Because the size of an integer varies from architecture to architecture, the MIDL compiler requires all integer variables that may be remoted to be explicitly declared as short, long, or their unsigned equivalents.

A frequently used data type for pointers, the void * construct, allows the callee of a method to interpret the data pointed to according to the need. While local interfaces can use this construct, distributed applications cannot. This is because the MIDL compiler must know exactly what types of data are being transmitted so that the data can be accurately re-created on the receiving side. The void * construct is too vague.

Pointers to data must be used carefully. To recreate the data in the callee's address space, RPC 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 (as is implied), 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 non-aliased should never alias each other.

The MIDL has directional attributes that let you specify the data flow direction. The proxies and stubs use these attributes to determine whether to send the data from the client to the server or from the server to the client, respectively. Data labeled as out only is uninitialized on the way to the interface stub; data labeled as in only does not affect the data structures upon return. The in, out attribute indicates that data is sent to the server initialized and the server will change it before sending it back.

It is also imperative that you realize the importance of defining status codes. This must be coordinated by the definer of the interface to avoid conflicting error codes. This is explained in Component Object Interfaces and Functions, particularly in the sections beginning with Error Handling Functions and Macros."


Creating a Custom Interface

Writing an application using a custom interface is similar to writing an OLE application using standard interfaces, with two important differences. First, in addition to registering the class identifier (CLSID) for your server, you also must register the universally unique identifier (UUID) for your custom interface. Second, you must use the MIDL compiler to generate code to create the proxy/stub DLL for your custom interface.

As an OLE developer, you should already be familiar with the concept of using CLSIDs and interface identifiers (IIDs) to uniquely identify class objects and interfaces. The MIDL compiler uses UUIDs to uniquely represent its interfaces. Every custom interface must have a UUID; the UUIDGEN.EXE tool provided with the toolkit can create one for you.

MIDL is a rich, complex language that allows you to define interface parameters carefully in terms of their direction and type. Although the MIDL compiler offers much, you will need only a small subset of its attributes to define your interface.

The IDL file consists of two parts: the interface header and the interface body. The interface header, delimited by brackets, contains information about the interface as a whole, such as its UUID. A prerequisite to completing the interface header is running the RPC utility UUIDGEN.EXE to create the UUID. Because UUIDs must be unique, never reuse one of them by copying it from one IDL file to another. The header also contains the keyword object, indicating that this interface is a COM-style interface (that is, derived from IUnknown). Whereas standard RPC interfaces have the version attribute in their interface headers, object interfaces do not.

Caution  IDL filenames are six or fewer characters and have the extension .idl. Assign your names carefully: if you use the default compiler settings that generate files with names based on your IDL file name, you may end up with a file with the same name as one of your other project files. When this happens, your project file is overwritten.

This excerpt is the interface header from the SAMPLE1 IDL file, ITF.IDL:

[

    object,

    uuid(7ACC12C3-C4BB-101A-BB6E-0000C09A6549),

    pointer_default(unique)

]

The uuid attribute precedes the unique identifier for the interface. The pointer_default attribute specifies the default IDL type for all pointers except for those included in parameter lists. Parameter list pointers must be explicitly declared with the pointer attribute. The default pointer type may either be unique, ref, or ptr. For more information on using IDL with pointers, see the RPC Programmer's Guide and Reference.

The interface body contains declarations for data members, prototypes for all methods, and other information such as directives to the preprocessor and include statements for other IDL files. The following sample is the interface body for ICustomInterface:

interface ICustomInterface : IUnknown

{

    import "unknwn.idl";

    HRESULT CustomReport(void);

}

The import attribute allows an interface to reference constructs defined in other IDL files. Because ICustomInterface derives from IUnknown, the UNKNWN.IDL file must be included. ICustomInterface has only one method that takes no parameters; few interfaces will be this simple.

Building a Proxy/Stub DLL

Completed IDL files must be compiled with the IDL compiler, MIDL.EXE. The IDL compiler takes each IDL file and generates proxy/stub code (IDLNAME_P.C), an interface header file (IDLNAME.H), and an interface identifier file (IDLNAME_I.C). The header files contain both C and C++ class definitions.

By default, MIDL.EXE generates names based on your IDL file's name. However, you can use the following command line switches to override the default:

Command line switch	Description
-header	Specifies the name of the interface header file.
-proxy	Specifies the name of the proxy source file.
-iid	Specifies the name of the interface identifier file.
After a successful compile of the IDL file, the generated files are run through the standard C/C++ compile and link steps. These source files implement helper functions for marshalling and an implementation of DllGetClassObject for the proxy/stub libraries, among other things. At present, you need to include them in their build; eventually they will be bundled into a library.

The following diagram shows all the pieces involved in a build of a custom proxy/stub DLL. The IDL file, ITF.IDL, is fed into the IDL compiler. Three files are generated: ITF_P.C, ITF.H, and ITF_I.C. These three files are compiled and linked and the result is PROXSTUB.DLL.

Build Process for Proxy/Stub DLL.

The sample code provides a series of makefiles that you can use for building your proxy/stub DLL. The sample-code build process involves four passes; in the future, this process will be simplified.

Registering a Proxy/Stub DLL

Before a client can use your custom interface, the proxy/stub DLL for the interface must be installed in the system registry. Registering the proxy/stub DLL involves creating a .REG file and running REGINI. The following entries are taken from the sample code's CUSTOM32.REG file. Notice that the UUID for ICustomInterface is the same as the CLSID for the proxy/stub DLL.

 \Registry\MACHINE\SOFTWARE\Classes\Interface\

{7ACC12C3-C4BB-101A-BB6E-0000C09A6549}

    = ICustomInterface

\Registry\MACHINE\SOFTWARE\Classes\Interface\

{7ACC12C3-C4BB-101A-BB6E-0000C09A6549}\ProxyStubClsid32

    = {7ACC12C3-C4BB-101A-BB6E-0000C09A6549}

\Registry\MACHINE\SOFTWARE\Classes\CLSID\

{7ACC12C3-C4BB-101A-BB6E-0000C09A6549}

    = ICustomInterface_PSFactory

\Registry\MACHINE\SOFTWARE\Classes\CLSID\

{7ACC12C3-C4BB-101A-BB6E-0000C09A6549}\InprocServer32

    = proxstub.dll


Using a Custom Interface

The client code is the user of the custom interface. To use any interface, custom or standard, a client must know its UUID. The following function, CustomRpt, is from MAIN.CXX in the SAMPLE1 client directory. 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 server 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 server function that displays the time and

    // date information on the server.

    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;

}


OLE Registry Information

Object applications must register in the Windows NT Registry. This section discusses registering object applications, as well as describing OLE registry entries and the subkeys under which they are entered.

The subkeys are contained in the following registry keys:

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface
HKEY_CLASSES_ROOT\CLSID
HKEY_CLASSES_ROOT\Interface

HKEY_LOCAL_MACHINE is the repository for information about the configuration state of the system. Most applications store information under this key during installation. Along with information about bus type, system memory, and installed hardware, it also contains information about installed software. This includes information about application classes and the document types that they understand, and object linking and embedding and dynamic data exchange information for those applications.

Note that HKEY_CLASSES_ROOT is an alias for the \Classes subkey of HKEY_LOCAL_MACHINE\SOFTWARE; that is, the same data appears in both places. HKEY_CLASSES_ROOT provides compatibility with the Windows 3.1 registration database.

Your installation/setup program must add information to the registry, and its associated subkeys, if it is to perform any of the three following types of installations:

•	Installing an object application.

•	Installing a container/object application.

•	Installing a container application that allows linking to its embedded objects.

In all three cases, you must register OLE 2 library (DLL) information as well as application-specific information.

For information about OLE registry functions, refer to Object Class Conversion and Emulation Functions in Component Object Interfaces and Functions.

For information on the registry, the structure of registry entries, and registry functions refer to the Microsoft Win32 Programmer's Reference.


Registry Support for 32-bit OLE

This section describes changes made to the Windows NT registry to allow it to support 32-bit OLE. A 32-bit application is registered in exactly the same manner as a 16-bit application, except that new subkeys provided for 32-bit applications are used instead of the subkeys used for 16-bit applications.

16-bit OLE uses, and will continue to use, the following registry subkeys:

LocalServer	Full path to the application '16- or 32-bit'.
InProcServer	Registers a 16-bit in-process server DLL.
InProcHandler	Registers a 16-bit handler DLL.
ProxyStubClsid	Maps an IID to a CLSID in a 16-bit proxy DLL.

32-bit OLE will use the following new registry subkeys:

LocalServer32	Full path to the application '32-bit'.
InProcServer32	Registers a 32-bit in-process server DLL.
InProcHandler32	Registers a 32-bit handler DLL.
ProxyStubClsid32	Maps an IID to a CLSID in a 32-bit proxy DLL.

By default, 32-bit implementations take priority over 16-bit implementations.

Existing 16-bit applications look in the registry for the insertable keyword. This key informs the application that the server supports embeddings. If the insertable keyword 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, in order for a 32-bit implementation to be insertable by a 16-bit application, the 32-bit application should register LocalServer in addition to registering LocalServer32.

For 32-bit applications, the only required keys are LocalServer, LocalServer32, InprocHandler32, and Insertable. InprocHandler, InprocServer and InprocServer32 are optional.

For a 32-bit InprocServer, the required entries are InprocHandler32, InprocServer, InprocServer32, and Insertable. Note that InprocServer provides backward compatibility. If it is missing, the class still works, but can't be inserted in 16-bit applications.

Note that ProxyStubClsid32 is required because 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.


Layout of the Registration Database File
OLE Registry Entries

Programmatic Identifiers

The ProgID Key and Subkeys

The CLSID Key and Subkeys

The Version-Independent ProgID Key and Subkeys

The File Extension Key

The (Non-Compound) FileType Key and Subkeys

The New Interface Key and Subkeys


Programmatic Identifiers

Every OLE 2 object class that is to appear in an Insert Object dialog box (hereafter referred to as an "insertable class") must have a programmatic identifier or ProgID. This string uniquely identifies a given class and is intended to be in a form that can be read by humans. Programmatic identifiers are not guaranteed to be universally 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 an object application (OLE 1 server).

The ProgID string 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.

Because it is necessary to make a conversion between the ProgId and the CLSID, it is important to note that there are two kinds of ProgIDs. One depends on the version of the object application (the version-dependent ProgId); and one does not (the version-independent ProgId).

The version-dependent ProgID is the string used when OLE 1 is trying to contact OLE 2 using DDE. Version-dependent ProgID-to-CLSID conversions must be specific, well-defined, and one-to-one.

The version-independent ProgID, on the other hand, can be used when a container application creates a chart or table with a toolbar button. In this situation, the application can use the version-independent ProgID to determine the latest version of the needed object application.

The CLSIDFromProgID and ProgIdFromCLSID functions can be called to convert back and forth between the two representations. These functions use information stored in the registry to perform the conversion.

The ProgID-to-CLSID conversion is done using the HKEY_LOCAL_MACHINE\SOFTWARE\Classes\<ProgID>\CLSID key. The reverse translation is done using the HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\<clsid>\ProgID subkey. This means that the ProgID subkey under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\<clsid> is version-dependent.

The version-independent ProgID is stored and maintained solely by application code. When given the version-independent ProgID, CLSIDFromProgID returns the CLSID of the current version. CLSIDFromProgID works on the version-independent ProgID because the subkey, CLSID, is the same as it is for the version-dependent one.

The user must never see the ProgID in the user interface. If you need a short human-readable string for an object, call IOleObject::GetUserType(USERCLASSTYPE_SHORT, lpszShortName).


The ProgID Key and Subkeys

OLE 2 classes that belong in the Insert Object dialog box each have a ProgID. The value assigned to this key is the name displayed in the dialog box; it should be the same as the "MainUserTypeName" of the class. If this class is insertable in an OLE 2 container, the ProgID key must have an immediate subkey named Insertable, which must have no value assigned to it.

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.

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 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.

Note  In this registry example and others that follow, boldface indicates a literal standard key or subkey, <italics> indicates an application-supplied string or value, and <boldface-italics> indicates an application-supplied key or subkey. In the first example, "OLE1ClassName," "OLE1UserTypeName," and "CLSID" are all supplied by the application.

<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 as the
1 = <verb 1>	// 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 human-readable 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.

Long Form String Subkey Entry

The entry to register the string (long form), such as that used in The Insert New dialog box, is as follows. The recommended maximum length for the string is 40 characters.

HKEY_CLASSES_ROOT\OLE2ISvrOtl = Ole 2 In-Place Server Outline

Information for OLE 1 Applications Subkey Entries

To maintain compatibility with OLE 1, include the following 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

Windows 3.1 Shell Subkey Entries

These entries are for Windows 3.1 shell printing and File Open use. They should contain the path and filename of the object application. The examples below contain simple entries only; more complicated ones could include 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

Insertable Subkey Entry

This entry indicates that the object application should appear in the Insert New dialog box's list box when used by OLE 2 container applications.

HKEY_CLASSES_ROOT\OLE2ISvrOtl\Insertable

Entry Point Subkey

This entry points to the application's OLE 2 information in the registry. To assign a CLSID for your application, run the UUIDGEN.EXE found in the \TOOLS directory of the OLE 2 Toolkit.

HKEY_CLASSES_ROOT\OLE2ISvrOtl\CLSID = {00000402-0000-0000-C000-000000000046}


The CLSID Key and Subkeys

Most of the OLE 2 object application information is stored in subkeys under the CLSID root key. The immediate subkey of the CLSID key is a string version CLSID. Subkeys of this version of the CLSID indicate where the code that services this class is found. For more information about converting the CLSID, see StringFromCLSID. For other related information see LocalServer, InprocServer, and InprocHandler under The CLSID Key and SubkeysMost of the CLSID information is used by the default OLE 2 handler to return various information about the class when it is in the loaded state. Examples of this include the Verb, the AuxUserType, and the MiscStatus entries. The Insertable subkey appears under both this key and the ProgId key.

CLSID
<CLSID> = <Main User Type Name>
LocalServer = <path to 16- or 32-bit exe>	// local (same machine) server; see "Server ="
// under ProgID key.
LocalServer32 = <path to 32-bit exe>	// local (same machine) server; see "Server ="
// under ProgID key.
InprocServer = <path to dll>	// in process server; relatively rare for insertable classes.
InprocServer32 = <path to 32-bit dll>	// in process server; relatively rare for insertable classes.
InprocHandler = <path to dll>	// in process handler. "OLE2.DLL" for the default OLE 2 handler
InprocHandler32 = <path to 32-bit dll>	// in process handler. "OLE2.DLL" for the default OLE 2
// handler
Verb	// info returned in IOleObject::EnumVerbs().
verb number = <name, menu flags, verb flags>
// several examples follow:
 0 = &Edit, 0, 2	// primary verb; often Edit; on menu; possibly dirties object,
// MF_STRING | MF_UNCHECKED | MF_ENABLED == 0.
 1 = &Play, 0, 3	// other verb; appears on menu; leaves object clean
-3 = Hide, 0, 1	// pseudo verb for hiding window; not on menu, opt.
-2 = Open, 0, 1	// pseudo verb for opening in sep. window; not on menu, opt.
-1 = Show, 0, 1	// pseudo verb for showing in preferred state; not on menu, opt.
AuxUserType	// auxiliary user types (main user type above)
<form of type> = <string>	// See IOleObject::GetUserType(); for example:
2 = <ShortName>	// key 1 should not be used
3 = <Application name>	// Contains the human-readable name of the application. Used when
// the actual name of the app is needed (for example in
// the Paste Special dialog's result field) Example: Acme Draw
MiscStatus = <default>	// def status used for all aspects; see IOleObject::GetMiscStatus
<aspect> = <integer>	// exceptions to above;  for example:
4 = 1	// DVASPECT_ICON = OLEMISC_RECOMPOSEONRESIZE
DataFormats
DefaultFile = <format>	// default main file/object format of objects of this class.
GetSet	// list of formats for default impl. of EnumFormatEtc; very similar
// to Request/SetDataFormats in OLE 1 entries
<n> = <format ,aspect, medium, flag>	// in this line,
// n  is a zero-based integer index;
// format is clipboard format;
// aspect is one or more of DVASPECT_*, -1 for "all";
// medium is one or more of TYMED_*;
// flag is one or more of DATADIR_*.
// three examples follow:
0 = 3, -1, 32, 1	// CF_METAFILE, all aspects, TYMED_MFPICT,
// DATADIR_GET
1 = Biff3, 1, 15, 3	// this example shows
// Microsoft Excel's Biff format version 3,
// DVASPECT_CONTENT,
// TYMED_HGLOBAL | TYMED_FILE |
// TYMED_ISTREAM | TYMED_ISTORAGE,
// (DATADIR_SET | DATADIR_GET)
2 = Rich Text Format, 1,1,3
Insertable	// when present, the class appears in the Insert Object dialog.
// (not present for internal classes like the moniker classes)
ProgID = <ProgID>	// the programmatic identifier for this class.
TreatAs = <CLSID>	// see CoGetTreatAs()
AutoTreatAs = <CLSID>	//see CoTreatAsClass
AutoConvertTo = <CLSID>	// see OleGetAutoConvert()
Conversion	// support for Change Type dialog box
Readable
Main = <format,format,format,format, ...>
Readwritable
Main = <format,format,format,format, ...>
DefaultIcon = <path to exe, index>	// parameters passed to ExtractIcon
Interfaces = <IID, IID, ...>	// optional. If this key is present, then its values are the
// totality of the interfaces supported by this class: if the
// IID is not in this list, then the interface is never supported
// by an instance of this class.
VersionIndependentProgID = <VersionIndependentProgID>

By default, 32-bit implementations take priority over 16-bit implementations.

Existing 16-bit applications look in the registry for the insertable keyword. This key informs the application that the server supports embeddings. If the insertable keyword 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, to be insertable by 16-bit applications, 32-bit applications should register LocalServer in addition to registering LocalServer32.

For 32-bit applications, the only required keys are LocalServer, LocalServer32, InprocHandler32, and Insertable. InprocHandler, InprocServer, and InproServer32 are optional.

For a 32-bit InprocServer, the required entries are InprocHandler32, InprocServer, InprocServer32, and Insertable. Note that InprocServer provides backward compatibility. If missing, the class still works, but isn't insertable in 16-bit applications.

CLSID (Object Class ID) Subkey Entry

The following information creates a CLSID subkey under the CLSID key. To obtain a CLSID for your application, run the UUIDGEN.EXE found in the \TOOLS directory of the OLE 2 Toolkit.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046} = Ole 2 In-Place Server Outline

Note  The subkey entries described in this "CLSID Key Entry" section are subkeys to your CLSID subkey. (Not all possible subkeys are described.)

LocalServer Subkey Entry

This subkey designates the application's location. The LocalServer subkey has the same value as the OLE2ISvrOtl\protocol\StdFileEditing\server key and should contain a full path. The entry can contain command-line arguments. Note that OLE 2 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 an OLE object server in a separate memory space ( Windows NT 3.5 only), change the LocalServer key in the registry for the CLSID to the following:

cmd /c start /separate <path.exe

LocalServer32 Subkey Entry

This subkey designates the location of the 32-bit version of the application. The LocalServer32 subkey has the same value as the OLE2ISvrOtl\protocol\StdFileEditing\server key and should contain a full path name. The entry can contain command-line arguments. Note that OLE 2 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}\LocalServer = c:\samp\isvrotl.exe

When OLE starts a 32-bit local server, the server must register a class object within an elapsed time set by the user. By default, this value must be at least five minutes, in milliseconds, but cannot exceed the number of milliseconds in 30 days. To set this value, add the following entry:

HKEY_CLASSES_ROOT\\Software\Microsoft\OLE2\ServerStartElapsedTime

InprocHandler Subkey Entry

This subkey designates whether the application uses a custom handler. If no custom handler is used, the entry should be set to OLE2.DLL, as shown in the following example.

HKEY_-CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\InprocHandler = ole2.dll

InprocHandler32 Subkey Entry

This subkey designates whether the application uses a custom handler. If no custom handler is 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

Verb Subkey Entry

The verbs to be registered for the application must be numbered consecutively. The first value after the verb string describes how the verb is appended by AppendMenu function call. See the Windows 3.1 SDK documentation for details.

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 OLEVERBATTRIB_). For more information, see the IOleObject::EnumVerbs method.

Verb 0: "Edit", MF_UNCHECKED | MF_ENABLED, no OLEVERATTRIB flags

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\Verb\0 = &Edit,0,0

Verb 1: "Open", MF_UNCHECKED | MF_ENABLED, no OLEVERATTRIB flags

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\Verb\1 = &Open,0,0

AuxUserType Subkey Entry

This key describes the short and actual human-readable names of the application. See also IOleObject::GetUserType. The short name is used in the menus, including pop-ups, and the recommended maximum length for the string is 15 characters. A short name example follows.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\AuxUserType\2 = In-Place Outline

The long human-readable name of the application is used in the Results field of the Paste Special dialog box. This string should contain the actual name of the application (such as "Acme Draw 2.0").

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\AuxUserType\3 = Ole 2 In-Place Server

MiscStatus Subkey Entry

The following is an example of a MiscStatus subkey. Refer to the IOleObject::GetMiscStatus method description for information on the different settings.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\MiscStatus = 0

DataFormats Subkey Entry

The DataFormats subkey lists the default and main data formats supported by the application. This entry is used by the 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

Insertable Subkey Entry

The Insertable entry indicates that this application should appear in the Insert New dialog box's list box when used by OLE 2 container applications. Note that this is a duplicate entry of the one above but is required for future use.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\Insertable

ProgID Subkey Entry

Every insertable object class has a "programmatic identifier" or ProgID. For information on the creation and syntax of programmatic identifiers, see Programmatic Identifiers.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\ProgID = OLE2ISvrOtl

Conversion Subkey Entry

Conversion information is used by the Convert dialog box to determine what formats the 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). Note that in this case the readable and writeable format is CF_OUTLINE (private).

Shown below is an entry that registers a file format the application can read (convert from) is shown below.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\Conversion\Readable\Main = Outline,1

Shown below is an entry that registers a file format the application can read and write (activate as) is shown below.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\Conversion\Readwritable\Main = Outline,1

DefaultIcon Subkey Entry

The DefaultIcon subkey provides default icon information for iconic presentations of objects. This entry contains the full path to the executable name of the object application and the index of the icon within the executable. Applications can use this information to obtain an icon handle with ExtractIcon.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-C000-000000000046}\DefaultIcon = c:\samp\isvrotl.exe,0


The Version-Independent ProgID Key and Subkeys

An application must register a version-independent programmatic identifier in the "Version-Independent ProgID" key. That is, a second ProgID referring to its class, one that would not change from version to version, but would remain constant across all versions. This key provides a constant name, referred to as the MainUserTypeName. It is used with macro languages and refers to the currently installed version of the application's class. The MainUserTypeName must correspond to the name of the latest version of the object application.

<VersionIndependentProgID> = <MainUserTypeName>
CLSID = <CLSID> 	// the class id of the newest installed version of that class
CurVer = <ProgID>	// the ProgID of the newest installed version of that class

In addition to the preceding entry, you should add the following entry to the CLSID root key.

CLSID
<CLSID> = <Main User Type Name>
VersionIndependentProgID = <VersionIndependentProgID>


The File Extension Key

If your OLE 2 application can handle requests from the Windows 3.1 File Manager, create the file extension root key.

<.ext>  = <ProgID>	// used by File Manager and by GetClassFile (and thus File Monikers)


The (Non-Compound) FileType Key and Subkeys

Entries under the FileType key are used by the GetClassFile function to pattern-match against various file bytes in a non-compound file. FileType has CLSID subkeys, each of which has a series of subkeys 0, 1, 2, ... whose values contain a pattern that, if matched, should yield the indicated CLSID. See also GetClassFile.

FileType	// used by GetClassFile()
<CLSID>
<n> = <offset, cb, mask, value>	// Offset and cb are limited to 16 bits. Offset can
//be negative for file end.
// As above, offset and byte count are decimal unless preceded by "0x", in which
// case they are hex. The mask and the pattern are always hex and cannot be
// preceded by "0x". Mask can be omitted, implying a value of all ones.
// An example:
0 = 0, 4, FFFFFFFF, ABCD1234	// This requires that the first 4 bytes be AB CD
// 12 34, in that order,
1 = 0, 4, FFFFFFFF, 9876543	// or requires that they match 9876543.
2 = -4, 4, , FEFEFEFE	// The last four bytes in the file must be FEFEFEFE


The New Interface Key and Subkeys
The Interface Key and Subkeys

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.

Interface
<IID> = <textual name of interface> 	// for example: "IOleObject"
ProxyStubClsID = <CLSID	>	// Maps an IID to a CLSID in a 16-bit proxy DLL.
//Used internally by OLE 2 for interprocess
// communication.
ProxyStubClsID32 = <CLSID	>	// Maps an IID to a CLSID in a 32-bit proxy DLL.
//Used internally by OLE 2 for interprocess
// communication.
NumMethods = <integer>	// Number of methods in the interface.
BaseInterface = <IID>	// Interface from which this was derived. Absence
// of key means IUnknown. Key present but empty value means derived
// from nothing.

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 mapping depends on the way the interface proxies are packaged into a set of proxy DLLs.


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. For more information, see When the OLE 1 Version is Overwritten.CLSID
<CLSID> = <OLE1UserTypeName>	// This entry is generated automatically by OLE 2 for an OLE 1 	class.
OLE1Class = <OLE 1 class name>	// This entry is created the first time an object of that class
// is inserted in an OLE 2 container.
ProgId = <OLE 1 class name>	// Allows OLE 2 to convert CLSID back to an OLE 1 class name


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>


Registering OLE 2 Libraries

The OLE 2 libraries require that many internal interfaces be registered. Thus, if your installation program installs the OLE 2 libraries on a machine that does not already have them, it should register OLE 2 specific information during installation.

If your installation program does not install OLE 2 libraries when it finds more recent libraries on the user's drive, it should not register the OLE 2 interface information.

Note  When checking the version stamp (using VER.DLL) on existing OLE 2 libraries to determine whether or not to replace them on the user's hard drive, do so on a per file basis.


Accommodating OLE 1 Versions of the Object Application

Whenever 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, two basic situations can arise:

•	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 under 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:

a.	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

b.	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  Since 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.

c.	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 registration entry file by calling CLSIDFromProgID.

d.	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:

a.	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.}

b.	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

When 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), but 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, 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 above).

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.

Installing a new version of a server application.


Checking Registration During Run Time

The application should check its registration at the application load time, noting three particular issues:

•	If the CLSID(s) that the application services are not present in the registry, the application should register as it does during the original setup.

•	If the application's CLSID is present, but has no OLE 2-related information in it, the application should register as it does during the original setup.

•	If the path containing server entries (LocalServer and LocalServer32, InprocServer and InprocServer32, and DefaultIcon entries) does not point to the location in which the application is currently installed, the application should rewrite the path entries to point to its current location.


Using the Registration Database for Localization
Using the Registry for Localization

It is possible to address a pair of product-localization issues by adding a key to the registry. This key, described below, allows functions to return a specified string instead of a default value or "Unknown."

This section includes the following topics:

Specifying Unregistered Verbs
Specifying Unknown User Types


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 IOleObject::EnumVerbs (or OleRegEnumVerbs) 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, a key can be included in the registry. This key is

\Software\Microsoft\OLE1\UnregisteredVerb = <verbname>

(where verbname is the value that will be returned from the enumeration) and 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.


Specifying Unknown User Types

The OLE 2 default handler's implementation of IOleObject::GetUserType first 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 is returned. If the class is not found in the object's IStorage, the string "Unknown" is returned.

By inserting the

\Software\Microsoft\OLE2\UnknownUserType = <usertype>

key in the registry, IOleObject::GetUserType returns the value of the string specified by <usertype>. This string can be localized for a different language user-type name, instead of using the "Unknown" string, for the User Type.


User Interface Guidelines

Although OLE objects and containers can implement any user interface supported by the OLE protocol, you'll benefit most from OLE's integrated nature if you subscribe to a common user model. This section illustrates the user model and explains the user interface implementation guidelines for OLE. The goals of the OLE user interface include the following:

•	To express a unified model of compound-document composition and interaction that allows users to accomplish tasks efficiently.

•	To exploit the integration power of OLE by using current user-interface frameworks and mechanisms.

•	To establish a sound user model of compound documents that meets current application needs and that gracefully leads to more data-centric systems in the future.

Note  In general, the concepts in this section are an extension of the principles set forth for the implementation of OLE 1. However, there are some major differences in the capabilities of OLE 1 and OLE 2. One crucial difference is the ability of the OLE 2 user interface to perform in-place activation, sometimes referred to as visual editing. Whereas OLE 1 required users to edit objects in their original application windows, OLE 2 lets the user activate and manipulate objects from within the document containing them. OLE 2 presents an object's commands and tools within the container document's window. As a result, the conceptual model of containment is no longer compromised by the need to remove objects from the document for editing.

In-place activation is the most efficient way for users to manipulate objects in OLE 2, although the user may still edit in a separate window. Therefore, OLE 2 objects usually set some kind of in-place activation as their primary action (or verb), but can also be opened separately, as OLE 1 objects must be.

Simply put, a user creates and manipulates various types of information (objects) that reside in a containing document. As the user focuses a particular object, its corresponding commands and tools become available, allowing the user to interact with the object directly from within the document (in-place activation). Objects may be transferred within and across documents and still retain their full-featured editing and operating capabilities. In addition, information may be connected so that changes in one object are automatically reflected in another (linking).


Object Type

An OLE object, as portrayed to the user, is a unit of information that resides in a document and whose behavior is constant no matter where it is located. The object's intrinsic behavior is defined by the object itself rather than determined by the document that holds it. The user may interact with an object as a whole (by using OLE verbs and container commands), or with its contents (by engaging its proper tools such as those provided by its object application). Objects may be nested arbitrarily within other objects; objects may be moved, copied, or linked from one location to another.

An object's type is the human-readable form of its class and conveys the object's behavior or capability to the user in dialog boxes and menu commands throughout the OLE 2 interface. CorelDRAW! 4.0 Drawing is a typical example. The object's type is not meant to imply its storage format. Two different object types may use the same storage format (such as .bmp or .rtf file formats), but their types are distinct if they are handled by different object applications. An object's type is expressed as a string (maximum of 40 characters) in one of the three recommended forms below:

•	<brandname> <application> [<version>] <data type>
For example, Ami Pro 2.0 Document.

•	<brandname-application> [<version>] <data type>, for cases when the brand name and application are the same.
For example, WordPerfect Text.

•	<brandname> <application> [<version>], for cases where the application sufficiently describes the item.
For example, Microsoft Graph 3.0.

As shown in the following list, the four components of the type name convey a wide range of information to the user:

•	<brandname>: communicates product identity and differentiation.

•	<application>: indicates which object application is responsible for activating the object.

•	<data type>: suggests the basic nature or category of the object (such as drawing, spreadsheet, or sound) and should be a maximum of 15 characters.

•	<version>: when there are multiple versions of the same basic type, a version number distinguishes them for purposes of upgrading.

These object type names provide users with a precise language for referring to objects. Because object type names appear throughout the OLE interface, users become conscious of an object's type and its associated behavior. The full type name (also referred to as <descriptive type name> in the Status Bar Messages at the end of this section) should be used in dialog boxes, messages, and status lines.

Because of the restrictions on the length of type names, only their <data type> should be used in menus (both pull-down and pop-up), title bars, and in the list pane of the Links dialog box. The data type is considered to be the short form of the full type name (referred to as <shortform> in the "Status Bar Messages"). If a short form name is not available for an object (because it is an OLE 1 object or the string was simply not registered), the full type name should be used instead. For example, an Ami Pro 2.0 Document is referred to simply as a "Document" in menus, title bars, and in the list pane of the Links dialog box. All dialog boxes that display the full type name must allocate enough space for 40 characters in width. Likewise, dialog boxes must accommodate 15 characters when using the short-form name.


Class Registration

An object type is also called an object class, particularly as it relates to the registry. When an object class is registered, such as during installation of the object application that supports its class, the installation program should first determine whether a previous version of the same class already exists in the registry. If a previous version exists, the user has the option of upgrading all older-version objects to the new version automatically the next time each is activated. If a previous version is not detected, the installation program simply registers the new version without prompting the user.

Note  Refer to Registering Object Applications for more information.

When appropriate, a message should point out any important differences between object versions to help the user decide whether or not to upgrade. If the user elects to upgrade all the older objects, the older version is replaced in the registry by the newer one. From that point on, previous-version objects open with the new version of the application and are saved in its format.

The new object application can offer to emulate the older version's objects without actually converting them to the new format. This lets a user use the features of the new object application while maintaining the object in its old format for as long as possible. However, if the user makes use of a particular feature of the new object application that cannot be accommodated by the old type's format, the user should have the option of abandoning such edits and staying within the old format or keeping the edits and accepting the necessary conversion to the new format. If the user specifies no upgrading or emulating, the new object application is registered in addition to the old one, and the two are considered to be completely independent types. The user may rerun the installation program any time later to specify version-upgrading options.

The following diagram illustrates the decision tree and recommended dialog boxes for installation of an object application named Acme Draw 2.0.

Installing a new version of a object application.

As part of the class-registration procedure, a setup program may register a list of data formats, such as .tif, .eps, .xls, that the object application is capable of reading and activating, as well as a list of formats it is capable of writing. These formats are discussed further in the following sections; they are listed in the registry. See Registering Object Applications for more information.


Objects and Other Object Applications

Although an object is usually activated by the object application that corresponds to its class in the registry, this is not always the case. By comparing the lists of registered data formats with other types of object applications, an application can determine which other object types a newly registered application is capable of converting and emulating. For example, if a newly registered application reads and writes objects saved in the .tif format and an existing object application writes objects in the .tif format, the new object application can convert objects saved by the existing application. The Convert dialog box, as shown in the figure called "The Convert dialog box," lets users convert and emulate object types.

There are three specific situations in which an existing object is not activated by the object application corresponding to its class in the registry.


Activating an Object Having an Upgraded Version

If the user activates an object whose application version has been reassigned to a newer version, the object is activated by launching and is saved by the new object application.


Converting an Object from One Type to Another

Users must often convert an object from one type to another permanently. To do so, the user selects the object and, using the Edit/Object submenu, chooses Convert.... This displays the Convert dialog box, which allows the user to select the Convert to: option button. This button displays a list of other types (classes) to which the object may be converted. It is selected by default when the user chooses Convert using the menus or if the selected object's type is stored in the registry. This list of other object types is composed of all types registered as capable of reading the selected object's format. (The list, however, does not guarantee that a reverse conversion is possible).

The list of object types also contains the selected object's current type. This lets the user change to Display As Icon without necessarily converting the selection's type. If the user chooses a new type from the list and confirms the dialog box selection, the selected object is immediately and silently converted to the new type. (If the object was opened, the container closes it before performing the conversion.) If, however, the Convert dialog box resulted from an attempt by the user to edit an unregistered object, the new type application should activate immediately after the actual conversion takes place, as part of loading the object.


Emulating Different Object Types

Because users exchange OLE documents between different machines, they may receive an object and not have the object application necessary to activate it. In addition, users may want to use other than default applications with their own objects to exploit unique editing features of a particular object application. The Convert dialog box also supports these situations.

When the user chooses the Activate as: option button in the Convert dialog box, the subset of types capable of emulating (reading and writing) the selected object are displayed in the Object Type list box. This button is chosen by default if the object's type is not registered. The remaining types, which are only capable of conversion, do not appear. When the user chooses a type and responds Yes to the resulting message box, every object of the selected object's type is activated as an object of the "emulating type." This provides an alternate type to be used thereafter. This means that these objects take on the alternate type's verbs in the Edit Object submenu and  are activated by using the alternate application. However, they keep their original type name throughout the entire user interface because they continue to be stored in their original type's format. Since the alternate application does not actually convert the objects, they can continue to be exchanged among users in their original type. (Behind the user interface, this dialog box registers the alternate application for the selected object's type in the registry. As a result, the emulation applies only to a particular machine; each machine will activate the type with the particular object application stored in its own registry.)

An Error dialog box, which provides immediate access to the Convert dialog box, should appear when a user attempts to activate (double-click) an unregistered object type. Additionally, the user may voluntarily access the dialog box through the Edit / Object > Convert... command (the only command available for an unregistered object since its verbs are unknown). Because the Convert dialog box can be invoked at any time, the user may specify an alternate type whenever necessary. Even if there are no conversions or alternate types available for the selected object's type, the Convert dialog box should still be available as a means of simply specifying the icon of the object. The Result Help text elaborates on the outcome of the conversion and emulation options. The following table outlines the Result text that should be used within the Convert dialog box.

When an embedded object is selected:

Function	Result text
Convert to Original Type	The selected <original type> will not be converted.
Convert to Original Type + Display as Icon	The selected <original type> will not be converted. It will be displayed as an icon.
Convert to Different Type	Permanently converts the selected <original type> to a <selected type>.
Convert to Different Type + Display as Icon	Permanently converts the selected <original type> to a <selected type>. It will be displayed as an icon.
When a linked object is selected:

Function	Result text
Convert to Original Type (only)	The selected <original type> will not be converted.
Convert to Original Type (only) + Display as Icon	The selected <original type> will not be converted. It will be displayed as an icon.
When either a linked or embedded object is selected (Display As Icon is disabled):

Function	Result text
Activate as Original Type	Every <original type> will be activated as a <selected type>.
Activate as Emulating Type	Every <original type> will be activated as a <selected type>, but it will not be converted.
The next two figures show how these cases are handled on a system that has three registered object applications, Acme Draw 1.0, Acme Draw 2.0, and Megasoft Draw 1.0. Beginning at the point "Select an Acme Draw 1.0 object," the user would usually follow the vertical path downward to activation. However, if Acme Draw 1.0 is replaced by Acme Draw 2.0, or if the user wants to convert or emulate Acme Draw 1.0, the appropriate alternate routes are taken. The second of the next two figures shows the result of trying to activate an unregistered ProDraw 1.0 drawing and the ensuing Error and Convert dialog boxes. Note that in this example both Acme Draw 2.0 and Megasoft Draw 1.0 are possible conversions of the ProDraw 1.0 drawing, but only Megasoft Draw 1.0 is a legal emulating type. The Convert dialog boxes drawing shows the recommended dialog boxes referred to in the next two figures.

Upgrading and converting object types.

Emulating object types.

The Convert dialog box.

A user who specified activation of all Acme Draw 1.0 objects as Acme Draw 2.0 objects could have done so either 1) while running the Acme Draw 2.0 setup program or 2) at any point afterward using the Activate As: option in the Convert dialog box. If the user makes use of features unique to Acme Draw 2.0 that cannot be accommodated in the Acme Draw 1.0 format, the warning message shown in the next figure should be given at the moment the object is deactivated (when using an in-place application) or closed (using an open application). Selecting Yes performs the conversion and saves the object with all of its changes intact. Selecting No discards certain changes to preserve the object in Acme Draw 1.0 format. This dialog box should be issued not only with different versions of the same type, but also with competitor type emulations that can incur a loss of data or compromise the integrity of the object's image when being saved in the emulating type's format.

Possible warning message for emulation data loss.


Creating Objects

Users can create objects either from existing objects or from "scratch." When creating objects from existing objects, users can either modify a copy of an existing object of the same type (class) or convert an object from one type to another using the Convert dialog box, as discussed in the preceding section. Converting spreadsheet cells to a word-processor table is an example.

To create new objects, OLE 2 provides the Insert Object dialog box. Using this dialog box lets the user create new objects by:

•	embedding an object of a selected class (type). See the first of the next three figures.

•	inserting a file as an embedded object. See the second of the next three figures.

•	inserting a file as a linked object. See the third of the next three figures.

The Insert Object dialog box, embedding an object of a selected class.

The Insert Object dialog box, inserting a file as an embedded object.

The Insert Object dialog box, inserting a file as a linked object.

Access to this dialog box is provided by an Insert Object... command, so it should be placed within the menu responsible for instantiating or importing new objects into the document. If no such menu exists, use the Edit menu.

The user can embed a new object by choosing a type from the Object Type list box and selecting OK. Certain types of new objects might make use of the current selection in the container document and build themselves accordingly. For example, an object created by an application whose function is to create charts might assume an appearance that reflects the values of data contained in a selected word-processor table.

To ensure predictable insertion behavior, use the following guidelines.

•	If a newly inserted object is not based on the current selection, the selection should be replaced by the new object, effectively pasting over it.

•	If the new object is to remain linked to the current selection, the new object should be inserted in addition to the selection. It then becomes the new selection.

•	If the object is based on, but will not remain linked to, the selection, it is up to the new object's application whether to remove the given selection. Whether it is more meaningful to replace or add depends on the specific use of an object type.

The user can also link to an existing file as an object by checking the Link check box. Leaving it unchecked embeds the file as an object. When the user selects the Create from File option button, the Object Type list box is replaced with a File text box and a Browse command button because the Object Type list box is unrelated to the file options. The embedded or linked file specifies the class of the inserted or linked object. The Result box contains text describing the final outcome of the insert operation to the user.

Function	Result Text
Create New	Inserts a new <object type name> into your document.
Create New as Icon	Inserts a new <object type name> into your document as an icon.
Create From File	Inserts the contents of the file as an object into your document so you can activate it by using the application that created it.
Create From File as Icon	Inserts the contents of the file as an object into your document so you can activate it using the application that created it. It will be displayed as an icon.
Create From File + Link	Inserts a picture of the file contents into your document. The picture will be linked to the file so that changes to the file will be reflected in your document.
Create From File as Icon + Link	Inserts an icon into your document that represents the file. The icon will be linked to the file so that changes to the file will be reflected in your document.
Whether the inserted object is new or already exists, the user can choose to insert it as an icon by selecting the Display as Icon check box as shown in the next figure. If the user has chosen a non-OLE file for insertion, it may only be inserted as an icon, effectively "packaging" the file.

The Insert Object dialog box, displaying an object as an icon.

Because a newly created object does not exist in another file, it can be inserted only as an embedded object. Only objects created from existing files can be inserted as linked objects.

When the user selects the Display as Icon check box, the icon appears beneath the check box and the Change Icon... command button is enabled. The Change Icon... command button provides the Change Icon dialog box, as shown in the next figure, which allows the user to choose another icon and give it a label. A related discussion about OLE 2 and the Packager is presented later in this section.

In the Change Icon dialog box, the current icon is the one presently assigned to the object, persisting with the object even if it moves to other machines. The default icon is determined by the object's type and is the same as the application's default icon.

The From File: option in this dialog box lets the user pick an icon from a file. The From File: text box displays the path of the current icon. This text box is parsed and checked for a legal path when the focus moves to some other dialog box element. Once the icon is chosen and the dialog box is committed, the chosen icon becomes the current icon.

The Change Icon dialog box.

The Convert dialog box, described earlier and shown in the Convert dialog box figure, contains a Display as Icon check box that can be used to change an object's icon at any time. When this dialog box is invoked for an object that already has an icon assigned, it is handled as a user-chosen icon. This means the dialog box does not automatically change it to the default icon of the new type after the conversion. A user who decides to adopt the default icon of the new conversion type must return to the Change Icon dialog box and choose the Default icon option button. The Convert dialog box then dynamically displays the appropriate icon of the selected type in the Convert type list.

If an item is embedded as an icon (by means of Paste Special, Insert Object, or drag/drop from the File Manager), its default label should be one of the following:

•	For an OLE object, the label should be the short form of its type name (for example, Picture, Worksheet, and so on).

•	For an OLE object that has not registered a short form name, use its full type name (such as Microsoft Paintbrush Picture, Microsoft Excel Worksheet)

•	For an item that is not associated with an OLE class, the label is simply "Document."

If an entire file is linked into a container as an icon by using Insert Object Create from File + Link or by transfer from the File Manager, its default label is the source file's <filename>.<extension> name in lower case (for example, source.xls). If a portion of a document is linked as an icon by using the Paste Special dialog box, its default label should be formed from the display name of the link source. In general, the display name of a link source may have a completely arbitrary syntax. Consequently, the following algorithm for determining the default label is inherently heuristic. It is suggested, however, that the default label be formed as follows.

1.	From the end of the display name of the link source, scan for the last and second-last occurrence of the following characters:

\   /   !   :

The label should be formed from the display name starting at the penultimate scanned-for character through the end of the string.

2.	If this scan does not consume the whole display name, the first line of the label should begin with "...". Following this, the portion of the display name between the penultimate and ultimate scanned-for characters (inclusive) should be displayed.

3.	The second line of the label should contain the portion of the display name that follows the ultimate scanned-for character.

The occurrence of only one scanned-for character should be handled in the obvious way. Further, if the entire label fits on one line, do not break it into pieces. It is acceptable for applications to display the full name of the link source, although the above algorithm produces more pleasing results.

In general, the label should be no more than two lines, and each line should be no wider than 10 uppercase W's in the rendered font. The text of each line should be centered beneath the icon image. The label of an icon persists with the object as it is transferred between containers and can only be changed afterward by using the Change Icon dialog box.

After an in-place object has been inserted, its application becomes active. If the inserted object appears in an open application, an opened-style hatch rectangle appears in the container, similar to the open selected object shown in Figure 4-16, until an image from the object application is available for an update.

Note  Applications can also display buttons in their control bars that insert objects directly. Such buttons function in the same manner as the Insert Object dialog box.


States and the Appearance of Objects

With the availability of in-place editing, OLE enhances the conceptual structure of a compound document by exposing the entire hierarchy of containment in a single window. OLE 1 handles two kinds of objects, the container (or client) and the embedded objects it contains. That the contained objects themselves might also contain embedded objects is of no consequence; because they are not accessible to the container, it is not necessary to consider embedded objects as containers themselves. In OLE 2, however, the open object becomes the root of a hierarchy of embedded objects, each of which may be a container of another object (or objects). For example, a word-processor document can contain a range of cells from a spreadsheet program, which in turn contains a chart representing the values contained in those cells. The chart is accessible from within the word-processor document even though the chart's container is the range of spreadsheet cells. The following figure illustrates this concept.

OLE 1 and OLE 2 object hierarchies.

As the user navigates through the object hierarchy within a document, OLE objects assume different states and appearances. An OLE object may be:

•	inactive

•	selected

•	active

•	open

The following sections discuss each of these states and their differences.


The Inactive State

An object is said to be inactive when it is neither active nor part of a selection. It is displayed in its presentation form, which is usually conveyed through its cached metafile description. A user may want to know whether an object is embedded or linked without being required to interact with it. If showing the difference between linked and embedded objects is important to a container application, you may want to place a black solid border single pixel wide around the extent of an embedded object and a dotted border around linked objects as shown in the next figure.

The effect of the Show Objects command.

If the container application cannot guarantee that a linked object is up to date with its source (because an automatic update was unsuccessful or the link is manual), the dotted border should appear in the Windows disabled-text color (typically gray), suggesting the link is potentially out of date. Only the container document's first level objects should be bordered. For example, if the Show Objects command was chosen from an outer-level container, only directly embedded objects would be bordered; nested objects would not. If you use this technique in your application, make the appearance of these borders distinct from the appearance of other object states.

An inactive object may be selected by clicking once anywhere within its extent, or it may be double-clicked to perform the action named by its primary verb.


The Selected State

An object is selected when the user clicks it or when it is included in a selection of multiple objects. An object is selected (and deselected) and rendered according to the normal rules of its container. When the object is selected, object-specific operations can be performed on it using the verbs associated with the object. The container application retrieves the verbs from the registry. When the object is selected, as shown in the next figure, the container may supply handles (for resizing) that affect the object as a unit in relation to the container. It is recommended that resizing an OLE object, while it is selected, result in a scaling operation.

A selected OLE object.

When an object is selected, but not as part of a multiple selection, any of its registered verbs may be applied. For example, the verbs Edit and Open activate and open the object, respectively, but verbs such as Play might operate and leave the object in the selected state. Any number of single clicks simply reselect the object, while clicking outside the object deselects it.

Verbs that are meaningful only for certain states of the object should be enabled and disabled appropriately. For example, a media object that has Play and Rewind as verbs should disable Rewind when the object is already at the beginning. Similarly, if an object has two verbs, Edit (for in-place editing) and Open (for opened editing), Edit should be disabled when the object is open, because the object cannot directly achieve the in-place active state without first closing.


The Active State

OLE objects may enter an active state in which the user may interact with the object's contents in place, using the container document's window for the menus of its object application and for its interface controls. The user can make an object active either by performing its appropriate verb (Edit), double-clicking it (the primary verb will be Edit for many objects), or selecting the object and pressing Enter. If the Enter key is already used by the container application, then Alt+Enter is recommended.

When an object becomes active, its application's menus and interface controls are grafted into the containing document's window and apply over the extent of the active object as shown in the next figure. Frame adornments appear outside the extent of the object, and thus may temporarily cover neighboring material in the document.

The active object and its frame adornments are surrounded by a black diagonal hatch border to indicate the active state and to suggest the area of focus. The hatch-pattern border consists of right-ascending diagonal four-pixel lines with three transparent pixels between the lines. Because the border is translucent, underlying material shows between the diagonal lines. The hatch border is considered to be part of the object's layout; consequently, it is the object's pointer that appears when over the border.

In the active state, the object takes on the appearance that is best suited for its own editing. Frame adornments such as table gridlines, handles, scroll bars, as well as other editing aids may be shown with the object. The next figure shows row/column headers as adornments to the object, an active worksheet. Scroll bars could be included if the object is large and scrolling is needed to show the entire object in its viewport.

Clicking the hatch pattern (and not the resize handles) should be re-interpreted by the object as clicking just inside the edge of the border. The hatch area is effectively a click slop zone that prevents inadvertent deactivations and makes it easier to select the contents of the object that lie right along its edge.

Because the container may be set at a view-scale (zoom ratio) that the object cannot match to perform in-place activation, the object should instead open into a separate window. If the object does not support an open mode, it should not respond to the verb but should issue an appropriate error message.

An in-place active OLE object.

Although all objects that use the same object application can be considered a "set," only one object is activated at any given time. For example, if all Paintbrush objects in a document were activated, commands such as Clear All or Delete would be ambiguous. In that case, the user might not know which objects would be affected by such document-scoped commands.

A single click in the container area, or a double-click on a new OLE object (which may be nested in the currently active object) deactivates the current object and gives the focus to the new object. An active object may be deactivated by clicking outside its extent in the container document or by pressing escape. If an in-place object already uses escape for getting out of its own internal modes, pressing it should return focus to its container, making the object the selection, when the object "escapes" from all its internal modes. If an object uses the Escape key at all times, it is recommended that Shift+Escape be used to deactivate the object.

Edits made to an active object immediately and automatically become a part of the container document. Consequently, there is no "Update changes?" prompt when an in-place active object deactivates. Of course, the user can abandon changes to the entire document, embedded or otherwise, by declining to save the file to disk.

Those objects that support resizing while in-place active should include square resize handles within the active hatch pattern as shown in the next figure. The solid black handles should be of the same width as the hatch pattern. To provide optimum clarity to the user, resize handles can have a single white-pixel separation from the diagonal lines of the hatch pattern. This is not shown in the following figure.

In-place resizing should expose more or less of the object's content (for example, adding or removing rows/columns in the case of a spreadsheet). It should be seen as adjusting the viewport rather than scaling the object's appearance. However, certain objects may default to in-place scaling if cropping is not meaningful.

The border of an object that supports in-place activation and resizing.


The Open State

The semantics of the open state are similar to that of the open state introduced in OLE 1, with one important distinction. In OLE 1, the open object was seen by the user in a separate open object application that updated changes to the object back to the container document upon exit. In OLE 2, the open object application appears to the user as an alternate viewer onto the same object, under the hatch pattern, still within the container document. The next figure shows how a selected open object appears to the user. In this example, the container application is a word processor while the object application for the object is a drawing program.

An open selected object.

If the user clicks elsewhere in the container document, the object becomes deselected but remains in the open state as indicated in the next figure.

There is no sense of a local version kept within the object application; the opened object in the document and the object within the object application are the same. As a result, changes to the open object made within the object application window are immediately and automatically reflected in the object (beneath the hatch pattern) in the container. There is no longer a need for the Update command or the update confirmation upon exiting the object application.

Nevertheless, open object applications can still include an "Update <source file>" command for the user to force an update at any time. This is useful when the application's "real-time" image updates are not very frequent because they may, generally, be computationally expensive. In any event, an update is always performed automatically at the time the open window is closed.

Import File... commands are still appropriate because they conceptually import a file into the document directly.

This interpretation of open object applications unifies open and in-place activation. In either case, the user is interacting with objects at the document level directly without updating latencies. The only difference is the matter of where the interaction is performed; in place or in an alternate viewer looking at the same site in the document.

An OLE object capable of in-place activation should also include the Open verb. The Open verb gives the user the opportunity of seeing more of the object or seeing the object in a different view state. Ideally, an in-place object should support in-place editing at arbitrary view scales because its container might be scaled unpredictably. If an object cannot accommodate in-place editing in its container's current view scale, or if its container does not support in-place editing, the object should enter the open state in a separate window for editing.

An open object, not selected.

When an object is opened, it becomes the selected object of the container document. Also, when the container document is printed, the presentation form of objects should be used. Neither the open nor active hatch patterns should appear in the printed document since these are meta-appearances (like selection indication) and not part of the content.

Any number of objects may be open. The object is deactivated (closed) by closing the application window that updates the embedded object in the container document. Because object application windows can be modal, the user is free to switch between object application and document windows and edit independently. It follows that the opened object may participate in the container application's selections as usual.


Object State Transitions

The preceding in-place activation examples show the OLE 2 outside-in rule of activation. This means that any number of single-clicks within or on the border of an object select but do not activate or open the object. An explicit activation (or open) verb, or double-clicking is necessary to tunnel into the object and work with its contents.

The next figure shows the state-transition diagram for an outside-in activated object whose primary verb is Edit (in place). Not shown in the diagram is the effect of double-clicking an opened object to bring the object application window to the front and activate it (similar to bringing a window to the surface by double-clicking its icon again). Container applications should display the northwest arrow pointer above an outside-in OLE object when it is not activated to indicate that it behaves as a single opaque item.

From the user's perspective, outside-in objects can be selected with little or no deliberation. Activating them, however, requires an explicit action, such as double-clicking.

The outside-in rule results from the following considerations:

•	Many object applications require several seconds to load, so activating objects indiscriminately on every mouse event would be unmanageable. Instead, the user performs a deliberate action to activate the object.

•	Outside-in objects can be selected easily because the user need only click somewhere within the object's extent.

Object-state transition diagram for outside-in object applications.

To accommodate the broad range of container types and the prospect of new, lighter weight, quick-activating objects in the future, OLE also supports an inside-out activation model as well as related registry information. This activation model, shown in the next figure, enables a container to ensure uniform activation across its components. The inside-out model can also further reduces the editing seam between native and OLE objects by presenting OLE objects to the user as effectively always-active and ready-to-use elements.

An inside-out object controls the appearance of the pointer as it moves within the object's extent, responds immediately to mouse actions, and is selected as a whole by clicking on its edge (or some other appropriate selection target provided by its container). From the user's perspective, inside-out objects are truly seamless with respect to the container's native data since their behaviors are virtually indistinguishable. The container should clear its current selection when an inside-out object begins its selection (typically on mouse down); otherwise, it appears there are two cooperating selections when only the object's selection is meaningful.

Object state transition diagram for inside-out object applications.

Two class registry bits express the activation style of an object class to a container. By default an object application is presumed to behave as outside-in but, as explained below, the object might also register that it is capable of inside-out operation. It might also indicate that inside-out operation would be more effective. The two class registry bits, OLEMISC_INSIDEOUT and OLEMISC_ACTIVATEWHENVISIBLE, can be obtained by calling the object's IOleObject::GetMiscStatus method. These bits are described below.

Bit Name	Meaning
OLEMISC_INSIDEOUT	This object application is capable of running without activating its user interface, thus enabling the application to exhibit inside-out behavior. An instance of this application might affect the mouse pointer and respond immediately to a mouse action (surfacing its user interface after the mouse action is completed).
OLEMISC_ACTIVATEWHENVISIBLE	This object application is not only inside-out capable, but actually is more effectively activated inside-out. This might stem from the object's user interface, which mandates inside-out (such as with forms control objects whose visual affordances invite immediate interaction), or simply because the object's application costs a container little time or memory to keep activated when in view.
Using registration information, a container ultimately determines whether it allows the inside-out objects to handle the events directly or whether it intercedes and only activates components on a double-click.

Note  If an inside-out object's application is unavailable, the appropriate error message in a dialog box should be issued when the user tries to interact with the object, not when the container tries to preload the application. This prevents surfacing error messages to a user whose only intention is to read the document.

The container should use an activation style that is most appropriate for its specific use and that is in keeping with its own native style of activation so OLE objects can be assimilated most seamlessly. Regardless of which approach the container uses, it is critical that the container activate the same class of object consistently, either outside-in or inside-out. Any inconsistency in the way an object activates hinders its ease of use. Although it is not as critical for objects of different types to activate in the same manner in a given container, it is still recommended that all components of a container uniformly follow either one activation style or the other. Below are four examples of potential container activation strategies:

Outside-In Throughout

This will commonly be the case for compound-document editors that often embed large objects and deal with them as whole units. Because compound documents are the popular initial expression of OLE and many of the available objects are not yet inside-out capable, document editors will probably use outside-in throughout to preserve uniformity.

Inside-Out Throughout

Ultimately, OLE containers will blend embedded objects with native data seamlessly so that the distinction dissolves. Inside-out throughout containers will become more feasible as increasing numbers of OLE objects support inside-out activation.

Outside-In Plus Inside-Out for Preferred Objects

Some containers might use an outside-in model for large foreign embeddings but also include some inside-out objects as though they were native objects (by honoring the ACTIVATEWHENVISIBLE preference). For example, a compound-document editor might disguise form-control objects as inside-out native data while activating more sluggish spreadsheet and chart objects as outside-in.

Switch Between Inside-Out Throughout and Outside-In Throughout

Visual programming and forms layout design containers often include the concept of design and run modes. This class of container would typically hold inside-out capable (if not preferable) objects and would alternate between outside-in throughout when designing and inside-out throughout when running.

Separate from the issue of how and when an object activates is the question of how much to distinguish or blend the object and container user interfaces. Again, the eventual goal is to factor components' and containers' user interfaces into complementary self-contained sets with little or no overlap so that a uniform combination of the two is presented to the user. However, recognizing that many initial in-place components are derived directly from stand-alone applications with fully defined and familiar user interfaces, the initial OLE user-interface strategy creates a clear context switch as one interface replaces another. This strategy factors object applications only at the broad level (workspace, active editor, selected object) and exchanges control of the bulk of the interface (active editor menus, control bars, and so on) between the container and object across activations. The coarse factoring leaves an object application's original menus intact so that experienced users find it familiar to use as an OLE object. This, in combination with the active hatch border, delineates it from the container application.

However, as more refined inside-out objects become available and as containers use OLE Automation to drive components programmatically, objects may be allowed (or even forced) by the container to surface few if any user-interface components. As a result, the delineation between container and component application might become unnecessary or even inappropriate. This use of an object casts it more as a native component of a container rather than as a peer application. As such, neither the menus nor hatch border would be displayed. As a general guideline, if the object's menus appear in a menu bar (meaning the object's request was granted by the container), the active hatch border should be displayed to designate the active region. Conversely, if the object's menus do not appear in the menu bar (because the object did not request it or the container refused to do so), the object is being accommodated by the container's user interface, and it is not necessary to display the hatch border. Even so, the container can still apply its own focus border style as it would for its own objects.


Undo for Active and Open Objects

Because different applications take control of a window during in-place activation, commands like Undo or Redo require special consideration. How the actions performed within an in-place object are reconciled with actions performed on the native data of the container as it relates to Undo is the main consideration for Undo functionality. The OLE Undo model specifies a single undo stack per open window. Thus, all actions that can be undone, whether generated by an in-place object application or the container's application, conceptually accumulate on the same undo-state sequence. Issuing Undo from either the container application's menus or from the in-place object application's menus undoes the last action (that is capable of being undone) performed in that open window, whether it occurred within an in-place object or not.

If the container has the focus and the last action in the window occurred within an embedded object, Undo activates the embedded object, reverses the action, and leaves the embedded object active in place. In the case of open object applications, each open window manages a single stack of states that can be undone. Actions performed in an open object application are local to that application's window and consequently must be undone from there. Actions in the open object application (even if they cause updates in the container) do not contribute to the undo state of the container.

Sending a verb to an object (or double-clicking) cannot be undone, so that action does not add to a container's undo stack. This includes opening an object into another window for editing: if the user unintentionally opens an object, the object application must be closed as usual because that action cannot be undone from the container.

The next figure shows two windows: container Window A has an in-place active object, and Window B, an open object application. There has been a total of nine actions between the two applications, all of which can be undone in the order and at the location indicated by the circled numbers. The resulting undo stacks are displayed beneath the windows. The in-place and native actions within Window A have been serialized into the same stack, while the actions in Window B have accumulated on their own separate stack.

The undo actions discussed so far are bound to a single window. When a single action spans multiple windows, it can be undone from the last window involved. In most cases, the user is focused on that window when the mistake is realized. Therefore, if the user had dragged and dropped an item from Window A into Window B of the following figure, the action would be appended to Window B's undo stack. The entire effect of the action can be undone even if multiple windows were affected. Unfortunately, no technical support exists in OLE for this sort of multiwindow undo coordination. As a result, multiwindow actions create, at best, independent actions that cannot be undone in each window participating in the action.

Undo stacks of in-place and open objects.

Note  The stack of undo states pictured in the preceding figure is not meant to imply a specific number of undo levels, but presents a timeline of actions that can be undone at different levels, depending on the degree of undo the container/object supports.


Menu Commands

The addition of in-place activation functionality presents potential problems, as several objects may be required to expose their commands in the interface. If no restrictions are imposed on where these commands can appear, users may be faced with an interface that changes unpredictably. In addition, the user will be unable to reliably identify the object to which a given command applies. Developers are faced with the task of accommodating complex negotiations among objects to build menus. To avoid these problems, OLE defines a classification of menus that segregates the interface based on menu groupings. These include the following categories:

•	Workspace menus

•	Active Editor menus

•	Selected Object submenus

This classification system is designed to enhance the usability of the interface by regularizing and limiting the changes that occur as different objects come and go from the interface. Following are the descriptions of these menu categories.


Workspace Menu(s)

Objects that are the top container in an application control the workspace of that application. As a result, these objects are responsible for the organization of windows in a multiple-document-instance (MDI) application, file-level operations, and how edits are ultimately saved. These objects must supply at least two menus, one of which is a single File menu containing file-level commands such as Open, Close, Save, and Print. If the object is an opened-object object application, the commands in its File menu are modified according to the guidelines shown in the next figure. OLE updates are automatic between the open object application window and the container document, eliminating the need for the Update command or the update confirmation upon closing the open object application.

The object application and container file menus.

As indicated in the next figure, MDI applications must also supply a Window menu to control the child windows of the application. Workspace menus remain in the menu bar at all times no matter which object is active. Note that the View menu is not a workspace menu, but an active-editor menu, so any document-level viewing/zooming commands that are to be kept available must be moved to a workspace menu. If present, the Window menu is the preferable location for them. Also, if the View menu needs to contain a zoom command, this command name should indicate that it will scale the object rather than zoom in on or out of the entire document.

Workspace Menus.


Active-Editor Menu(s)

Most OLE objects must supply active-editor menus, similar to those shown in the next figure, that hold the majority of their commands. This includes those commands that actually operate on object content. Commands like moving, deleting, searching and replacing, creating new items, applying tools, styles, and help would be located in these menus. Active-editor menus occupy the majority of the menu bar's space and may be slightly different depending on whether the object is activated in place or opened. As the name suggests, active-editor menus are executed by the object that is currently active (which might be the top container application). None of these menus can be named File or Window because those titles are reserved for workspace menus.

The title bar is not affected by in-place activations; it always displays the container (workspace) application name followed by the document name. (This is a change from an earlier design which dynamically presented the active object application name followed by <shortform> in <container-document>.

Active editor menus.

Objects that use direct manipulation or OLE verbs as their sole user interface do not need to provide active-editor menus. Nor do they need to alter the menu bar when activated.


Selected Object Submenu

All container applications must include a menu that contains the verbs for any OLE objects currently selected in them. This menu is preferably a submenu of an active-editor menu, such as the Edit menu, or possibly exists as its own menu on the menu bar. The submenu uses the short form of the object type name. If the object is a link, it must include the word "Linked" preceding the object type name.

The first letter of the appropriately localized word for "Object" should be underscored as the stable mnemonic character for keyboard users. When there is no object selected, the menu command is simply "Object" and it is disabled.

As illustrated in the figure below, the syntax for the submenu of the active-editor menu is:

•	[Linked] <shortform> Object -> <verb0>, ..., <verbN>, Convert...

The Convert... command is unavailable if there are no other types (classes) registered that can convert or emulate the selected object.

Selected-object menu.


Menu Summary

To summarize how these menus mesh on the menu bar:

•	Workspace menus are present all times in each window whether it is the container application or an opened object application. They include the File and Window menus.

•	The File menu commands in an open object application window are modified to point back to the container document and do not include the Update command or the update confirmation found in the OLE 1 user interface.

•	The currently active object (possibly the container itself) supplies the active-editor menu(s) as the bulk of the menu bar.

•	If an object is selected, its commands are available through its selected-object submenu within an active-editor menu.


Keyboard Commands

In addition to integrating the groupings of menus already discussed, the keyboard commands associated with them must be blended as well. These keyboard commands include the following categories of key combinations:

•	Mnemonic Keys

The mnemonic keys are those Alt key sequences that pull down menus and choose commands according to which letters are underscored. The rule is Alt+<letter> to pull down the corresponding menu, followed by additional letters to invoke the command.

•	Shortcut Keys

The shortcut keys are the quick-access key commands that can use any combination of the Ctrl, Shift, and Alt modifier keys.

The integration strategy is intended to offer all of the mnemonic and shortcut keys used in the previously described workspace, active-editor, and selected-object menus to the user without ambiguity due to duplicate assignment. To do this, the reserved key sequences in each of the three menu categories must not be reused by the other categories.


Mnemonic Keys

The only chance for the mnemonic keys used in workspace and active-editor menus to conflict is between top-level menus, because presumably command mnemonics within each menu have already been uniquely assigned. Applications must be informed about which menu title letters they may use.

•	Workspace menus should use File and Window as their mnemonics (or the appropriately localized File and Window words).

•	In-place object applications may underscore letters (for mnemonics) in their active-editor menu titles other than those used for workspace menus. (If an in-place object application has previously existed as a stand-alone application, its menus probably have avoided these letters already.)

•	OLE verbs should be registered with uniquely assigned mnemonics.

If the same mnemonic is used for more than one menu command, pressing Alt+<letter> cycles through the mnemonic candidates. To choose a specific menu or command in this manner, press Enter when the menu or command is highlighted.


Shortcut Keys

The object application is given the first opportunity to intercept a shortcut key event, passing it on to the container application if no match is found. The object application must avoid using shortcut keys that are likely to be used by a container application. Both container and object applications should adhere to the shortcut key guidelines outlined in "The Windows Interface: An Application Style Guide" for their workspace menu commands and active-editor menu commands. Active-editor commands should make use of the standard editing shortcut keys and not the workspace menu shortcut keys.

Note  There is no provision for registering shortcut keys for commands on the selected-object menu shown in the previous figure.

In the event the container and object application share a common shortcut key, the active editor intercepts it. This means that if the object application is active, it gets the shortcut key and takes appropriate action, with no negotiation or passing to the container application. Similarly, if the container application is the active editor (even if an OLE object is selected), it intercepts and services the key command. In these collision cases, users need to set the focus to the application that is to respond to the shortcut key.


Pop-Up Menus

Just as keyboard commands are shortcuts to the pull-down menu commands, pop-up menus (also called shortcut or context menus) provide quick access to frequently used and contextual commands by means of the mouse. If an application implements pop-up menus, they can be used as follows.

Pop-up menus are an auxiliary command interface and therefore contain a subset of the pull-down menu commands. This type of menu looks like a standard drop-down menu in that it contains a frame, a shadow, and a list of commands with ellipses (where appropriate) and separator lines. However, pop-up menus are popped-up at the pointer's location, as shown in the next figure, so the user doesn't have to navigate to a menu or button bar. Pop-up menus take up no screen space since they are only displayed upon demand.

When a user presses down the right mouse button, a pop-up menu appears, two pixels beneath and to the right of the current pointer position, to display commands that relate to the object beneath the pointer. Unlike a drop-down menu, no items on the pop-up menu should initially be highlighted; only after the pointer has entered into the menu should a command be highlighted. To prevent the user from inadvertently selecting a command, the pointer is not initially positioned on any of the menu commands. The position of the menu allows the user to move the pointer down into it conveniently.

Text selection pop-up menu displayed by a word processor.

The pop-up menu is displayed when the user presses down the right mouse button while the pointer is over an object or selection of objects. If the user moves the pointer into the menu and releases the button, the command beneath the pointer is executed. If the user simply releases the button at the button-down point (specifically within four pixels of the button-down point), the menu remains displayed. However, if the user moves the pointer and releases the button outside the menu, then the menu is removed (canceled). This behavior is identical to that of a drop-down menu when it is clicked in the menu bar.

Clicking either mouse button within the pop-up menu while the menu is displayed carries out that command and removes the menu. While the user holds down the mouse button, selection highlight feedback is provided on the selected command. The action is not executed until the user releases the button on the command, allowing the user to drag through the menu and highlight other commands or drag off and release to cancel the menu.

While a pop-up menu is displayed, clicking either mouse button outside the menu cancels the menu. This means that there is at most one pop-up menu displayed at any time. As pointed out in an earlier paragraph, it is only after the pointer is dragged down the menu that any command should be highlighted. As with drop-down menus, the ESC key cancels the menu at any point.

Generally, a pop-up menu is displayed over an explicit selection; such as highlighted text in a word-processor document. However, pressing the right button over certain objects both determines the selection and brings up the pop-up menu. For example, pressing the right mouse button over a discrete target such as a spreadsheet cell or an icon could automatically make it the current selection and display its pop-up menu.

The right mouse button may be pressed over items contained outside a normal selection context, such as over interface controls or the screen background. For instance, a pop-up menu over a scroll bar may display useful navigation commands; a pop-up menu over a control bar button may display commands to re-program its function. Invoking pop-up menus on these objects has no effect on the selection within the window's content; they are completely separate domains.

If the pointer is positioned so the menu would appear off the bottom or right side of the screen, the menu position is adjusted so that it is fully visible. Generally, this means that the top-left corner of the menu is slid to the left to avoid the right screen edge or slid up to avoid the bottom screen edge. If the menu would appear both off the bottom and the right edge of the screen, the pop-up menu would be positioned to the left and above the pointer.

The pop-up menu interface provides no mnemonic or shortcut keys because they are defined in terms of the drop-down menus. Pop-up menus are an efficient means of accessing commonly used contextual commands, so they should contain only frequently used commands and not simply repeat entire drop-down menus. It is recommended that commands in pop-up menus appear from top to bottom in decreasing frequency of use.

Note  Including too many commands or multilevel cascade/hierarchical submenus can defeat the purpose of the pop-up menu. While cascade submenus are acceptable, they should be limited to a single level.

The appearance of pop-up menus depends somewhat on their state. When an object is active, like the following Microsoft Paintbrush Picture, pop-up menus provided by the object application are in effect. The next figure shows how a pop-up menu for an active bitmap object might appear.

When an object is selected, the pop-up menu is provided by the container application and it should list each of the object's verbs in-line rather than presenting them in a cascade menu. The syntax should be:

<verb> [Linked] <shortform>

It is not necessary to append the word "Object" after the short-form name because mnemonics are not present in pop-up menus and including "Object" would make the menu unreasonably large.

Pop-up menu for an active picture object.

Typical pop-up menu commands could be "Edit Picture Link" or "Play Recording." The Convert... command follows the verb(s) as shown in the figure below.

Pop-up menu for an embedded picture object.

For uniformity, pop-up menus on a selected object should display the object's verbs in order and in line, unlike the cascade verb menu recommendation for the Edit pull-down menu. It is also recommended that a pop-up menu have no more than ten commands. Cascade menus or dialogs are acceptable.


Control Bars, Frame Adornments, Floating Palettes

An object application's commands and interface controls should be presented in their entirety during in-place activation so that users have full access to object application functionality. OLE 2 employs a replacement strategy for arbitrating control bars, frame adornments, floating palettes. The next three figures show some examples of these devices.

Control bar examples.

Frame adornment examples.

Floating palette examples.

Like menus on the menu bar, control bars, floating palettes, and frame adornments are displayed and removed in their entirety. The display of floating palettes, which are independent from the container window and are solely under the control of the active object, is relatively simple. Displaying control bars and frame adornments is more complex, because it might require using the same space in the container application window. This could require repainting, relocation, or resizing of some objects.

In keeping with document-centered interaction, swapping active objects' interfaces should keep disturbances of the document's appearance and position to a minimum. Because drop-down and pop-up menus are confined within a particular area and already follow some of the conventions established for Windows applications, their integration into the container is fairly straightforward. However, tool bars and frame adornments are less predictably integrated.

From OLE's perspective, control bars, frame adornments, and floating palettes are all basically the same, differing primarily in their location and the degree of shared control between the container and object application. As indicated in the next figure, controls of this type can reside in four locations in the user interface. The choice of location is principally determined by the scope over which the particular control or tool applies.

Four possible positions for interface controls.

•	Object Frame

Object-specific controls like a table header or a local coordinate ruler can be placed directly adjacent to the object for tightly coupled interaction between the object and its interface. An object, such as a spreadsheet, may include scroll bars if its content extends beyond the boundaries of its frame.

•	Pane Frame

Controls and tools that are specific to a view or a single document should be located at the pane level. Rulers and viewing tools are common examples.

•	Application Window Frame

Tools that apply to the entire document (or documents in the case of an MDI application) may be attached just inside any edge of the application window frame. Popular examples include ribbons, drawing tools, and status lines. Because of Windows' behavior, however, the MDI child windows shift up and down as different-sized toolbars come and go with context switches. This can be disruptive to the user's task, so it is recommended that toolbars either float or be placed elsewhere when MDI child windows are in the "restored" state.

•	Floating Palette

Objects can "float" their tools above the document, allowing the user to arrange them as preferred.

As an object becomes active, it requests a specific area from its container in which to post its tools. The container may do one of the following:

•	Replace its tool(s) with those of the object if the requested space is already occupied by a container tool.

•	Add the object's tool(s) requested space if it is not already occupied by a container tool.

•	Refuse to put up the tool(s)at all. (This is the least preferable.)

If container control bars are still visible while an object is active (like the pane ruler in the preceding figure), they are available for use simply by interacting with them. However, if object-application control bars contain "workspace" commands, such as save, print, or open icons, they must be disabled and should be visually distinguished as unavailable.

As windows are resized and the document is scrolled, the following interface controls are forced to be clipped with respect to their containers.

•	An active object and its frame adornments are clipped by its immediate window pane just like all document content. Frame adornments can be thought of as handles that lie in the same plane as the object. When the object is clipped, the visible part of the object can be edited in place and the visible frame adornments are operational. Some containers may scroll at certain increments that prevent portions of an embedded object from being edited in place. For example, a large picture embedded in a worksheet cell of a spreadsheet application could exhibit this behavior. The spreadsheet application scrolls vertically in one-row increments so that the top of the pane is always aligned with the top edge of a row. If the embedded picture is too large to fit within the pane at one time, its bottom portion is always clipped and, consequently, never can be viewed or edited in place. In cases like this, the user will probably open the picture into its own object application window for editing. Frame adornments of nested embedded objects are also clipped by the immediate window pane, but not by the extent of any parent object.

Objects close to the edge of their container's extent or boundary may potentially bring adornments to the surface, which will extend beyond the bounds of the container's defined area. In this case, if the container can display items that extend beyond the edge, it should display all the adornments; otherwise, the adornments should be clipped at the edge of the container. The container should not temporarily move the object with respect to the content just to accommodate the adornment's appearance.

•	A pane-level control may be potentially clipped by the application window frame (in the case of MDI) and of course an application window-level control may be clipped by other Windows applications.

•	A floating control floats above all windows.

As indicated in the next figure, alternating between control bars of different sizes in the document pane should have no effect on the content's position as it relates to the pane. In other words, there is no automatic scrolling provision to guarantee the active object remains in view, because in many cases that is not possible. Consequently, think of pane control bars as lying atop the document's content, requiring the container to repaint parts of the pane as control bars are removed or replaced with smaller ones.

Interface control clipping.

The content's position is preserved to avoid disturbing the user's focus with the active object bumping down to accommodate the new set of control bars, and bumping back up when it is deactivated. This rule is to be followed even if the arriving control bars will clip the object of interest. There is one exception: when the container is to scroll the document as a result of a control bar change. If the document is at its very top or bottom and removing a control bar would expose an undefined area (an area beyond the document's extent), then the document may scroll to take up the space left by the control bar.

When the focus is removed from a window that contains an active object (either because another MDI child window or a different application has taken focus) the following guidelines should be observed for switching the user-interface elements.

•	With respect to MDI, those user-interface elements that the active object puts up in the space shared by MDI documents (such as menu bar, frame level toolbars, and the floating palette layer) should be removed as focus is given to another MDI document. The new MDI document will then show the user-interface elements of its pending active object (if it has one) in these spaces. This guideline does not apply to workspace-provided tools that equally apply to all MDI documents. Such workspace elements should persist between document focus changes.

•	Object user-interface elements that are wholly contained within the MDI document window (such as pane rulers, object-frame adornments, and the active hatch pattern) remain visible and unchanged when their document (or application in the case of SDI) loses focus. This scheme preserves the editing context so the user can return focus and easily resume editing.


Embedded versus Linked Objects

An embedded OLE object contains data that retains the original editing and operating capability of its creating application while the embedded object resides in a container document of another application. An embedded object is wholly and exclusively contained within the container document and is saved and moved with that document.

A linked object is an updating image of another piece of data residing at another point in the same document or within a different document altogether. A linked object does not contain data that can be edited by the containing application, but rather an image of the data referred to as presentation data, often a Windows metafile. This image of the source data provides the user with a visual representation of the linked object and provides the user with access to the object's verbs.

Note  Based on a linked object's appearance, a user may interact with it as if the source data of the object was contained in the document. In many instances, users interact with embedded and linked objects without differentiating between them.


Automatic and Manual Updating

When a link is created, it is automatic by default. Whenever the appearance of its source data changes, the automatically linked object's appearance also changes without any request from the user. The user can specify a link as manual in the Links dialog box. A manually linked object is updated only when requested by the user. The update request is issued by choosing the Update Now button within the Links dialog box and optionally in conjunction with the container's usual "update fields" or "recalc" action.


Verbs and Links

An object responds to the same set of verbs defined for its class whether it is embedded or linked. Issuing verbs like "play" or "rewind" to a linked sound object appears to activate the object in place. However, issuing a verb that is intended to alter the object's content data (such as "open" or "edit") exposes the linked source data by launching the associated object application.

While links may play in place, they may not edit in place. Editing in place implies that the actual data resides, even if only temporarily, in the document containing the link. It is important to avoid blurring the fact that the link source holds the object's data and that the data does not move to the container of the link for editing. So, in order for link sources to respond to editing verbs, the source object, and any containing objects and documents, are opened to properly respond to the verb.

The most common case of a link responding to an edit verb is when the user double-clicks a link whose primary verb is edit. The source document opens to reveal the link source object for editing. This functionality might appear similar to opening an embedded object into a separate window, but this is not the case. Editing a linked object is functionally identical to launching an application on the link source's document, such as through the Program Manager or File Manager. Unlike an opened embedded-object application, which is bound to the container application window and causes the hatch pattern across the object, there is no special association between the link-source and link-destination application windows. The applications operate and close independently.

Note  The link source may be on an unmounted network server, so it may require the user to establish access to the source before OLE can open it. If the user tries to open an available source document, the container should post a message such as "Please connect to \\foo\bar and retry opening the <object_type> link."

It is the responsibility of the object application, when implementing the execution of an editing verb, to notify its immediate container that the source must be exposed. Below are the three different situations in which an object might notify its container:

•	Embedded

If the verb is executed by an embedded object, the container document and parent objects are already exposed, so there is no need to start the object's container.

•	Already Opened Link Source Document

If the verb is issued to a link source whose document is already opened, the currently active view of that document (in the case of a multiview or multipane application) scrolls as necessary and unveils the source object ready for interaction.

•	Closed Link Source Document

The verb notifies its immediate container requesting it to expose the source to the user. All additional containers are opened, and the link source is fully exposed and ready for editing.


Types and Links

A link's type is a cached copy of its source's type taken when the last update was issued. Because it is possible to change the type of a link source object, all links derived from a converted object bear the old type and verbs until either an update occurs or the link application is launched. Because out-of-date links might display obsolete verbs to the user, on issuing a verb from a link to its source, the container should compare the cached link type with that of its sources. If the cached link type has changed, the container should do one of the following:

•	If the verb issued from the old link is syntactically identical to one of the verbs registered for the source's new type, execute the verb index of that new verb.

•	If the issued verb is no longer supported by the link source's new type, issue an error message like the one in the figure below.

Error message for obsolete link verb.OBSLTMSG

The link should then adopt the source's new type and display the new verbs in its menu.


Maintaining Links

A link has three properties: the name of its source data, its type (or class, as it's known internally), and its updating basis, either automatic (default) or manual. As shown in the figure below, the container application supplies a Links dialog box that allows the user to change the name of its source data and its updating basis. A link's type is determined by its source's type, and it can be changed by converting it to another type.

The Links dialog box.

If a link's source cannot be found, "Unavailable" should appear in the status column. Multiple link sources from the same file are grouped and indented beneath the common filename. Only the eight-character filename with a three-character extension appears in the list box; the full path is exposed in the lower portion of the dialog box.

Allow 15 characters for the Type field, and enough space for "Automatic," "Manual," and "Unavailable" to appear completely. As each link in the list is selected, its type, name, and updating status appear in their entirety in the lower left of the dialog box.

Break Link effectively disconnects the selected link(s). Update Now forces the selected link(s) to connect to their sources and retrieve the latest information.

The Open Source button opens the source document(s) for the selected link(s). The Open Source button should be the default when a user is clicking within the links list, so that double-clicking a list item opens the source of the particular link.

The Change Source dialog box.

The Change Source button in the Links dialog box invokes a Change Source dialog box (shown in the figure above that is similar to the standard File Open dialog box. The Change Source dialog box allows the user to respecify the link source. It edits only the file portion of the link source, so it is up to the user to enter the subdocument portion of the string manually if the source points to a location within the file. The user may type a source name that does not designate an already existing object. If the user selects OK, the Change Source dialog box prompts "Invalid Source. Do you wish to correct it? Yes, No." Answering Yes returns to the Change Source dialog box so the string can be corrected; answering No causes the container to hold onto the unparsed display name of the link source until such time later as the user successfully causes the link to connect to a newly created object that satisfies the dangling reference. Some containers may allow users to connect only to presently valid links.

If multiple links share any portion of their source's names and the user edits that common portion for one link, the user is given the option of making the same change for the other similar links. The dialog box shown in the next figure lets users redirect links to new locations, and is useful when directory or file names have been changed.

Changing additional links with same source message.


Object Transfer Model

This section explains how objects are moved, copied, and linked within and across documents. This is done using:

•	The standard Clipboard method invoked through commands from either drop-down or pop-up menus.

•	The drag-and-drop method, which lets users drag objects directly from one location to another.

The Clipboard method allows the user to move, copy, and link data and specify data formats. The drag-and-drop method lets the user quickly perform moves, copies, and links when little navigation is needed. The two approaches cooperate to give the user a global and expedient means of arranging information in documents.


Clipboard Method of Transferring Object Data

Using the Clipboard method, a user performs the following steps (refer to the next figure):

•	Make a selection in the source document.

•	Choose either Cut (Ctrl+X) or Copy (Ctrl+V) from the menu (either the Edit pull-down menu or a pop-up menu).

•	Navigate or edit as preferred within the destination document.

•	Choose an insertion point (or a selection if it is to be replaced) within the destination document.

•	Choose Paste [<shortform>] (Ctrl+V) or Paste Special... to insert the Clipboard's contents.

The Paste [shortform] and Paste Link [shortform] menu commands.

The Cut/Paste operation removes the user's selection from the source document and relocates it at the insertion point in the destination document, while the Copy/Paste operation inserts an independent duplicate of the selection, leaving the original unaffected. Choosing Paste Special... displays the dialog box shown in the next figure to give the user explicit control over how to paste the Clipboard's contents. The default Paste operation embeds the object if possible. If the destination document application can edit the Clipboard data, the application pastes the data format it is capable of editing. For example, pasting a bitmap object from one bitmap editor to the document of another causes the object to become part of the destination document's data instead of an embedded object.

If the default settings for the Paste operation result in embedding the object, the command should read Paste <shortform>; for example, Paste Worksheet and Paste Recording. Similarly, if the container application includes the Paste Link command, the command should read Paste Link Worksheet, Paste Link Recording, and so on. If the Paste command appears on the menu with no item extension, the data will be pasted as native information without OLE intervention. Note that the short form of the object type is used for <shortform>.

The Paste Special dialog box shows the name of the source data (using moniker-type syntax), provides the Paste and Paste Link option buttons, and lists the possible data formats associated with each option button. A section of result text describes the result of the currently chosen function and format. Pictures are displayed with the Help text to communicate the end result of the chosen operation. Also note the same Display as Icon option found in the other dialog boxes.

When the Paste option button is selected, the list box shows the full object type name (without the trailing "object") and other native data forms. When the Clipboard contains an already linked object, its object type should be preceded by "Linked" in the format list. For example, if the user copied a linked Microsoft Excel 5.0 Worksheet to the Clipboard, Paste Special would show "Linked Microsoft Excel 5.0 Worksheet" under the Paste format options since a Paste would insert an exact duplicate of the original linked worksheet.

Native data formats should be expressed in the same terms the destination application uses in its own menus. When Paste Link is chosen, both the object type (full form) and any native format that supports linking appears. The default formats for the Paste and Paste Link options are the same as what the default Paste [<shortform>] and Paste Link [<shortform>] would use as formats.

The Paste Special dialog box with different selections.

The recommended Result text for the Paste Special dialog box is listed below.

Function	Result Text
Paste.[Linked] Object	Inserts the contents of the Clipboard into your document so you may activate it using <object app name>.
Paste.[Linked] Object as Icon	Inserts the contents of the Clipboard into your document so you can activate it using <object app name>. It is displayed as an icon.
Paste.Data	Inserts the contents of the Clipboard into your document as <native type name and optionally an additional help sentence>.
Paste Link.Object	Inserts a picture of the Clipboard's contents into your document. Paste Link creates a link to the source file so changes to the source file will be reflected in your document.
Paste Link.Object as Icon	Inserts an icon into your document that represents the Clipboard contents. Paste Link creates a link to the source file so that changes to the source file will be reflected in your document.
Paste Link.Data	Inserts the contents of the Clipboard into your document as <native type name>. Paste Link creates a link to the source file so that changes to the source file will be reflected in your document.
In implementing the Paste Special (the preceding figure) and Insert Object dialog boxes, it is recommended that container applications use monikers to hold the paths to link sources. Monikers perform the binding to the object and properly track names across networks.


Drag-and-Drop Method of Transferring Object Data

Using the drag-and-drop method for object movement, a user can directly transfer objects from document to document as well as supply objects to system resources such as printers and mailboxes. The user's steps for performing a drag and drop are:

•	Press the left mouse button with the pointer over the object to grab it. For objects such as text or table cells, it may be necessary to first select the correct range. Depending on its visual representation, an object may also be dragged by a move handle or its frame border.

•	Holding the left mouse button, drag the object to the intended target location.

•	Release the left button over the intended insertion point to drop the object.

The pointer should change to the following shape when it is over invalid drop targets.

Unmodified drag should almost always perform a move operation. Only in exceptional cases (like dragging to a printer) should unmodified drag not perform a move. CTRL-drag should always perform a copy operation. SHIFT-CTRL drag is the suggested modifier for linking, and ALT-drag is suggested for insisting a move operation (mainly for backwards compatibility with existing applications that use it now). In cases where a drop of a particular object or the insisted operation is not permitted (because of improper type or context), the pointer shown above indicates that a drop will not happen. Typically, the pointer's appearance generally suggests which type of transfer is to be performed as feedback to the user.

For example, for a cell selected in a range of embedded worksheet cells, as shown in the figure below, the user would start the drag by grabbing the selection's border with the mouse and pointer.

Select and start drag.

The "move" pointer appears by default and the user has a choice of six different categories of targets. These are enumerated in the next figure. The significance of each of the target categories in this figure is described below.

•	Drag Scrolling

Dragging usually passes over pane borders, but if the user wants to scroll to an out-of-view drop target, the pane can be scrolled while the user is dragging the object. After the document has scrolled to the proper point, the user can drop the object at the intended target.

•	Container Area

An object can be dropped in the container of the currently active object.

•	Active Object

This drag operation is completely internal to the spreadsheet application shown in the next figure and is handled like a usual dragging operation in the application.

•	Other Document

In this case, the drag operation has left the pane (and application window) and is being dropped into another application.

•	System Resource (Icon)

Here the object is being dropped on a particular printer that highlights by means of the selection color. Copy is the default operation for the drag-and-drop operation to a system resource.

•	Illegal Target

When the target, such as the screen background in the next figure,  cannot accept a drop, the "not allowed" pointer should be displayed.

•	Inside-out Activated Object (not shown in the next figure)

Since inside-out activated objects are effectively always active, they can serve as a drop target. They should provide accurate drop-target feedback to indicate the result of the dropping item.

Note  The "not allowed" pointer should be displayed when dragging over a linked object because OLE does not support dropping on linked targets.

Drag scrolling, container area, and active object types of targets are possible in any window, not just the active application window. The plus sign near the arrowhead of the system resource pointer indicates that the default drag function is copy (not move). The user may force a move operation by holding down the shift key, force a copy operation by holding down the ctrl key, or force a link operation by holding down both the shift and Ctrl keys. The drag is qualified by the modifier keys only while they are held down; releasing the modifiers before the drop results in a default drop. The pointer's appearance suggests which type of transfer is to be performed as feedback to the user. (See "The Windows Interface: An Application Design Guide" for these pointer appearances.)

Possible drag-and-drop targets.

These drag-and-drop operations produce the same end result as Cut/Paste, Copy/Paste, and Copy/Paste Link, respectively, but there is no implied bearing on the state of the Clipboard because the two transfer methods are completely independent.

When a user drags an icon from the Windows File Manager into a document, it should effectively perform an "insert object from file," exposing the content of the object if at all possible. Of course, if the file is not OLE aware, it is embedded and continues to be displayed as a document icon.

An inactive object cannot be used as a drop target because it can't give precise feedback on where within itself an item would be dropped.

It is further suggested that the "drop-point" indicator (the checkered I-beam) shown below be displayed with the pointer as target feedback when dragging over text areas. Because text areas are a common drop site, it is particularly necessary for the user to see this consistent feedback.


Dialog Box Messages

If the user attempts to launch an object application that cannot be run as a stand-alone application, the error message in the figure below should be issued.

Warning Message, object application cannot be run as a stand-alone application.

If the container application fails to locate the requested object application (stored in the registry) when the user selects the entry from the Insert Object dialog box, or when the user double-clicks on an object, the error message shown in the next figure should be displayed. Browse... invokes the standard File Open dialog box, and the user-supplied path should be entered in the registry database as the new object application path.

Note  To ensure that the registry is current, object applications should register themselves, or verify registration, when started.

Warning Message when object application cannot be found.

An object application can be busy or unavailable for several reasons. For example, it might be busy printing, waiting for user input to a modeless error message, or it could be hung or accidentally deleted. If the object application is not available, the warning message in the figure below should be displayed.

Warning Message when object application is busy.

The progress indicator shown in the next figure should be displayed while the links are being updated (when a document containing automatic links is opened). The Stop button interrupts the update process and prevents any additional links from being updated.

Progress Indicator for link updating.

If some of the linked files are unavailable, the warning shown in the next figure is displayed. This dialog box contains OK and Links... buttons. The OK button closes the dialog box without updating the links. The Links... button displays the Links dialog box  with all the links listed in a list box. Unavailable linked files are marked with the word "Unavailable" in the third column of the list box. The user can attempt to locate the unavailable files by using the Change Source dialog , which is available from the Change Source... command button in the Links dialog box.

Warning Message for unavailable links.

If the user issues a command to a link whose source is unavailable, the warning message in the next figure should be issued. The unavailable link will be marked as such in the Links dialog box.

Warning Message for issuing a command to an unavailable link.


Status Bar Messages

Menu Commands	Status Bar Message
File Menu
Update <container-document>	Updates the appearance of this <descriptive type name> in <container document>.
Save Copy As...	Saves a copy of <descriptive type name> in a separate file.
Exit & Return to <container-document> If the open object is within an MDI application with other open documents, this command should simply be "Exit." There is no way to guarantee a successful "Return to <container document>" after exiting since the container might have been one of the other documents in that MIDI instance.	Exits <object application> and return to <container-document>.
Edit Menu
Paste <shortform>	Inserts Clipboard contents as <descriptive type name>*.
Paste Special...	Inserts Clipboard contents as a linked object, embedded object, or other format.
Paste Link <shortform>	Inserts a link to <descriptive class name> Object from <source-document>.
Insert Object...	Inserts a new embedded object.
<verb> [Linked] <shortform>**	None.
[Linked] <shortform> Object ->	Applies the following commands to <descriptive class name> Object.
[Linked] <shortform> Object -> <verb>	None.
Links...	Allows links to be viewed, updated, opened, or canceled.
Options (Preferences) Menu
Show Objects	Displays the borders around objects (toggle).
Mouse Interface
When an object is selected	Double click to or press ALT+ENTER to <primary-verb>***-<descriptive type name> Object.
*The <descriptive type name> is identical to the initially highlighted value in the list box in the Paste Special dialog box. This status bar message indicates the data format used to paste Clipboard contents. **If no verb in the registry is specified, "Activate" should be used as a default verb. ***The verb that is stored in the registry will contain "&" (a mnemonic indicator) that must be stripped out before the verb is displayed in the status bar.


Component Object Interfaces and Functions

IUnknown Interface

IClassFactory Interface

IMalloc Interface

IExternalConnection Interface

IEnumX Interface

IMarshal Interface

IStdMarshalInfo Interface

Custom Marshalling Functions

Component Object Functions

DLL Initialization Functions

Object Creation Function

File Time Conversion Functions

DLL Object Class Functions

Validation Functions

OLE Library Functions

Object Class Conversion and Emulation Functions

Error Handling Functions and Macros

Debugging Functions

String and CLSID Conversion Functions

Registry Functions


IUnknown Interface

Reference Counting Rules

The IUnknown interface is used to obtain and manage interface pointers. IUnknown is OLE's base interface; all other OLE interfaces are derived from it. Therefore, IUnknown must be implemented by every OLE-aware application.

IUnknown contains the following methods:

DECLARE_INTERFACE(IUnknown)

{

  HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

  ULONG AddRef (THIS )  ;

  ULONG Release (THIS ) ;

};


Reference Counting Rules

OLE uses a relatively simple reference-counting mechanism to manage the freeing of interface pointers on an object. IUnknown::AddRef and IUnknown::Release keep track of the reference count, or the number of interface pointers being held, for an object. When an application obtains access to interface pointer pInterface, for example, pInterface->AddRef is called to increment its reference count. When the application has finished using the pointer, it calls pInterface->Release to decrement the count. The interface implementation may be freed when the reference count for the interface reduces to zero. The object exposing the interface may be freed when the reference count for the object as a whole is zero.

The general reference counting rules are described in the following paragraphs.

If a local copy of an interface pointer is made from an existing global interface pointer, the local copy must be independently reference-counted. This separate reference count is necessary because the global copy can be destroyed while the local copy is in use.

Global copies of interface pointers passed as function parameters must be independently reference-counted.

Interface pointers returned from functions must be reference-counted. The call to AddRef must be made by the function returning the pointer; that is, the function that was called.

Interface pointers passed as function parameters are considered temporary and do not need to be reference-counted. These pointers can be used by the function implementor to call interface methods and can safely assume that the object exposing the interface will remain alive during the calls. However, if an interface pointer is returned from one of these method calls, this pointer is considered fully owned by the recipient and will have been reference-counted by the callee, as stated in the previous rule.

When an object implements several interfaces, the object may have one implementation of IUnknown::QueryInterface that handles requests for all of the object's interfaces. If individual callers request pointers to separate interfaces, these pointers must be independently released. That is, if an object passes out ptr1 and ptr2, the caller with ptr1 must invoke ptr1->Release, and the caller with ptr2 must invoke ptr2->Release.

Note  Although it is not preferable, an object's reference count, at the object's creation, may initially be zero. If a newly created object with a zero reference is passed as a function parameter, you must ensure the object's stability by incrementing the reference count prior to the function call. This technique is referred to as artificial reference counting. After the function returns, the reference count can be decremented. Safeguarding an object through artificial reference counting may also be necessary prior to the object's destruction, which is an unstable time for the object.


Stabilizing the this Pointer and Keeping it Valid

Re-entrancy is a useful programming technique in OLE 2 but it does create some interesting questions. One of the most important is how an application can ensure the this pointer will remain stable to keep the object alive throughout the entire method or as long as the object requires. A valid this pointer must have a reference count of at least one.

It is primarily the client's responsibility to ensure that the this pointer is stable for the duration of an entire method call. The client code has been chosen for simplicity and its ability to conform to a set of rather rigid rules without incurring a substantial amount of overhead. These include the following:

Rule One

The behavior of objects is consistent with the this pointer being stable for the duration of an entire method call. Additional AddRef/Release calls guard stability and are beneficial but not required. The implementation of IUnknown methods provide an exception: In some cases, QueryInterface must be stabilized by the object instead of the client. However, this is required only when an implementation of QueryInterface can cause a re-entrant call, which is rare.

Before a re-entrant call can occur, AddRef must stabilize the new incrementation of the reference count; however, it is very rare for an implementation of AddRef to cause a re-entrant call. The just-released pointer may not be used again so Release might be required to stabilize itself after decrementing the reference count.

Rule Two

If a client calls on an interface pointer and that pointer can be released before the destructor of the object, the client must stabilize the pointer. The pointer that is stabilized must be the actual pointer used to make the call and not another one because of interface reference counts. Stabilization is done by calling AddRef and Release as follows:

IUnknown *pUnkStablize = pInterfaceToBeCalled;

pUnkStablize->AddRef();

pInterfaceToBeCalled->CallMethod(...);

pUnkStablize->Release();

However, there is also an exception to this rule. Some clients can avoid the calls to AddRef and Release required by the preceding if they have knowledge of the implementation they are calling. For example, the OLE and Data Advise Holders created by the OLE APIs are self-stabilizing. Calls to them, such as SendOnDataChange, need not be additionally stabilized.

Rule Three

Global data must be treated as explained in Rule Two. If an interface pointer is retrieved from global data and used, and the global pointer can be released in a recursive call, the pointer must be stabilized around the call. The pointer in the stub manager is a global pointer (from the object's point of view) and each use of the pointer is properly stabilized. Many applications keep pointers in window properties; window messages that retrieve such pointers probably need to be changed. Notably, if the pointer in the window property does not contribute to the object's reference count (a weak registration), all uses of the pointer must call AddRef and Release around a call.

Rule Four

Clients that release a pointer before the destructor must ensure that the pointer is not used on a re-entrant call. An application can prevent this particular re-entrancy problem by using the following code:

if (m_pUnk != NULL)

{

IUnknown *pUnkToBeReleased = m_pUnk;

m_pUnk = NULL;

pUnkToBeReleased ->Release();

}


Avoiding Reference Counting Cycles

It is possible to create cycles whereby a reference count would never reach zero. This can occur when two objects hold pointers to each other and a third object holds a pointer to one of the other two objects. Two strategies can be implemented to avoid a potential cycle. Depending on the object and its use, it may be possible to avoid reference counting one of the interface pointers. An alternate strategy is to manage separate reference counts, whereby the object is not destroyed until all of the reference counts are zero.

The figure below illustrates a potential cycle scenario and the two strategies for avoiding that cycle. ObjectX and ObjectY both hold pointers to interfaces in ObjectAB; ObjectAB holds a pointer to InterfaceA in ObjectY. In the first case, ObjectAB avoids a cycle by not counting its reference to ObjectY, as is indicated by the dotted line.

In the second case, ObjectAB avoids a cycle by maintaining separate reference counts, as indicated by the dotted line.

Avoiding a reference counting cycle

The next figure shows how a reference cycle can occur in an aggregate object. The controlling unknown points to the IUnknown implementation in the noncontrol object, which in turn points to the controlling unknown. To avoid a cycle, the noncontrol object's pointer to the controlling unknown is not reference-counted, as indicated by the dotted line.

Reference counting cycle in an aggregate object


Optimizing Reference Counting

Reference counting manages the lifetimes of an object so that it is kept alive only if necessary. If it is possible to know the relationships between the lifetimes of two or more copies of an interface pointer, the reference counting process can be streamlined. Most relationships fall into two categories: 1) nested lifetimes and 2) overlapping lifetimes.

The next figure presents an illustration of the two types of relationships. In a nested lifetime relationship, the first copy of the interface pointer is created, followed by the creation of the second copy.

Nested versus overlapping lifetimes of an interface pointer

The second copy is destroyed before the end of the first copy's lifetime. The calls to AddRef (A2) and Release (R2) can be omitted. The overlapping lifetime relationship begins like the nested lifetime relationship: the second interface pointer is created after the first. In overlapping lifetimes, however, the first pointer copy is destroyed before the second pointer copy. The AddRef (A2) and Release (R1) calls can be omitted.

The following general rules help to optimize reference-counting:

•	In parameters to functions

An interface pointer passed as a function parameter has a lifetime that is nested in the pointer used to initialize the parameter. Therefore, the function parameter need not be reference-counted separately.

•	Out parameters from functions

Parameters returned from function calls (including return values), must have a stable copy of the interface pointer. On exit, the caller must release the pointer. The returned parameter need not be reference-counted separately.

•	Local variables

A function clearly knows the lifetimes of each local pointer variable allocated on the stack frame. It can use this information to omit redundant AddRef and Release calls.

•	Back pointers

Data structures often contain two components, A and B, each containing a pointer to the other. If A is known to contain the lifetime of B, then the pointer from B to A need not be reference-counted.


IUnknown::QueryInterface

HRESULT IUnknown::QueryInterface(riid, ppvObject)
REFIID riid
void ** ppvObject

IUnknown::QueryInterface returns a pointer to a specified interface. Callers use QueryInterface to determine which interfaces the called object supports.

Parameters

riid

[in] Specifies the interface ID of the interface being queried for.

ppvObject

[out] Points to a pointer to the requested interface unless the requested interface is not supported or another error occurred. In either of those cases, it points to NULL. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Interface is supported and ppvObject is set.
E_NOINTERFACE	Interface is not supported by the object; ppvObject is set to NULL.
CO_E_OBJNOTCONNECTED	Handler created but object application is not running. The object application must be running to determine whether it supports the interface (much like OLE_E_NOTRUNNING but more general).
REGDB_E_IIDNOTREG	Object application is running, but riid cannot be remoted because no information exists in the registry to indicate how it is to be done.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If an object supports multiple interfaces that are each derived from IUnknown, calling IUnknown::QueryInterface(IID_IUnknown) on any one of the interfaces must return the same pointer, regardless of which interface is called. To determine whether two pointers point to the same object, call IUnknown::QueryInterface(IID_IUnknown,...) on both and compare the results.

In contrast, queries for interfaces on the same object other than IUnknown are not required to return the same pointer value each time QueryInterface is called to return a pointer to them.

The set of interfaces accessible on an object through IUnknown::QueryInterface must be static, not dynamic. For example, consider the following pointer:

ISomeInterface *pSome = (some function returning an ISomeInterface *);

where ISomeInterface derives from IUnknown. Now suppose the following operation is attempted:

IOtherInterface * pOther;

HRESULT herr = pSome->QueryInterface(IID_IOtherInterface, &pOther); //line 3

Then, based on the assignment made in line 3, the following must be true:

•	If herr==NOERROR, calling IUnknown::QueryInterface a second time from the same pSome pointer returns NOERROR again. This action is independent of whether pOther was released in the interim.

•	If herr==E_NOINTERFACE, calling IUnknown::QueryInterface a second time from the same pSome pointer returns E_NOINTERFACE again.

In lines 1-3, IUnknown::QueryInterface must be symmetric, reflexive, and transitive with respect to the set of accessible interfaces as follows:

Symmetric:

pOther->QueryInterface(IID_ISomeInterface, ...)

must succeed.

Reflexive::If, in line 3, pOther were successfully obtained, then

pSome->QueryInterface(IID_ISomeInterface, ...)

must succeed.

Transitive::If in line 3, pOther were successfully obtained, and the following assignment were then made:

IYetAnother * pyet;
HRESULT herr = pother->QueryInterface(IID_IYetAnother, &pyet); // line 6

and pyet were successfully obtained in line 6, then

pyet->QueryInterface(IID_ISomeInterface, ...)

must succeed.

Notice that success does not mean that two QueryInterface calls on the same pointer asking for the same interface must succeed and return exactly the same pointer; however, QueryInterface must indicate only that the interface is available. If you return two or more different implementations of an interface, they must have the same meaning. OLE's remoting system can only handle one of them.

IUnknown::QueryInterface will not force the object into the running state; it must be running prior to the call.

Due to interface inheritance, if an interface derives from any other interfaces, it has access to all the methods in those interfaces.

Examples

The first example is a simple implementation of IUnknown::QueryInterface that returns a pointer to one of two interfaces supported by the application's class factory object: IUnknown or IClassFactory. Notice that the out parameter, ppvObject, is set to NULL initially. If the caller requests an interface other than the two that are supported, E_NOINTERFACE is returned, indicating that the interface is not available. Before a pointer is returned, IUnknown::AddRef for the class factory object is called to increment its reference count.

HRESULT IUnknown_CO_QueryInterface(IClassFactory lpThis,

REFIID riid, VOID ** ppvObject)

{

HRESULT hrErr;

*ppvObject = NULL;

LPMYCLASSOBJECT lpMyClassObj = (LPMYCLASSOBJECT)lpThis;

if (IsEqualIID(riid, &IID_IClassFactory) ||

    IsEqualIID(riid, &IID_IUnknown)) {

*ppvObject = lpThis;

IUnknown_CO_AddRef(lpMyClassObj);

hrErr = S_OK;

}

else {

hrErr = E_NOINTERFACE;

}

return hrErr;

}

The next example is a more complex implementation of IUnknown::QueryInterface for a document object. This object supports different interfaces depending on its use. The first check is for IUnknown, since the document object supports it in all cases. The next check is whether this object is a data-transfer object that has been placed on the Clipboard or made available with a drag-and-drop operation. Data-transfer objects support IDataObject and IDropSource in addition to IUnknown. If the object is not a data-transfer object, IPersist and IPersistFile are supported. Since IPersistFile is derived from IPersist, the same pointer is returned when either interface is requested.

HRESULT IUnknown_Doc_QueryInterface(

LPOLEDOC             lpOleDoc,

REFIID            riid,

VOID **           ppvObject)

{

LPAPPDOC lpAppDoc = (LPAPPDOC)lpOleDoc;

SCODE sc = E_NOINTERFACE;

*ppvObject = NULL;

if (IsEqualIID(riid, &IID_IUnknown)) {

*ppvObject = (LPVOID) &lpOleDoc->m_Unknown;

IUnknown_Doc_AddRef(lpOleDoc);

sc = S_OK;

}

else if(lpDoc->m_fDataTransferDoc  {

&& IsEqualIID(riid, &IID_IDataObject)) {

*ppvObject = (LPVOID) &lpOleDoc->m_DataObject;

IUnknown_Doc_AddRef(lpOleDoc);

sc = S_OK;

}

if((bDropSource) && IsEqualIID(riid, &IID_DropSource)) {

*ppvObject = (LPVOID) &lpOleDoc->m_DropSource;

IUnknown_Doc_AddRef(lpOleDoc);

sc = S_OK;

}

}

if(IsEqualID(riid,&IID_IPersist) ||

IsEqualIID(riid,	&IID_IPersistFile)) {

*ppvObject = (LPVOID) &lpOleDoc->m_PersistFile;

IUnknown_Doc_AddRef(lpOleDoc);

sc = S_OK;

}

}

The following two examples of IUnknown::QueryInterface are C++ implementations for aggregate objects. The first example demonstrates how to code an implementation for one of the public aggregated (noncontrolling) pieces. QueryInterface simply delegates to the controlling QueryInterface implementation. Note that IUnknown::AddRef and IUnknown::Release are implemented in a similar fashion; they also delegate to the controlling unknown implementations.

HRESULT PublicUnknown::QueryInterface(riid, ppvObject)

{

return m_pUnkOuter->QueryInterface(riid, ppvObject);

}

The second example is C++ pseudo code for a private IUnknown implementation; this object operates independently with no ties to the control object. This implementation is identical to most implementations for non-aggregated objects. Reference counting takes place locally so that when the local reference count goes to zero, the memory for this object can be freed.

HRESULT PrivateUnknown::QueryInterface(riid, ppvObject)

{

if (IsEqualIID(riid, &IID_IUnknown))

**ppvObject = this;

else if (IsEqualIID(riid, some other supported interface))

**ppvObj = pointer to supported interface;

else{

**ppvObj = NULL;

return ResultFromScode(E_NOINTERFACE);

}

((IUnknown *) *ppv)->AddRef();

return ResultFromScode(E_NOERROR);

}

For more information on aggregation, refer to Programming Considerations.

See Also

IUnknown::AddRef, IUnknown::Release


IUnknown::AddRef

ULONG IUnknown::AddRef()

IUnknown::AddRef is used to increment a reference count for every new copy of an interface pointer to a specified interface on a particular object.

Return Values

Value	Meaning
0 to n.	The value of the reference count. When fully released, it is zero. This information is used for diagnostics and testing only. It cannot be used by shipping code because, in certain situations, it is unstable.
Remarks

IUnknown::AddRef is used to stabilize a copy of an interface pointer. IUnknown::AddRef is also used in those instances where 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 AddRef maintains must be a 32-bit unsigned integer.

Example

The following example, IUnknown_Doc_AddRef, simply increments the reference count maintained for the document object and then returns it.

ULONG IUnknown_Doc_AddRef(LPDOC lpDoc)

{

++lpDoc->m_cRef;

return lpDoc->m_cRef;

}

See Also

IUnknown::Release


IUnknown::Release

ULONG IUnknown::Release()

IUnknown::Release decrements the reference count on a specified interface of a particular object. If the object reference count goes to zero as a result of the Release, the object is freed from memory.

Return Values

Value	Meaning
0 to n	The value of the reference count on the whole object. When fully released, it is zero. This information can be used for diagnostics and testing only. It cannot be used by shipping code because, in certain situations, it is unstable. If a client needs to know that resources have been freed, it must use an interface with higher-level semantics before calling Release.
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 interface pointer will free itself. An object frees itself if the released pointer is the only pointer and if the object supports multiple interfaces through IUnknown::QueryInterface.

Note  Aggregation of objects restricts the ability to recover interface pointers.

Example

The following example is an implementation of IUnknown::Release for a document object. The reference count is decremented and if it has reached zero, Doc_Destroy is called to destroy the object.

ULONG IUnknown_Doc_Release (LPDOC lpDoc)

{

ULONG cRef;

cRef = --lpDoc->m_cRef;

if (cRef == 0)

Doc_Destroy((LPDOC)lpDoc);

return cRef;

}

See Also

IUnknown::AddRef, IUnknown::QueryInterface


IClassFactory Interface

The IClassFactory interface is used to create instances of a specific object class in memory. IClassFactory is implemented by all object applications, object handlers, and by container applications that allow linking to their embedded objects. IClassFactory is used by OLE.

To support object creation, an application must include one class factory object (implementation of IClassFactory) for each supported object class. Class factory objects, or simply class factories, are registered so that containers are aware of their availability and can use them. Object and container applications call CoRegisterClassObject to register their class factories.

IClassFactory contains the following methods:

DECLARE_INTERFACE_(IClassFactory, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

  ULONG  AddRef (THIS );

  ULONG  Release (THIS );

    // *** IClassFactory methods ***

  HRESULT CreateInstance (THIS _ IUnknown * pUnkOuter, REFIID riid,  VOID ** ppvObject) ;

  HRESULT LockServer (THIS _ BOOL fLock) ;

};

Before describing the IClassFactory methods, it is helpful to understand how objects are created. The following section (How Objects are Created) provides an overview of this process.


How Objects are Created

The first step in creating an object is to determine the class identifier, or CLSID, for the new object. Every object class has a unique CLSID, a 32-bit number known as a UUID (universally unique ID) that can be generated with the OLE development kit tool UUIDGEN.EXE, through a call to CoCreateGuid, or by contacting Microsoft directly. UUIDGEN.EXE works on machines either with or without netcards. However, when no netcard is found, UUIDGEN.EXE returns a UUID that is not guaranteed to work across the network and the informational error RPC_S_UUID_LOCAL_ONLY.

CLSIDs can be determined with the Insert Object dialog box, which is displayed when the user selects the "Insert Object" menu command to create a new embedded or linked object, or from reading an existing object's persistent storage. CLSIDs are always stored with the object's data.

Given the CLSID, an application creating a new object can call CoGetClassObject to obtain the class factory for the object class as follows:

LPCLASSFACTORY lpcf;

CoGetClassObject(clsid, CLSCTX_INPROC, 0, IID_IClassFactory,
(void**)&lpcf);

The second parameter is used to specify the class context, or type of object application that contains the class factory. The third parameter is reserved and must be NULL. The fourth parameter contains the interface identifier, or IID, for IClassFactory, and the last parameter provides a pointer to the returned class factory. Refer to the discussion of CoGetClassObject later in this section for more information.

A call to IClassFactory::CreateInstance creates an uninitialized object. Initialization is performed typically with either IPersistStorage::InitNew for newly created objects or IPersistStorage::Load for existing ones. Until an object is initialized, only the following operations are guaranteed to succeed:

•	QueryInterface calls to either IUnknown or to one of the object's initialization interfaces. For embedded objects, IPersistStorage is the initialization interface.

•	AddRef and Release calls on any interface derived from IUnknown.

•	IPersistStorage::InitNew and IPersistStorage::Load calls.

Once the object is created and initialized, it's possible to obtain a pointer to any of the interfaces that the object supports by calling IUnknown::QueryInterface. If the new object is an embedded compound-document object, for example, applications typically request a pointer to IOleObject. Once initialization is complete, the IPersistStorage pointer is no longer needed, so it is released. The following few lines or code illustrate how to query for a non-initialization interface and decrement the reference count on the IPersistStorage pointer.

LPOLEOBJECT pOleObject;

pPersistStorage->QueryInterface(IID_IOleObject,

(LPVOID  *)&pOleObject);

pPersistStorage->Release();

Refer to the discussion of IClassFactory::CreateInstance for a more detailed description of the instantiation process.


IClassFactory::CreateInstance

HRESULT IClassFactory::CreateInstance(pUnkOuter, riid, ppvObject)
IUnknown *pUnkOuter
REFIID riid
VOID  ** ppvObject

IClassFactory::CreateInstance creates an uninitialized instance of an object class. Initialization is subsequently performed using another interface-specific method, such as IPersistStorage::InitNew, IPersistStorage::Load, IPersistStream::Load, or IPersistFile::Load.

Parameters

pUnkOuter

[in] Points to the controlling IUnknown interface if the object is being created as part of an aggregate. If the object is not part of an aggregate, it is NULL.

riid

[in] Identifies the interface by which the caller will communicate with the resulting object and through which it initializes the object.

ppvObject

[out] Points to where the pointer to the object will be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The specified class of the object was created.
CLASS_E_NOAGGREGATION	pUnkOuter was non-NULL and the object does not support aggregation.
E_NOINTERFACE	The object pointed to by ppvObject does not support the interface identified by riid.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
Remarks

The pUnkOuter parameter indicates whether the object is being created as part of an aggregation. Because object classes must be designed to be aggregate objects, not all classes can participate in aggregation. If pUnkOuter is not NULL, indicating that the object is aggregatable, riid must represent a private interface. Private interfaces are never made available outside of the aggregate. If the object is not aggregatable, set the pointer to NULL and return an error.

To IClassFactory::CreateInstance, the container passes the interface to be used to communicate with the resulting object instance. The newly created object is usually initialized by using this interface. For OLE compound-document objects (embedded and linked), this process is encapsulated by the OleLoad helper function.

Note:  All implementations of IClassFactory::CreateInstance must check pUnkOuter if they cannot be aggregated. This even includes local servers even though they might never be aggregatable.

In general, if an application supports one object class and its class factory is registered for single use, only one instance of the object class can be created. The application must refuse to create other instances of the class, returning an error from IClassFactory::CreateInstance. The same is true for applications that support multiple object classes, each having a class factory registered for single use: a CreateInstance for one class followed by a CreateInstance for any of the other classes should return an error.

To avoid returning an error, applications that support multiple classes with single-use class factories can revoke the registered class factory of the first class by calling CoRevokeClassObject when a request for instantiating a second class is received. For example, suppose there are two classes, A and B. When IClassFactory::CreateInstance is called for class A, revoke the IClassFactory for B. When B is created, revoke A. This solution complicates shutdown because one of the IClassFactory objects might have already been revoked (and cannot be revoked twice).

Example

The following example shows IClassFactory::CreateInstance for a single-use application. Notice that a single-use application does not support the creation of more than one instance of an object at a time; CreateInstance guards against this happening. After the new document instance has been created, its reference count is zero. IUnknown_Doc_QueryInterface is called to return a pointer to the requested interface. If the requested interface is available, the reference count is incremented to one.

HRESULT IClassFactory_CreateInstance (

LPCLASSFACTORY      lpThis,

IUnknown *          pUnkOuter,

REFIID              riid,

VOID **             ppvObject)

{

LPAPP	lpApp = (LPAPP)g_lpApp;

LPDOC	lpDoc;

HRESULT	hrErr;

*ppvObject = NULL;

if (pUnkOuter != NULL)

return CLASS_E_NOAGGREGATION;

//guard against calling CreateInstance twice for SDI apps

if (lpApp->m_lpDoc != NULL)

return E_UNEXPECTED;

// Create new doc instance and set ref count to 1

lpApp->m_lpDoc = App_CreateDoc(lpApp, FALSE);

lpDoc = (LPDOC)lpApp->m_lpDoc;

if (! lpDoc)

return E_OUTOFMEMORY;

hrErr = IUnknown_Doc_QueryInterface(lpDoc, riid, ppvObject);

return hrErr;

}

See Also

CoGetClassObject, CoRegisterClassObject, CoRevokeClassObject


IClassFactory::LockServer

HRESULT IClassFactory::LockServer(fLock)
BOOL fLock

IClassFactory::LockServer controls whether an object application is kept in memory. Keeping the application alive in memory allows instances of this class to be created more quickly.

Parameters

fLock

[in] Specifies the lock count. If TRUE, it increments the lock count and locks the application in memory; if FALSE, it decrements the lock count.

Return Values

Value	Meaning
S_OK	The specified object was either locked (fLock = TRUE) or unlocked from memory (fLock = FALSE).
E_FAIL	Indicates an unspecified error.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

A container application can call this method to obtain better performance when there is a need to create and release instances frequently.

All calls to IClassFactory::LockServer must be counted, not only the last one. Calls will be balanced; that is, for every LockServer(TRUE) call, there will be a LockServer(FALSE) call. If the lock count and the class object reference count are both zero, the class object can be freed.

Most clients do not need to call IClassFactory::LockServer. It is used primarily by sophisticated clients with special performance needs from certain object classes.

The process that locks the object application is responsible for unlocking it. Once the IClassFactory pointer is released, the caller cannot be guaranteed to connect later to the same class (for example, consider single-use classes). It is an error to call IClassFactory::LockServer(TRUE) and then release the IClassFactory pointer without first releasing the lock with IClassFactory::LockServer(FALSE).

Most implementations of IClassFactory::LockServer do nothing more than call CoLockObjectExternal. For more information on locking objects in memory, see CoLockObjectExternal.

Example

In the following example, IClassFactory::LockServer first checks the value of fLock. If fLock is TRUE, the class object reference count is incremented. If fLock is FALSE, the reference count is decremented. This example is useful for debugging only. Then CoLockObjectExternal is called to create a strong lock on the application object.

HRESULT IClassFactory_LockServer (

        LPCLASSFACTORY      lpThis,

        BOOL                fLock)

{

LPMYCLASSOBJECT lpMyClassObj = (LPMYCLASSOBJECT)lpThis;

LPAPP lpApp = (LPAPP)g_lpApp;

HRESULT hrErr;

if (fLock)

++lpMyClassObj->m_cLock;

else

--lpMyClassObj->m_cLock;

hrErr = CoLockObjectExternal((LPUNKNOWN)&lpApp->m_Unknown,

fLock, TRUE);

return hrErr;

}


IMalloc Interface

The IMalloc interface is implemented by OLE and is used to manage memory. To retrieve a pointer to OLE's IMalloc implementation, an application sets pMalloc to NULL in its call to OleInitialize or CoInitialize. If an application has special memory-management needs, it can provide its own implementation of IMalloc.

IMalloc contains the following methods, the first three of which are similar to the C library functions malloc, realloc, and free:

DECLARE_INTERFACE_(IMalloc, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

  ULONG  AddRef (THIS ) ;

  ULONG  Release (THIS ) ;

    // *** IMalloc methods ***

  void * Alloc (THIS _ ULONG cb) ;

  void Free (THIS _ void * pv) ;

  void * Realloc (THIS _ void * pv, ULONG cb) ;

  ULONG GetSize (THIS _ void * pv) ;

  int DidAlloc (THIS _ void * pv) ;

  void HeapMinimize (THIS ) ;

};


IMalloc::Alloc

void  * IMalloc::Alloc(cb)
ULONG cb

IMalloc::Alloc allocates a block of memory.

Parameters

cb

[in] Specifies the size (in bytes) of the memory block to allocate.

Return Values

Value	Meaning
Allocated memory block	Memory block allocated successfully.
NULL	Insufficient memory available.
Remarks

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, 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.

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.

See Also

IMalloc::Free, IMalloc::Realloc


IMalloc::Free

void IMalloc::Free(pv)
void * pv

IMalloc::Free frees a block of memory previously allocated through a call to IMalloc::Alloc or IMalloc::Realloc.

Parameters

pv

[in] Points to the memory block to be freed.

Remarks

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.

See Also

IMalloc::Alloc, IMalloc::Realloc


IMalloc::Realloc

void *IMalloc::Realloc(pv, cb)
void *pv
ULONG cb

IMalloc::Realloc changes the size of a previously allocated memory block.

Parameters

pv

[in] Points to the memory block to be reallocated. It can be a NULL pointer, as discussed in the following Remarks section.

cb

[in] Specifies the size of the memory block (in bytes) to be reallocated. It can be zero, as discussed in the following remarks.

Return Values

Value	Meaning
Reallocated memory block	Memory block successfully reallocated.
NULL	Insufficient memory or cb is zero and pv is not NULL.
Remarks

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 as IMalloc::Alloc. 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, then 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.

See Also

IMalloc::Alloc, IMalloc::Free


IMalloc::GetSize

ULONG IMalloc::GetSize(pv)
void *pv

IMalloc::GetSize returns the size (in bytes) of a memory block previously allocated with IMalloc::Alloc or IMalloc::Realloc.

Parameters

pv

[in] Points to the memory block whose size is requested.

Return Value

Size of allocated memory block in bytes or -1 if pv is a NULL pointer.

See Also

IMalloc::Alloc, IMalloc::Realloc


IMalloc::DidAlloc

int IMalloc::DidAlloc(pv)
void *pv

IMalloc::DidAlloc determines if this allocator was used to allocate the specified block of memory.

Parameters

pv

Specifies the pointer to the memory block; can be a NULL pointer, in which case, -1 is returned.

Return Values

Value	Meaning
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

Pointers returned from the shared allocator cannot be directly manipulated using the host global memory allocator. Except when a NULL pointer is passed, the shared memory allocator does not return -1 to IMalloc::DidAlloc, the allocator always indicates that it either did or did not allocate the passed pointer.

The memory allocator passed to CoInitialize may always respond with -1 in this function.

See Also

IMalloc::Alloc, IMalloc::HeapMinimize, IMalloc::Realloc


IMalloc::HeapMinimize

void IMalloc::HeapMinimize()

IMalloc::HeapMinimize minimizes the heap as much as possible by releasing unused memory to the operating system.

Remarks

Calling IMalloc::HeapMinimize is useful when several memory blocks have been freed using IMalloc::Free and you want the application to make the freed memory available for other purposes.

See Also

IMalloc::Alloc, IMalloc::Free, IMalloc::Realloc


IExternalConnection Interface

The IExternalConnection interface must be implemented by DLL object applications to support the correct shutdown of links to embedded objects. Object handlers never implement this interface.

External connections are made to an object during the process of marshalling. The MSHLFLAGS enumeration determines how the marshalling is to be done. If MSHLFLAGS_NORMAL is used, the marshalled data packet is unmarshalled in the process itself.

If MSHLFLAGS_TABLESTRONG is used, marshalling occurs because the data packet is to be stored in a globally accessible table from which it is to be unmarshalled. The presence of the data packet in the table is counted as a reference on the marshalled interface and, as a result, it is held in memory.

When MSHLFLAGS_TABLEWEAK is used, the data packet is to be stored in a globally accessible table from which it is to be unmarshalled. It does not, however, count as a reference on the marshalled interface.

The stub manager calls IExternalConnection methods when external (i.e. linking) connections to an object are made and broken or when CoLockObjectExternal is called. Embedded objects should maintain a count of these connections and when the count goes to zero, IOleObject::Close should be called.

The IExternalConnection interface contains the following methods:

DECLARE_INTERFACE_(IExternalConnection, IUnknown)

{

    // *** IUnknown methods ***

    HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

    ULONG  AddRef (THIS )  ;

    ULONG  Release (THIS ) ;

    // *** IExternalConnection methods ***

    STDMETHOD_(DWORD, AddConnection (THIS _ DWORD extconn, DWORD dwReserved) ;

    STDMETHOD_(DWORD, ReleaseConnection (THIS _ DWORD extconn, DWORD dwReserved,

    BOOL fLastReleaseCloses) ;

};


IExternalConnection::AddConnection

DWORD IExternalConnection::AddConnection(extconn, dwReserved)
DWORD extconn
DWORD dwReserved

IExternalConnection::AddConnection indicates that another external connection has been created on a running object.

extconn

[in] Specifies the type of external connection that exists on the object; its value is taken from the enumeration EXTCONN, which is defined as follows:

typedef enum tagEXTCONN

{

EXTCONN_STRONG = 0x0001

}EXTCONN

dwReserved

[in] Used by OLE to pass information about the connection; it can be zero, but it may not be. Therefore, AddConnection implementers should not include a check for zero.

Return Values

Value	Meaning
DWORD value	The remaining number of reference counts on the object; used for debugging purposes only.
Remarks

An external connection represents strong bindings (by means of a moniker bind) or external locks (by means of CoLockObjectExternal). As with all connections (weak or strong), the user can always override them and shut down the application.

Example

The following is a typical implementation for the AddConnection method:

DWORD XX::AddConnection(DWORD extconn, DWORD dwReserved)

{

return extconn&EXTCONN_STRONG ? ++m_cStrong : 0;

}

See Also

CoLockObjectExternal, IExternalConnection::ReleaseConnection


IExternalConnection::ReleaseConnection

DWORD IExternalConnection::ReleaseConnection(extconn, dwReserved, fLastReleaseCloses)
DWORD extconn
DWORD dwReserved
BOOL fLastReleaseCloses

IExternalConnection::ReleaseConnection indicates the release of an external connection to a running object.

Parameters

extconn

[in] Specifies the type of external connection that exists on the object; the value is obtained from the enumeration EXTCONN (see IExternalConnection::AddConnection).

dwReserved

[in] Used by OLE used to pass information about the connection; it can be zero, but not necessarily.

fLastReleaseCloses

[in] TRUE specifies that if this is the last external reference to the object, the object should close. FALSE leaves the object in a state in which its closing is controlled by some other means.

Return Values

Value	Meaning
DWORD value	The remaining number of reference counts on the object; used for debugging purposes only.
Remarks

Losing the last strong connection (along with fLastReleaseCloses == TRUE) causes the object to close. If an object has no closing semantics, then it is not required to count the strong connections. An enumerator object, for example, has no closing semantics.

The following is a typical implementation for ReleaseConnection:

DWORD XX::ReleaseConnection(DWORD extconn, DWORD dwReserved,

   BOOL fLastReleaseCloses)

{

if (extconn & EXTCONN_STRONG)

{

if (--m_cStrong == 0 && fLastReleaseCloses)

save if dirty

close ...

return m_cStrong;

}

else

return 0;

}

See Also

IExternalConnection:AddConnection


IEnumX Interface
Enumerators

A frequent OLE programming task is to iterate through a sequence of items. OLE uses enumerators, or objects that implement an IEnum-type interface, to perform this task. Enumerators cleanly separate the caller's need to loop over a set of objects from the calling routine's knowledge of how to accomplish that task. Enumerators are just a concept; there is no interface called IEnumerator or IEnum. Because the method signatures in an enumerator interface are specific to the type of items being enumerated, OLE provides a generic enumerator interface rather than a specific one. That is, an enumerator for strings implements IEnumString, an enumerator for format information implements IEnumFormatEtc. The only difference between these interfaces is the type of item being enumerated; all enumerators are used in fundamentally the same way.

All enumerator interfaces contain the following methods:

template <class ELT_T> interface IEnum : IUnknown {

virtual	SCODE	Next(ULONG celt, <ELT_T> * rgelt, ULONG* pceltFetched)
= 0;

virtual	SCODE	Skip(ULONG celt) = 0;

virtual	SCODE	Reset() = 0;

virtual	SCODE	Clone(IEnum<ELT_T>** ppenum) = 0;

};

The following code example illustrates a typical use of an enumerator. StringContainer implements the IEnumString interface that can enumerate a sequence of strings. SomeFunc takes a pointer to a StringContainer, which provides access to the string enumerator. SomeFunc loops through each of the strings by calling the enumerator's Next method and then releases the enumerator by calling its Release method.

/* The following is a typical use of an enumerator.*/

interface StringContainer {

virtual IEnumString* EnumStrings() = 0;

};

void SomeFunc(StringContainer * pstringcont) {

LPOLESTR lpsz;

IEnumString* penum;

penum = pstringcont->EnumStrings();

while (penum->Next(1, &lpsz, NULL) == S_OK)

{

// do something with the string in lpsz

// free the string in lpsz with the allocator returned by
// CoGetMalloc(MEMCTX_TASK, ...);

}

penum->Release();

// penum is not valid here

}

Note  In general, the actual enumerator interfaces used in OLE are defined and documented with the functions that use them.

If you delete an element during an enumeration, you must know the number of elements remaining to get a deterministic count.


IEnumX::Next

HRESULT IEnumX::Next(celt, rgelt, pceltFetched)
ULONG celt
IUnknown**rgelt
ULONG *pceltFetched

IEnumX::Next retrieves the specified number of items in the enumeration sequence.

Parameters

celt

[in] Specifies the number of elements to return. If the number of elements requested is greater than remains in the sequence, only the remaining elements are returned. The number of elements returned is passed through pceltFetched (unless it is NULL).

rgelt

[out] Points to the array in which to return the elements.

pceltFetched

[out] Points to the number of elements actually returned in rgelt. The pceltFetched parameter cannot be NULL if celt is greater than one. If pceltFetched is NULL, celt must be one.

Return Values

Value	Meaning
S_OK	Returned requested number of elements—*pceltFetched set if non-NULL. All requested entries are valid.
S_FALSE	Returned fewer elements than requested by celt. In this case, unused slots in the enumeration are not set to NULL and *pceltFetched holds the number of valid entries, even if zero is returned.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	Value of celt is invalid.
E_UNEXPECTED	An unexpected error occurred.
If an error value is returned, no entries in the rgelt array are valid on exit; they are all in an indeterminate state.

Remarks

It is illegal to call

...Next(celt>1, ...,NULL);

Thus, no more than one element can be specified without passing a valid value for pceltFetched.


IEnumX::Skip

HRESULT IEnumX::Skip(celt)
ULONG celt

IEnumX::Skip skips over a specified number of elements in the enumeration sequence.

Parameters

celt

[in] Specifies the number of elements to be skipped.

Return Values

Value	Meaning
S_OK	The number of elements skipped is equal to celt.
S_FALSE	The number of elements skipped is fewer than celt.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	Value of celt is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks:

If the number of elements left is equal to or less than the number remaining, IEnumx::Skip returns S_OK. If the number of elements to be skipped exceeds the number remaining, IEnumx::Skip returns S_FALSE and sets the seek pointer to the end.


IEnumX::Reset

HRESULT IEnumX::Reset()

IEnumX::Reset resets the enumeration sequence to the beginning.

Return Values

Value	Meaning
S_OK	The enumeration sequence was reset to the beginning.
S_FALSE	The enumeration sequence was not reset to the beginning.
Remarks

There is no guarantee that the same set of objects will be enumerated the second time as was enumerated the first: it depends on the collection being enumerated. It is too expensive for some collections, such as files in a directory, to maintain this condition.


IEnumX::Clone

HRESULT IEnumX::Clone(ppenum)
IEnumX **ppenum

IEnumX::Clone returns another enumerator containing the same state as the current enumerator.

Parameters

ppenum

[out] Pointer to the place to return the cloned enumerator. The type of the cloned enumerator is the same as the original enumerator. For example, if the enumerator name is IEnumFORMATETC, ppenum is also type IEnumFORMATETC. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	Value of ppenum is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Using IEnumX::Clone, it is possible to record a particular point in the enumeration sequence, then return to that point at a later time. The enumerator returned is of the same interface type as the one being cloned.


IMarshal Interface

The IMarshal interface is implemented and used by OLE. However, object and container applications can also implement IMarshal to provide custom-interface marshalling. For more information about designing custom interfaces, see OLE: Custom Interfaces.The IMarshal interface contains the following methods:

DECLARE_INTERFACE_(IMarshal, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

  ULONG  AddRef (THIS );

  ULONG  Release (THIS ) ;

    // *** IMarshal methods ***

  HRESULT GetUnmarshalClass (THIS_ REFIID riid, VOID *pv, DWORD dwDestContext,

    VOID * pvDestContext, DWORD mshlflags, CLSID *pclsid) ;

  HRESULT GetMarshalSizeMax (THIS_ REFIID riid, VOID *pv,  DWORD dwDestContext,

    VOID pvDestContext, DWORD mshlflags, DWORD *pdwSize) ;

  HRESULT MarshalInterface (THIS_ IStream *pStm, REFIID riid, VOID * pv,

    DWORD dwDestContext, VOID *pvDestContext, DWORD mshlflags) ;

  HRESULT UnmarshalInterface (THIS_ IStream *pStm, REFIID riid, VOID ** ppv) ;

  HRESULT ReleaseMarshalData (THIS_ IStream * pStm) ;

  HRESULT DisconnectObject (THIS_ DWORD dwReserved) ;

};

The IMarshal interface is used during the process of packaging and sending interface-method parameters from one application process to another. In a given call, method parameters are marshalled and unmarshalled in one direction and return values are marshalled and unmarshalled in the other direction. Marshalling allows pointers to interfaces to be passed by remote procedure calls and enables clients in other processes to access and manipulate objects across process boundaries.


Common IMarshal Data Structures
Data Structures

This section describes the enumerations that are used in the IMarshal interface methods and related marshalling functions.


MSHCTX Enumeration

The MSHCTX enumeration provides values for the destination context of the marshalling operation and is defined as follows:

typedef enum tagMSHCTX

{

    MSHCTX_LOCAL = 0,

    MSHCTX_NOSHAREDMEM = 1,

} MSHCTX;

However, at present, only MSHCTX_LOCAL is a valid value.

MSHCTX values have the following meanings:

Marshalling Context Values	Description
MSHCTX_LOCAL	Unmarshalling context is local; it has shared memory access.
MSHCTX_NOSHAREDMEM	Unmarshalling context does not have shared memory access with the marshalling context.


MSHLFLAGS Enumeration

The MSHLFLAGS enumeration provides values that determine how the marshalling is to be done; MSHFLAGS is defined as follows:

typedef enum tagMSHLFLAGS

{

    MSHLFLAGS_NORMAL = 0,

    MSHLFLAGS_TABLESTRONG = 1,

    MSHLFLAGS_TABLEWEAK = 2

} MSHLFLAGS;

MSHLFLAGS values have the following meanings:

Marshalling Control Flag Values	Description
MSHLFLAGS_NORMAL	Marshalling is done by passing an interface from one process to another. The marshalled data-packet that results from the call will be transported to the other process, where it will be unmarshalled (see CoUnmarshalInterface). By means of this flag, the marshalled data packet is unmarshalled either one or zero times. If it is unmarshalled successfully, CoReleaseMarshalData is not called on the data packet; and any necessary processing is done in the unmarshal itself. Only if unmarshalling fails, or is not attempted, is IMarshal::ReleaseMarshalData called on the data packet.
MSHLFLAGS_TABLESTRONG	Marshalling is happening because the data packet is to be stored in a globally accessible table from which it is to be unmarshalled zero, one, or more times. Further, the presence of the data packet in the table is to count as a reference on the marshalled interface. When the data packet is removed from the table, it is the responsibility of the table implementor to call CoReleaseMarshalData on the data-packet.
MSHLFLAGS_TABLEWEAK	Marshalling is happening because the data packet is to be stored in a globally accessible table from which it is to be unmarshalled zero, one, or more times. However, the presence of the data packet in the table does not count as a reference on the marshalled interface. The data packet is destroyed by calling CoReleaseMarshalData.
Note  NORMAL maps to standard marshalling, TABLESTRONG maps to Strong External Connection, and TABLEWEAK maps to Weak External Connection.


IMarshal::GetUnmarshalClass

HRESULT  IMarshal::GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlflags, pCid)
REFIID	riid
VOID *pv
DWORD dwDestContext
VOID *pvDestContext
DWORD mshlflags
CLSID *pCid

IMarshal::GetUnmarshalClass determines the object class that should be used to create an uninitialized proxy in the unmarshalling process.

Parameters

riid

[in] Specifies the interface ID of the object to be marshalled.

pv

[in] Points to the interface pointer to be marshalled; can be NULL.

dwDestContext

[in] Specifies the destination context relative to the current context in which the unmarshalling is to be done.

For a definition of the MSHCTX enumeration (on which dwDestContext is based), see "Data Structures" at the beginning of the IMarshal interface description.

pvDestContext

[in] Reserved for use with future MSHCTX values.

mshflags

[in] Specifies why marshalling is taking place. Valid values are from the MSHLFLAGS enumeration. For a description of it, see "Data Structures" at the beginning of the IMarshal interface description.

pCid

[out] Points to the CLSID of the class to be used in the unmarshalling process.

Return Values

Value	Meaning
S_OK	Object class was successfully obtained.
E_INVALIDARG	An invalid argument was passed as a parameter.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Out of memory.
E_FAIL	Object class could not be obtained.
Remarks

The dwDestContext parameter identifies the destination context, which is relative to the current context in which the unmarshalling is to be done. For example, marshalling might be done differently depending on whether the unmarshalling will happen on the local workstation or on a workstation on the network. An object could do custom marshalling in one case but not the other. The legal values for dwDestContext are taken from the enumeration MSHCTX.

An implementation of IMarshal::GetUnmarshalClass can delegate some destination contexts to the standard marshalling implementation, which is available by calling CoGetStandardMarshal. Delegating destination contexts should always be done if the dwDestContext parameter contains any flags that the IMarshal::GetUnmarshalClass does not understand.

The mshlflags parameter indicates the purpose of the marshalling. The value of mshlflags is taken from the enumeration MSHLFLAGS.

If the caller already has the riid of the interface being marshalled, it should pass the interface pointer through pv. If the caller does not have the riid, then it should pass NULL.

The pv pointer is sometimes used to determine the appropriate object class. If the IMarshal interface needs the class, it can call QueryInterface on itself to get the interface pointer. The pointer is passed here only to improve efficiency.

See Also

GetMarshalSizeMax, MarshalInterface, UnmarshalInterface


IMarshal::GetMarshalSizeMax

HRESULT IMarshal::GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)
REFIID riid
VOID *pv
DWORD dwDestContext
VOID *pvDestContext
DWORD mshlflags
DWORD *pSize

IMarshal::GetMarshalSizeMax returns the upper memory bound needed to write the specified data into the marshalling stream.

Parameters

riid

[in] Identifies the interface of the object to be marshalled.

pv

[in] The interface pointer that is to be marshalled; it can be NULL.

dwDestContext

[in] The destination context relative to the current context in which the unmarshalling is to be done. For a definition of the MSHCTX enumeration (from which dwDestContext is based), see "Data Structures," at the beginning of the IMarshal interface description.

pvDestContext

[in] Reserved for use with future MSHCTX values.

mshlflags

[in] Specifies why marshalling is taking place. For a definition of the MSHLFLAGS enumeration (on which mshflags is based), see "Data Structures," at the beginning of the IMarshal interface description.

pSize

[out] Points to the maximum marshal size.

Return Values

Value	Meaning
S_OK	Maximum size needed for marshalling successfully returned.
E_INVALIDARG	An invalid argument was passed as a parameter.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Out of memory.
E_FAIL	Object class could not be obtained.
Remarks

Calling applications have the option of using the data size returned to pre-allocate stream buffers for use in the marshalling process. RPC also uses it.

The dwDestContext parameter identifies the destination context, which is relative to the current context in which the unmarshalling is to be done. For example, different marshalling might be done depending on whether the unmarshalling is to happen on the local workstation or on a workstation on the network. An object might do custom marshalling in one case but not the other.

IMarshal::MarshalInterface cannot rely on the caller having already called GetMarshalSizeMax; it must still be aware of STG_E_MEDIUMFULL errors returned by the stream.

The return value is guaranteed to be valid only as long as the internal state of the object being marshalled does not change. Marshalling should be done immediately after IMarshal::GetMarshalSizeMax returns; otherwise, the caller runs the risk that the object might require more memory than was originally indicated.

See Also

IMarshal::GetUnmarshalClass, IMarshal::MarshalInterface, IMarshal::UnmarshalInterface


IMarshal::MarshalInterface

HRESULT IMarshal::MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)
IStream *pStm
REFIID riid
VOID *pv
DWORD dwDestContext
VOID *pvDestContext
DWORD mshlflags

IMarshal::MarshalInterface marshals a reference of the object's interface ID into the specified stream.

Parameters

pStm

[in] Points to the stream in which the object is to be marshalled.

riid

[in] Specifies the interface ID of the object to be marshalled.

pv

[in] Points to the interface to be marshalled; it can be NULL.

dwDestContext

[in] Specifies the destination context relative to the current context in which the unmarshalling will be done. For a definition of the MSHCTX enumeration (on which dwDestContext is based), see "Data Structures," at the beginning of the IMarshal interface description.

pvDestContext

[in] Reserved for use with future MSHCTX values. Although this value should be NULL, do not check for it.

mshlflags

[in] Specifies why unmarshalling is to be done. Valid values are from the MSHLFLAGS enumeration. For more information, see "Data Structures," at the beginning of the IMarshal interface description.

Return Values

Value	Meaning
S_OK	The interface ID reference was marshalled successfully.
E_FAIL	Indicates an unspecified error.
STG_E_MEDIUMFULL.	The medium is full.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
For information on possible stream access errors, see the IStream methods.
Remarks

Once the contents of the stream are sent to the destination, the interface reference can be restored by using the CLSID used to create the handler and then calling IMarshal::UnmarshalInterface. An implementation of IMarshal::MarshalInterface writes to the stream any data required for initialization of the proxy.

The dwDestContext parameter identifies the destination context, which is relative to the current context for the unmarshalling. For example, different marshalling might be done depending on whether the unmarshalling will happen on the local workstation or on a workstation on the network; an object could do custom marshalling in one case but not the other.

A custom implementation of IMarshal::MarshalInterface can delegate some destination contexts to the standard marshalling implementation, which is available by calling CoGetStandardMarshal. Delegating destination contexts should always be done if dwDestContext contains any flags that MarshalInterface does not understand.

The data marshalled in a particular IMarshal::MarshalInterface call can be unmarshalled zero or more times; marshallers must be able to handle the unmarshalling. If a proxy relies on state information in the object application (such as a stub whose functions unmarshal the arguments and then forward the call onto the real receiver), by implication this state must deal with zero or more proxies being created from the same initialization data.

If the caller already has the riid of the interface being marshalled, it should pass the interface pointer through pv. If the caller does not have the riid, it should pass NULL; IMarshal::MarshalInterface will call QueryInterface on itself to get the interface pointer. On exit from this method, the seek pointer in the data stream must be positioned after the last byte of data written.

See Also

IMarshal::GetUnmarshalClass, IMarshal::GetMarshalSizeMax, IMarshal::UnmarshalInterface


IMarshal::UnmarshalInterface

HRESULT IMarshal::UnmarshalInterface(pStm, riid, ppv)
IStream *pStm
REFIID riid
VOID **ppv

IMarshal::UnmarshalInterface initializes a newly created proxy as part of the unmarshalling process.

Parameters

pStm

[in] Points to the stream in which the interface is to be unmarshalled.

riid

[in] Identifies the interface that the caller wants from the object.

ppv

[out] Points to where the proxy is to be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The proxy was initialized successfully.
E_FAIL	Indicates an unspecified error.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
For information on possible stream access errors, see the IStream interface.
Remarks

To get the information required to complete the call, IMarshal::UnmarshalInterface often calls QueryInterface(riid, ppvObject) on itself immediately before returning. On exit from this method, the seek pointer in the data stream must be positioned after the last byte of data read.

See Also

IMarshal::GetUnmarshalClass, IMarshal::GetMarshalSizeMax, IMarshal::MarshalInterface


IMarshal::ReleaseMarshalData

HRESULT IMarshal::ReleaseMarshalData(pStm)
IStream *pStm

IMarshal::ReleaseMarshalData is called by the unmarshaller (usually CoReleaseMarshalData or CoUnMarshalInterface) to destroy a marshalled data packet.

Parameters

pStm

[in] Points to a stream that contains the data packet to be destroyed.

Return Values

Value	Meaning
S_OK	The data packet was released successfully.
E_FAIL	Indicates an unspecified error.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
See the IStream interface for information on possible stream access errors.
Remarks

On success, the stream position must be after the data that is released.

See Also

CoUnMarshalInterface, CoReleaseMarshalData


IMarshal::DisconnectObject

HRESULT IMarshal::DisconnectObject(dwReserved)
DWORD dwReserved

IMarshal::DisconnectObject is called by CoDisconnectObject when the object that is being disconnected supports custom marshalling. This is analogous to how CoMarshalInterface defers to IMarshal::MarshalInterface if the object supports IMarshal.

Parameters

dwReserved

[in] Reserved for future use; must be set to zero by the caller. To ensure compatibility in the future, IMarshal:Disconnect must not check for zero.

Return Values

Value	Meaning
S_OK	The object was disconnected successfully.
E_FAIL	Indicates an unspecified error.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.


IStdMarshalInfo Interface

The IStdMarshalInfo interface is implemented by objects as part of the marshalling process. Applications that support object class emulation must use IStdMarshalInfo in order to obtain the CLSID of the new class.

IStdMarshalInfo contains the following methods:

DECLARE_INTERFACE_(IStdMarshalInfo, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS _ REFIID riid, void ** ppvObject) ;

  ULONG  AddRef (THIS )  ;

  ULONG  Release (THIS ) ;

    // *** IStdMarshalInfo method ***

  HRESULT GetClassForHandler (THIS_ DWORD dwDestContext,

    VOID * pvDestContext, CLSID * pClsid) ;

};


IStdMarshalInfo::GetClassForHandler

HRESULT IStdMarshalInfo::GetClassForHandler(dwDestContext, pvDestContext, pClsid)
DWORD dwDestContext
VOID *pvDestContext
CLSID *pClsid

IStdMarshalInfo::GetClassForHandler retrieves the CLSID of the object handler that is used for standard marshalling in the destination process.

Parameters

dwDestContext

[in] Specifies the type of destination context to which this object is being passed. For a definition of the MSHCTX enumeration (on which dwDestContext is based), see "Data Structures," at the beginning of the IMarshal interface description.

pvDestContext

[in] Points to the destination context.

pClsid

[out] Points to the returned CLSID.

Return Values

Value	Meaning
S_OK	The CLSID was retrieved successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Object applications that support class emulation (Activate As in the Convert dialog box) must implement IStdMarshalInfo so that the correct object handler can be determined in all cases.

Example

In the following example, IStdMarshalInfo::GetClassForHandler supports only the MSHCTX_LOCAL context, meaning it supports marshalling and unmarshalling on the local workstation and not over the network. IStdMarshalInfo::GetClassForHandler returns the CLSID, which is used later in object conversion.

For more information on converting and emulating objects, see Object Class Conversion and Emulation Functions.

HRESULT IStdMarshalInfo_GetClassForHandler(

LPSTDMARSHALINFO	lpThis,

DWORD	dwDestContext,

VOID	*	pvDestContext,

CLSID	*	pClsid)

{

// Only handle LOCAL marshal context.

if (dwDestContext != MSHCTX_LOCAL |)

return E_INVALIDARG;

// Return REAL clsid

*pClsid = CLSID_APP;

return NOERROR;

}

See Also

CoTreatAsClass, CoGetTreatAsClass


Custom Marshalling Functions

The following functions have been created to help in the marshalling and unmarshalling of interface method parameters. These functions are defined as follows:

CoGetStandardMarshal(REFIID riid, LPUNKNOWN pUnk, DWORD dwDestContext,
    LPVOID pvDestContext, DWORD mshlflags, LPMARSHAL * ppMarshal);
CoGetMarshalSizeMax(ULONG * pulSize, REFIID riid, LPUNKNOWN pUnk,
    DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags)

CoMarshalHresult(LPSTREAM pstm, HRESULT hresult);

CoUmarshalHresult(LPSTREAM pstm, HRESULT * phresult);

CoMarshalInterface(LPSTREAM pStm, REFIID riid, LPUNKNOWN pUnk,
    DWORD dwDestContext, LPVOID pvDextContext, DWORD mshlflags);

CoUnmarshalInterface(LPSTREAM pStm, REFIID riid, LPVOID * ppv);

CoReleaseMarshalData(LPSTREAM pStm);

The process of marshalling and unmarshalling a method parameter depends on the type of the parameter. The differences usually center around how pointers are followed and when memory allocations are done in order to make a copy of the parameter to pass to the other process.

Conceptually, there are tables in the marshalling and unmarshalling code that describe how to marshal and unmarshal different types of data, including int, long, double, LPOLESTR strings, and various kinds of structures.

If the object being remoted does not support custom marshalling (as indicated by the lack of support for the IMarshal interface), then OLE's standard marshalling is used. When using standard marshalling, the actual marshalling and unmarshalling of interface method parameters is handled by OLE.

The object being marshalled can make use of a custom object handler to do some processing locally; however, the majority of requests are sent to the object using the OLE-supplied standard marshalling mechanism. Custom object handlers must use standard marshalling, which means that custom handlers must aggregate the OLE 2 default handler, as described in OleCreateDefaultHandler.

Once OLE determines that standard marshalling is to be used, it checks for a custom handler by querying the object for the IStdMarshalInfo interface. If either of these interfaces is supported, the returned CLSID is used to identify the custom handler to be loaded in the container's process space (see CoGetClassObject). The CLSID is obtained by calling the method contained in each of the interfaces.

If a custom handler is not supported, the OLE default remoting handler is used. For components that are not embedded objects, this is the common situation. It corresponds to the classic RPC scenario in which the remote proxy is little more than a forwarder of requests.


Custom Marshaling
Custom Marshalling

Objects can implement their own marshalling and unmarshalling functionality or let OLE do it for them. An OLE object may choose to do custom marshalling for three reasons:

•	Objects that are already proxies (or handlers) use custom marshalling to avoid creating proxies to proxies. Instead, new proxies are short circuited back to the original stub. This is an important consideration for both efficiency and robustness.

•	An object whose entire state is kept in shared memory can often be remoted by creating an object in the container application that communicates directly with the shared memory rather than with the original object. This can improve performance significantly, because access to the remoted object does not result in context switches. The compound file implementations of IStorage and IStream are examples of custom marshalling.

•	 From the time they are created, some objects do not change; that is, their internal state does not change. Monikers are an example of such objects. Custom marshalling remotes this kind of object efficiently by making independent copies of them in their client processes.

Note  Because of the way OLE 2 object handlers communicate with object applications as an object's running state is entered, embedded objects can't use custom marshalling unless they are completely implemented in the object handler.


Storing Marshaled Interface Pointers in Global Tables
Storing Marshalled Interface Pointers in Global Tables

Usually, marshalled interface pointers are sent across a process boundary from one process to another (container or object) where they are unmarshalled. In this usage, the data packet that results from the marshalling process is unmarshalled just once. However, there are occasions where marshalled interface pointers need to be stored in a globally accessible table. Once in the table, the data packet can be retrieved and unmarshalled zero, one, or more times.

The running object table and the internal table maintained by CoRegisterClassObject are examples of this concept. In effect, the marshalled data packet sitting in the table acts much like another pointer to the object. Depending on the semantics of the table in question, the data-packet pointer may be either a reference-counted or nonreference-counted pointer to the interface. Thus, depending on the table in which the object is placed, the presence of the object in the table can or might not keep the object alive. This represents the concept of strong and weak connections. For more information, see CoLockObjectExternal.

Because of this behavior, the marshalling code must execute at the time these data packets are removed from these tables and destroyed. The packets cannot be simply discarded, because the presence or absence of the internal state they maintain might be important to the object they indicate.

When an interface pointer is marshalled it is told why it is being marshalled. There are three possible reasons for marshalling:

1.	This is a typical case of marshal then unmarshal once (this is the responsibility of the unmarshaller).

2.	For storing into a global-table case. The presence of the entry in the table is to count as an additional reference to the interface.

3.	For storing into a global-table case. The entry in the table should not count as an additional reference to the interface.

Whenever a marshalled data packet from case 2 or 3 is removed from the table, it is the responsibility of the table implementor to call CoReleaseMarshalData.


CoGetStandardMarshal

HRESULT CoGetStandardMarshal(riid, pUnk, dwDestContext, DestContext, mshlflags,
 ppMarshal)
REFIID riid
LPUNKNOWN pUnk
DWORD dwDestContext
LPVOID pvDestContext
DWORD mshlflags
LPMARSHAL  *ppMarshal

CoGetStandardMarshal returns a marshaller that performs default marshalling.

Parameters

riid

Specifies the interface to be marshalled.

pUnk

Points to the object to be marshalled. This interface does not have to be of type riid; it can be any interface on the object which conforms to IUnknown. The standard marshaller will internally call QueryInterface.

dwDestContext

Specifies the destination context relative to the current context for the unmarshalling.

pvDestContext

Points to the associated destination context.

mshlflags

Specifies why unmarshalling is to be done. For a definition of the MSHLFLAGS enumeration (from which mshlflags is derived), see "Data Structures," at the beginning of the IMarshal interface description.

ppMarshal

Points to where to return the standard marshaller.

Return Values

Value	Meaning
S_OK	The IMarshal instance was returned successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	Indicates an unspecified error.
Remarks

CoGetStandardMarshal can be used only in 32-bit applications. Custom marshalling implementations should delegate any destination contexts they do not understand and those they want the standard marshaller to handle. The standard marshaller is also used when the object being marshalled does not support the IMarshal interface.

See Also

IMarshal Interface


CoGetMarshalSizeMax

CoGetMarshalSizeMax(pulSize, riid, pUnk, dwDestContext, pvDestContext, mshlflags)
ULONG * pulSize
REFIID riid
LPUNKNOWN pUnk
DWORD dwDestContext
LPVOID pvDestContext
DWORD mshlflags

CoGetMarshalSizeMax provides the maximum size of a stream required by a custom marshaller.

Parameters

pulSize

Points to the upper memory bound on the amount of data that marshalling will write into a stream when the object supports the IMarshal interface.

riid

Identifies the interface of the object to be marshalled.

pUnk

Points to the object to be marshalled.

dwDestContext

Specifies the destination context relative to the current context in which the unmarshalling is to be done.

For a definition of the MSHCTX enumeration (on which dwDestContext is based), see "Data Structures" at the beginning of the IMarshal interface description.

pvDestContext

Reserved for use with future MSHCTX values.

mshlflags

Specifies why marshalling is taking place. Valid values are from the MSHLFLAGS enumeration. For a description of it, see "Data Structures" at the beginning of the IMarshal interface description.

Return Values

Value	Meaning
S_OK	Maximum size needed for marshalling successfully returned.
E_INVALIDARG	An invalid argument was passed as a parameter.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Out of memory.
E_FAIL	Object class could not be obtained.
Remarks

If an object supports IMarshal, meaning that it does custom marshalling, it calls this function. If the object does not support custom marshalling, it calls CoGetStandardMarshal to get the standard marshaller for the object and uses its IMarshal interface to determine the maximum stream size required. In either case, it returns this maximum size plus a bit extra to account for the space required by CoMarshalInterface itself.

Calling applications have the option of using the data size returned to pre-allocate stream buffers for use in the marshalling process. RPC also uses it.

The dwDestContext parameter identifies the destination context, which is relative to the current context in which the unmarshalling is to be done. For example, different marshalling might be done depending on whether the unmarshalling is to happen on the local workstation or on a workstation on the network. An object might do custom marshalling in one case but not the other.


CoMarshalHresult

HRESULT CoMarshalHresult(pstm, hresult)
LPSTREAM pstm
HRESULT hresult

CoMarshalHresult marshals an HRESULT to the specified stream so it can be unmarshalled using CoUnmarshalHresult.

Parameters

pstm

Points to the target stream.

hresult

Specifies the original HRESULT.

Return Values

Value	Meaning
S_OK	The HRESULT was marshalled successfully.
STG_E_INVALIDPOINTER	Bad pointer passed in for pstm.
STG_E_MEDIUMFULL	The medium is full.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

HRESULTs are process-specific; an HRESULT valid in one process might not be valid in another. CoMarshalHresult is used to marshal an HRESULT from one process to another. It is needed only by those developers who are writing custom marshalling code. They are required to use it for marshalling HRESULTs, either as parameters or as return codes.

The HRESULT returned by CoMarshalHresult indicates the success or failure of the marshalling process and is unrelated to the HRESULT parameter.

See Also

CoUnmarshalHresult


CoUnmarshalHresult

HRESULT CoUnmarshalHresult(pstm, phresult)
LPSTREAM pstm
HRESULT * phresult

CoUnmarshalHresult unmarshals an HRESULT from the specified stream.

Parameters

pstm

Points to the stream that contains the HRESULT.

phresult

Points to the target memory location.

Return Values

Value	Meaning
S_OK	The HRESULT was unmarshalled successfully.
STG_E_INVALIDPOINTER	Bad pointer passed in for pstm.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

CoUnmarshalHresult is needed only by applications that implement custom marshalling. Custom marshallers must use CoUnmarshalHresult to unmarshal all HRESULTs.

See Also

CoMarshalHresult


CoMarshalInterface

HRESULT CoMarshalInterface(pStm, riid, pUnk, dwDestContext, pvDestContext, mshlflags)
LPSTREAM pStm
REFIID riid
LPUNKNOWN pUnk
DWORD dwDestContext
LPVOID pvDestContext
DWORD mshlflags

CoMarshalInterface marshals an object instance the specified stream so that it can be unmarshalled with CoUnMarshalInterface. CoMarshalInterface only marshals interfaces derived from IUnknown.

Parameters

pStm

Specifies the target stream.

riid

Specifies the interface exposed by the object to be marshalled. This interface must be derived from IUnknown.

pUnk

Points to the object to be marshalled. Only objects derived from IUnknown can be marshalled.

dwDestContext

Specifies the destination context relative to the current context in which the unmarshalling is to be done; the values are from the enumeration MSHCTX. For a definition of MSHCTX, see "Data Structures," at the beginning of the IMarshal interface description.

pvDestContext

Reserved for future use; must be set to NULL by the caller.

mshlflags

Specifies why unmarshalling is to be done. For a definition of the MSHLFLAGS enumeration (from which mshlflags is derived), see "Data Structures," at the beginning of the IMarshal interface description.

Return Values

Value	Meaning
S_OK	The object was marshalled successfully.
STG_E_MEDIUMFULL	The medium is full.
STG_E_INVALIDPOINTER	Invalid value for pStm.
CO_E_OBJNOTCONNECTED	Similar to OLE_E_NOTRUNNING. The object is loaded but no server is running.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	Indicates an unspecified error.
Remarks

The dwDestContext parameter identifies the destination context, which is relative to the current context in which the unmarshalling is to be done.

CoMarshalInterface first tries to obtain a marshaller from the object by calling pUnk -> QueryInterface. If that fails, the standard marshaller (accessible by CoGetStandardMarshal) is used. CoMarshalInterface calls IMarshal::MarshalInterface and IMarshal::GetMarshalSizeMax to marshal the interface. IMarshal::GetMarshalSizeMax is  used in 32-bit OLE 2; the stream passed to MarshalInterface must be able to grow dynamically.

CoMarshalInterface is usually only called by code in interface proxies or stubs that marshal an interface pointer parameter, although it is sometimes used by objects that support custom marshalling.

To custom-marshal an object, the sending application performs the following steps:

1.	Get the CLSID to be used for creating an uninitialized proxy in the unmarshalling context by calling IMarshal::GetUnmarshalClass.

2.	(Optional.) Get an upper bound on the amount of memory needed to do the marshalling by calling IMarshal::GetMarshalSizeMax.

3.	Marshal by calling IMarshal::MarshalInterface. The CLSID and the data marshalled into the stream are then sent to the destination, where they are unmarshalled.

Calling CoMarshalInterface on an OLE 1 object will always fail with CO_E_OBJNOTCONNECTED, even if the OLE 1 object is running. To allow CoMarshalInterface to succeed would introduce multiple DDE conversations between different processes, which OLE 1 was not designed to support.

See Also

CoUnmarshalInterface


CoUnmarshalInterface

HRESULT CoUnmarshalInterface(pStm, riid, ppv)
LPSTREAM pStm
REFIID riid
LPVOID  * ppv

CoUnmarshalInterface unmarshals a previously marshalled object from the specified stream. This function is provided for convenience in unmarshalling previously marshalled objects.

Parameters

pStm

Points to the stream from which the object is to be unmarshalled.

riid

Specifies the interface that is to communicate with the unmarshalled object.

ppv

Points to the location where the interface pointer is to be returned. It is NULL on error.

Return Values

Value	Meaning
S_OK	The interface was unmarshalled successfully.
STG_E_INVALIDPOINTER	An IStream error dealing with the pStm parameter.
CO_E_OBJNOTCONNECTED	The object application has been disconnected from the remoting system (for example, as a result of CoDisconnectObject).
REGDB_E_CLASSNOTREG	An error occurred reading the registry.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_NOINTERFACE	The final QueryInterface of this function for the requested interface returned E_NOINTERFACE.
E_FAIL	Indicates an unspecified error.
CoCreateInstance errors	An error occurred when creating the handler (standard or custom marshalling).
Remarks

Unmarshalling an object requires the receiving application to perform the following steps:

1.	Load the class factory corresponding to the CLSID that the object application passed to IMarshal::GetUnmarshalClass:

LPCLASSFACTORY lpcf;

GetClassObject(cid, CLSCTX_INPROCHANDLER, IID_IClassFactory, &lpcf);

2.	Instantiate the class, requesting the IMarshal interface:

LPMARSHAL lpProxy;

lpcf->CreateInstance(NULL, IID_IMarshal, &lpProxy);

Note  If the proxy is being created as part of an aggregate, IUnknown is requested and the IMarshal interface queried.

3.	Initialize the proxy by calling IMarshal::UnmarshalInterface. Use a copy of the data originally produced by IMarshal::MarshalInterface and request the interface that was originally marshalled:

IOriginal * pobj;

lpProxy->UnmarshalInterface(lpstm, IID_Original, &pobj);

lpProxy->Release();

lpcf->Release();

if (MSHLFLAGS_NORMAL){

    save seek pos;

    reset Stm;

    lpProxy->ReleaseMarshalData(lpStm);

    restore seek pos;

}

See Also

CoMarshalInterface


CoReleaseMarshalData

HRESULT CoReleaseMarshalData(pStm)
LPSTREAM pStm

CoReleaseMarshalData destroys a previously marshalled data packet.

Parameters

pStm

Points to a stream containing the data packet to be destroyed.

Return Values

Value	Meaning
S_OK	The data packet was successfully destroyed.
STG_E_INVALIDPOINTER	An IStream error dealing with the pStm parameter.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	pStm is invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	Indicates an unspecified error.
Remarks

CoReleaseMarshalData must be called to destroy data packets. Examples of when CoReleaseMarshalData should be called include the following situations:

1.	An attempt was made to unmarshal the data packet, but failed.

2.	A marshalled data packet was removed from a global table.

See Also

IMarshal::ReleaseMarshalData


Component Object Functions

The following functions are used to locate and connect to a class of object, to register a class of object, and to revoke the registration of an object class. These functions are defined as follows:

  CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext, LPVOID pvReserved,
     REFIID riid, LPVOID * ppv);

  CoIsHandlerConnected(LPUNKNOWN pUnk);

  CoRegisterClassObject(REFCLSID rclsid, LPUNKNOWN pUnk, DWORD dwClsContext,
     DWORD flags,  LPDWORD lpdwRegister);

  CoRevokeClassObject(DWORD dwRegister);

  CoLockObjectExternal(LPUNKNOWN pUnk, BOOL fLock, BOOL fLastUnlockReleases);

CoDisconnectObject(LPUNKNOWN pUnk, DWORD dwReserved);


CoGetClassObject

HRESULT CoGetClassObject(rclsid, grfContext, pvReserved, riid, ppvj)
REFCLSID rclsid
DWORD dwClsContext
LPVOID pvReserved
REFIID riid
LPVOID * ppv

CoGetClassObject locates and connects to a specified class object.

Parameters

rclsid

Specifies the object class to be loaded.

dwClsContext

Specifies the context in which the executable code is to be run.

pvReserved

Reserved for future use; must be NULL.

riid

Specifies the interface to be used to communicate with the class object.

ppv

Points to where the interface is to be stored. NULL on error.

Return Values

Value	Meaning
S_OK	The specified object was located and connected to successfully.
REGDB_E_CLASSNOTREG	CLSID is not properly registered.
E_NOINTERFACE	The object pointed to by ppv does not support the interface identified by riid.
REGDB_E_READREGDB	Error reading the registry.
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/WinExec).
CO_E_WRONGOSFORAPP	EXE or DLL is written for a different operating system than is currently running.
CO_E_ERRORINAPP	EXE has an error in image.
CO_E_APPSINGLEUSE	EXE can only be launched once.
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 shutdown).
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_NOINTERFACE	The QueryInterface on the class object returned E_NOINTERFACE.
Remarks

Given a CLSID, a container can opaquely locate and dynamically load the executable code that manipulates an object by calling CoGetClassObject. The container passes CoGetClassObject to the CLSID and to the interface it wants to use to communicate with the object. Most often, this is the IClassFactory interface, through which the container can then make an instance of the object by calling IClassFactory::CreateInstance.

If necessary, the object's executable code can be dynamically loaded in order to locate and connect to the class object. The riid parameter specifies the interface used by the caller to communicate with the class object; this is usually IID_IClassFactory.

Different pieces of code can be associated with one rclsid for use in different execution contexts. The context in which the caller is interested is indicated by the parameter dwClsContext, a group of flags taken from the enumeration CLSCTX, which is defined as follows:

typedef enum tagCLSCTX

{

    CLSCTX_INPROC_SERVER = 1

    CLSCTX_INPROC_HANDLER = 2,

    CLSCTX_LOCAL_SERVER = 4

} CLSCTX;

The contexts are tried in the order in which they are listed. Multiple values can be ORed together, indicating that multiple contexts are acceptable to the caller; for example:

#define CLSCTX_INPROC (CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER)
#define CLSCTX_SERVER (CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER)

These contexts are used as follows:

Object Class Context Values	Description
CLSCTX_INPROC_SERVER	The code that creates and manages objects of this class is loaded in the container's process space (runs in same process as caller).
CLSCTX_INPROC_HANDLER	The DLL code that implements container-side structures of this class (when instances of it are accessed remotely) is loaded in the container's process space (runs in same process as caller).
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).
CoGetClassObject operates by consulting (as appropriate for the context indicated) both the registry and the existing class objects registered using CoRegisterClassObject.

Example uses of particular flag combinations are as follows:

Function Called	Context Flag Used
OleLoad	CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER Putting an OLE object into the loaded state requires in-process access; however, it doesn't matter if all of the object's functionality 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   Unmarshalling needs the form of the class designed for remote access.
See Also

CoRegisterClassObject, CoRevokeClassObject


CoIsHandlerConnected

BOOL CoIsHandlerConnected(pUnk)
LPUNKNOWN pUnk

CoIsHandlerConnected determines if the specified handler is connected to its corresponding object.

Parameters

pUnk

Specifies the object in question.

Return Values

Value	Meaning
TRUE	A handler is connected to its object.
FALSE	A handler is not connected to its object.
Remarks

If a pointer to the real object is passed in instead of the handler, TRUE will be returned.


CoRegisterClassObject

HRESULT CoRegisterClassObject(rclsid, pUnk, dwClsContext, flags, lpdwRegister)
REFCLSID rclsid
LPUNKNOWN pUnk
DWORD dwClsContext
DWORD flags
LPDWORD lpdwRegister

CoRegisterClassObject registers a class factory for an object class with OLE so that other applications can connect to it.

Parameters

rclsid

Specifies the CLSID of the object class being registered.

pUnk

Points to the class factory's implementation of IUnknown whose availability is being published.

dwClsContext

Specifies the context in which the executable code is to be run: CLSCTX_INPROC_SERVER, CLSCTX_INPROC_HANDLER, or CLSCTX_LOCAL_SERVER. For more information on these context values, see CoGetClassObject.

flags

Determines how connections are made to the class object; see the following comments.

lpdwRegister

Points to a value that identifies the class factory; later used by CoRevokeClassObject to revoke the class registration.

Return Values

Value	Meaning
S_OK	The object was registered successfully.
CO_E_OBJISREG	Object class already registered in the class object table.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

When an object application starts, it instantiates a class factory object and calls CoRegisterClassObject. Object applications that support several different object classes must allocate a different class factory for each object class.

CoRegisterClassObject is called by EXE object applications only; it should not be called by object handlers or DLL object applications. These applications must instead implement and export DllGetClassObject.

Multiple document interface (MDI) applications must register their class objects. Single document interface (SDI) applications must register their class objects only if they are started by means of the /Embedding switch.

Any single-use, or multiple-use, application that does not have a filename and is started with the /Embedding switch must register their class of object.

When the subsequent reference count on the class object reaches zero, the class object can be destroyed, allowing the application to exit.

A sophisticated container can keep an object application running by obtaining its class object and calling IClassFactory::LockServer(TRUE) on it. However, because some class objects are single use, the container must hold on to the actual class object from which objects are instantiated. This prohibits the container application from using helper functions like OleLoad. Instead, the container has to code variants of such functions itself.

The flags parameter controls how connections are made to the class object; the values are taken from the enumeration REGCLS, which is defined as follows:

typedef enum tagREGCLS {

REGCLS_SINGLEUSE 	 	= 0,

REGCLS_MULTIPLEUSE 	= 1,

REGCLS_MULTI_SEPARATE 	= 2,

} REGCLS;

Legal values for flags are as follows:

Registration Class Flag Values	Description
REGCLS_SINGLEUSE	Once a container has connected to the class object with CoGetClassObject, the class object should be removed from public view so that no other container applications can similarly connect to it. This flag is commonly given for single document interface (SDI) applications. Specifying this flag does not affect the responsibility of the object application to call CoRevokeClassObject; it must always call CoRevokeClassObject when it is done by using an object class.
REGCLS_MULTIPLEUSE	Enables multiple CoGetClassObject calls to connect to the same class object.
REGCLS_MULTI_SEPARATE	Similar to REGCLS_MULTIPLEUSE, except that REGCLS_MULTI_SEPARATE does not automatically register the class object as CLSCTX_INPROC_SERVER for a local server; instead it provides separate control over each context.
Notice that registering as

CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE

is the same as

(CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER), REGCLS_MULTI_SEPARATE

but is different than registering as

CLSCTX_LOCAL_SERVER, REGCLS_MULTI_SEPARATE.

The following table summarizes the allowable flag combinations and the object registrations that are affected by the combinations:

	REGCLS_ SINGLEUSE	REGCLS_ MULTIPLEUSE	REGCLS_ MULTI_ SEPARATE	Other
CLSCTX_ INPROC_ SERVER	Error	Inproc	Inproc	Error
CLSCTX_ LOCAL_ SERVER	Local	Inproc/local	Local	Error
Both of the above	Error	Inproc/local	Inproc/local	Error
Other	Error	Error	Error	Error
The key difference is in the middle columns and the middle rows. In the REGCLS_MULTIPLEUSE column, they are the same (registers multiple use for both inproc and local). In the REGCLS_MULTI_SEPARATE column, CLSCTX_LOCAL_SERVER is local only.

See Also

CoGetClassObject, CoRevokeClassObject, DllGetClassObject


CoRevokeClassObject

HRESULT CoRevokeClassObject(dwRegister)
DWORD dwRegister

CoRevokeClassObject informs OLE that a class object, previously registered with CoRegisterClassObject, is no longer available for use.

Parameters

dwRegister

Specifies the token previously returned from CoRegisterClassObject.

Return Values

Value	Meaning
S_OK	The class object was successfully revoked.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The dwRegister parameter does not map to a registered class object.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If a call to CoRevokeClassObject is successful, it means only that the class factory has been removed from the global class factory table. However, the reference count will not be zero if other clients still have pointers to the class factory that have cause the reference count to be incremented by calls to AddRef. When this occurs, applications may benefit if subsequent calls to the class factory are failed, with the obvious exceptions of AddRef and Release.

The object application must call CoRevokeClassObject on all registered class objects prior to quitting program execution. CoRevokeClassObject should be called by class object implementors as part of their release sequence.

Specifying the flags value REGCLS_SINGLEUSE in a call to CoRegisterClassObject does not affect the responsibility of the object application to revoke registered class objects. This task must always be done prior to quitting.

See Also

CoGetClassObject, CoRegisterClassObject


CoLockObjectExternal

HRESULT CoLockObjectExternal(pUnk, fLock, fLastUnlockReleases)
LPUNKNOWN pUnk
BOOL fLock
BOOL fLastUnlockReleases

CoLockObjectExternal is one way to lock an object so its reference count cannot decrement to zero; it is also used to release a lock. See the Remarks section below.

Parameters

pUnk

Points to the object to be locked or unlocked.

fLock

Specifies whether the object is to be locked. TRUE holds a reference to the object, locking it independently of external or internal AddRef/Release operations, registrations, or revokes. If fLock is TRUE, fLastLockReleases is ignored. FALSE releases such locks.

fLastLockReleases

Specifies whether a given lock is the last reference to an object that is supposed to keep it alive. TRUE releases all pointers to the object if this lock is the last reference to the object that is supposed to keep it alive (there may be other references that are not supposed to keep it alive).

Return Values

Value	Meaning
S_OK	The object reference count was locked successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

CoLockObjectExternal must be called in the process in which the object is loaded. From the object's point of view, the lock is implemented by having the system call IUnknown::AddRef on the object and then not call IUnknown::Release on the object until CoLockObjectExternal(...,FALSE,...) is later called. Because it acts outside of the object, much like the user does, it can be used to maintain a reference count on the object on behalf of the user.

Using CoLockObjectExternal does not change the typical registration/revoking process for objects. CoLockObjectExternal can be used in IOleContainer::LockContainer, although the container may still keep a lock count so that it exits when the lock count reaches zero and the container is invisible.

OLE uses the concept of strong and weak locks. Whereas a strong lock on an object will keep it in memory, a weak lock will not. Strong locks are required, for example, when links to embedded objects need to be updated. 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's object to ensure the application stays alive until it has finished providing object services to its clients. All external references to an object result in a strong reference lock being placed on the object.

After an application calls CoRegisterClassObject, it should call CoLockObjectExternal to ask OLE to maintain a strong lock on its class object. CoLockObjectExternal locks any object so that its reference count cannot decrement to zero. It can also be used to release such a lock. CoLockObjectExternal can only be called by the process in which the object is located.

CoLockObjectExternal is typically used in three places:

•	Object applications call CoLockObjectExternal(pUnk, TRUE, TRUE) when they become visible. This call creates a strong lock on behalf of the user. When the application is closing, the lock must be freed with a call to CoLockObjectExternal (pUnk, FALSE, TRUE).

•	A call to CoLockObjectExternal can be included in the implementation of IClassFactory::LockServer. Containers call IClassFactory::LockServer to keep an object application that handles a particular class of object(s) in memory, thereby eliminating startup time and allowing faster throughput during activation.

•	A call to CoLockObjectExternal on the application object can also be in the implementation of IOleContainer::LockContainer. IOleContainer::LockContainer is called to keep an embedded object's container alive.

Conversely, a container must call OleSetContainedObject to weaken its connection to the object so other connections can determine what is to happen to the object while it is invisible. OleSetContainedObject is similar to calling CoLockObjectExternal(pUnk, FALSE, FALSE).

If an application manages all aspects of its application and document shutdown completely with calls to CoLockObjectExternal, it is not necessary to maintain a private count of calls in IOleContainer::LockContainer. Maintaining all aspects of shutdown means that CoLockObjectExternal is called whenever one of the following conditions occur:

•	A document is created and destroyed or made visible or invisible.

•	The 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 made on the application.

Figure 5.4 shows the state of an application after CoLockObjectExternal is called. OLE creates a transparent stub manager that keeps track of the type and number of external references counts. As shown, the class object's reference count is undefined, which reflects the strong lock held by the stub manager and the weak lock held by the application object.

Figure 5.4    Creating a strong reference lock on an object

Weak locks are created when an object is registered in the running object table, when a drop target is registered, or if a container's reference to an embedding is weak as a result of using OleSetContainerObject. When there is a weak lock, the last external release releases the object. The object is still responsible for revoking its drag/drop registration and running object table registration. This enables a moniker bind and release to shut down the object when it is invisible.

Note  A user has explicit control over the lifetime of an application, even if there are external locks on the application. That is, if a user decides to close the application (File, Exit), the application must shut down. In the presence of external locks on an object, the application can call CoDisconnectObject to force these connections to close prior to shutdown.

See Also

IOleContainer::LockContainer, OleSetContainedObject


CoDisconnectObject

HRESULT CoDisconnectObject(pUnk, dwReserved)
LPUNKNOWN pUnk
DWORD dwReserved

CoDisconnectObject disconnects all remote process connections maintained on behalf of the interface pointers on a specified object.

Parameters

pUnk

Points to the object to be disconnected. Can be any interface on the object that is polymorphic with IUnknown, not necessarily the exact interface returned by QueryInterface(IID_IUnknown...).

dwReserved

Reserved for future use; must be set to zero by the caller.

Return Values

Value	Meaning
S_OK	All remote process connections were deleted successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	Indicates an unspecified error.
Remarks

CoDisconnectObject is not used in the typical course of processing (clients of interfaces should use IUnknown::Release). The primary purpose of CoDisconnectObject is to give an application process control over remote connections to other processes that might have been made from objects managed by the process. For example, suppose an OLE container loads an embedded object and then links are established between the embedded object and outside containers. If the container application process wanted to exit, it is not useful for these remote connections to keep the object active beyond the lifetime of its container. When the object's container closes, such links should go into a disconnected state. To forcefully close any existing connection(s), the object's container can call CoDisconnectObject.

CoDisconnectObject effectively causes a controlled failure of the remoting connections to the object. This function is a privileged operation, to be invoked only by the process in which the object is actually managed.


DLL Initialization Functions

The following functions can be used to load and unload DLLs. They keep track of reference counts on the loaded DLL, removing them when the libraries are unloaded. The functions are defined as follows:

CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree);

CoFreeAllLibraries(void);

CoFreeLibrary(HINSTANCE hInst);

CoFreeUnusedLibraries(void);


CoLoadLibrary

HINSTANCE CoLoadLibrary(lpszLibName, bAutoFree)
LPOLESTR lpszLibName
BOOL bAutoFree

CoLoadLibrary loads the DLL of the given name into the caller's process. This is called internally by CoGetClassObject and not directly by a container.

Parameters

lpszLibName

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

Indicates, if TRUE, that this library is freed when it is no longer needed, either through CoFreeUnusedLibraries or at uninitialization time through CoUninitialize. If it is FALSE, the library should be explicitly freed with CoFreeLibrary.

Return Values

Value	Meaning
Module handle	Handle of the now-loaded library.
NULL	Library could not be loaded.
Remarks

Internally, the loaded DLL is reference-counted, by using CoLoadLibrary to increment the count and CoFreeLibrary to decrement it.

See Also

CoFreeAllLibraries, CoFreeLibrary, CoFreeUnusedLibraries


CoFreeAllLibraries

void CoFreeAllLibraries()

CoFreeAllLibraries frees all the DLLs that have been loaded with CoLoadLibrary, whether or not they are currently in use.

Remarks

The component object model library maintains a list of loaded DLLs for each process that is used by CoFreeAllLibraries to unload the libraries. CoFreeAllLibraries is called internally by CoUninitialize; thus containers usually have no need to call this function directly.

See Also

CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries


CoFreeLibrary

void CoFreeLibrary(hInst)
HINSTANCE hInst

CoFreeLibrary frees a library that was previously loaded with CoLoadLibrary(..., FALSE).

Parameters

hInst

Specifies the handle to the library module to be freed, as returned by CoLoadLibrary.

Remarks

It is illegal to explicitly free a library whose corresponding CoLoadLibrary call specified auto-free (CoLoadLibrary(...,TRUE)).

See Also

CoFreeAllLibraries, CoFreeUnusedLibraries, CoLoadLibrary


CoFreeUnusedLibraries

void CoFreeUnusedLibraries()

CoFreeUnusedLibraries is implemented in 32- and 16-bit OLE. It unloads any DLLs that have been loaded by CoLoadLibrary(..., TRUE) but are no longer in use.

Remarks

Applications can call CoFreeUnusedLibraries periodically to free resources, either at the top of their message loop or in some idle-time task.

See Also

CoFreeLibrary, CoFreeUnusedLibraries, CoLoadLibrary


Object Creation Function

The following function can be used to create an instance of a specified object class:

CoCreateInstance(REFCLSID rclsid, IUnknown * pUnkOuter, DWORD dwClsContext, REFIID riid,
    LPVOID  * ppv);


CoCreateInstance

HRESULT CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
REFCLSID rclsid
LPUNKNOWN * pUnkOuter
DWORD dwClsContext
REFIID riid
LPVOID  * ppv

CoCreateInstance creates an instance of the specified object class.

Parameters

rclsid

Specifies the CLSID of the object class to create.

pUnkOuter

Specifies the controlling unknown of the aggregate or NULL if the instance cannot be aggregated.

dwClsContext

Specifies the context in which the executable is to be run. The values are taken from the enumeration CLSCTX. For more information on CLSCTX, see the description for CoGetClassObject.

riid

Specifies the interface to be used to communicate with the object.

ppv

Points to where to return the pointer to the requested interface. Must be NULL on error.

Return Values

Value	Meaning
S_OK	An instance of the specified object class was successfully created.
REGDB_E_CLASSNOTREG	Specified class not registered in registry.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
CLASS_E_NOAGGREGATION	This class cannot be created as part of an aggregate.
Remarks

CoCreateInstance is a helper function that encapsulates the following functionality:

CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory,
 &pCF);

hresult = pCF->CreateInstance(pUnkOuter, riid, ppvObj)

pCF->Release();

CoCreateInstance will not force handler objects into the running state.

See Also

CoGetClassObject, IClassFactory::CreateInstance


File Time Conversion Functions
File-Time Conversion Functions

The following functions can be used to convert 16-bit MS-DOS representations of the time and date to 64-bit representations for use on platforms that support the Win32 API. The functions are defined as follows:

CoDosDateTimeToFileTime(WORD nDosDate, WORD nDosTime, FILETIME * lpFileTime);

  CoFileTimeNow(FILETIME * lpFileTime);

  CoFileTimeToDosDateTime(FILETIME * lpFileTime, LPWORD lpDosDate, LPWORD, lpDosTime);


FILETIME Data Structure

The FILETIME data structure is used by the CoDosDateTimeToFileTime, CoFileTimeToDosDateTime, and CoFileTimeNow functions; it is defined in COMPOBJ.H, as follows:

typedef struct STRUCT tagFILETIME

{

    DWORD dwLowDateTime;

    DWORD dwHighDateTime;

} FILETIME;


CoDosDateTimeToFileTime

BOOL CoDosDateTimeToFileTime (nDosDate, nDosTime, lpFileTime)
WORD nDosDate
WORD nDosTime
FILETIME * lpFileTime

CoDosDateTimeToFileTime converts the MS-DOS representation of the time and date to a FILETIME structure. This is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601.

Parameters

nDosDate

Specifies the 16-bit MS-DOS date.

nDosTime

Specifies the 16-bit MS-DOS time.

lpFileTime

Points to the FILETIME structure; for more information, see "FILETIME Data Structure," at the beginning of this section.

Return Values

Value	Meaning
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 were originally defined as 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).
See Also

CoFileTimeToDosDateTime, CoFileTimeNow


CoFileTimeNow

HRESULT CoFileTimeNow(lpFileTime)
FILETIME  * lpFileTime

CoFileTimeNow returns the current time as a FILETIME structure.

Parameters

lpFileTime

Points to where to return the FILETIME structure; for more information, see "FILETIME Data Structure," at the beginning of this section.

Return Values

Value	Meaning
S_OK	The current time was converted to a FILETIME structure.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	lpFileTime is invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	The current time was not converted to a FILETIME structure.
See Also

CoDosDateTimeToFileTime, CoFileTimeToDosDateTime


CoFileTimeToDosDateTime

BOOL CoFileTimeToDosDateTime (lpFileTime, lpDosDate, lpDosTime)
FILETIME  * lpFileTime
LPWORD lpDosDate
LPWORD lpDosTime

CoFileTimeToDosDateTime converts a FILETIME into MS-DOS date and time values.

Parameter

lpFileTime

Points to the FILETIME structure to convert; for more information, see FILETIME Data Structure at the beginning of this section.

lpDosDate

Points to the 16-bit MS-DOS date.

lpDosTime

Points to the 16-bit MS-DOS time.

Return Values

Value	Meaning
TRUE	The FILETIME structure was converted successfully.
FALSE	The FILETIME structure was not converted successfully.

Remarks

This is the inverse operation provided by CoDosDateTimeToFileTime.

See Also

CoDosDateTimeToFileTime, CoFileTimeNow


DLL Object Class Functions

The following functions can be implemented in DLL object handlers to enable containers to access a class of object implemented within an object handler or to indicate whether it is OK to unload a DLL handler from memory. The functions are defined as follows:

DllCanUnloadNow(void);

DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppv);


DllCanUnloadNow

HRESULT DllCanUnloadNow()

DllCanUnloadNow indicates whether the DLL is no longer in use and should be unloaded.

Return Values

Value	Meaning
S_OK	DLL can be unloaded now.
S_FALSE	DLL cannot be unloaded now.
Remarks

DllCanUnloadNow is not provided with OLE. Rather, it is a function implemented by and exported from DLLs supporting the Component Object Model.

DllCanUnloadNow should be exported from DLLs designed to be dynamically loaded in CoGetClassObject or CoLoadLibrary calls. A DLL is no longer in use when there are no existing instances of classes it manages; at this point, the DLL can be safely freed by calling CoFreeUnusedLibraries.

If the DLL loaded by CoGetClassObject fails to export DllCanUnloadNow, the DLL will only be unloaded when CoUninitialize is called 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 DLL should call CoLoadLibrary(path to second DLL, TRUE) which forces the Component Object Model library to reload the second DLL and set it up for CoFreeUnusedLibraries to free it when appropriate.

S_FALSE should be returned if there are any references to any objects or if there has been an IClassFactory::LockServer(TRUE) call.

See Also

DllGetClassObject


DllGetClassObject

HRESULT DllGetClassObject(rclsid, riid, ppv)
REFCLSID rclsid
REFIID riid
LPVOID  * ppv

DllGetClassObject retrieves the class object from a DLL object handler or object application.

Parameters

rclsid

Identifies the CLSID of the object class to be loaded.

riid

Specifies the interface that the caller is to use to communicate with the class object. Most often, this is IID_IClassFactory.

ppv

Points to either the pointer of the requested interface or to NULL if an error occurs.

Return Values

Value	Meaning
S_OK	The object was retrieved successfully.
CLASS_E_CLASSNOTAVAILABLE	DLL does not support class.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If a call to CoGetClassObject results in the class object being loaded from a DLL, CoGetClassObject uses the DLL's exported DllGetClassObject function to retrieve the class.

DllGetClassObject is not provided with OLE; rather, it is a function implemented and exported from DLLs supporting the Component Object Model.

Example

In the following C++ example, DllGetClassObject creates a class factory object and calls its QueryInterface method to retrieve a pointer to the interface requested in riid. Because the container is given a reference-counted pointer to IClassFactory, DllGetClassObject can safely release the reference it holds to IClassFactory.

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;

}

See Also

CoGetClassObject, DllCanUnloadNow


Validation Functions

The following APIs can be used to determine the validity of an interface pointer, a memory pointer, or an interface ID. The functions are defined as follows:

  IsValidInterface( void * pv );	// Obsolete in 32-bit OLE

  IsValidPtrIn( const void * pv, UINT cb );	// Obsolete in 32-bit OLE

  IsValidPtrOut( void * pv, UINT cb );	// Obsolete in 32-bit OLE


IsValidInterface

BOOL IsValidInterface(pv)
void  * pv

This function is obsolete in 32-bit OLE, but is included for compatibility with 16-bit OLE, in which IsValidInterface determines whether an interface pointer is valid.

Parameters

pv

Specifies the interface pointer to test; points to an instantiation of an interface; must not be NULL.

Return Values

Value	Meaning
TRUE	The interface pointer is valid.
FALSE	The interface pointer is not valid.
See Also

IsValidPtrIn, IsValidPtrOut


IsValidPtrIn

BOOL IsValidPtrIn(pv, cb)
const void * pv
UINT cb

This function is obsolete in 32-bit OLE, but is included for compatibility with 16-bit OLE, in which IsValidPtrIn determines whether a pointer to readable memory is valid.

Parameters

pv

Specifies the pointer to test; points to the beginning of a block of allocated memory; NULL is not acceptable.

cb

Specifies the number of bytes in the block of readable memory to which pv points.

Return Values

Value	Meaning
TRUE	The pointer is valid.
FALSE	The pointer is not valid.
See Also

IsValidPtrOut


IsValidPtrOut

BOOL IsValidPtrOut(pv, cb)
void  * pv
UINT cb

This function is obsolete in 32-bit OLE, but is included for compatibility with 16-bit OLE, in which IsValidPtrOut determines whether a pointer to memory that can be written to is valid.

Parameters

pv

Specifies the pointer to test; points to the beginning of a block of allocated memory. It must not be NULL.

cb

Specifies the number of bytes in the block of writable memory to which pv points.

Return Values

Value	Meaning
TRUE	The pointer is valid.
FALSE	The pointer is not valid.
See Also

IsValidPtrIn


OLE Library Functions

The following functions can be used to manage the OLE Component Object Model library. The functions are defined as follows:

CoBuildVersion();	// Obsolete in 32-bit OLE

CoGetCurrentProcess();

CoGetMalloc(DWORD dwMemContext, LPMALLOC * ppMalloc);

CoCreateStandardMalloc(DWORD memctx, IMalloc Far* Far* ppMalloc);	// Obsolete in 32-bit OLE

CoTaskMemAlloc(ULONG cb);

CoTaskMemFree(LPVOID pv);

CoTaskMemRealloc(LPVOID pv, ULONG cb);

CoInitialize(LPMALLOC pMalloc);

CoUninitialize();


CoBuildVersion

DWORD CoBuildVersion()

This function is obsolete in 32-bit OLE, but is included for compatibility with 16-bit OLE, in which CoBuildVersion returns the major and the minor version number of Component Object Model library.

Return Values

Value	Meaning
32-bit integer	The 16 high-order bits are the major build number; the 16 low-order bits are the minor build number.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

This function is obsolete in 32-bit OLE, but 16-bit applications should call CoBuildVersion before using any other Component Object Model library API function or interface method. CoBuildVersion returns the major and minor build numbers in a DWORD where the high-order word is the major version number and the low-order word is the minor version number. Applications can run one major version and any minor version. Therefore, the application must check that the returned major version is equal to the expected major version (the version that the application compiled against). If there are differences, CoInitialize must not be called.

In 16-bit OLE, for any given release of the Component Object Model library, there is a file called OLExVER.H. OLE2VER.H defines the symbol rmm as the major version and rup as the minor version. It is unlikely that the major build version would change between releases, but applications should still ensure compatibility by calling CoBuildVersion.

CoBuildVersion is always the first function a 16-bit application calls in the Component Object Model library.

// excerpt from the current ole2ver.h:

#define rmm	23

#define rup	563

DWORD dwBuildVersion = CoBuildVersion();

// fail if different major version or older minor version.

if (HIWORD(dwBuildVersion) != rmm || LOWORD(dwBuildVersion) < rup)

//error: don't initialize OLE

else

CoInitialize (NULL);

See Also

CoInitialize, OleBuildVersion


CoCreateStandardMalloc

HRESULT CoCreateStandardMalloc(memctx, ppMalloc)
DWORD memctx
IMalloc ** ppMalloc

This function is obsolete in 32-bit OLE, but is included for compatibility with 16-bit OLE, in which CoCreateStandardMalloc creates and returns an OLE-provided memory allocator for the given memory context.

Parameters

dwmemctx

Specifies how the memory block is to be allocated. A value of 1 specifies that the memory block is private to a task (MEMCTX_TASK); 2 specifies that the block is shared between processes (MEMCTX_SHARED). For more information on the MEMCTX values, see CoGetMalloc.

ppMalloc

Points to where to return the IMalloc memory allocator.

Return Values

Value	Meaning
S_OK	The memory allocator was created successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The new allocator creates and maintains its own new heap. All memory allocated in the heap is freed when the last reference to the returned ppMalloc is released.

The allocator returned by CoCreateStandardMalloc is not the same allocator as the one returned by CoGetMalloc. This means that the allocator returned here is not the one used for values passed across interface boundaries. Rather, CoCreateStandardMalloc provides a handy memory allocator that can be used internally by an object.

See Also

CoGetMalloc


CoTaskMemAlloc

LPVOID CoTaskMemAlloc(cb)
ULONG cb

In 32-bit OLE only, CoTaskMemAlloc allocates a block of task memory.

Parameter

cb

Specifies the size (in bytes) of the memory block to allocate.

Return Values

Value	Meaning
Allocated memory block	Memory block allocated successfully.
NULL	Insufficient memory available.
Remarks

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.

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.

See Also

CoTaskMemFree, CoTaskMemRealloc, CoGetMalloc


CoTaskMemFree

LPVOID CoTaskMemFree(pv)
LPVOID pv

In 32-bit OLE only, CoTaskMemFree frees a block of task memory previously allocated through a call to CoTaskMemAlloc or CoTaskMemRealloc.

Parameter

pv

Points to the memory block to be freed.

Remarks

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.

See Also

CoTaskMemAlloc, CoTaskMemRealloc, CoGetMalloc


CoTaskMemRealloc

LPVOID CoTaskMemRealloc(pv, cb)
LPVOID pv
ULONG cb

In 32-bit OLE only, CoTaskMemRealloc changes the size of a previously allocated block of task memory.

Parameters

pv

Points to the memory block to be reallocated. It can be a NULL pointer, as discussed in the following Remarks section.

cb

Specifies the size of the memory block (in bytes) to be reallocated. It can be zero, as discussed in the following remarks.

Return Values

Value	Meaning
Reallocated memory block	Memory block successfully reallocated.
NULL	Insufficient memory or cb is zero and pv is not NULL.
Remarks

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 CoTaskMemAlloc. 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.

See Also

CoTaskMemAlloc, CoTaskMemFree, CoGetMalloc


CoGetCurrentProcess

DWORD CoGetCurrentProcess()

CoGetCurrentProcess returns a value that is unique to the current process.

Return Values

Value	Meaning
DWORD value	Unique value for current process that can be used to avoid PROCESSID reuse problems.
Remarks

CoGetCurrentProcess is useful in helping maintain tables that are keyed by processes.

The value returned is not used again until 232 more processes have been created on the current workstation. Because Windows task handles can be reused relatively quickly when a window's task dies, the value returned by CoGetCurrentProcess provides significantly better robustness than the HTASK in cases of abnormal process termination.


CoGetMalloc

HRESULT CoGetMalloc(dwMemContext, ppMalloc)
DWORD dwMemContext
LPMALLOC * ppMalloc

CoGetMalloc retrieves the task memory allocator so applications can use it. Object handlers can also use the task allocator returned by CoGetMalloc for their local memory management needs. Separate memory allocation functions CoTaskMemAlloc and CoTaskMemFree, are also available and can be used without maintaining a pointer to the IMalloc interface.

Parameters

dwMemContext

Specifies a value that indicates whether the memory block is private to a task or shared between processes. See the following comments.

ppMalloc

Points to where the memory allocator is to be returned.

Return Values

Value	Meaning
S_OK	The allocator was retrieved successfully.
E_FAIL	Indicates an unspecified error.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Out of memory.
Remarks

Legal values for the dwMemctx parameter are taken from the enumeration MEMCTX, which is defined as follows:

typedef enum tagMEMCTX

{

    MEMCTX_TASK =     1,   // task (private) memory

    MEMCTX_SHARED =   2,   // shared memory (between processes)

} MEMCTX;

MEMCTX_TASK returns the task allocator passed to CoInitialize. Specifying MEMCTX_SHARED returns E_INVALIDARG because 32-bit OLE does not support shared memory.

In 16-bit OLE, the shared allocator returned by CoGetMalloc is the OLE-provided implementation of the IMalloc interface, which allocates memory so it can be accessed by other applications. Memory allocated by this shared allocator in one application can be freed by the shared allocator in another.

In versions of OLE 2 on 32-bit platforms, the shared memory allocator is not available. In these cases, CoGetMalloc(MEMCTX_SHARED,...) will return E_INVALIDARG. The unavailability of the shared memory allocator will not affect custom marshallers; for information on custom marshalling, see "IMarshal Interface," earlier in this section.


CoInitialize

HRESULT CoInitialize(pvReserved)
LPVOID pvReserved

CoInitialize initializes the Component Object Model library so it can be used. With the exception of CoBuildVersion (in 16-bit applications) and IMalloc, CoGetMalloc, and CoTaskMemAlloc, applications must call CoInitialize before they can make any other Component Object Model library calls.

Parameters

pvReserved

In 32-bit OLE, this parameter must be NULL. The 32-bit version of OLE does not support applications replacing OLE's allocator and if the parameter is not NULL, CoInitialize returns E_INVALIDARG. For information related to 16-bit OLE applications, see the Remarks section below.

Return Values

Value	Meaning
S_OK	The library was initialized successfully.
S_FALSE	Already initialized or cannot release default allocator; didn't use pMalloc, if given.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Applications that call OleInitialize do not need to call CoInitialize because OleInitialize calls it internally.

Calls to CoInitialize must be balanced by corresponding calls to CoUninitialize. Typically, CoInitialize is called only once by the process that wants to use the OLE library, although multiple calls can be made. Subsequent calls to CoInitialize return S_FALSE. The OLE libraries are uninitialized when CoUninitialize has been called for each CoInitialize call that returned a success HRESULT (including S_FALSE).

In 16-bit OLE only, the pvReserved parameter is identified as pMalloc and points to the memory allocator to be used for task memory by the OLE library and by object handlers. To use the OLE implementation of IMalloc, pass NULL as this parameter. Because CoInitialize retains the pMalloc pointer beyond the duration of the call in 16-bit OLE, it calls pMalloc->AddRef. Applications can release this pointer by calling pMalloc->Release.

See Also

CoGetMalloc, Uninitialize, OleInitialize


CoUninitialize

void CoUninitialize()

CoUninitialize closes the Component Object Model library, freeing any resources that it maintains and forcing all RPC connections closed.

Remarks

CoInitialize and CoUnitialize calls must be balanced: only the CoUnitialize call corresponding to the CoInitialize call that actually initialized the library can uninitialize it.

CoUninitialize should be called on application shutdown, as the last call made to the Component Object Model library and 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 OLE application. By dispatching the messages, CoUninitialize ensures that the application does not quit before receiving all of its pending messages. Non-OLE messages are discarded. It also releases the allocator provided during the OleInitialize or CoInitialize and replaces the task allocator with the default version.

CoUnInitialize is called internally by OleUnInitialize so applications that call OleUnitialize do not need to call CoUnInitialize.

See Also

CoInitialize, OleUnInitialize


Object Class Conversion and Emulation Functions

Object class conversion allows an object of one class to be permanently changed to an object of another class. Object class emulation allows the change to be temporary; the target object is treated as an object of the new class only for the lifetime of the session. The following functions allow an object of one class to emulate an object of a different class; these functions are defined:

 CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID pclsidNew);

CoTreatAsClass(REFCLSID clsidOld, LPCLSID pclsidNew);

The following functions can be used to convert an object of one class to an object of another class:

OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pclsidNew);

OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pclsidNew);

OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew);

GetConvertStg(LPSTORAGE pStg);

SetConvertStg(LPSTORAGE pStg, BOOL fConvert);

An object's class determines the verbs and the application the user uses to interact with the object. Usually, an object is activated by the object application corresponding to its CLSID, as registered in the registry. However, implementing the Convert dialog box in a container application allows users to change an object's current type to that of a different object type (Convert to: button) or to activate (emulate) objects of one type as if they were objects of another type (Activate as: button).

The next figure shows a sample Convert dialog box. When the user selects either the Convert to: or Activate as: button, the container generates a list of eligible destination object types. Associated with this type list is a list of class names; the corresponding type name for each class is obtained from the registry entry for that class. In turn, the list of eligible classes is obtained from the main data formats stored by OLE with the object.

The Convert dialog box.

To be the destination class of a conversion operation, the object application must register the formats it can read from other objects. These formats are registered under the CLSID\{clsid}\Conversion\Readable subkeys in the registry, as the following example from the registry shows:

CLSID

{CLSID} = Main User Type Name

.

.

.

Conversion

\Readable

Main = format (1) , format (2) ,...format (n

\Readwritable

Main = ) format (1) , format (2) ,...format (n

)

To activate an object of a different class as one of its own (emulation), the object application registers the formats it can both read and write under the "CLSID\{clsid}\Conversion\Readwritable" subkeys.

Note that converting an object to another type can destroy its original data format, while activating an object as a different type (emulating) preserves the existing object's data format. Emulation remains in effect until the user changes the object's class to a different one using the Activate As button.


Tagging Objects for Automatic Conversion and Emulation

Whenever a newer version of an existing object application is being installed, the setup program should give the user the option of automatically converting objects of the older class to the newer class the next time an object of that class is activated in the object's container, as shown in the figure below.

Automatic conversion prompt during installation

If the user wants to tag the object class for automatic conversion, the setup program should call OleSetAutoConvert, passing the object's old and new CLSIDs to OleSetAutoConvert. OleSetAutoConvert installs the new CLSID in the registry under the CLSID\{clsid}\AutoConvertTo = {CLSID} subkey entry. This means the object application of the destination class must be able to convert an object to a different class manually, on a case-by-case basis, as objects of the tagged class are loaded into memory.

When an object that has been tagged for conversion is loaded, the container should check whether an automatic conversion is necessary by calling OleDoAutoConvert. (If the container calls OleLoad to load the object, the call to OleDoAutoConvert is done internally by OleLoad, and need not be called explicitly.)

Similarly, for automatically converting objects, an object application's setup program can offer to tag an object class for activation as a different class, as shown in the next figure. If the user decides to tag an object class for activation under a different CLSID, the setup program should call CoTreatAsClass(clsidOld, clsidNew) to register the CLSID for the object under the "CLSID\{clsidOld}\TreatAs = {CLSID}" subkey entry in the registry.

The AutoTreatAs key is used in conjunction with the TreatAs key. If clsidOld has an AutoTreatAs key, then instead of treating clsidOld as itself, it is treated as the class indicated by AutoTreatAs. See CoTreatAsClass for more details.

Tagging an object for activation as a different class at installation time

For more information on user interface considerations for installing OLE applications, User Interface Guidelines. For information on the registry and its subkey entries, see OLE Registry Information.


Converting Objects

To convert objects from one type to another, an OLE object application must register (in the registry) the data formats it can read from objects created by other applications. To activate an object as one of its supported types, the object application registers the data formats it can both read and write (when emulating an object, the object application needs to write the data changes to the object's IStorage in the container).

Note  The ConvertDlg helper function can be used to create and display the Convert dialog box.

Container Responsibilities

When the user selects the Convert To option button in the Convert dialog box, OLE containers perform the following tasks:

1.	Call ReadFmtUserTypeStg to retrieve the object's user type name and data format from persistent storage. The user type name is used to fill in the "Current Type:" string and other strings in the Convert dialog box.

2.	Generate a list of conversion targets-object classes that are registered as understanding the object's data format. Appropriate classes have "CLSID\{clsid}\Conversion\Readable" subkeys in the registry.

3.	Retrieve the user's selection from the Convert dialog box.

4.	Unload the object if it is currently loaded. Since the conversion takes place at load time, currently loaded objects will not be included.

5.	Call WriteClassStg to store the new CLSID persistently.

6.	Call WriteFmtUserTypeStg to store the new user type name and existing data format persistently.

7.	Call SetConvertStg(TRUE) to set the conversion bit. The conversion bit signals the load process that the object has been converted to a new class and should now be associated with a new object application.

8.	Load the object and call IOleObject::Update. For more information on loading objects, see Loading Objects from Storage.

Object Application Responsibilities

Object conversion depends on object application support. Object applications may choose not to support additional object classes. However, for those object applications that do support object conversion, a few tasks must be incorporated into the load and save processing.

After opening the object from persistent storage, part of the initialization procedure must incorporate calling GetConvertStg to read the conversion bit to determine if the object has been converted. If the bit is set, the application must read the data from storage according to its tagged format rather than its native format.

When saving the object, SetConvertStg(FALSE) must be called to clear the conversion bit.


Emulating Objects

Object emulation takes place when the object is activated. Containers perform many of the same tasks for emulation as for conversion, such as displaying the Convert dialog box with a list of target object classes and unloading the object. Once the user has selected a class for emulation, however, the container tasks are as follows:

1.	Ensure that an entry for the chosen class exists in the registry. If an entry does not exist, create one. If the registration is lacking a main user type entry, call ReadFmtUserTypeStg to retrieve the user type and set the main user type string in the registry with the retrieved type.

2.	Call CoTreatAsClass to notify OLE that all non-loaded objects of the original class are now to be treated as objects of the new class.

3.	Perform any necessary object conversion before loading the object by either calling OleLoad or OleDoAutoConvert. Since OleLoad internally calls OleDoAutoConvert, applications that load objects with OleLoad do not need to call OleDoAutoConvert.

For more information on loading objects, see Loading Objects from Storage.

Object Application Responsibilities

Object emulation, like object conversion, is an optional feature that object applications do not have to support. Implementing support for emulation in the object application is different from implementing support for conversion. To activate an object from a different class as one of its own class, an object application must perform the following tasks:

1.	Call IStorage::Stat to retrieve the object's current CLSID and ReadFmtUserTypeStg to retrieve the user type. The current CLSID may or may not be the current class of the object; this does not matter. The conversion bit in the storage is off.

2.	In response to any of the calls for user type or CLSID (such as IPersist::GetClassID, IOleObject::GetUserClassID, IOleObject::GetUserType), the object must respond with the CLSID and type found in the storage. This allows an object to appear to the user as an emulated class while still maintaining its original class in the persisted storage. Note that this will make the old class appear to inherit the verbs of the new class.

3.	The CLSID returned from IStdMarshalInfo::GetClassForHandler is always the CLSID of the object that is actually running even when an object is being treated as a different class.

4.	Read the object's data from persistent storage using its main data format. When the object is saved, write the data back in the same format.


CoGetTreatAsClass

HRESULT CoGetTreatAsClass(clsidOld, pClsidNew)
REFCLSID clsidOld
LPCLSID pClsidNew

CoGetTreatAsClass returns the existing emulation information for a given object class.

Parameter

clsidOld

Specifies the object class for which the emulation information is to be retrieved.

pClsidNew

Points to where to return the CLSID, if any, that emulates clsidOld. If there is no CLSID, clsidNew is set to clsidOld and S_FALSE is returned. The pclsidNew parameter cannot be NULL.

Return Values

Value	Meaning
S_OK	Indicates that a value was successfully returned through pclsidNew although it has not been checked in any manner.
S_FALSE	Indicates that pclsidNew is set to clsidOld.
REGDB_E_READREGDB	Error reading the registry.
See CLSIDFromString for other possible errors.

See Also

CoTreatAsClass


CoTreatAsClass

HRESULT CoTreateAsClass(clsidOld, clsidNew)
REFCLSID clsidOld
REFCLSID clsidNew

CoTreatAsClass establishes or removes an emulation from one object class to another.

Parameter

clsidOld

Specifies the CLSID of the object class that is to be emulated.

clsidNew

Specifies the CLSID of the object class that should emulate clsidOld. This replaces any existing emulation for clsidOld. Can be CLSID_NULL, in which case any existing emulation for clsidOld is removed.

Return Values

Value	Meaning
S_OK	Emulation connection successfully established or removed.
REGDB_E_CLASSNOTREG	clsidOld is not properly registered in the registry.
REGDB_E_READREGDB	Error reading from registry.
REGDB_E_WRITEREGDB	Error writing to registry.
Remarks

When clsidNew is emulating clsidOld, attempts by both CoGetClassObject and by object handlers (in particular, the OLE default handler) to consult the registry for clsidOld should be transparently forwarded to the entry for clsidNew. For example, launching the object application for clsidOld launches the object application for clsidNew. The default handler's IOleObject::EnumVerbs implementation will enumerate the verbs from clsidNew.

CoTreatAsClass does no validation on whether an appropriate registry entry for clsidNew currently exists.

During installation, setup programs should use CoTreatAsClass(clsidOld, CLSID_NULL) to remove any existing emulation entries for the classes they install.

Additionally, a class can be set to be treated as itself, as in CoTreatAsClass(clsidNew, clsidNew), which has special behavior. That is, if there is an AutoTreatAs entry for this class in the registration database, then the TreatAs setting is set (back) to the registered class specified by AutoTreatAs. If there is no such registration database entry, then the behavior is equivalent to CoTreatAsClass(clsidNew, CLSID_NULL).

The AutoTreatAs registry key is used in conjunction with TreatAs when an application wants to install a setup-time defined TreatAs to handle version upgrading of their object and does not want this to be disabled later by the convert dialog. In addition to installing a TreatAs = <clsidNew> key, the application also installs the AutoTreatAs = <clsidNew> key. The presence of the AutoTreatAs key changes the behavior of the CoTreatAsClass API.

AutoTreatAs can be used as an alternative to AutoConvertTo. AutoTreatAs may be preferable to AutoConvertTo because it lets the new server be used for all old objects, yet does not force the old objects to be autoconverted into new objects. Conversion of old objects can be performed individually as needed by the user through the Convert dialog.

See Also

CoGetTreatAsClass


OleDoAutoConvert

HRESULT OleDoAutoConvert(pStg, pClsidNew)
LPSTORAGE pStg
LPCLSID pClsidNew

OleDoAutoConvert automatically converts an object to a new class. The object must have been previously tagged for automatic conversion, typically at setup time for the object application.

Parameter

pStg

Points to the persistent storage of the object to be converted.

pClsidNew

Points to the CLSID of the new object class; this might be the same as the original class if no auto-conversion was done.

Return Values

Value	Meaning
S_OK	No conversion is needed or a conversion was successfully completed.
REGDB_E_KEYMISSING	Cannot read a key from the registry.
OleGetAutoConvert errors.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
See IStorage::OpenStorage and IStorage::OpenStream for possible errors when accessing storage and stream objects.

IStream errors. Cannot determine existing CLSID or cannot update IStorage with new information.

Remarks

The object to be converted is found in pStg.

OleDoAutoConvert first determines whether any conversion is required by calling OleGetAutoConvert. If not, it returns S_OK. The object's storage is modified to convert the object. If the object being auto-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.

A container application that supports object conversion should call OleDoAutoConvert each time it loads an object. However, if the container calls OleLoad for loading, it need not call OleDoAutoConvert, as well. OleDoAutoConvert is called internally by OleLoad. The object must be in the unloaded state when OleDoAutoConvert is called.

See Also

OleSetAutoConvert


OleGetAutoConvert

HRESULT OleGetAutoConvert(clsidOld, pClsidNew)
REFCLSID clsidOld
LPCLSID pClsidNew

OleGetAutoConvert returns the existing auto-conversion information for a given object class.

Parameter

clsidOld

Specifies the CLSID of the object class for which the auto-conversion information is to be retrieved.

pClsidNew

Points to where to return the class, if any, that auto-converts from clsidOld. If there is no such class, CLSID_NULL is returned. pclsidNew cannot be NULL.

Return Values

Value	Meaning
S_OK	Indicates that a value was successfully returned through pclsidNew.
REGDB_E_CLASSNOTREG	CLSID is not properly registered in the registry.
REGDB_E_READREGDB	Error reading the registry.
REGDB_E_KEYMISSING	Auto convert is not active or indicates that there was no registry entry for clsidOld.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

During installation of an object application, the setup program can optionally tag a class of object for automatic conversion to a different class of object. This is done by calling OleSetAutoConvert, passing it the CLSIDs of the source and destination object classes. OleSetAutoConvert then creates the required "CLSID\{clsid}\AutoConvertTo = {CLSID}" entry in the registry.

On loading an object tagged for conversion, the container calls OleGetAutoConvert to retrieves the new CLSID from the registry. To perform the conversion, the container calls OleDoAutoConvert.

See Also

OleSetAutoConvert, OleDoAutoConvert


OleSetAutoConvert

HRESULT OleSetAutoConvert(clsidOld, clsidNew)
REFCLSID clsidOld
REFCLSID clsidNew

OleSetAutoConvert tags an object for automatic conversion to a different class on being loaded.

Parameter

clsidOld

Specifies the CLSID of the object class to be converted.

clsidNew

Specifies the CLSID of the object class that should replace clsidOld. This replaces any existing conversion for clsidOld. May be CLSID_NULL. If so, any existing conversion for clsidOld is removed.

Return Values

Value	Meaning
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.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

OleSetAutoConvert does not validate whether an appropriate registry entry for clsidNew currently exists. On being called, OleSetAutoConvert records the new CLSID for the object under the "CLSID\{clsidOld}\AutoConvertTo = CLSID" subkey in the registry. For more information on tagging an object class for conversion at the time of setup, see User Interface Guidelines, as well as Converting Objects.

When installing a new CLSID, setup programs may call OleSetAutoConvert for various clsidOld values for older versions of their applications. They should also call OleSetAutoConvert(clsidNew, NULL) to remove any existing conversion for the new class.

See Also

OleDoAutoConvert


SetConvertStg

HRESULT SetConvertStg(pStg, fConvert)
LPSTORAGE pStg
BOOL fConvert

SetConvertStg sets the conversion bit in an object's storage so that the setting is retrievable with GetConvertStg.

Parameter

pStg

Specifies the storage that contains the conversion bit to be set.

fConvert

TRUE tags an object for conversion; FALSE prevents conversion.

Return Values

Value	Meaning
S_OK	The object's storage conversion bit was set successfully.
STG_E_ACCESSDENIED	Access to the storage not available.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
See IStorage::CreateStream, IStorage::OpenStream, IStream::Read, and IStream::Write for possible storage and stream access errors.
Remarks

Containers typically call SetConvertBit during object class conversion. After the object has been converted, the object application calls SetConvertStg(FALSE) to clear the conversion bit. The conversion bit can be read by calling GetConvertStg.

See Also

GetConvertStg


GetConvertStg

HRESULT GetConvertStg(pStg)
LPSTORAGE pStg

GetConvertStg returns the current value of the conversion bit from an object's storage, which was previously set with SetConvertStg.

Parameter

pStg

Points to the storage from which to retrieve the conversion bit.

Return Values

Value	Meaning
S_OK	Indicates that the conversion bit is set (TRUE).
S_FALSE	Indicates the conversion bit is not set (FALSE).
STG_E_ACCESSDENIED	Cannot access the storage.
See also IStorage::OpenStream, IStorage::OpenStorage, and IStream::Read for storage and stream access errors.
Remarks

Object applications call GetConvertStg during load time to determine if an object should be converted. After retrieving the bit value, the application should call SetConvertStg(FALSE) to clear it.

See Also

SetConvertStg


Error Handling Functions and Macros

The following functions and macros can be used to handle error and status information returned from OLE interface methods and functions:

PropagateResult(HRESULT hrPrev, SCODE scNew);	// Obsolete in 32-bit OLE

#define SCODE_CODE(scode)	((scode) & 0xFFFF)

#define SCODE_FACILITY(scode)	(((scode)>> 16) & 0x000f)

#define SCODE_SEVERITY(scode)	(((scode) >> 31) & 0x1)

#define SEVERITY_SUCCESS	0

#define SEVERITY_ERROR	1

#define SUCCEEDED(scode)	((LONG)(scode) >= 0)

#define FAILED(scode)	((LONG)(scode)<0)

#define MAKE_SCODE(sev,fac,code)	((SCODE) (((ULONG)(sev)<<31) |

((ULONG)(fac)<<16) | ((ULONG)(code))))

#define GetScode(hr)	((SCODE)(hr) & 0x800FFFFF)	// Obsolete in 32-bit OLE

#define ResultFromScode(sc)	((HRESULT)((SCODE)(sc) & 0x800FFFFF))	// Obsolete 	in 32-bit OLE

Note  Throughout the reference portion of this book, specific error codes are described for the individual methods and functions. However, two error codes are common to all methods, even though they may not be mentioned in all method descriptions. These are E_UNEXPECTED, an unexpected error occurred; and E_OUTOFMEMORY, an out-of-memory condition occurred.

A third error code is common to all me4thods that accept parameters. This is E_INVALIDARG, one or more arguments are invalid.


OLE Error Information

Note  This section begins with an explanation of a return value type called HRESULT. Although this type is valid, it is being replaced by the type SCODE in 32-bit OLE. HRESULTs and SCODEs are now synonymous, as explained in the section "HRESULTs and SCODEs".

OLE interface methods and functions use a certain stylistic convention in order to pass back to the caller useful return values and/or status or error information. The return value is an opaque "result handle" of type HRESULT, and is otherwise passed as the return value in the absence of the need for status information. The return value is passed through a pointer as the last argument.

HRESULT values, in 16-bit OLE, are defined to be zero for success, and nonzero if error or informational status is being returned. If the result value is non-zero, the application calls the GetScode macro to map the HRESULT into a known code. GetScode is defined to return S_OK for a zero HRESULT.

On the implementor's side, an interface method that must indicate a result other than simple success must call ResultFromScode to generate the corresponding HRESULT, and return that to its caller. For convenience, ResultFromScode is defined to return zero for a status code of S_OK. The constant NOERROR is defined as an HRESULT that corresponds to S_OK.

Sometimes functions will either return a Boolean result or, if the result is not currently available, return an error status. For functions of this type, two special status codes, S_OK and S_FALSE, are used to indicate the return value. This eliminates the need for a separate BOOL* parameter for these functions.

HRESULTs do not need to be explicitly freed.

The status codes for the OLE interfaces and APIs are defined in FACILITY_ITF (see the OLE header files shipped with the Win32 SDK for details). By design, none of the OLE-defined status codes have the same value, even if they are returned by different interfaces (although it would have been legal). The following basic interoperability rules and limitations apply to which errors can be returned by which methods or functions:

•	Any OLE-defined status code can be returned by any OLE-defined interface method or function.

•	Any error in FACILITY_RPC or FACILITY_DISPATCH, even those not presently defined, can be returned.

The reference portion of this manual lists and describes the most common error codes that are appropriate for each interface method and function. These are considered to be the legal error codes that can be returned.

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 since they generally only provide hints at what might have gone wrong. Microsoft reserves the right to define new failure codes (but not success codes) for the interfaces described in this book in FACILITY_ITF or new facilities.


Handling Error Information

Keep in mind that it is only legal to return a status code 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. In particular, pay attention to this potential problem when propagating error codes from internally called functions.

It is wise for applications that call interfaces to 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 OLE-defined interfaces and APIs. 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 don't return anything special (other than S_OK or some unexpected error):

if (xxMethod() == NOERROR)

//success

else

//general failure;


Structure of OLE Error Codes

In 32-bit OLE, SCODEs are comprised of a status code (bits 0-15), a facility code (bits 16-30), and a severity code (bit 31).

In 16-bit OLE, as shown below, OLE interface methods and functions return a 32-bit result handle, which includes a context field (bits 20-30).

Order and size of error fields in 16-bit OLE

Severity Field

Bit 31 is used to indicate success or failure of the given interface or API call. The following values indicate success or failure:

Severity Field Value	Meaning
0	The function completed successfully.
1	The function incurred an error and failed.
Context Field

This field does not exist in 32-bit OLE. In 16-bit OLE, bits 20 − 30 are reserved for future use; applications that generate SCODEs must set this field to zero. In general, applications should not examine this field because it might be set to nonzero values in the implementation of PropagateResult in future system releases. None of the OLE-defined status codes set these bits. For more information, see HRESULTs and SCODEs.

Facility Field

As explained above, in 16-bit OLE, bits 16 − 19 are used to indicate the group of status codes to which this error belongs. In 32-bit OLE, bits 16-30 are used for this information. New facilities are allocated by Microsoft, because they must be unique. However, in nearly all cases FACILITY_ITF will be adequate, as described below:

Facility Field	Description
FACILITY_NULL	This facility is used for common status codes that are applicable to a broad range of functions. S_OK belongs to this facility, for example. This facility code has a value of zero.
FACILITY_RPC	Used for errors that result from an underlying remote procedure call (RPC) implementation. In general, RPC errors are not documented in this manual. This facility has a value of one.
FACILITY_DISPATCH	Used for late binding IDispatch interface errors. This facility has a value of two.
FACILITY_STORAGE	Used for persistent-storage-related errors. Status codes whose code (lower 16 bits) value is in the range of DOS error codes (less than 256) have the same meaning as the corresponding DOS error. This facility has a value of three.
FACILITY_ITF	Used for most status codes returned from an interface method. Use of this facility indicates that the meaning of the error code is defined solely by the definition of the particular interface in question. An SCODE with exactly the same 32-bit value returned from another interface might have a different meaning. This facility has a value of four.
FACILITY_WINDOWS	Used for additional error codes from Microsoft defined interfaces.
FACILITY_WIN32	Used to provide a means of handling an error code from a function 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.
Code Field

Bits 0 − 15 are used to describe the error that occurred. The meaning of the code value is related to the facility.

By convention, SCODEs generally have names in the following form:

<Facility>_<Severity>_<Reason>

where <Facility> is either the facility name or some other distinguishing identifier, <Severity> is a single letter, S or E, that indicates the severity of the error, 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.


Codes in FACILITY_ITF

SCODEs with facilities such as FACILITY_NULL and FACILITY_RPC have universal meaning because they are defined at a single source, Microsoft. However, SCODEs in FACILITY_ITF are determined by the interface method (or function) from which they are returned. That is, the same 32-bit value in FACILITY_ITF returned from two different interface methods might have different meanings.

The reason SCODEs in FACILITY_ITF can have different meanings in different interfaces is that SCODEs are kept to an efficient data type size of 32 bits. Unfortunately 32 bits is not large enough for the development of an allocation system for error codes that avoid conflict between codes allocated by different non-communicating programmers at different times in different places (unlike the handling of interface IDs and CLSIDs). As a result, the 32-bit SCODE is structured in a way that Microsoft can define some universally defined 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 only be defined by Microsoft.

2.	Status codes in facility FACILITY_ITF are defined solely by the developer of the interface or API 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 OLE-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 OLE-defined errors.

It is also recommended that developers define new functions and interfaces to return error codes as defined by OLE 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.


PropagateResult

HRESULT PropagateResult(hrPrev, scNew)
HRESULT hrPrev
SCODE scNew

PropagateResult is obsolete in 32-bit OLE, but in 16-bit OLE it generates an HRESULT to return to a function's caller in cases where the error being returned was caused by an internally called function, which was also returning an HRESULT error.

Parameters

hrPrev

Specifies the HRESULT returned from the internally called routine.

scNew

Specifies the new SCODE to return to the caller, wrapped in an HRESULT.

Return Values

Value	Meaning
HRESULT	The (new) HRESULT that should be returned to the caller.
See Also

GetScode, ResultFromScode


GetScode

SCODE GetScode(hRes)
HRESULT hRes

GetScode is obsolete in 32-bit OLE, but in 16-bit OLE it returns the status code contained in an HRESULT.

Parameters

hRes

Specifies the HRESULT returned from the internally called routine.

Return Values

Value	Meaning
SCODE	The status code extracted from the specified HRESULT.
Remarks

GetScode should be used to return the HRESULT if an error is being returned as a result of some internal state error. By contrast, it should not be used if the error is being returned as a result of some internally called routine, itself returning an HRESULT error. Instead, the calling routine should use PropagateResult to return its error.


ResultFromScode

ResultFromScode(sc)
SCODE sc

Obsolete in 32-bit OLE but in 16-bit OLE ResultFromScode creates a new HRESULT that contains the given SCODE.

Parameters

sc

Specifies the status code from which the HRESULT is to be returned.

Return Values

Value	Meaning
HRESULT	The HRESULT created from the specified status code.


Status Code Macros

The following macros can be used to manipulate status code values:

SCODE_CODE

SCODE_CODE(sc)
SCODE sc

SCODE_CODE returns the error code part from a specified status code.

SCODE_FACILITY

SCODE_FACILITY(sc)
SCODE sc

SCODE_FACILITY extracts the facility from a specified status code.

SCODE_SEVERITY

SCODE_SEVERITY(sc)
SCODE sc

SCODE_SEVERITY extracts the severity field from the specified status code.

SUCCEEDED

SUCCEEDED(sc)
SCODE sc

SUCCEEDED returns TRUE if the severity of the status code is either success or information; otherwise, FALSE is returned. SUCCEEDED works on both SCODE and HRESULT values.

FAILED

FAILED(sc)
SCODE	sc

FAILED returns TRUE if the severity of the status code is either a warning or error; otherwise, FALSE is returned. FAILED works on both SCODE and HRESULT values.

MAKE_SCODE

MAKE_SCODE(sev,fac,sc)
SEVERITY	sev
FACILITY	fac
SCODE sc

MAKE_SCODE makes a new status code given a severity, a facility, and a status code.


Debugging Functions

The following functions are obsolete in 32-bit OLE but remain in this documentation for compatibility with 16-bit OLE. They can be used to display debug information about internal objects (such as monikers, proxies, and stubs).

DbgDumpObject( LPUnknown pUnk, DWORD dwReserved);

DbgDumpExternalObject( LPUnknown pUnk, DWORD dwReserved );

DbgIsObjectValid( LPUnknown pUnk );

DbgDumpClassName( LPUnknown pUnk );

For 16-bit applications only, the functions can be called either in source or in CodeView. CodeView requires a function symbol to be "in scope" before one of the debug functions can be called from the command prompt. For example, if CodeView is stopped within the OLE library, any of the debug APIs can be called, as the code example below for DbgDumpObject shows:

> ? DbgDumpObject(pFoo, 0)

If CodeView is stopped within an application, that application must have a wrapper function that calls the debug API, as shown in the following example:

void AppDumpObject( LPUNKNOWN pUnk )

{

    DbgDumpObject(pUnk, 0);

}

As shown in the following example, a hexadecimal address may also be used as an argument in Codeview:

> ? DbgDumpObject((IUnknown  *) 0x43ac120f, 0)

Because many objects contain other objects, it is useful to follow a chain or to access an object from out of scope. By default, debug output is sent to the debug terminal (such as DBWIN or CodeView) and to the file, DEBUG.LOG, in the current directory. The following example shows a typical dump obtained by calling DbgDumpObject on the object pointer returned from OleCreate:

Default Handler @ 5CE74792

    Refcount is 1

    CLSID of app is {00000400-0000-0000-C000-000000000046}

    CLSID of the bits in storage is NULL CLSID

    Unknown delegate object @ 5CE7486A

    Object is running

    Object is an Inproc handler

    Proxy Manager is @ 5CE74866

    Container is locked

    Clientsite object @ 5CE746F8


DbgDumpObject

void DbgDumpObject(pUnk, dwReserved);
LPUNKNOWN pUnk
DWORD dwReserved

Obsolete in 32-bit OLE. For 16-bit OLE applications only, DbgDumpObject returns information about an object, such as the type or reference count.

Parameters

pUnk

Points to the object's IUnknown interface implementation.

dwReserved

Reserved for future use; must be set to zero by the caller.

See Also

DbgDumpClassName


DbgDumpExternalObject

void DbgDumpExternalObject( pUnk, dwReserved);
LPUNKNOWN pUnk
DWORD dwReserved

Obsolete in 32-bit OLE. For 16-bit OLE applications, DbgDumpExternalObject returns information about an object's stub manager.

Parameters

pUnk

Points to the object's IUnknown interface implementation.

dwReserved

Reserved for future use; must be set to zero by the caller.


DbgIsObjectValid

BOOL DbgIsObjectValid( pUnk);
LPUNKNOWN pUnk

Obsolete in 32-bit OLE. For 16-bit OLE applications, DbgIsObjectValid does a validation check on the object.

Parameters

pUnk

Points to the object's IUnknown interface implementation.

Return Values

Value	Meaning
TRUE	As can best be determined, the object will perform as expected.
FALSE	The object might fail in some operations.
Remarks

Only objects that support debug dumping will perform the validation, which attempts to verify the integrity of the object. The test includes checking to ensure that the object has a valid reference count and, if there are pointers to other objects, that these objects are valid. There is no guarantee that this object will perform as expected based on calling this function; the information is a best guess at this point.

This function is also called by DbgDumpObject. If this function fails in DbgDumpObject, a message to that effect is displayed on the debug monitor.

See Also

DbgDumpObject


DbgDumpClassName

void DbgDumpClassName(pUnk);
LPUNKNOWN pUnk

Obsolete in 32-bit OLE. For 16-bit OLE applications, DbgDumpClassName prints the object's CLSID.

Parameters

pUnk

Points to the object's IUnknown interface implementation.

Remarks

The CLSID is also displayed as part of the output from DbgDumpObject. Once you have the CLSID, you can use regedt32.exe to search for the string in the registry to determine which application owns the object.

See Also

DbgDumpObject


String and CLSID Conversion Functions

This section describes the functions that can be used to convert strings, GUIDs, CLSIDs, and ProgIDs from one form to another, using entries listed in the registry. The functions are defined as follows:

CoCreateGUID(GUID  * pguid);

IsEqualGUID(REFGUID rguid1, REFGUID rguid2);

IsEqualIID(REFGUID rguid1, REFGUID rguid2);

IsEqualCLSID(REFCLSID clsid1, REFCLSID clsid2);

CLSIDFromProgID(LPCOLESTR lpszProgID, LPCLSID pclsid);

ProgIDFromCLSID(REFCLSID clsid, LPOLESTR * lplpszProgID);

CLSIDFromString(LPOLESTR lpsz, LPCLSID pclsid);

StringFromCLSID(REFCLSID rclsid, LPOLESTR * lplpsz);

IIDFromString(LPOLESTR lpsz, LPIID lpiid);

StringFromIID(REFIID rclsid, LPOLESTR * lplpsz);

StringFromGUID2(REFGUID rguid, LPOLESTR lpsz, int cbMax);


CoCreateGuid

CoCreateGuid(pguid)
GUID  *pguid

CoCreateGuid is used to create a GUID, which is a 128-bit integer used to represent CLSIDs and interface IDs.

Parameters

pguid

Points to where to return the GUID.

Return Values

Value	Meaning
S_OK	The GUID was successfully created.
E_FAIL	The GUID was not created.
REGDB_E_WRITEREGDB	Unable to write necessary state information to registry.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	pguid is invalid.
E_UNEXPECTED	An unexpected error occurred.
The use of CoCreateGuid does not require a network card. If no card is present, a machine identifier is created from variable machine state and stored persistently.

The 32-bit implementation of CoCreateGuid calls UuidCreate (documented in the RPC Programmer's Guide and Reference), which uses the address from the network card (if present) to construct the GUID.


IsEqualGUID

BOOL IsEqualGUID(rguid1, rguid2)
REFGUID rguid1
REFGUID rguid2

IsEqualGUID compares two GUIDs to see whether they are equal.

Parameters

rguid1

Specifies the GUID to compare with rguid2.

rguid2

Specifies the GUID that is to compare with rguid1.

Return Values

Value	Meaning
TRUE	The GUIDs are equal.
FALSE	The GUIDs are not equal.
Remarks

IsEqualGUID is used by IsEqualCLSID and IsEqualIID.

See Also

IsEqualCLSID, IsEqualIID


IsEqualIID

BOOL IsEqualIID(riid1, riid2)
REFGUID riid1
REFGUID riid2

IsEqualIID compares two interface IDs to see whether they are equal.

Parameters

rclsid1

Specifies the interface ID to compare with riid2.

rclsid2

Specifies the interface IID to compare with riid1.

Return Values

Value	Meaning
TRUE	The interface IDs are equal.
FALSE	The interface IDs are not equal.
See Also

IsEqualGUID, IsEqualCLSID


IsEqualCLSID

BOOL IsEqualCLSID(rclsid1, rclsid2)
REFCLSID rclsid1
REFCLSID rclsid2

IsEqualCLSID compares two CLSIDs to see whether they are equal.

Parameters

rclsid1

Specifies the CLSID to compare with rclsid2.

rclsid2

Specifies the CLSID to compare with rclsid1.

Return Values

Value	Meaning
TRUE	The CLSIDs are equal.
FALSE	The CLSIDs are not equal.
See Also

IsEqualGUID, IsEqualIID


CLSIDFromProgID

HRESULT CLSIDFromProgID(lpszProgID, pclsid)
LPCOLESTR lpszProgID
LPCLSID pclsid

CLSIDFromProgID looks up a CLSID from a ProgID in the registry, a string that uniquely identifies a given object class.

Parameters

lpszProgID

Points to the ProgID whose CLSID is requested.

pclsid

Points to where to the created CLSID.

Return Values

Value	Meaning
S_OK	The CLSID was created successfully.
CO_E_CLASSSTRING	The registered CLSID for the ProgID is invalid.
REGDB_E_WRITEREGDB	Error writing to the registry.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If the ProgID is the CLSID of an OLE 1 object class, CLSIDFromProgID automatically creates a CLSID for it. 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.

See Also

ProgIDFromCLSID


ProgIDFromCLSID

HRESULT ProgIDFromCLSID(clsid, lplpszProgID)
REFCLSID clsid
LPOLESTR * lplpszProgID

ProgIDFromCLSID retrieves the ProgID for a given CLSID.

Parameters

clsid

Specifies the CLSID for which the ProgID is requested.

lplpszProgID

Points to where to return the ProgID.

Return Values

Value	Meaning
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 2 object class listed in the Insert Object dialog box must have a programmatic identifier (ProgID), a string that uniquely identifies a given object class. 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 OLE 2 class when placed in an OLE 1 container.

The ProgID string must be different from 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 or start with a digit. Except for a single period, it cannot contain any punctuation.

The ProgID must never be shown to the user in the user interface. If you need a short human-readable string for an object, call IOleObject::GetUserType.

CLSIDFromProgID can be called to create a CLSID from a given ProgID. CLSIDs can be freed with the task allocator (see CoGetMalloc).

See Also

CLSIDFromProgID


CLSIDFromString

HRESULT CLSIDFromString(lpsz, pclsid)
LPOLESTR lpsz
LPCLSID pclsid

CLSIDFromString converts a string generated by StringFromCLSID back into the original CLSID.

Parameters

lpsz

Points to the string representation of the CLSID.

pclsid

Points to the returned CLSID.

Return Values

Value	Meaning
S_OK	CLSID returned successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Because of the restrictions placed on OLE 1 CLSID values, CLSIDFromProgIDand CLSIDFromString are the only two functions that can be used to generate a CLSID for an OLE 1 object.

See Also

CLSIDFromProgID, StringFromCLSID


StringFromCLSID

HRESULT StringFromCLSID(rclsid, lplpsz)
REFCLSID rclsid
LPOLESTR * lplpsz

StringFromCLSID converts the CLSID into a string of printable characters so that different CLSIDs always convert to different strings.

Parameters

rclsid

Specifies the CLSID to be converted.

lplpsz

Points to where to return the resulting string.

Return Values

Value	Meaning
S_OK	The CLSID was successfully converted and returned.
E_OUTOFMEMORY	Out of memory.
Remarks

StringFromCLSID is used by OLE to look up CLSIDs as keys in the registry.

The returned string is freed in the standard way, using the task allocator (see CoGetMalloc).

See Also

CLSIDFromString


IIDFromString

HRESULT IIDFromString(lpsz, lpiid)
LPOLESTR lpsz
LPIID lpiid

IIDFromString converts a string generated by StringFromIID back into the original interface ID.

Parameters

lpsz

Points to the string representation of the IID.

lpiid

Points to where to return the IID.

Return Values

Value	Meaning
S_OK	The string was successfully converted.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
Remarks

The interface ID is converted in a way that guarantees different interface IDs will always convert to different strings. One way this might work, for example, is to print the interface ID in hexadecimal.

See Also

StringFromIID


StringFromIID

HRESULT StringFromIID(rclsid, lplpsz)
REFIID rclsid
LPOLESTR * lplpsz

StringFromIID converts an interface ID into a string of printable characters.

Parameters

rclsid

Specifies the interface ID to be converted.

lplpsz

Points to where to return the resulting string.

Return Values

Value	Meaning
S_OK	The character string was successfully returned.
E_OUTOFMEMORY	Out of memory.
The returned string is freed in the standard way, using the task allocator (see CoGetMalloc).

See Also

IIDFromString


StringFromGUID2

int StringFromGUID2(rguid, lpsz, cbMax)
REFGUID rguid
LPOLESTR lpsz
int cbMax

StringFromGUID2 converts a globally unique interface ID into a string of printable characters.

Parameters

rguid

Specifies the interface ID to be converted.

lpsz

Points to where to return the resulting string.

cbMax

Specifies the maximum expected size of the returned string.

Return Values

Value	Meaning
0 (zero)	Buffer is too small for returned string.
Non-zero value	The number of characters in the returned string, including the null terminator.


Registry Functions

The following functions search the registry. These functions are primarily used in custom handlers or DLL object applications. This information is sometimes available from the object. For example, IOleObject::GetUserType returns the user type. Clients of an object must always try calling the object for the needed information, since the object might, for example, be a DLL object application which does not use the registry entries at all.

The registry functions are defined as follows:

OleRegGetUserType (REFCLSID clsid, DWORD dwFormOfType,

     LPOLESTR * pszUserType);

OleRegGetMiscStatus (REFCLSID clsid, DWORD dwAspect,

     DWORD * pdwStatus);

OleRegEnumFormatEtc (REFCLSID clsid, DWORD dwDirection,

     LPENUMFORMATETC * ppenumFormatetc);

OleRegEnumVerbs (REFCLSID clsid, LPENUMOLEVERB * ppenumOleVerb);


OleRegGetUserType

HRESULT OleRegGetUserType (clsid, dwFormOfType, pszUserType)
REFCLSID clsid
DWORD dwFormOfType
LPOLESTR * pszUserType

OleRegGetUserType returns the user type of the indicated class from the registry.

Parameters

clsid

Specifies the class whose user type is requested.

dwFormOfType

Specifies a value that describes the form of the user-presentable string from the enumeration USERCLASSTYPE. For information on USERCLASSTYPE, see IOleObject::GetUserType.

pszUserType

Points to the returned user type.

Return Values

Value	Meaning
S_OK	The user type was returned successfully.
E_OUTOFMEMORY	There is insufficient memory to complete the operation.
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.
See Also

IOleObject::GetUserType


OleRegGetMiscStatus

HRESULT OleRegGetMiscStatus (clsid, dwAspect, pdwStatus)
REFCLSID clsid
DWORD dwAspect
DWORD * pdwStatus

OleRegGetMiscStatus returns the miscellaneous status information for the given class from the registry.

Parameters

clsid

Specifies the class whose status information is requested.

dwAspect

Specifies the aspect of the class whose information is requested.

pdwStatus

Points to the returned status.

Return Values

Value	Meaning
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.
See Also

IOleObject::GetMiscStatus


OleRegEnumFormatEtc

HRESULT OleRegEnumFormatEtc (clsid, dwDirection, ppenumFormatetc)
REFCLSID clsid
DWORD dwDirection
LPENUMFORMATETC * ppenumFormatetc

OleRegEnumFormatEtc returns an enumerator for the entries in a FORMATETC data structure containing the supported format information for the indicated class.

Parameters

clsid

Specifies the target class.

dwDirection

Specifies the set of formats to be enumerated; valid values are taken from the enumeration DATADIR:

typedef enum tagDATADIR

{

    DATADIR_GET = 1,

    DATADIR_SET = 2,

} DATADIR;

DATADIR_GET enumerates those formats that can be passed to IDataObject::GetData; DATADIR_SET enumerates those formats that can be passed to IDataObject::SetData.

ppenumFormatetc

Points to where to return the enumerator.

Return Values

Value	Meaning
S_OK	The eunumerator was returned successfully.
E_OUTOFMEMORY	An out-of-memory condition occurred.
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.
See Also

IDataObject::EnumFormatEtc


OleRegEnumVerbs

HRESULT OleRegEnumVerbs (clsid, ppenumOleVerb)
REFCLSID clsid
LPENUMOLEVERB * ppenumOleVerb

OleRegEnumVerbs returns an enumerator for the entries in an OLEVERB data structure containing the registered verbs of the indicated class. For information on the OLEVERB structure, refer to IOleObject::EnumVerbs.Parameters

clsid

Specifies the target class.

ppenumOleVerb

Points to where to return the enumerator.

Return Values

Value	Meaning
S_OK	The enumerator was returned successfully.
E_OUTOFMEMORY	An out of memory condition occurred.
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.
See Also

IOleObject::EnumVerbs


Compound Document Interfaces and Functions
Compound-Document Interfaces and Functions

Compound-document interfaces and supporting functions provide the functionality for creating and managing compound documents. The use of these interfaces depends on an application's role as a container application, object application, or combination container/object application. Most compound-document interfaces are implemented by at least one type of OLE application. This section provides sample implementations of theses interface methods.


IAdviseSink Interface

The IAdviseSink interface, implemented by containers and OLE, is used to receive asynchronous notifications, of which there are three types: compound-document (also referred to as OLE notifications), data change, and view change. The compound-document notifications are generated when an object closes, is renamed, is saved, or its link source changes.

IAdviseSink implementors register for one or more types of notification and each IAdviseSink method is a callback for a specific notification. 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, IAdviseSink::OnClose is called to notify its container.

Note  Making synchronous calls within asynchronous methods is not valid, so you cannot make synchronous calls within any of the following IAdviseSink methods. For example, IAdviseSink::OnDataChange cannot contain a call to IDataObject::GetData.

IAdviseSink contains the following methods:

DECLARE_INTERFACE_(IAdviseSink, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IAdviseSink methods ***

  void OnDataChange (THIS_ FORMATETC * pFormatetc,

    STGMEDIUM * pStgmed);

  void OnViewChange (THIS_ DWORD dwAspect, LONG lindex);

  void OnRename (THIS_ LPMONIKER pmk);

  void OnSave (THIS);

  void OnClose (THIS);

};


IAdviseSink::OnDataChange

void IAdviseSink::OnDataChange(pFormatetc, pStgmed)
FORMATETC *pFormatetc
STGMEDIUM *pStgmed

IAdviseSink::OnDataChange notifies the object that registered the advise sink that data in the calling object has changed.

Parameters

pFormatetc

[in] Points to the data format information on which the data advise was originally set up.

pStgmed

[in] Points to the storage medium on which the data is passed.

Remarks

Containers and other objects requiring notification when data in the object changes, such as the OLE cache and link objects, call IDataObject::DAdvise to set up an advisory connection. Parameters to IDataObject::DAdvise include a pointer to a FORMATETC structure, a pointer to the IAdviseSink interface, and a set of flags.

The FORMATETC structure describes the pertinent data in terms of its format, aspect, and storage medium. The set of flags specify one or more values that control the advisory connection. The flag ADVF_NODATA indicates that pStgmed->tymed might be NULL instead of containing data. The flag ADVF_PRIMEFIRST instructs the object to call IAdviseSink::OnDataChange immediately, thereby enabling the advisory sink to receive data right away.

Not all containers register for data-change notifications. Those that do not register have an empty implementation of IAdviseSink::OnDataChange. For example, a container that would register for data-change notifications would not use OLE's caching support. The implementation of IAdviseSink::OnDataChange can post an internal message or invalidate the rectangle for the changed data by calling InvalidateRect and then waiting for a WM_PAINT message.

When IAdviseSink::OnDataChange is called on the cache, the cache is updated; when the method is called on the link object, if the link type is automatic, the link is updated.

The data itself, valid only for the duration of the call, is passed using the storage medium pointed to by pStgmed. Since the medium is owned by the caller, the sink should not free it. Also, if pStgmed points to an IStorage or IStream, the sink must not increment the reference count.

See Also

IDataObject::DAdvise


IAdviseSink::OnViewChange

void IAdviseSink::OnViewChange(dwAspect, lindex)
DWORD dwAspect
LONG lindex

IAdviseSink::OnViewChange notifies the object that registered the advise sink that an object's view or presentation has changed. These actions typically include redrawing the object.

Parameters

dwAspect

[in] Specifies the aspect, or view, of the object. Contains a value taken from the enumeration, DVASPECT:

typedef enum tagDVASPECT

{

DVASPECT_CONTENT 	= 1,

DVASPECT_THUMBNAIL	= 2,

DVASPECT_ICON 	= 4,

DVASPECT_DOCPRINT 	= 8,

} DVASPECT;

lindex

[in] Identifies which piece of the view 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.

Even though DVASPECT values are individual flag bits, dwAspect may only represent one value. That is, dwAspect cannot contain the result of DVASPECT values that have been Or'ed together. DVASPECT values have the following meaning:

Value	Meaning
DVASPECT_CONTENT	Gives an appropriate representation so that the object can be displayed as an embedded object inside its container. This is the most common value for compound-document objects. It is appropriate to use DVASPECT_CONTENT to get a presentation of the embedded object either for rendering on the screen or on a printer. DVASPECT_PRINT, by contrast, indicates the look of the object as if it were printed from the top level.
DVASPECT_THUMBNAIL	Gives an appropriate thumbnail representation so the object can be displayed in a browsing tool.
DVASPECT_ICON	Gives an iconic representation of the object.
DVASPECT_DOCPRINT	Represents the object as if it were printed using the Print command from the File menu. The described data represents a sequence of pages.
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.

To handle changes that affect the extent of the object, containers can set a flag (fDoGetExtent) in OnViewChange to TRUE and then call InvalidateRect to force repainting. In the drawing code, insert the following:

if (fDoGetExtent)  {

fDoGetExtent = FALSE;

IViewObject2::GetExtent(&newExtents);

if (newExtents are different)

relayout the contents.

}

draw the object

OnViewChange can be called when the object is in either the loaded or running state.

Example

The implementation of IAdviseSink::OnViewChange shown below invalidates the rectangle of the object to force a repaint in the future. The object whose extents may change is marked (m_fDoGetExtent set to TRUE) and a message (WM_U_UPDATEOBJECTEXTENT) is posted to the document indicating that one or more OLE objects may need their extents updated. If drawing is disabled, however, the extent-update process is postponed. When this message is processed, the document calls IOleObject::GetExtent to determine whether the extents did change. If they did, space is laid out for the object before it is redrawn and the document is marked as dirty.

void IAdviseSink_OnViewChange(

        IAdviseSink *       lpThis,

        DWORD               dwAspect,

        LONG                lindex)

{

LPCONTAINERLINE lpContainerLine;

LPDOC lpDoc;

HWND hWndDoc;

LPLINELIST lpLL

MSG msg;

int nIndex;

lpContainerLine = ((struct CAdviseSinkImpl *)lpThis)-

>lpContainerLine;

lpDoc = (LPDOC)lpContainerLine->m_lpDoc;

lpContainerLine->m_fDoGetExtent = TRUE;

hWndDoc = GetDocWindow((LPDOC)lpContainerLine->m_lpDoc);

if (lpDoc->m_nDisableDraw) == 0 &&

 ! PeekMessage(&msg, hWndDoc, 	WM_U_UPDATEOBJECTEXTENT,

WM_U_UPDATEOBJECTEXTENT, PM_NOREMOVE | PM_NOYIELD))

{

PostMessage(hWndDoc, WM_U_UPDATEOBJECTEXTENT, 0, 0L);

}

lpLL = Doc_GetLineList(lpDoc);

nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpLine);

LineList_ForceLineRedraw(lpLL, nIndex, TRUE);

}

HRESULT IOleClientSite_GetContainer

(LPOLECLIENTSITE lpThis, IOleContainer ** ppContainer)

{

LPCONTAINERLINE lpContainerLine;

    HRESULT hrErr;

lpContainerLine = ((struct COleClientSiteImpl *)lpThis)-

>lpContainerLine;

hrErr = IUnknown_Doc_QueryInterface(

            (LPOLEDOC)lpContainerLine->m_lpDoc,

            &IID_IOleContainer,

            (LPVOID *)ppContainer);

    return hrErr;

}

See Also

IViewObject::SetAdvise


IAdviseSink::OnRename

void IAdviseSink::OnRename(pmk)
IMoniker  *pmk

IAdviseSink::OnRename notifies the object that registered the advise sink that an object has been renamed.

Parameters

pmk

[in] Points to a moniker that represents the new (full) name of the object.

Remarks

The OnRename notification belongs to a group of notifications that containers register to receive by calling IOleObject::Advise. IAdviseSink::OnRename is called when the object's name in the container changes or when the container's name changes. Although the container and the OLE link object typically receive this notification, the container can ignore it. The link object, however, must update its moniker. Most containers will not need to register for OLE advise notifications.

See Also

IAdviseSink2::OnLinkSrcChange


IAdviseSink::OnSave

void IAdviseSink::OnSave()

IAdviseSink::OnSave notifies the object that registered the advise sink that an object has been saved persistently.

Remarks

Objects call IAdviseSink::OnSave to send OnSave notifications when the object has been saved persistently, either to the object's original storage or to a new one (that is, Save or Save As). OnSave notifications are usually relevant to object handlers rather than to containers; containers can typically ignore them. When an object handler receives the OnSave notification, it updates its cache if the advise flag passed during registration specified ADVFCACHE_ONSAVE.

IOleObject::Advise is called to register for OnSave notifications.

See Also

IAdviseSink2::OnLinkSrcChange


IAdviseSink::OnClose

void IAdviseSink::OnClose()

IAdviseSink::OnClose notifies the object that registered the advise sink that an object has changed from the running into the loaded state.

Remarks

If the object is implemented with an EXE object application, this method indicates that the object in the application is shutting down. If the object application is not servicing any other objects and has no other reason to remain in the running state, such as the presence of IClassFactory::LockServer locks or if the application is under user control, then this notification also indicates that the application itself is shutting down.

Containers register to receive OnClose notifications by calling IOleObject::Advise. IAdviseSink::OnClose is called to inform its sinks that they are shutting down.

Upon receipt of this notification, the object handler and object link implementations should immediately release pointers to the object, because the object in the EXE application is shutting down. In the case of linked objects, the OnClose notification should always be interpreted to mean that the ultimate link source has closed and not that the linked object itself is no longer connected to the link source.

In the case of an object application supporting multiple instances of the object, IAdviseSink::OnClose is a notification that the object itself is being released. It does not indicate that the object application has shut down.

In the OLE link object's implementation of OnClose, the link object releases its pointer to the bound link source. In the OLE default handler's implementation of this method, the handler releases its pointer to the object in the object application. A typical container can ignore this notification. It need not release its pointers to the loaded object nor revert the object's storage.


IAdviseSink2 Interface

The IAdviseSink2 interface, an extension of IAdviseSink, can be implemented by container applications capable of linking. When a linking container stores a representation of the link source outside of the link, it may be desirable to notify the object that registered the advise sink when the link source changes. The OnLinkSrcChange notification can be used for this purpose.

IAdviseSink2 contains one additional method.

DECLARE_INTERFACE_(IAdviseSink2, IAdviseSink)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IAdviseSink methods ***

  void OnDataChange (THIS_ FORMATETC * pFormatetc,

    STGMEDIUM * pmedium);

  void OnViewChange (THIS_ DWORD dwAspect, LONG lindex);

  void OnRename (THIS_ LPMONIKER pmk);

  void OnSave (THIS);

  void OnClose (THIS);

    // *** IAdviseSink2 methods ***

  void OnLinkSrcChange (THIS_ LPMONIKER pmk);

};


IAdviseSink2::OnLinkSrcChange

void IAdviseSink2::OnLinkSrcChange(pmk)
IMoniker  *pmk

IAdviseSink2::OnLinkSrcChange notifies the object that registered the advise sink that a link source has changed.

Parameters

pmk

[in] Points to the new link source contained within the link object.

Remarks

A container of linked objects implements this method so it is notified of a change in the link source's moniker. A typical container uses the link-source name as part of its application.

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.

See Also

IAdviseSink::OnRename


IEnumOLEVERB Interface

The IEnumOLEVERB interface is implemented by OLE and by object applications that have a dynamically changing set of verbs. It is used by container applications to enumerate the verbs available for an object.

The IEnumOLEVERB interface contains the following methods, as do all enumerators:

DECLARE_INTERFACE_(IEnumOLEVERB, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IEnumOLEVERB methods ***

   HRESULT Next (THIS_ ULONG celt, LPOLEVERB rgelt, ULONG * pceltFetched);

    HRESULT Skip (THIS_ ULONG celt);

    HRESULT Reset (THIS);

    HRESULT Clone (THIS_ IEnumOLEVERB ** ppenumOleVerb);

};

IEnumOLEVERB enumerates the entries in an OLEVERB structure. The OLEVERB structure shown here is described in detail at IOleObject::EnumVerbs.:

typedef struct STRUCT tagOLEVERB

{

    LONG    lVerb;

    LPOLESTR   lpszVerbName;

    DWORD   fuFlags;

    DWORD   grfAttribs;

} OLEVERB, * LPOLEVERB;

Refer to the discussion of the IEnumX Interface in the "Component Object Interfaces and Functions" chapter for more information.


IOleAdviseHolder Interface

The IOleAdviseHolder interface, implemented by OLE, is used by object applications to manage OLE notification registration and deregistration. It also sends notifications to registered sinks when appropriate. Use of the IOleAdviseHolder methods is optional because object applications can provide their own notification management if preferred. IOleAdviseHolder is provided solely for the convenience of object-application implementors.

IOleAdviseHolder contains the following methods:

DECLARE_INTERFACE_(IOleAdviseHolder, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IOleAdviseHolder methods ***

  HRESULT Advise (THIS_ LPADVISESINK pAdvise, DWORD * pdwConnection);

  HRESULT Unadvise (THIS_ DWORD dwConnection);

  HRESULT EnumAdvise (THIS_ LPENUMSTATDATA * ppenumAdvise);

  HRESULT SendOnRename (THIS_ LPMONIKER pmk);

  HRESULT SendOnSave (THIS);

  HRESULT SendOnClose (THIS);

};


IOleAdviseHolder::Advise

HRESULT IOleAdviseHolder::Advise(pAdvise, pdwConnection)
IADviseSInk  *pAdvise
DWORD *pdwConnection

IOleAdviseHolder::Advise sets up an advisory connection between the object and an advisory sink through which the sink can be informed of events that occur in the object.

Parameters

pAdvise

[in] Points to the advisory sink that should be informed of changes.

pdwConnection

[out] Points to a token that can be passed to IOleAdviseHolder::Unadvise to delete the advisory connection.

Return Values

Value	Meaning
S_OK	Advisory connections set up successfully.
E_INVALIDARG	pAdvise is NULL.
Remarks

Sinks that must receive compound-document (OLE) notifications for an object call IOleObject::Advise to register. Compound-document notifications include OnSave, OnRename, OnLinkSrcChange, and OnClose. The implementation of IOleObject::Advise generally calls CreateOleAdviseHolder to instantiate an IOleAdviseHolder interface instance and then delegates the call to IOleAdviseHolder::Advise.

If an advisory connection is successfully set up, the callee returns a nonzero value through pdwConnection. If no connection is established, zero is returned. The connection can be deleted by passing this nonzero token back to the object in a call to IOleAdviseHolder::Unadvise.

See Also

IOleAdviseHolder::UnAdvise, IOleAdviseHolder::EnumAdvise, IOleObject::Advise


IOleAdviseHolder::Unadvise

HRESULT IOleAdviseHolder::Unadvise(dwConnection)
DWORD dwConnection

IOleAdviseHolder::Unadvise deletes an advisory connection previously established with IOleAdviseHolder::Advise.

Parameters

dwConnection

[in] Contains a nonzero value previously returned from IOleAdviseHolder::Advise in pdwConnection.

Return Values

Value	Meaning
S_OK	Advisory connection deleted successfully.
OLE_E_NOCONNECTION	dwConnection does not represent a valid advisory connection.
See Also

IOleAdviseHolder::Advise, IOleAdviseHolder::EnumAdvise, IOleObject::Unadvise


IOleAdviseHolder::EnumAdvise

HRESULT IOleAdviseHolder::EnumAdvise(ppenumAdvise)
IEnumSTATDATA ** ppenumAdvise

IOleAdviseHolder::EnumAdvise enumerates the advisory connections currently established on the object.

Parameters

ppenumAdvise

[out] Points to where the new enumerator should be returned. NULL is a legal return value indicating that there are presently no advisory connections on the object. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator returned successfully.
E_FAIL	Enumerator could not be returned.
E_NOTIMPL	EnumAdvise is not implemented.
Remarks

While an enumeration is in progress, the effect of registering or revoking advisory connections on what is to be enumerated is undefined. The returned enumerator is of type IEnumSTATDATA. It enumerates items of type STATDATA, which are defined as follows:

typedef struct tagSTATDATA {

FORMATETC         Formatetc;

DWORD             grfAdvf;

IAdviseSink *     pAdvise;

DWORD             dwConnection;

}STATDATA;

See Also

IOleAdviseHolder::Advise, IOleAdviseHolder::UnAdvise, IOleObject::EnumAdvise


IOleAdviseHolder::SendOnRename

HRESULT IOleAdviseHolder::SendOnRename(pmk)
IMoniker  *pmk

IOleAdviseHolder::SendOnRename calls IAdviseSink::OnRename on all of the advisory sinks currently registered in the advise holder.

Parameters

pmk

[in] Points to the new full moniker of the object.

Return Values

Value	Meaning
S_OK	Appropriate sinks were sent OnRename notifications.


IOleAdviseHolder::SendOnSave

HRESULT IOleAdviseHolder::SendOnSave()

IOleAdviseHolder::SendOnSave calls IAdviseSink::OnSave on all of the sinks currently registered in the advise holder.

Return Values

Value	Meaning
S_OK	Appropriate sinks were sent OnSave notifications.
See Also

IAdviseSink::OnSave


IOleAdviseHolder::SendOnClose

HRESULT IOleAdviseHolder::SendOnClose()

IOleAdviseHolder::SendOnClose calls IAdviseSink::OnClose on all of the sinks currently registered in the advise holder.

Return Values

Value	Meaning
S_OK	Appropriate sinks were sent OnClose notifications.
See Also

IAdviseSink::OnClose


IOleClientSite Interface

The IOleClientSite interface, implemented by containers, is the object's view of its context: where it is anchored in the document, where it gets its storage, user interface, and other resources. IOleClientSite interface is used by object applications to request services from its container. A container must provide one instance of IOleClientSite for every compound-document object it contains.

IOleClientSite contains the following methods:

DECLARE_INTERFACE_(IOleClientSite, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IOleClientSite methods ***

  HRESULT GetContainer (THIS_ LPOLECONTAINER * ppContainer);

  HRESULT OnShowWindow (THIS_ BOOL fShow);

  HRESULT GetMoniker (THIS_ DWORD dwAssign, DWORD dwWhichMoniker,

    LPMONIKER * ppmk);

  HRESULT RequestNewObjectLayout (THIS);

  HRESULT SaveObject (THIS);

  HRESULT ShowObject (THIS);

};


IOleClientSite::GetContainer

HRESULT IOleClientSite::GetContainer(ppContainer)
IOleContainer **ppContainer

IOleClientSite::GetContainer returns a pointer to the embedding container's IOleContainer interface.

Parameters

ppContainer

[out] Points to where the object's IOleContainer interface pointer is to be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	IOleContainer interface pointer successfully returned.
OLE_E_NOT_SUPPORTED	Client site is in OLE 1 container.
E_NOINTERFACE	IOleContainer is not implemented by container.
Remarks

IOleClientSite::GetContainer can be used to traverse up a hierarchy of compound-document objects. A successful call to IOleClientSite::GetContainer can be followed by IOleContainer::QueryInterface, asking for IOleObject and IOleObject::GetClientSite to get the client site for the next object. The sequence of three calls can be repeated for each object.

Simple containers that do not support linking to embedded objects may not need to implement this method. Instead, these containers should return E_NOINTERFACE and set ppContainer to NULL.

Example

The following example, a typical implementation of IOleClientSite::GetContainer, calls QueryInterface on the document object to get a pointer to IOleContainer.

HRESULT IOleClientSite_GetContainer

(LPOLECLIENTSITE lpThis, IOleContainer ** ppContainer)

{

LPCONTAINERLINE lpContainerLine;

    HRESULT hrErr;

lpContainerLine = ((struct COleClientSiteImpl *)lpThis)-

>lpContainerLine;

hrErr = IUnknown_Doc_QueryInterface(

            (LPOLEDOC)lpContainerLine->m_lpDoc,

            &IID_IOleContainer,

            (LPVOID *)ppContainer);

    return hrErr;

}

See Also

IOleClientSite::SaveObject, IOleClientSite::GetMoniker, IOleClientSite::RequestNewObjectLayout


IOleClientSite::OnShowWindow

HRESULT IOleClientSite::OnShowWindow(fShow)
BOOL fShow

IOleClientSite::OnShowWindow notifies a container when an object's windows become visible or invisible. This method is called when an object is open in its own window as opposed to being active in place.

Parameters

fShow

[in] Indicates whether an object window is becoming visible.

Return Values

Value	Meaning
S_OK	Shading/hatching has been successfully removed or added.
Remarks

An object calls IOleClientSite::OnShowWindow if an IOleObject::DoVerb call causes it to show its window. IOleClientSite::OnShowWindow adds or removes object shading. If fShow is TRUE, the object is open in a window elsewhere and shading/hatching should be added. If fShow is FALSE, the window is not open elsewhere and shading/hatching should be removed.

Example

The following implementation of IOleClientSite::OnShowWindow checks the value of fShow and if it is TRUE, a flag in the site object is also set to TRUE, and ForceDataRedraw is called to redraw the data. If fShow is FALSE, the flag is reset, ForceDataRedraw is called, and the container-document window is then brought to the top with focus restored.

HRESULT IOleClientSite_OnShowWindow(IOleClientSite lpThis, BOOL fShow)

{

RECT   rect;

BOOL   fErase;

LPSITE lpSite = ((struct COleClientSiteImpl *)lpThis)->lpSite;

LPDOC  lpDoc = (LPDOC)lpSite->m_lpDoc;

LPDATA lpData = GetDocData(lpDoc);

if (fShow) {

lpSite->m_fObjWinOpen = TRUE;

ForceDataRedraw(lpData, FALSE);

}

else {

lpSite->m_fObjWinOpen = FALSE;

ForceLineRedraw(lpData, TRUE);

// Win32 Note: We want the foreground window so we must

// make an explicit call to the Win32 API SetForegroundWindow.

SetForgroundWindow(lpDoc->m_hWndDoc);

BringWindowToTop(lpDoc->m_hWndDoc);

SetFocus(lpDoc->m_hWndDoc);

}

return S_OK;

}


IOleClientSite::GetMoniker

HRESULT IOleClientSite::GetMoniker(dwAssign, dwWhichMoniker, ppmk)
DWORD dwAssign
DWORD dwWhichMoniker
IMoniker **ppmk

IOleClientSite::GetMoniker returns the container's moniker, the object's moniker relative to the container, or the object's full moniker.

Parameters

dwAssign

[in] Specifies the type of moniker to be returned in ppmk. Valid values are contained in the enumeration OLEGETMONIKER:

typedef enum tagOLEGETMONIKER

{

    OLEGETMONIKER_ONLYIFTHERE      =  1,

    OLEGETMONIKER_FORCEASSIGN      =  2,

    OLEGETMONIKER_UNASSIGN         =  3,

    OLEGETMONIKER_TEMPFORUSER      =  4

} OLEGETMONIKER;

OLEGETMONIKER_ONLYIFTHERE returns a moniker only if one has previously been assigned.

OLEGETMONIKER_FORCEASSIGN returns an assigned moniker.

OLEGETMONIKER_UNASSIGN removes moniker assignment and returns NULL.

OLEGETMONIKER_TEMPFORUSER returns a temporary, unassigned moniker. This flag is used when a moniker is needed, for example, to represent data being copied to the Clipboard at the time of the copy operation. Forced moniker assignment should be postponed until such time that a Paste Link actually occurs.

dwWhichMoniker

[in] Specifies which moniker to return in ppmk; the value is taken from the enumeration OLEWHICHMK:

typedef enum tagOLEWHICHMK

{

    OLEWHICHMK_CONTAINER       =  1,

    OLEWHICHMK_OBJREL          =  2,

    OLEWHICHMK_OBJFULL         =  3

}OLEWHICHMK;

OLEWHICHMK_CONTAINER indicates that the moniker belonging to the object's container, typically a file moniker, should be returned.

OLEWHICHMK_OBJREL returns the object's moniker relative to the client site, typically an item moniker. OLEWHICHMK_OBJFULL returns the object's full moniker, typically a composite moniker.

ppmk

[out] Points to where to return the moniker. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Requested moniker returned successfully.
E_FAIL	An unspecified error occurred.
E_UNEXPECTED	A relatively catastrophic failure has occurred.
E_NOTIMPL	This container cannot assign monikers to objects.
Remarks

Every container that may contain links 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.

When a link is made to an entire embedded object or to pieces of it, the embedded object needs a moniker to use in constructing a 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.

In some cases, an object may no longer need the moniker previously assigned to it; such as high-frequency links used for object connections in a programming language. In such circumstances, the object can call IOleClientSite::GetMoniker with OLEGETMONIKER_UNASSIGN to inform its container that it no longer needs the moniker; the container can unassign the moniker as an optimization.

Example

The following implementation of IOleClientSite::GetMoniker calls three other functions to return the requested moniker. Depending on the value of dwWhichMoniker, IOleClientSite_GetMoniker calls either GetFullDocMoniker to get the full moniker of the document, CGetFullMoniker to get the full moniker of the container, or CGetRelMoniker to get the moniker of the object relative to the container. If a valid moniker is returned, IOleClientSite::GetMoniker returns S_OK (or NOERROR). Otherwise, E_FAIL is returned.

HRESULT IOleClientSite_GetMoniker(

LPOLECLIENTSITE     lpThis,

DWORD               dwAssign,

DWORD               dwWhichMoniker,

IMoniker **         ppmk)

{

LPCONTAINERLINE lpContainerLine;

lpContainerLine = ((struct COleClientSiteImpl *)lpThis)-

>lpContainerLine;

*ppmk = NULL;

switch (dwWhichMoniker)

{

case OLEWHICHMK_CONTAINER:

*ppmk = GetFullDocMoniker

((LPOLEDOC)lpContainerLine->m_lpDoc,	 dwAssign);

break;

case OLEWHICHMK_OBJREL:

*ppmk = CGetRelMoniker(lpContainerLine, dwAssign);

break;

case OLEWHICHMK_OBJFULL:

*ppmk = CGetFullMoniker(lpContainerLine, dwAssign);

break;

}

if (*ppmk != NULL)

return NOERROR;

else

return E_FAIL;

GetFullDocMoniker returns a moniker for the following three types of documents:

•	Data transfer document: a document that is copied to the Clipboard or is participating in a drag operation.

•	Document under user control (new or reopened).

•	Document that is an embedded object. In this case the document belongs to an application that can serve as both a container and a creator of objects.

In the first case, the moniker that represents the source document of the copied data is returned. GetFullDocMoniker is called recursively with the source of the copy passed as the first parameter. In the second case, GetFullDocMoniker returns the file moniker stored with the document after calling AddRef to increase its reference count. In the last case where the document is itself an embedded object, IOleClientSite::GetMoniker is called on the container to return the moniker.

LPMONIKER GetFullDocMoniker(LPOLEDOC lpOleDoc, DWORD dwAssign)

{

LPMONIKER lpMoniker = NULL;

if (lpOleDoc->m_lpSrcDocOfCopy) {

if (! lpOleDoc->m_fLinkSourceAvail)

goto done;

lpMoniker = GetFullDocMoniker(lpOleDoc->m_lpSrcDocOfCopy,

dwAssign);

}

else if (lpOleDoc->m_lpFileMoniker) {

lpOleDoc->m_lpFileMoniker->lpVtbl->AddRef(lpOleDoc-

>m_lpFileMoniker);

lpMoniker = lpOleDoc->m_lpFileMoniker;

}

#if defined(OLE_SERVER)

else if (((LPSERVERDOC)lpOleDoc)->m_lpClientSite) {

((LPSERVERDOC)lpOleDoc)->m_lpClientSite->lpVtbl->GetMoniker(

((LPSERVERDOC)lpDoc)->m_lpClientSite,

dwAssign,

OLEWHICHMK_OBJFULL,

&lpMoniker

);

}

#endif

else{

lpMoniker = NULL;

}

done;

return lpMoniker;

}

CGetRelMoniker returns an item moniker with the name of the object's storage according to the value of dwAssign. If dwAssign is GETMONIKER_FORCEASSIGN, CreateItemMoniker is called to create the item moniker if it is being assigned for the first time. IOleObject::SetMoniker is then called to instruct the object to register it in the running object table. If dwAssign is GETMONIKER_ONLYIFTHERE, the m_fMonikerAssigned flag in the container site is checked to determine if a moniker is currently assigned before CreateItemMoniker is called. If dwAssign is GETMONIKER_TEMPFORUSER, the item moniker is generated without checking whether the moniker is currently assigned. In the last case, where dwAssign is GETMONIKER_UNASSIGN, the m_fMonikerAssigned flag is set to FALSE.

LPMONIKER CGetRelMoniker(LPSITE lpSite, DWORD dwAssign)

{

LPMONIKER pmk = NULL;

switch (dwAssign)

{

case GETMONIKER_FORCEASSIGN:

CreateItemMoniker(OLESTR("\\"), lpSite->m_szStgName, &pmk);

if (! lpSite->m_fMonikerAssigned) {

lpSite->m_fMonikerAssigned = TRUE;

SetDocModified(lpSite->m_lpDoc);

if (lpSite->m_lpOleObj) {

lpSite->m_lpOleObj->lpVtbl->SetMoniker(

                     lpSite->m_lpOleObj, OLEWHICHMK_OBJREL, pmk);

}

}

break;

case GETMONIKER_ONLYIFTHERE:

if (lpSite->m_fMonikerAssigned)

CreateItemMoniker(OLESTR("\\"), lpSite->m_szStgName,

&pmk);

break;

case GETMONIKER_TEMPFORUSER:

CreateItemMoniker(OLESTR("\\"), lpSite->m_szStgName, &pmk);

break;

case GETMONIKER_UNASSIGN:

lpSite->m_fMonikerAssigned = FALSE;

break;

}

return pmk;

}

CGetFullMoniker returns a composite moniker. First, GetFullDocMoniker is called to get the file moniker of the document. Next, CGetRelMoniker is called to get the item moniker for the container site. If both of these monikers are returned successfully, CreateGenericComposite is called to generate the composite moniker and Release is called to decrement the reference count on the file and item monikers.

LPMONIKER CGetFullMoniker(LPSITE lpSite, DWORD dwAssign)

{

LPMONIKER pmkDoc = NULL;

LPMONIKER pmkItem = NULL;

LPMONIKER pmkFull = NULL;

pmkDoc = GetFullDocMoniker((LPDOC)lpSite->m_lpDoc, dwAssign);

if (! pmkDoc) return NULL;

pmkItem = CGetRelMoniker(lpSite, dwAssign);

if (pmkItem) {

CreateGenericComposite(pmkDoc, pmkItem,
(LPMONIKER *)&pmkFull);

(LPUNKNOWN)pmkItem->lpVtbl->Release((LPUNKNOWN)pmkItem);

}

if (pmkDoc)

(LPUNKNOWN)pmkDoc->lpVtbl->Release((LPUNKNOWN)pmkDoc);

return pmkFull;

}

See Also

IOleClientSite::SaveObject, IOleClientSite::GetContainer, IOleClientSite::RequestNewObjectLayout


IOleClientSite::RequestNewObjectLayout

HRESULT IOleClientSite::RequestNewObjectLayout()

IOleClientsite::RequestNewObjectLayout is called when a compound-document object is going to request more or less room.

Return Values

Value	Meaning
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 would negotiate to determine how much room the object would like. When such a mechanism is defined, responding to it will be optional for containers.

See Also

IOleClientSite::SaveObject, IOleClientSite::GetMoniker, IOleClientSite::GetContainer


IOleClientSite::SaveObject

HRESULT IOleClientSite::SaveObject()

IOleClientSite::SaveObject requests that the object attached to this client site be saved.

Return Values

Value	Meaning
S_OK	Object was saved successfully.
E_FAIL	Object was not loaded.
Values returned from IPersistStorage::Save, IPersistStorage::SaveCompleted, or IOleObject::Update	Refer to description of methods.
Remarks

IOleClientSite::SaveObject is synchronous; by the time it returns, the save operation will be completed. IOleClientSite::SaveObject is called by objects when the user chooses the File Update or Exit commands.

Example

This example checks to ensure that the object is loaded before starting the save operation by examining the IPersistStorage pointer. If the object is loaded, the object is told to save itself with a call to the helper function OleSave. It is not sufficient to call only IPersistStorage::Save. WriteClassStg, the function that writes the class of an object to storage, must also be called. OleSave makes the call to WriteClassStg automatically. Regardless of whether OleSave succeeds or fails, IPersistStorage::SaveCompleted must be called.

HRESULT IOleClientSite_SaveObject(LPOLECLIENTSITE lpThis)

{

LPCONTAINERLINE 	lpContainerLine = ((struct COleClientSiteImpl

*)lpThis)->lpContainerLine;

LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg;

HRESULT 	hrErrSave, hrErr;

if (! lpPersistStg)

return E_FAIL;

Doc_SetModified (LPOLEDOC)lpContainerLine->m_lpdoc, TRUE, TRUE,

FALSE);

hrErrSave = OleSave(lpPersistStg,lpContainerLine->m_lpStg, TRUE);

hrErr = lpPersistStg->lpVtbl->SaveCompleted (lpPersistStg, NULL);

if (hrErrSave != NOERROR) {

hrErr = hrErrSave;

}

    return hrErr;

}

See Also

IOleClientSite::GetMoniker, IOleClientSite::GetContainer, IOleClientSite::RequestNewObjectLayout


IOleClientSite::ShowObject

HRESULT IOleClientSite::ShowObject()

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.

Return Values

Value	Meaning
S_OK	Container has tried to make the object visible.
OLE_E_NOT_SUPPORTED	Client site is in OLE 1 container.
Remarks

IOleClientSite::ShowObject is typically part of an object's implementation of IOleObject::DoVerb. If this container is itself an embedded object, it will recursively invoke IOleClientSite::ShowObject on its container.

The container may not be able to position the object so it is either partially or completely visible. Therefore, when ShowObject returns, the object cannot rely on its degree of visibility. The intent of ShowObject is to respond to the request for visibility whenever possible.

After an object that is being edited in place scrolls into view, you must ask it to update its rectangle for the new clip RECT coordinates.


IOleContainer Interface

The IOleContainer interface, implemented by containers and object applications, is used to enumerate objects in a container. IOleContainer is typically implemented by applications that support standard linking and linking to embeddings, however, it is useful for other applications as well. An extension of IOleContainer, IOleItemContainer, is used by item monikers in their bind process. The IOleContainer interface inherits from IParseDisplayName.

IOleContainer is generic to many kinds of containers. It provides name parsing and enumeration of objects, that is, an outside-in view of the composite set of objects managed by a container. OLE 2 applications will normally use IOleItemContainer to expose the contents of a container.

Simple, nonlinking containers need not implement IOleContainer. The default object handler provides default behavior when a pointer to IOleContainer is not available.

IOleContainer contains the following methods:

DECLARE_INTERFACE_(IOleContainer, IParseDisplayName)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IParseDisplayName method ***

  HRESULT ParseDisplayName (THIS_ LPBC pbc, LPOLESTR lpszDisplayName,

    ULONG * pchEaten, LPMONIKER * ppmkOut);

    // *** IOleContainer methods ***

  HRESULT EnumObjects) ( DWORD grfFlags,

    LPENUMUNKNOWN * ppenumUnknown);

  HRESULT LockContainer (THIS_ BOOL fLock);

};


IOleContainer::EnumObjects

HRESULT IOleContainer::EnumObjects(grfFlags, ppenum)
DWORD grfFlags
IEnumUnknown **ppenum

IOleContainer::EnumObjects enumerates the objects in the current container.

Parameters

grfFlags

[in] Controls the enumeration. Valid values are taken from the enumeration OLECONTF:

typedef enum tagOLECONTF

{

    OLECONTF_EMBEDDINGS     =  1,

    OLECONTF_LINKS          =  2,

    OLECONTF_OTHERS         =  4,

    OLECONTF_ONLYUSER       =  8,

    OLECONTF_ONLYIFRUNNING  = 16

} OLECONTF;

The values have the following meanings:

Value	Meaning
OLECONTF_EMBEDDINGS	Enumerates the embedded objects in the container.
OLECONTF_LINKS	Enumerates the linked objects in the container.
OLECONTF_OTHER	Enumerates all objects in the container other than OLE compound-document objects. If this flag is not given, pseudo objects in the container will be omitted.
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 the objects that are currently running inside this container.
ppenum

[out] Points to where the enumerator, of type IEnumUnknown, should be returned. If an error is returned, this parameter must be set to NULL. For more information, see the discussion of enumerators in IEnumX Interface.

Return Values

Value	Meaning
S_OK	Enumerator successfully returned.
E_FAIL	An unspecified error occurred.
E_NOTIMPL	Object enumeration not supported.
Remarks

EnumObjects should be implemented to enable programmatic clients to find out what elements the container holds. It is not called in standard linking scenarios.

See Also

IOleItemContainer Interface


IOleContainer::LockContainer

HRESULT IOleContainer::LockContainer(fLock)
BOOL fLock

IOleContainer::LockContainer is used by an embedded object to control the running of its container manually.

Parameters

fLock

[in] Specifies whether to lock (TRUE) or unlock (FALSE) a container.

Return Values

Value	Meaning
S_OK	Container was locked successfully.
E_FAIL	An unspecified error occurred.
E_OUTOFMEMORY	Container could not be locked due to lack of memory.
Remarks

An embedded object calls IOleContainer::LockContainer to ensure that its container remains alive when other containers are linked to it and require an update. IOleContainer::LockContainer calls CoLockObjectExternal with a flag set the same as fLock to either increment or decrement the reference count.

IOleContainer::LockContainer with the fLock parameter set to TRUE is called when an embedded object changes to the running state. When the embedded object shuts down (transitions from running to loaded), it calls IOleContainer::LockContainer with the fLock parameter set to FALSE. However, if the user selects File Close from the menu, all outstanding LockContainer locks are ignored and the document will shut down.

The container must keep track of whether and how many calls to LockContainer(TRUE) have been made. 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 transitions to and from the running state. Object applications not using the default handler, such as DLL object applications, must make the calls directly.

Example

The following example illustrates the use of artificial reference counting to protect the stability of an object during an important operation. AddRef is called; then, next is the call to CoLockObjectExternal. It can release the container, and then Release is called. For more information about artificial reference counting, see Reference Counting Rules.

HRESULT IOleContainer_LockContainer(LPOLECONTAINER lpThis, BOOL fLock)

{

HRESULT hrErr;

LPDOC lpDoc = ((struct CDocOleItemContainerImpl *)lpThis)->lpDoc;

IUnknown_Doc_AddRef(lpDoc);

hrErr = CoLockObjectExternal((LPUNKNOWN)&lpDoc->m_Unknown,

fLock, TRUE);

IUnknown_Doc_Release(lpDoc);

return hrErr;

}


IOleItemContainer Interface

The IOleItemContainer interface, an extension to IOleContainer, is used to bind item monikers. Containers that use item monikers to identify contained objects must implement IOleItemContainer. IOleItemContainer contains the following methods:

DECLARE_INTERFACE_(IOleItemContainer, IOleContainer)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IParseDisplayName method ***

  HRESULT ParseDisplayName (THIS_ LPBC pbc, LPOLESTR lpszDisplayName,

    ULONG * pchEaten, LPMONIKER * ppmkOut);

    // *** IOleContainer methods ***

  HRESULT EnumObjects (THIS_ DWORD grfFlags,

    LPENUMUNKNOWN *ppenumUnknown);

  HRESULT LockContainer (THIS_ BOOL fLock);

    // *** IOleItemContainer methods ***

  HRESULT GetObject (THIS_ LPOLESTR pszItem, DWORD dwSpeedNeeded,

    LPBINDCTX pbc, REFIID riid, LPVOID * ppvObject);

  HRESULT GetObjectStorage (THIS_ LPOLESTR pszItem, LPBINDCTX pbc,

    REFIID riid, LPVOID * ppvStorage);

  HRESULT IsRunning (THIS_ LPOLESTR pszItem);

};


IOleItemContainer::GetObject

HRESULT IOleItemContainer::GetObject(pszItem, dwSpeedNeeded, pbc, riid, ppvObject)
LPOLESTR pszItem
DWORD dwSpeedNeeded
IBindCtx *pbc
REFIID riid
void **ppvObject

IOleItemContainer::GetObject is called as part of the item moniker binding process. It returns the object represented by lpszItem.

Parameters

pszItem

[in] Points to the item in this container to which the item moniker should be bound.

dwSpeedNeeded

[in] Indicates how long the caller is willing to wait to get to the object. Valid values are from the enumeration BINDSPEED; see subsequent comments for detail.

pbc

[in] Points to an instance of the IBindCtx interface which contains the actual bind context.

riid

[in] Specifies the interface with which a connection to that object should be made.

ppvObject

[out] Points to where the bound-to object is returned. If an object cannot be returned, NULL is returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Object returned successfully.
MK_E_EXCEEEDEDDEADLINE	Deadline was exceeded.
MK_E_SYNTAX	Error in parsing a display name or creating a file moniker.
MK_E_NOOBJECT	Intermediate object could not be found.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED	Intermediate object needed did not support a required interface.
E_NOINTERFACE	The object cannot be returned because it does not support the interface identified by riid.
E_OUTOFMEMORY	Object cannot be returned due to lack of memory.
Remarks

If MK_E_EXCEEEDEDDEADLINE was returned, the caller can retrieve the moniker of the object for which the deadline was exceeded by calling the method pbc->GetObjectParam with the keys ExceededDeadline, ExceededDeadline1, ExceededDeadline2, and so on.

IOleItemContainer::GetObject first checks to see if the given item designates an embedded object. If the item is an embedded object, IOleItemContainer::GetObject loads and runs the object, and then returns it. If the item is not an embedded object, IOleItemContainer::GetObject checks to see if it designates a local object within the container. (This latter case is similar to OLESERVERDOC::GetObject in OLE 1.)

The parameter dwSpeedNeeded contains shorthand information found in the bind context. The value contained in dwSpeedNeeded is derived from the dwTickCountDeadline member in the BIND_OPTS structure that IOleItemContainer::GetObject receives in pbc. Instead of using dwSpeedNeeded, containers can call IBindCtx::GetBindOptions to get specific information about the time limit. Values for dwSpeedNeeded are from the enumeration BINDSPEED, which is defined as follows:

typedef enum tagBINDSPEED

{

    BINDSPEED_INDEFINITE  =  1,

    BINDSPEED_MODERATE    =  2,

    BINDSPEED_IMMEDIATE   =  3

} BINDSPEED;

These values have the following semantics:

Value	Meaning
BINDSPEED_INDEFINITE	Caller will wait indefinitely.  BINDSPEED_INDEFINITE is specified if the speed needed in the bind context is zero.
BINDSPEED_MODERATE	Caller will wait a moderate amount of time. BINDSPEED_MODERATE is specified if the speed needed in the bind context is greater than 2500 milliseconds. BINDSPEED_MODERATE includes those objects indicated by BINDSPEED_IMMEDIATE, plus those objects that are always running when loaded. In this case, the designated object should be loaded, checked to see whether it is running and if so, it should be returned. Otherwise, MK_E_EXCEEEDEDDEADLINE should be returned.
BINDSPEED_IMMEDIATE	Caller will wait only a short time. If BINDSPEED_IMMEDIATE is specified, the object should be returned only if it is already running or if it is a pseudo-object. This is an object internal to the item container, such as a cell range in a spreadsheet or a character range in a word processor. Otherwise, MK_E_EXCEEEDEDDEADLINE should be returned.
Example

The container implementation of IOleItemContainer::GetObject shown below loops through its list of document objects, looking for a match for pszItem. If a match is found, the item name is valid and the value of dwSpeedNeeded is checked. If dwSpeedNeeded is BINDSPEED_IMMEDIATE and the object is not yet loaded, MK_E_EXCEEDEDDEADLINE is returned.

When the object is loaded, IOleItemContainer::GetObject calls IOleObject::GetMiscStatus to determine whether the object can be linked to from the inside. Objects that can be linked to from the inside can generate a moniker that binds to the running OLE object. Only OLE 2 embedded objects are of this type; OLE linked objects and OLE 1 embedded objects cannot be linked to from the inside. If the object is one of the latter two types, MK_E_NOOBJECT is returned.

OleIsRunning is called next to determine whether the object is running. The object must be running before IOleItemContainer::GetObject can query for the requested interface. If it is not yet running and dwSpeedNeeded is BINDSPEED_MODERATE, MK_E_EXCEEDEDDEADLINE is returned.

HRESULT IOleItemContainer_GetObject(

        LPDOC          lpDoc,

        LPOLESTR       lpszItem,

        DWORD          dwSpeedNeeded,

        REFIID         riid,

        LPVOID *    ppvObj)

{

LPLIST	lpList = &((LPDOC)lpDoc)->m_List;

int	i;

LPITEM	lpItem;

BOOL	fMatchFound = FALSE;

DWORD	dwStatus;

HRESULT	hrErr;

*ppvObj = NULL;

for (i = 0; i < lpList->m_nNumObjs; i++) {

lpItem=GetItem(lpItem, i);

// is item OLE object?

if (lpItem && (GetItemType(lpItem)==OLEOBJTYPE)) {

LPSITE lpSite = (LPSITE)lpItem;

// is item the one we're looking for?

if (lstrcmp(lpSite->m_szStgName, lpszItem) == 0) {

fMatchFound = TRUE;

if (lpSite->m_lpOleObj == NULL) {

if (dwSpeedNeeded == BINDSPEED_IMMEDIATE)

return MK_E_EXCEEDEDDEADLINE;

LoadOleObject(lpSite);

if (! lpSite->m_lpOleObj)

return E_OUTOFMEMORY;

}

lpSite->m_lpOleObj->lpVtbl->GetMiscStatus(

lpSite->m_lpOleObj,

DVASPECT_CONTENT,

(LPDWORD)&dwStatus);

if (dwStatus & OLEMISC_CANTLINKINSIDE)

return MK_E_NOOBJECT;

if (! OleIsRunning(lpSite->m_lpOleObj)) {

if (dwSpeedNeeded == BINDSPEED_MODERATE)

 	return MK_E_EXCEEDEDDEADLINE;

hrErr = RunOleObject(lpSite);

if (hrErr != NOERROR)

return hrErr;

}

lpSite->m_lpOleObj->lpVtbl->QueryInterface(

(LPUNKNOWN)lpSite->m_lpOleObj,

riid,

(LPVOID *)ppvObj);

break;

}

}

}

if (*ppvObj != NULL) {

return NOERROR;

} else

return (fMatchFound ? E_NOINTERFACE

: MK_E_NOOBJECT);

}

The following object application implementation of IOleItemContainer::GetObject returns a pseudo object that corresponds to pszItem. If pszItem does not represent a valid pseudo object, MK_E_NOOBJECT is returned.

HRESULT IOleItemContainer_GetObject(

        LPDOC             lpDoc,

        LPOLESTR             lpszItem,

        REFIID            riid,

        LPVOID *       ppvObj

)

{

LPPSEUDOOBJ lpPseudoObj;

LPPSOBJLIST lpPSObjList = (LPPSOBJLIST)((LPDOC)lpDoc)->m_lpPSObjList;

*ppvObj = NULL;

lpPseudoObj = GetPseudoObj(lpPSObjList, lpszItem, lpDoc);

if (! lpPseudoObj) {

*ppvObj = NULL;

return MK_E_NOOBJECT;

}

return PseudoObj_QueryInterface(lpPseudoObj, riid, ppvObj);

}


IOleItemContainer::GetObjectStorage

HRESULT IOleItemContainer::GetObjectStorage(pszItem, pbc, riid, ppvStorage)
LPOLESTR pszItem
IBindCtx *pbc
REFIID riid
void **ppvStorage

IOleItemContainer::GetObjectStorage returns access to the object's storage using the indicated interface.

Parameters

pszItem

[in] Points to the item name for the object to whose storage access is being requested.

pbc

[in] Points to the bind context. Can be ignored for most containers.

riid

[in] Identifies the interface the caller will use to access that storage. Often either IID_IStorage or IID_IStream is used.

ppvStorage

[out] Place to return the requested interface. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Object's storage returned successfully.
E_FAIL	Cannot return object's storage.
MK_E_NOOBJECT	Object could not be found.
MK_E_NOSTORAGE	An attempt was made to access or bind to the storage of an object that does not have one.
In order for IOleItemContainer::GetObjectStorage to succeed, the object represented by pszItem must have an independently identifiable piece of storage, as is the case of an embedded object. If no such storage exists, NULL is returned in ppvStorage and MK_E_NOSTORAGE is the return value.

Example

The example below is implemented in a container that supports linking to its embeddings. IOleItemContainer::GetObjectStorage first checks to make sure that the requested interface is the IStorage interface because for this container, only IStorage is used. If IStorage is requested, IOleItemContainer::GetObjectStorage cycles through the list of objects for the specified document, looking for a compound-document object with a storage name that matches pszItem. If a match is found, a pointer to the storage is returned. If no match is found, MK_E_NOOBJECT is returned.

HRESULT IOleItemContainer_GetObjectStorage(

LPOLEITEMCONTAINER  lpThis,

LPOLESTR           	 pszItem,

LPBINDCTX           pbc,

REFIID              riid,

LPVOID *         lplpStg

)

{

LPLIST	lpList = &((LPDOC)lpContainerDoc)->m_List;

int	i;

LPITEM	lpItem;

*lplpStg = NULL;

for (i = 0; i < lpList->m_nNumObjs; i++) {

lpItem = GetItem(lpItem, i);

if (lpItem && (GetItemType(lpItem)==OLEOBJTYPE)) {

LPSITE lpSite = (LPSITE)lpItem;

if (OLESTRCMP(lpSite->m_szStgName, pszItem) == 0) {

*lplpStg = lpSite->m_lpStg;

break;

}

}

}

if (*lplpStg != NULL)

return NOERROR;

else

return MK_E_NOOBJECT;

}


IOleItemContainer::IsRunning

HRESULT IOleItemContainer::IsRunning(pszItem)
LPOLESTR	pszItem

IOleItemContainer::IsRunning indicates whether the specified item in this item container is running.

Parameters

pszItem

[in] Points to the item name for the object whose status is being queried.

Return Values

Value	Meaning
S_OK	Object is running.
S_FALSE	Object is not running.
MK_E_NOOBJECT	Object does not allow linking inside.
E_OUTOFMEMORY	Ran out of memory while running the object.
Examples

The first example below is from a container application. IOleItemContainer::IsRunning first locates the object represented by pszItem by looping through the document's list of objects. If the object is found and is not yet loaded, it is not considered to be running and S_FALSE is returned. When the object is loaded, IOleObject::GetMiscStatus is called to determine whether this object can be linked to from the inside. If it cannot, MK_E_NOOBJECT is returned. The last call is to OleIsRunning to check if the object is, in fact, running.

HRESULT IOleItemContainer_IsRunning(LPCONTAINERDOC lpContainerDoc, LPOLESTR

pszItem)

{

LPLIST      lpList = &((LPDOC)lpContainerDoc)->m_List;

int         i;

LPITEM      lpItem;

DWORD       dwStatus;

for (i = 0; i < lpList->m_nNumObjs; i++) {

lpItem=GetItem(lpItem, i);

if (lpItem && (GetItemType(lpItem)==OLEOBJTYPE)) {

LPSITE lpSite = (LPSITE)lpItem;

if (OLESTRCPY(lpSite->m_szStgName, pszItem) == 0) {

if (! lpSite->m_lpOleObj) {

return E_OUTOFMEMORY;

}

lpSite->m_lpOleObj->lpVtbl->GetMiscStatus (

lpSite->m_lpOleObj,

DVASPECT_CONTENT,

(LPDWORD)&dwStatus	);

if (dwStatus & OLEMISC_CANTLINKINSIDE)

return MK_E_NOOBJECT;

if (OleIsRunning(lpSite->m_lpOleObj))

return NOERROR;

else

return S_FALSE;

}

}

}

return MK_E_NOOBJECT;

}

The following version of IOleItemContainer::IsRunning, taken from an object application, looks in a table of pseudo-object names for a match for pszItem. If a match is found, NOERROR is returned; otherwise MK_E_NOOBJECT is returned.

HRESULT IOleItemContainer_IsRunning(LPDOC lpDoc, LPOLESTR lpszItem)

{

LPPSNAME lpPSName;

LPPSOBJLIST lpPSObjList = (LPPSOBJLIST)((LPDOC)lpDoc)->m_lpPSObjList;

lpPSName = (LPPSNAME)FindName(lpPSObjList, lpszItem);

    if (lpPSName)

        return NOERROR;

    else

        return MK_E_NOOBJECT;

}


IOleObject Interface

IOleObject, implemented by object applications and by OLE, contains methods for compound-document object management. It is the primary interface by which a linked or embedded object provides functionality to its container.

IOleObject contains the following methods:

DECLARE_INTERFACE_(IOleObject, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IOleObject methods ***

  HRESULT SetClientSite (THIS_ LPOLECLIENTSITE pClientSite);

  HRESULT GetClientSite (THIS_ LPOLECLIENTSITE * ppClientSite);

  HRESULT SetHostNames (THIS_ LPCOLESTR szContainerApp, LPCOLESTR szContainerObj);

  HRESULT Close (THIS_ DWORD dwSaveOption);

  HRESULT SetMoniker (THIS_ DWORD dwWhichMoniker, LPMONIKER pmk);

  HRESULT GetMoniker (THIS_ DWORD dwAssign, DWORD dwWhichMoniker,

    LPMONIKER * ppmk);

  HRESULT InitFromData (THIS_ LPDATAOBJECT pDataObject,

    BOOL fCreation, DWORD dwReserved);

  HRESULT GetClipboardData (THIS_ DWORD dwReserved, LPDATAOBJECT * ppDataObject);

  HRESULT DoVerb (THIS_ LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite,

    LONG lindex, HWND hwndParent LPCRECT lprcPosRect);

  HRESULT EnumVerbs (THIS_ LPENUMOLEVERB * ppEnumOleVerb);

  HRESULT Update (THIS);

  HRESULT IsUpToDate (THIS);

  HRESULT GetUserClassID (THIS_ CLSID * pClsid);

  HRESULT GetUserType (THIS_ DWORD dwFormOfType, LOLESTR * pszUserType);

  HRESULT SetExtent (THIS_ DWORD dwDrawAspect, LPSIZEL psizel);

  HRESULT GetExtent (THIS_ DWORD dwDrawAspect, LPSIZEL psizel);

  HRESULT Advise (THIS_ LPADVISESINK pAdvSink, DWORD * pdwConnection);

  HRESULT Unadvise (THIS_ DWORD dwConnection);

  HRESULT EnumAdvise (THIS_ LPENUMSTATDATA * ppenumAdvise);

  HRESULT GetMiscStatus (THIS_ DWORD dwAspect, DWORD * pdwStatus);

  HRESULT SetColorScheme (THIS_ LPLOGPALETTE lpLogpal);

};


IOleObject::SetClientSite

HRESULT IOleObject::SetClientSite(pClientSite)
IOleClientSite *pClientSite

IOleObject::SetClientSite informs a newly created or loaded embedded object of its client site within the container.

Parameters

pClientSite

[in] Points to the object's client-site interface.

Return Values

Value	Meaning
S_OK	Client site successfully set.
E_UNEXPECTED	Object is not embedded in a container.
Remarks

Each embedded object has an associated client site through which the object communicates with its container. IOleObject::SetClientSite must be called whenever an embedded object is created or loaded.

Example

The following code sample first checks that the target of the call is an embedded object. Next, if the object already has a non-NULL client site pointer, the pointer is released before a new one is set. To enable the object to hold on to the new client-site pointer, AddRef is called to increment the reference count.

HRESULT IOleObject_SetClientSite

(IOleObject * pThis, IOleClientSite pClientSite)

{

LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl *)lpThis)-

>lpServerDoc;

LPAPPDOC LpAppDoc = (LPAPPDOC)lpServerDoc;

if (lpAppDoc->m_docInitType != DOCTYPE_EMBEDDED)

return E_UNEXPECTED;

if (lpServerDoc->m_lpClientSite)

OleStdrelease((LPUNKNOWN)lpServerDoc->m_lpOleClientSite);

lpServerDoc->m_lpClientSite = (IOleClientSite) pClientSite;

if (pClientSite)

pClientSite->lpVtbl->AddRef(pClientSite);

return NOERROR;

}


IOleObject::GetClientSite

HRESULT IOleObject::GetClientSite(ppClientSite)
IOleClientSite **ppClientSite

IOleObject::GetClientSite queries an object for the IOleClientSite pointer most recently set by using IOleObject::SetClientSite.

Parameters

ppClientSite

[out] Points to the location at which to return the client site. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Client site pointer returned successfully.
Remarks

The returned client-site pointer will be NULL if the embedded object's client site has not yet been initialized.

Example

The implementation of IOleObject_GetClientSite shown below increments the reference count on the client site before returning a pointer to it.

HRESULT IOleObject_GetClientSite(

        IOleObject *            pThis,

        IOleClientSite **       ppClientSite)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)-	>lpServerDoc;

lpserverDoc->m_lpClientSite->lpVtbl->AddRef

(lpServerDoc->m_lpClientSite);

*ppClientSite = lpServerDoc->m_lpClientSite;

return NOERROR;

}


IOleObject::SetHostNames

HRESULT IOleObject::SetHostNames(szContainerApp, szContainerObj)
LPCOLESTR szContainerApp
LPCOLESTR szContainerObj

IOleObject::SetHostNames specifies window-title information to be used when an object is open for editing.

Parameters

szContainerApp

[in] Points to the user-presentable name of the container application.

szContainerObj

[in] Points to the name of the container document that contains this object. Can be NULL.

Return Values

Value	Meaning
S_OK	Window title information set successfully.
Remarks

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 is loaded or run.

Example

The example of IOleObject::SetHostNames first copies szContainerObj to the document object's local structure. The OLE user-model conventions do not require the use of szContainerApp. The second half of the window title is constructed and then concatenated to the end of szContainerApp to form the complete window title. The complete window title for an embedded object in an SDI container application or a MDI application with a maximized child should appear as follows:

<object app name> - <object short type> in <container document>

Otherwise, the title should be:

<object app name> - <container document>

UpdateMenu is then called to make the following changes to the File menu:

•	Remove File/New and File/Open (SDI ONLY)

•	Change File/Save As to File/Save Copy As

•	Change File menu so it contains "Update" instead of "Save"

•	Change File/Exit to File/Exit & Return to <container document>

HRESULT IOleObject_SetHostNames(

        IOleObject *            pThis,

        LPCOLESTR              	szContainerApp,

        LPCOLESTR              	szContainerObj)

{

    LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl *)pThis)-	>lpServ

erDoc;

LPAPPDOC lpAppDoc = (LPAPPDOC)lpServerDoc;

char szAnsiStr[256], szAnsiStr3[256];

OLESTRCPY((LPOLESTR)lpServerDoc->m_szContainerApp, szContainerApp;

OLESTRCPY((LPOLESTR)lpServerDoc->m_szContainerObj, szContainerObj,

    W2A (lpServerDoc->m_szContainerObj, szAnsiStr1, 256);

W2A (SHORTUSERTYPENAME, szAnsiStr3, 256);

wsprintf(szAnsiStr2, "%s in %s",

szAnsiStr3, (LPSTRszAnsiStr1);

A2W (szAnsiStr2, lpAppDoc->m_szFileName, OLEUI_CCHPATHMAX);

    lpAppDoc->m_lpszDocTitle = lpAppDoc->m_szFileName;

    SetTitle(lpAppDoc, FALSE);

    UpdateMenu(lpServerDoc);

    return NOERROR;

}

See Also

IOleObject::GetUserType


IOleObject::Close

HRESULT IOleObject::Close(dwSaveOption)
DWORD dwSaveOption

IOleObject::Close changes an embedded object from the running to the loaded state.

Parameters

dwSaveOption

[in] Determines whether the object is saved as part of the transition to the loaded state. Valid values are taken from the enumeration OLECLOSE:

typedef enum tagOLECLOSE

{

OLECLOSE_SAVEIFDIRTY = 0,

OLECLOSE_NOSAVE      = 1,

OLECLOSE_PROMPTSAVE  = 2

} OLECLOSE;

OLECLOSE_SAVEIFDIRTY indicates that the object is always to be saved if it is dirty.

OLECLOSE_NOSAVE indicates that a save operation is not to occur whether the object is dirty or not.

OLECLOSE_PROMPTSAVE indicates that the user will determine whether the save operation should occur when prompted with a message.

Note  The OLE 2 user model dictates that linked or embedded objects always be saved when closed without any prompting to the user. This is the recommendation regardless of whether the object is activated in place or open in its own window. This is a change from the OLE 1 user model, in which object applications always prompt to save changes.

Return Values

Value	Meaning
S_OK	Object closed successfully.
OLE_E_PROMPTSAVECANCELLED	The user was prompted to save but chose the Cancel button from the prompt message box.
Remarks

If the object is implemented by using an EXE object application, the object in the application is shut down. If the object application is not servicing any other objects, and has no other reason to remain in the running state, such as the presence of IClassFactory::LockServer locks or if the application is under user control, then it also causes the application itself to shut down.

IOleObject::Close is called by the object's linking or embedding container. This is one way that an object may be closed; the release of the object's last remaining interface pointer is another way. Calling IOleObject::Close when the object is not running has no effect.

When a link object is not connected to its link source, it is in the loaded state. As soon as it is connected, the link is in the running state. When using OleLoad on an automatic link, it tries to make a connection if the link source is running.

When IOleObject::Close is called on the link object, it disconnects from the link source. If the link source was visible to the user, the link source will not shut down, nor will it send an OnClose notification. Thus, even though the link object changes from connected to disconnected (as well as from the running to the loaded state), an OnClose notification is not sent to the link container.

If IOleObject::Close is called on an open embedded object and there are no other visible documents, the application window should be hidden. If there are no other documents/objects running, the application should also shut down. However, if there are other invisible documents/objects being used programmatically, such as in the silent update scenario, the application must only hide, postponing a shut down until the other documents are released by their clients.

The following pseudo code shows the recommended way of handling dwSaveOption:

switch (dwSaveOption)  {

case OLECLOSE_SAVEIFDIRTY:

If dirty, save. Then close.

case OLECLOSE_NOSAVE:

Close

case OLECLOSE_PROMPTSAVE:

If object visible, but not in-place active:

If not dirty, close.

Otherwise, switch(prompt)   {

case IDYES:

Save and close

case IDNO:

Close

case IDCANCEL:

return OLE_E_PROMPTSAVECANCELLED

If object invisible (includes UI deactivated object)

If dirty, save and close

NOTE: No prompt - not appropriate to prompt if object

  is not visible.

If object is in-place active:

If dirty, save and close

NOTE: No prompt - not appropriate to prompt if object

  is active in-place.

Example

The following implementation of IOleObject::Close is for an SDI object application that supports in-place activation, drag and drop, and linking to pseudo objects. IOleObject_Close starts by checking for the following conditions and returns if either of them are true:

•	the document is already closed

•	the document is in the process of closing

The value of dwSaveOption is examined in DoSaveIfRequired. If dwSaveOption is set to OLECLOSE_PROMPTSAVE, the user is prompted for a decision on saving. Dirty objects are saved without prompting if dwSaveOption is OLECLOSE_SAVEIFDIRTY. If dwSaveOption is OLECLOSE_NOSAVE, no save occurs. IOleObject::Close calls IOleClientSite::SaveObject to ask the container to save the object if appropriate.

After the optional save has finished, if the object is currently active in place, it is deactivated. OleFlushClipboard is called if the object is the source for Clipboard data. OleFlushClipboard flushes the clipboard, leaving all hGlobal-based data directly on the Clipboard. If the object is registered in the running object table, its registration is revoked.

Next, the container document is hidden and CoLockObjectExternal is called to release the external lock made when the document was made visible. Since the application is being shut down (fShutdown is TRUE), OLE is instructed to release its reference count on the document if there are no more strong locks.

All pseudo objects or other compound-document objects that exist within the documented are closed (CloseAllPseudoObjs and CloseAllOleObjs).

If there are outstanding data-advise connections established with the ADVF_DATAONSTOP flag, data change notifications are sent (IAdviseSink::OnDataChange) to these connections and the IDataAdviseHolder interface pointer is released.

If there are outstanding OLE advise connections, IAdviseSink::OnClose is called to notify these registered sinks and the IOleAdviseHolder interface pointer is released.

The last steps involve releasing pointers to the client site in the container, calling CoDisconnectObject to forcibly disconnect any remoting clients, and decrementing the reference count on both the document and the application level objects. Calling CoDisconnectObject breaks any remaining strong locks, guaranteeing that the object will get its final releases to make the reference count decrement to zero.

HRESULT IOleObject_Close (IOleObject * pThis, DWORD	 dwSaveOption)

{

LPDOC 	lpDoc = ((struct CDocOleObjectImpl *)pThis)->lpDoc;

BOOL 	fStatus;

LPAPP 	lpApp = (LPAPP)g_lpApp;

LPDOC 	lpClipboardDoc;

LPLIST 	lpList = (LPLIST)&((LPDOC)lpDoc)->m_List;

LPRUNNINGOBJECTTABLE prot;

HRESULT hrErr;

if (! lpDoc)	return NOERROR;

if (lpDoc->m_fObjIsClosing)	return NOERROR;

if (! DoSaveIfRequired((LPDOC)lpDoc, dwSaveOption))

return E_FAIL;

lpDoc->m_fObjIsClosing = TRUE;   // guard against recursive call

IUnknown_App_AddRef(lpApp);

IUnknown_Doc_AddRef(lpDoc);

DoInPlaceDeactivate((LPDOC)lpDoc);

lpClipboardDoc = (LPDOC)lpApp->m_lpClipboardDoc;

if (lpClipboardDoc && 	lpClipboardDoc->m_lpSrcDocOfCopy == lpDoc)

FlushClipboard(lpApp);

if (*lpDoc->m_dwRegROT != NULL) {

hrErr = GetRunningObjectTable(0,(LPRUNNINGOBJECTTABLE *)&prot);

if (hrErr == NOERROR) {

prot->lpVtbl->Revoke(prot, *lpdwRegister);

*lpDoc->m_dwRegROT = NULL;

prot->lpVtbl->Release(prot);

}

}

if (IsWindowVisible(lpDoc->m_hWndDoc))   {

CoLockObjectExternal((LPUNKNOWN)&lpDoc->m_Unknown, FALSE, TRUE);

ShowWindow(((LPDOC)lpDoc)->m_hWndDoc, SW_HIDE);

}

if (lpDoc->m_lpClientSite && !lpDoc->m_fInPlaceVisible) {

lpDoc->m_lpClientSite->lpVtbl->OnShowWindow(

lpDoc->m_lpClientSite,

FALSE);

}

CloseAllPseudoObjs(lpDoc);

CloseAllOleObjs(lpDoc);

if (lpDoc->m_fSendDataOnStop && lpDoc->m_lpDataAdviseHldr) {

lpDoc->m_lpDataAdviseHldr->lpVtbl->SendOnDataChange(

                       lpDoc->m_lpDataAdviseHldr,

                       (LPDATAOBJECT)&lpDoc->m_DataObject,

                       0,

                       ADVF_DATAONSTOP);

lpDoc->m_lpDataAdviseHldr->lpVtbl->Release

                            (lpDoc->m_lpDataAdviseHldr);

lpDoc->m_lpDataAdviseHldr = NULL;

}

if (lpDoc->m_lpOleAdviseHldr) {

lpDoc->m_lpOleAdviseHldr->lpVtbl->SendOnClose(

                      lpDoc->m_lpOleAdviseHldr);

lpDoc->m_lpOleAdviseHldr->lpVtbl->Release

(lpDoc->m_lpOleAdviseHldr);

lpDoc->m_lpOleAdviseHldr = NULL;

}

if(lpDoc->m_lpClientSite) {

lpDoc->m_lpClientSite->lpVtbl->Release

(lpDoc->m_lpClientSite);

lpDoc->m_lpClientSite = NULL;

}

CoDisconnectObject((LPUNKNOWN)&lpDoc->m_Unknown, TRUE);

IUnknown_Doc_Release(lpDoc);

IUnknown_App_Release(lpApp);

return NOERROR;

}


IOleObject::SetMoniker

HRESULT IOleObject::SetMoniker(dwWhichMoniker, pmk)
DWORD dwWhichMoniker
IMoniker *pmk

IOleObject::SetMoniker notifies the object of either its own moniker or its container's moniker, depending on the value for dwWhichMoniker.

Parameters

dwWhichMoniker

[in] Specifies which moniker is passed in pmk. Values are from the enumeration OLEWHICHMK. See IOleClientSite::GetMoniker for details.

pmk

[in] Points to where to return the moniker.

Return Values

Value	Meaning
S_OK	Moniker successfully set.
E_FAIL	No client site for object.
Remarks

IOleObject::SetMoniker is usually called only by the object's container, since the container is responsible for maintaining the object's identity.

If dwWhichMoniker is set to OLEWHICHMK_OBJREL, the moniker passed in pmk is the moniker of the object in the container's document. That is, if this moniker is composed onto the moniker of the object's container and the resulting moniker is bound, then the object itself will be connected too.

If dwWhichMoniker is OLEWHICHMK_CONTAINER, then the object is being notified that the name of its container has changed. If the object has registered itself as running and either of these monikers changes, then the object will need to change the name under which it is registered. Also, any of the object's presently loaded subobjects may need to be informed that the name of their container has changed. Refer to the description of IRunningObjectTable::Register for more information.

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 must not be persistently stored inside the object, because the container can be renamed at any time.

Example

The following implementation of IOleObject::SetMoniker first checks that the document object has a valid client-site pointer and if it doesn't, SetMoniker fails. Next, a call is made to IOleClientSite::GetMoniker to get the full, absolute moniker from the container. The document is registered as running with the full moniker in the running object table and an OnRename notification is sent with a call to IAdviseSink::OnRename.

    /* If the passed moniker was NOT a full moniker, we must call

    **    back to our ClientSite to get our full moniker. This is

    **    needed in order to register in the RunningObjectTable. If we

    **    don't have a ClientSite, this is an error.

    */

DOCTYPE_EMBEDDED -             /* OLE2NOTE: If this is a FILE-based or

 **	untitled document, we should accept this new moniker

**	as our document's moniker.

            **    We will remember this moniker instead of the

            **    FileMoniker we have by default. This allows

            **    systems that use special monikers to track the

            **    location of documents to inform a document that is a

            **    link source of its special moniker. This enables the

            **    document to use this special moniker when building

            **    composite monikers to identify contained objects and

            **    pseudo objects (ranges).

not full moniker:

    /* If the passed moniker was NOT a full moniker, we must call

    **    back to our ClientSite to get our full moniker. This is

    **    needed in order to register in the RunningObjectTable. If we

    **    don't have a ClientSite, then this is an error.

    */

            */

after GetRunningObjectTable call:

        /* register as running if a valid moniker is passed

        **

        ** OLE2NOTE: We deliberately register the new moniker BEFORE

        **    revoking the old moniker just in case the object

        **    currently has no external locks. If the object has no

        **    locks, revoking it from the running object table will

        **    cause the object's StubManager to initiate shutdown of

        **    the object.

        */

HRESULT IOleObject_SetMoniker(

        LPOLEOBJECT             lpThis,

        DWORD                   dwWhichMoniker,

        LPMONIKER               pmk)

{

LPRUNNINGOBJECTTABLE prot;

LPDOC lpDoc = ((struct CDocOleObjectImpl *)lpThis)->lpDoc;

LPMONIKER	 pmkFull = NULL;

HRESULT	 hrErr;

if (lpDoc->m_lpClientSite == NULL)

return E_FAIL;

if (dwWhichMoniker == OLEWHICHMK_OBJFULL) {

IUnknown_Doc_AddRef(lpDoc);

hrErr = GetRunningObjectTable

(0,(LPRUNNINGOBJECTTABLE *)&prot);

if (hrErr == NOERROR) {

if (pmk) {

prot->lpVtbl->Register (prot, 0, lpDoc->m_Unknown,

pmk,&lpDoc->m_dwRegROT);

if (lpDoc->m_dwRegROT != 0)

prot->lpVtbl->Revoke(prot, dwOldRegister);

(LPUNKNOWN)prot->lpVtbl->Release((LPUNKNOWN)prot);

}

}

// Send OnRename notification to linking clients

if (pmk && lpDoc->m_lpOleAdviseHldr) {

lpDoc->m_lpOleAdviseHldr->lpVtbl->SendOnRename(

lpDoc->m_lpOleAdviseHldr, pmk);

}

// Send OnRename notification to all pseudo objects

PseudoObjectsSendOnRename(

(LPNAMETABLE)lpDoc->m_lpNameTable, pmk);

IUnknown_Doc_Release(lpDoc);

// if file-based, use new moniker instead of our file moniker

if (lpDoc->m_docInitType != DOCTYPE_EMBEDDED) {

if (lpDoc->m_lpFileMoniker) {

lpDoc->m_lpFileMoniker->lpVtbl->Release(

                        lpDoc->m_lpFileMoniker);

}

lpDoc->m_lpFileMoniker = pmk;

pmkFull->lpVtbl->AddRef(pmk);

}

return NOERROR;

}

// not OLEWHICHMK_FULL

if (lpDoc->m_lpClientSite == NULL)

return E_FAIL;

hrErr = lpDoc->m_lpClientSite->lpVtbl->GetMoniker(

            lpDoc->m_lpClientSite,

            OLEGETMONIKER_ONLYIFTHERE,

            OLEWHICHMK_OBJFULL,

            &pmkFull

);

if (hrErr != NOERROR)

return hrErr;

IUnknown_Doc_AddRef(lpDoc);

hrErr = GetRunningObjectTable

(0,(LPRUNNINGOBJECTTABLE *)&prot);

if (hrErr == NOERROR) {

if (pmkFull) {

prot->lpVtbl->Register (prot, 0, lpDoc->m_Unknown,

pmkFull,&lpDoc->m_dwRegROT);

if (lpDoc->m_dwRegROT != 0)

prot->lpVtbl->Revoke(prot, dwOldRegister);

(LPUNKNOWN)prot->lpVtbl->Release((LPUNKNOWN)prot);

}

}

if (pmkFull && lpDoc->m_lpOleAdviseHldr) {

lpDoc->m_lpOleAdviseHldr->lpVtbl->SendOnRename(

lpDoc->m_lpOleAdviseHldr, pmkFull);

}

// Send OnRename notification to all pseudo objects

PseudoObjectsSendOnRename(

(LPNAMETABLE)lpDoc->m_lpNameTable, pmkFull);

IUnknown_Doc_Release(lpDoc);

if (pmkFull)

(LPUNKNOWN)pmkFull->lpVtbl->Release((LPUNKNOWN)pmkFull);

return NOERROR;

}

See Also

IAdviseSink::OnRename, IRunningObjectTable::Register


IOleObject::GetMoniker

HRESULT IOleObject::GetMoniker(dwAssign, dwWhichMoniker, ppmk)
DWORD dwAssign
DWORD dwWhichMoniker
IMoniker **ppmk

IOleObject::GetMoniker returns a moniker that can be used to connect to the object.

Parameters

dwAssign

[in] Determines how the moniker is assigned to the object. Depending on the value of dwAssign, IOleObject::GetMoniker returns either an assigned or unassigned moniker. The valid values for dwAssign are taken from the enumeration OLEGETMONIKER, with the exception of OLEGETMONIKER_UNASSIGN:

typedef enum tagOLEGETMONIKER

{

    OLEGETMONIKER_ONLYIFTHERE = 1,

    OLEGETMONIKER_FORCEASSIGN = 2,

    OLEGETMONIKER_UNASSIGN    = 3,

    OLEGETMONIKER_TEMPFORUSER = 4

} OLEGETMONIKER;

OLEGETMONIKER_ONLYIFTHERE returns a moniker only if one has already been assigned to the object.

In the event that the object does not already have a moniker, OLEGETMONIKER_FORCEASSIGN forces moniker assignment.

OLEGETMONIKER_TEMPFORUSER returns a temporary, unassigned moniker.

dwWhichMoniker

[in] Specifies the form of the moniker being requested; valid values are taken from the enumeration OLEWHICHMK:

typedef enum tagOLEWHICHMK

{

    OLEWHICHMK_CONTAINER = 1,

    OLEWHICHMK_OBJREL    = 2,

    OLEWHICHMK_OBJFULL   = 3

} OLEWHICHMK;

OLEWHICHMK_CONTAINER is the moniker of the object's container.

OLEWHICHMK_OBJREL is the moniker of the object relative to the container.

OLEWHICHMK_OBJFULL is the composition of the first two values.

ppmk

[out] Points to where to return the object's moniker. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Requested moniker returned successfully.
E_FAIL	Object cannot return a moniker.
MK_E_SYNTAX	Syntax error in CreateFileMoniker.
E_OUTOFMEMORY	CreateFileMoniker failed due to lack of memory.
Remarks

It is not legal to pass OLEGETMONIKER_UNASSIGN to IOleObject::GetMoniker; see IOleClientSite::GetMoniker for information on when to use OLEGETMONIKER_UNASSIGN.

Example

The following implementation of IOleObject::GetMoniker retrieves the moniker from the container if the current document is an embedded object, or calls CreateFileMoniker to generate a file moniker if the document is file-based. If the document is new or not yet fully initialized, a moniker is not available and E_FAIL is returned.

HRESULT IOleObject_GetMoniker(

        IOleObject *            pThis,

        DWORD                   dwAssign,

        DWORD                   dwWhichMoniker,

        IMoniker **             ppmk)

{

LPSERVERDOC 	lpServerDoc =

((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

LPOLEDOC	 lpOleDoc = (LPOLEDOC)lpServerDoc;

HRESULT hr;

*ppmk = NULL;

if (lpServerDoc->m_lpClientSite) {

hr = lpServerDoc->m_lpClientSite->lpVtbl->GetMoniker(

                lpServerDoc->m_lpClientSite,

                dwAssign,

                dwWhichMoniker,

                ppmk);

// Doc is user document; return file moniker stored with doc

} else if (lpOleDoc->m_lpFileMoniker) {

if (dwWhichMoniker == OLEWHICHMK_CONTAINER)

hr = E_INVALIDARG;  // no CONTAINER moniker

else {

*ppmk = lpOleDoc->m_lpFileMoniker;

(*ppmk)->lpVtbl->AddRef(*ppmk);

hr = S_OK;

}

// Doc is not yet fully initialized => no moniker

} else{

hr = E_FAIL;

}

    return hr;

}

See Also

IOleClientSite::GetMoniker


IOleObject::InitFromData

HRESULT IOleObject::InitFromData(pDataObject, fCreation, dwReserved)
IDataObject *pDataObject
BOOL fCreation
DWORD dwReserved

IOleObject::InitFromData initializes the contents of an object with data available from pDataObject.

Parameters

pDataObject

[in] Points to the data-transfer object from which the initialization data is obtained; can be NULL, indicating that the caller wants to know if it is worthwhile trying to send data.

fCreation

[in] TRUE indicates the initial creation of the object; FALSE indicates a more general programmatic data transfer.

dwReserved

[in] Reserved for future use; must be zero.

Return Values

Value	Meaning
S_OK	Object successfully attempted to initialize if  pDataObject is not NULL; object can attempt a successful initialization if pDataObject is NULL.
S_FALSE	Object made no attempt to initialize if pDataObject is not NULL; object can not attempt to initialize if pDataObject is NULL.
E_NOTIMPL	Object does not support InitFromData.
OLE_E_NOTRUNNING	Object must be running to perform the operation.
Remarks

The container must decide whether to base a new object on the current selection. The object returns S_OK if an attempt at initialization is made. The container should call IOleObject::GetMiscStatus to check the value of the OLEMISC_INSERTNOTREPLACE bit. If the bit is on, the new object is inserted after the selected data. If the bit is off, the new object replaces the selected data.

The object returns S_FALSE if it can never initialize itself from caller-provided data or cannot initialize itself with the data provided in this case. No return value is specified to indicate whether or not the call to IOleObject::InitFromData really did anything; the subsequent pDataObject method calls to actually transfer the data will serve this purpose.

If fCreation is TRUE, the object is being initialized with data from its container as part of the creation sequence. The pDataObject provided by the container has the same contents as it would have for a copy operation. That is, if the container were to call OleSetClipboard rather than IOleObject::InitFromData, the contents of pDataObject would be identical.

If fCreation is FALSE, the caller is attempting to do a more general programmatic paste operation. The object should replace its current contents with the data pointed to by pDataObject, just as it would for a paste operation. The usual constraints that apply to an object during a paste operation should be applied here. For example, if the shape of the data provided is unacceptable, the object should fail to initialize and return S_FALSE.

See Also

IOleObject::GetMiscStatus, IDataObject::SetData


IOleObject::GetClipboardData

HRESULT IOleObject::GetClipboardData(dwReserved, ppDataObject)
DWORD dwReserved
IDataObject **ppDataObject

IOleObject::GetClipboardData returns a data-transfer object that contains exactly what would have been passed to OleSetClipboard in a copy operation.

Parameters

dwReserved

[in] Reserved for future use; must be zero.

ppDataObject

[out] Points to where the data-transfer object is to be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Data transfer object successfully returned.
E_NOTIMPL	GetClipboardData not supported.
OLE_E_NOTRUNNING	Object is not running.
Remarks

The difference between the pDataObject returned from IOleObject::GetClipboardData and IOleObject::QueryInterface (IID_IDataObject,...) is that the former data-transfer object is a snapshot copy; it does not change as the object changes. The data object retrieved through the QueryInterface call may change as the object changes.

This method enables a client of an object to perform a copy operation on the entire object without disturbing the contents of the real Clipboard. This method could be used to convert a linked object to an embedded object, in which case the container application would call IOleObject::GetClipboardData and then pass that data to OleCreateFromData.

See Also

IOleObject::InitFromData


IOleObject::DoVerb

HRESULT IOleObject::DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect
LONG iVerb
LPMSG lpmsg
IOleClientSite *pActiveSite
LONG lindex
HWND hwndParent
LPCRECT lprcPosRect

IOleObject::DoVerb requests an object to perform one of its verbs.

Parameters

iVerb

[in] The number assigned to the verb specifying the action to take.

lpmsg

[in] Points to the message that invoked the verb.

pActiveSite

[in] Points to the active client site of the embedding or linking container in which the verb was invoked.

lindex

[in] Reserved for future use; should always be zero.

hwndParent

[in] Handle of the document window containing the object.

lprcPosRect

[in] Points to the rectangle containing the coordinates of the bounding rectangle in which the destination document displays the object. This position is in container-window pixel coordinates with respect to hwndParent.

Return Values

Value	Meaning
S_OK	Object successfully invoked specified verb.
OLE_E_NOT_INPLACEACTIVE	iVerb set to OLEIVERB_UIACTIVATE or OLEIVERB_INPLACEACTIVATE and object is not currently open.
OLE_E_CANTBINDTOSOURCE	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 object cannot do it now.
OLEOBJ_S_INVALIDHWND	DoVerb was successful but hwnd 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_CONNECTMANUALLY	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 object does not recognize a negative verb number.
Remarks

Verbs are available on an object while it is selected in its container. DoVerb can be invoked from either an object's container or one of its currently connected link clients.

A container can determine the set of verbs available to an object by calling IOleObject::EnumVerbs. EnumVerbs returns an OLEVERB structure; DoVerb matches the value of iVerb against the iVerb member of the structure to determine which verb to perform.

Except for a set of verbs defined by OLE, the meaning of a particular verb is determined by the object's application. The following table describes the verbs defined by OLE:

Verb	Description
OLEIVERB_PRIMARY  (0L)	Verb that is invoked when the user double-clicks the object in its container. Object defines the semantics of the verb. If the object supports in-place activation, the primary verb usually activates the object in place.
OLEIVERB_SHOW (-1L)	Indicates that the object is to be shown to the user for editing or viewing. Used to show a newly-inserted object to the user for initial editing.
OLEIVERB_OPEN (-2L)	Causes the object to be open-edited in a separate window. If the object does not support open-editing, this verb has the same semantics as OLEIVERB_SHOW.
OLEIVERB_HIDE (-3L)	Causes the object to remove its user interface from the user's view.
OLEIVERB_UIACTIVATE (-4L)	Used to activate the object in place and show any user interface tools that it needs, such as menus or toolbars. If the object does not support in-place activation, it should return E_NOTIMPL.
OLEIVERB_INPLACEACTIVATE	Used to run the object and install its window, but not attempt to install user interface tools. In this state an inside-out style of object can take focus and then negotiate for menus and other tools. An outside-in style object returns E_NOTIMPL.
OLEIVERB_DISCARDUNDOSTATE	Used to tell objects to discard any undo state that they may be maintaining without deactivating the object.
Any positive verb number that is not understood by the object should be treated as synonymous with the primary verb and OLE_S_INVALIDVERB returned. Negative verbs that are not understood should be ignored and E_NOTIMPL returned.

If the verb was invoked by some means other than a menu selection, the caller passes the Windows message that caused the verb to be invoked through lpmsg. On a double-click, a MSG structure containing WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, or WM_RBUTTONDBLCLK should be passed. If there is no message, lpmsg should be NULL. The object should not examine the hwnd member of the passed MSG structure. All the other MSG members, however, can be used.

Like the OleActivate function in OLE 1, IOleObject::DoVerb automatically runs the object application (in OLE 1, a server). If an error occurs during verb execution, the object application is shut down.

There are two cases when DoVerb is called:

•	the embedding container makes the call

•	a link delegates its DoVerb call

In the first case, the client-site pointer passed to DoVerb is the same as the embedding site, the pointer passed with IOleObject::SetClientSite. In the second case, the passed client site is not the same as the embedding site.

When IOleObject::DoVerb is invoked on an OLE link, it may return OLE_E_CLASSDIFF or MK_CONNECTMANUALLY. The former error is returned when a link has been made to an object that has been subjected to some sort of conversion while the link was passive. The latter error is returned when the link source is located on a network drive that is not currently connected to the caller's machine. 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 in-place activation can still play multimedia players in place using the hwndParent and lprcPosRect parameters. Containers must pass valid hwndParent and lprcPosRect parameters to IOleObject::DoVerb.

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 it was originally obscured by another process. For more information about SetForegroundWindow and SetActiveWindow, see the Win32 SDK.

Example

Typical implementations of IOleObject::DoVerb contain a switch statement that tests the valid values for iVerb. The table below summarizes the cases covered in the switch statement implemented in the IOleObject::DoVerb example that follows. Notice that OLEIVERB_DISCARDUNDOSTATE is not a supported verb; it is treated as invalid.

Value of iVerb	Action Taken
Unknown negative value	Do nothing and return E_NOTIMPL.
Unknown positive value	Treat as primary verb and return OLEOBJ_S_INVALIDVERB.
OLEIVERB_SHOW	If object is already open in a window, make the window visible.  If object is not yet open and in-place activation is supported, initiate in-place activation. If object is not yet open and in-place activation is not supported, show object in a window.
OLEIVERB_OPEN	Start an open-editing session and, if necessary, in-place deactivate.
OLEIVERB_HIDE	If the object is active in place, remove the object's user interface.  If the object is active in an open window, hide the window.
OLEIVERB_UIACTIVATE OLEIVERG_INPLACEACTIVATE	If the window is not yet open, start an in-place activation session. If the window is already open, return OLE_E_NOT_INPLACEACTIVE.

HRESULT IOleObject_DoVerb(

IOleObject *	pThis,

LONG	lVerb,

LPMSG	lpmsg,

IOleClientSite	 *	pActiveSite,

LONG	lindex,

HWND	hwndParent,

LPCRECT	lprcPosRect

)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

LPAPPDOC lpAppDoc = (LPAPPDOC)lpServerDoc;

HRESULT hr = NOERROR;

switch (lVerb) {

default:

if (lVerb < 0) {

return E_NOTIMPL;

} else {

hr = OLEOBJ_S_INVALIDVERB;

}

// deliberately fall through to Primary Verb

case 0:

case OLEIVERB_SHOW:

if (lpServerDoc->m_lpClientSite

  && ! (IsWindowVisible(lpAppDoc->m_hWndDoc)

  && ! lpServerDoc->m_fInPlaceActive)){

DoInPlaceActivate(lpServerDoc, lVerb, lpmsg,

pActiveSite);

}

DocShowWindow(lpAppDoc);

break;

case 1:

case OLEIVERB_OPEN:

DoInPlaceDeactivate(lpServerDoc);

DocShowWindow(lpAppDoc);

break;

case OLEIVERB_HIDE:

if (lpServerDoc->m_fInPlaceActive) {

lpServerDoc->m_OleInPlaceObject->lpVtbl->UIDeactivate

((LPOLEINPLACEOBJECT)&

lpServerDoc->m_OleInPlaceObject);

else{

HideWindow(lpDoc, FALSE /*fShutdown*/);

}

break;

case OLEIVERB_UIACTIVATE:

 OLEIVERB_INPLACEACTIVATE:

//outside-in style of object

if (IsWindowVisible(lpAppDoc->m_hWndDoc) &&

                        ! lpServerDoc->m_fInPlaceActive ) {

hr = OLE_E_NOT_INPLACEACTIVE;

            } else {

hr = DoInPlaceActivate(

                        lpServerDoc, lVerb, lpmsg, pActiveSite);

if (SUCCEEDED(hr))

                    DocShowWindow(lpAppDoc);

}

break;

}

return hr;

}

void DocShowWindow(CDocOleObjectImpl *pDoc)

{

SetForegroundWindow(pdoc->m_hWndDoc);

ShowWindow(pDoc->m_hWndDoc, SW_SHOWNORMAL);

SetFocus(pDoc->m_hWndDoc);

}

See Also

IOleObject::EnumVerbs, OleRun, IOleLink::BindToSource


IOleObject::EnumVerbs

HRESULT IOleObject::EnumVerbs(ppEnumOleVerb)
IEnumOleVerb **ppEnumOleVerb

IOleObject::EnumVerbs enumerates the verbs available for an object in increasing order by verb number.

Parameters

ppEnumOleVerb

[out] Points to where the new enumerator should be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
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.
Remarks

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 other predefined verb), these will be the first verbs enumerated, since they have the lowest verb numbers.
lpszVerbName	Name of the verb. On Windows, this value is suitable for passing to AppendMenu (may have embedded ampersand characters that indicate accelerator keys). On the Macintosh, the following metacharacters may be included: • ! marks the menu item with the subsequent character • < sets the character style of the item • ( disables the item. The meta-characters / 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 the object's verb menu. All Flags defined in AppendMenu are supported except for: • MF_BITMAP • MF_OWNERDRAW • MF_POPUP
grfAttribs	Group of flag bits taken from the enumeration OLEVERBATTRIB. OLEVERBATTRIB_NEVERDIRTIES indicates that the execution of this verb can never cause the object to become dirty and require 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.
Note  For more information on the Windows AppendMenu function, see the Microsoft Win32 Software Development Kit.

See Also

IOleObject::DoVerb


IOleObject::Update

HRESULT IOleObject::Update()

IOleObject::Update ensures that any data or view caches maintained within the object are up-to-date.

Return Values

Value	Meaning
S_OK	All caches are up-to-date.
E_FAIL	An unspecified error occurred.
OLE_E_CANT_BINDTOSOURCE	Cannot run object to get updated data.
CACHE_E_NOCACHE_UPDATED	No caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED	Some caches were not updated.
Remarks

When applied to an OLE link object, IOleObject::Update first finds the link source and gets a new presentation from it. This process may involve running one or more object applications, which could be time-consuming.

With an embedded object, IOleObject::Update works recursively, calling IOleObject::Update on each of its linked and embedded objects and running the object if needed.

See Also

IOleObject::IsUpToDate


IOleObject::IsUpToDate

HRESULT IOleObject::IsUpToDate()

IOleObject::IsUpToDate recursively checks whether or not an object is up-to-date.

Return Values

Value	Meaning
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

A linked 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. In general, determining whether an object is out-of-date can be as expensive as actually updating the object. In these cases, IOleObject::IsUpToDate should return OLE_E_UNAVAILABLE rather than do a lengthy query. In those cases where the answer may be learned efficiently, the function can return either S_OK or S_FALSE.

See Also

IOleObject::UpDate


IOleObject::GetUserClassID

HRESULT IOleObject::GetUserClassID(pClsid)
CLSID *pClsid

IOleObject::GetUserClassID returns the class identifier of the object that corresponds to the type returned in IOleObject::GetUserType.

Parameters

pClsid

[out] Points to the class identifier (CLSID) to be returned.

Return Values

Value	Meaning
S_OK	CLSID returned successfully.
E_FAIL	An unspecified error occurred.
Remarks

GetUserClassID returns the CLSID as the user knows it. For embedded objects, this is always the CLSID that is persistently stored and is returned by IPersist::GetClassID. For linked objects, this is the CLSID of the last bound link source. If a Treat As operation is taking place, this is the CLSID of the application being emulated (also the CLSID that will be written into storage).

Example

The following IOleObject::GetUserClassID example sets pClsid to a different value depending on whether or not the object is being treated as an object of a different class. If a Treat As operation is in progress, the CLSID written in the storage of the object is returned. Otherwise, IOleObject::GetUserClassID returns the CLSID of the application.

HRESULT IOleObject_GetUserClassID(IOleObject pThis, CLSID * pClsid)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

if (! IsEqualCLSID(&lpServerDoc->m_clsidTreatAs, &CLSID_NULL))

*pClsid = lpServerDoc->m_clsidTreatAs;

else

*pClsid = CLSID_APP;

return S_OK;

}

See Also

IOleObject::GetUserType, IPersist::GetClassID


IOleObject::GetUserType

HRESULT IOleObject::GetUserType(dwFormOfType, pszUserType)
DWORD dwFormOfType
LPOLESTR *pszUserType

IOleObject::GetUserType determines the user-presentable identification (human-readable) string for an object's type, such as "Word Document." The information returned is the same as for IOleObject::GetUserClassID; GetUserClassID returns the information in binary form while GetUserType returns it in printable form.

Parameters

dwFormOfType

[in] A value that describes the form of the user-presentable string. Valid values are obtained from the USERCLASSTYPE enumeration:

typedef enum tagUSERCLASSTYPE {

USERCLASSTYPE_FULL = 1,
USERCLASSTYPE_SHORT = 2,
USERCLASSTYPE_APPNAME = 3

} USERCLASSTYPE;

USERCLASSTYPE_FULL is the full type name of the class.

USERCLASSTYPE_SHORT is a short name (maximum of 15 characters) that is used for popup menus and the Links dialog box.

USERCLASSTYPE_APPNAME is the name of the application servicing the class and is used in the Result text in dialogs.

pszUserType

[out] Points to the place where the address of type string will be placed. The caller must free lpszUserType using the current IMalloc instance. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	User type successfully returned.
OLE_S_USEREG	Delegate to the default handler's implementation using the registry to provide the requested information.
Remarks

The default handler's implementation of IOleObject::GetUserType uses the object's class identifier (pClsid returned by IOleObject::GetUserClassID) and dwFormOfType as a key into the registry. If an entry is found which matches the whole key, then the entry is returned. If only the CLSID part of the key is found, the lowest-numbered entry available (usually USERCLASSTYPE_FULL) 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.

An object application can delegate to the default handler by returning OLE_S_USEREG. If the user type name is an empty string, "Unknown Object" is returned.

Example

The following implementation of IOleObject::GetUserType calls the OLE helper function RegGetUserType to return the appropriate user type. If a the object is being treated as an object of a different class, the user type for the class stored in lpDoc->m_clsidTreatAs is returned. Otherwise, OleRegGetUserType returns the application's CLSID.

HRESULT IOleObject_GetUserType(

        IOleObject *            pThis,

        DWORD                   dwFormOfType,

        LPOLESTR *              pszUserType

)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

*pszUserType = NULL;

if ( (TreatAs) &&

 (! IsEqualCLSID  (&lpServerDoc->m_clsidTreatAs, &CLSID_NULL)) )

    	return OleRegGetUserType (

(REFCLSID)&lpServerDoc->m_clsidTreatAs,

dwFormOfType, pszUserType);

else

return OLE_S_USEREG;

}

See Also

IOleObject::SetHostNames, IOleObject::GetUserClassID, ReadFmtUserTypeStg


IOleObject::SetExtent

HRESULT IOleObject::SetExtent(dwDrawAspect, psizel)
DWORD dwDrawAspect
SIZEL  *psizel

IOleObject::SetExtent sets the rectangular limits (logical size) that an object has available in the container.

Parameters

dwDrawAspect

[in] Describes the aspect of the object whose limit is to be set; the value is obtained from the enumeration DVASPECT (see FORMATETC Data Structure). Different aspects of the object may have different extents. The most common value is DVASPECT_CONTENT.

psizel

[in] Specifies the size limit for the object.

Return Values

Value	Meaning
S_OK	Object has resized successfully.
E_FAIL	Object's size is fixed.
OLE_E_NOTRUNNING	Object is not running.
Remarks

Containers call IOleObject::SetExtent to inform objects how much space is available to them. If possible, the object should compose itself accordingly.

SetExtent may only be called when the object is running. Therefore, if a container resizes while the object is not running, the container must remember to inform the object by calling SetExtent at a later time when the object is running. If the OLEMISC_RECOMPOSEONRESIZE bit is set, the container should force the object to run before calling SetExtent.

If the object's size is fixed (that is, cannot be set by its container), SetExtent should return E_FAIL.

See Also

IOleObject::GetExtent, IViewObject2::GetExtent


IOleObject::GetExtent

HRESULT IOleObject::GetExtent(dwDrawAspect, psizel)
DWORD dwDrawAspect
SIZEL *psizel

IOleObject::GetExtent gets an object's current extent, the extent that the object will actually use. This call is made to the running object; to get the extent from the cache, IViewObject2::GetExtent is called.

Parameters

dwDrawAspect

[in] Describes the aspect of the object whose limit is to be retrieved; the value is obtained from the enumeration DVASPECT.

psizel

[out] Points to where the object's extent is to be returned.

Return Values

Value	Meaning
S_OK	Extent information successfully returned.
E_INVALIDARG	Invalid value for dwAspect.
The extent returned from IOleObject::GetExtent may differ from the one last set by IOleObject::SetExtent. To the object, IOleObject::SetExtent offers advice on what rectangle to use.

Example

The following implementation of IOleObject::GetExtent checks the value of dwDrawAspect. If dwDrawAspect is DVASPECT_CONTENT, the width and height of the entire document in himetric units is calculated and returned in psizel.

HRESULT IOleObject_GetExtent(

        IOleObject *            pThis,

        DWORD                   dwDrawAspect,

        SIZEL *                 psizel)

{

LPOLEDOC lpOleDoc =

(LPOLEDOC)((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

if (dwDrawAspect == DVASPECT_CONTENT	{

CalcExtentInHimetric (lpOleDoc, psizel);

return NOERROR;

}

else

return E_FAIL;

}

See Also

IOleObject::SetExtent, IViewObject2::GetExtent


IOleObject::Advise

HRESULT IOleObject::Advise(pAdvSink, pdwConnection)
IAdviseSink *pAdvSink
DWORD *pdwConnection

IOleObject::Advise sets up an advisory connection between the object and an advisory sink through which the sink can be informed of close, save, rename, and link source change events that happen in the object.

Parameters

pAdvSink

[in] Points to the advisory sink that should be informed of changes.

pdwConnection

[out] Points to a token that can be passed to IOleObject::Unadvise to delete the advisory connection.

Return Values

Value	Meaning
S_OK	Advisory connection set up successfully.
E_OUTOFMEMORY	Cannot set up the connection due to lack of memory.
Remarks

When an advisory sink registers for notifications using this call, the advisory sink will receive compound document (OLE) notifications by using a call to the appropriate method in the sink's implementation of the IAdviseSink and/or IAdviseSink2 interfaces.

Objects can take advantage of the functionality provided by IOleAdviseHolder interface, implemented by OLE, in their implementation of IOleObject::Advise, Unadvise, and EnumAdvise.

If an advisory connection is successfully set up, the callee returns a nonzero value through pdwConnection (if a connection fails to be established, zero is returned). The connection can be deleted by passing this nonzero token back to the object in a call to IOleObject::Unadvise.

Example

The following example is a typical implementation of IOleObject::Advise. IOleObject::Advise calls CreateOleAdviseHolder to create an instance of the advise holder, and if the creation is successful, calls IOleAdviseHolder::Advise to do the actual work.

HRESULT IOleObject_Advise(

        IOleObject *            pThis,

        IAdviseSink *           pAdvise,

        DWORD *                 pdwConnection)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

HRESULT hrErr;

if (lpDoc->m_lpOleAdviseHldr == NULL &&

CreateOleAdviseHolder(&lpServerDoc->m_lpOleAdviseHldr)

!= NOERROR){

hrErr = E_OUTOFMEMORY;

goto error;

}

hrErr = lpServerDoc->m_lpOleAdviseHldr->lpVtbl->Advise(

lpServerDoc->m_lpOleAdvisHldr,

lpAdvSink,

lpdwConnection

);

error:

return hrErr;

}

See Also

IOleObject::UnAdvise, IOleObject::EnumAdvise, IOleAdviseHolder::Advise


IOleObject::Unadvise

HRESULT IOleObject::Unadvise(dwConnection)
DWORD dwConnection

IOleObject::Unadvise deletes an advisory connection previously established with IOleObject::Advise.

Parameters

dwConnection

[in] Contains a nonzero value previously returned from IOleObject::Advise through its pdwConnection parameter.

Return Values

Value	Meaning
S_OK	Connection removed successfully.
E_FAIL	IOleAdviseHolder instance no longer available.
OLE_E_NOCONNECTION	dwConnection is not a valid connection ID.
Example

The implementation of IOleObject_Unadvise shown below delegates to its advise holder, calling IOleAdviseHolder::Unadvise to delete the connection.

HRESULT IOleObject_Unadvise(IOleObject pThis, DWORD dwConnection)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

HRESULT hrErr;

if (lpServerDoc->m_lpOleAdviseHldr != NULL){

hrErr = lpServerDoc->m_lpOleAdviseHldr->lpVtbl->Unadvise

(lpServerDoc->m_lpOleAdviseHldr, dwConnection);

} else {

hrErr = E_FAIL;

}

}

See Also

IOleObject::Advise, IOleObject::EnumAdvise


IOleObject::EnumAdvise

HRESULT IOleObject::EnumAdvise(ppenumAdvise)
IEnumSTATDATA **ppenumAdvise

IOleObject::EnumAdvise enumerates the advisory connections registered for an object.

Parameters

ppenumAdvise

[out] Points to where the new enumerator should be returned. NULL is a legal return value; it indicates that the object does not have any advisory connections.  If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator returned successfully.
E_FAIL	IOleAdviseHolder instance is no longer available.
E_NOTIMPL	EnumAdvise is not implemented.
Remarks

IOleObject::EnumAdvise enumerates items of type STATDATA:

typedef struct STRUCT tagSTATDATA

{

    FORMATETC FormatEtc;

    DWORD advf;

    LPADVISESINK pAdvise;

    DWORD dwConnection;

} STATDATA;

Only the pAdvise and dwConnection members of STATDATA are relevant for IOleObject::EnumAdvise.

While an enumeration is in progress, the effect of registering or revoking advisory connections on what is to be enumerated is undefined.

Example

The following example uses the advise holder to enumerate the object's connections. If the IOleAdviseHolder instance cannot be found, E_FAIL is returned; otherwise, the result of the call to IOleAdviseHolder::EnumAdvise is returned. Note that all out pointer parameters must be set to NULL.

HRESULT IOleObject_EnumAdvise(

        IOleObject              pThis,

        IEnumSTATDATA **        ppenumAdvise)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

*ppenumAdvise = NULL;

if (lpServerDoc->m_lpOleAdviseHldr != NULL){

return lpServerDoc->m_lpOleAdviseHldr->lpVtbl->EnumAdvise(

lpServerDoc->m_lpOleAdviseHldr, ppenumAdvise);

} else {

return E_FAIL;

}

}

See Also

IOleObject::Advise, IOleObject::UnAdvise


IOleObject::GetMiscStatus

HRESULT IOleObject::GetMiscStatus(dwAspect, pdwStatus)
DWORD dwAspect
DWORD *pdwStatus

IOleObject::GetMiscStatus returns object status information.

Parameters

dwAspect

[in] The aspect of the object against which status information is being requested. The value is obtained from the enumeration DVASPECT (see FORMATETC Data Structure).

pdwStatus

[out] Points to where the status information is returned. May not be NULL.

Return Values

Value	Meaning
S_OK	Information returned successfully.
OLE_S_USEREG	Delegate to the default handler implementation to retrieve the MiscStatus information from the registry.
CO_E_CLASSNOTREG	There is no CLSID registered for the object.
CO_E_READREGDB	Error accessing the registry.
Remarks

Objects store status information in the registry; the default handler's implementation of IOleObject::GetMiscStatus retrieves this information if the object is not running. If the object is running, the default handler invokes IOleObject::GetMiscStatus on the running object.

The information stored varies depending on the object; the status values that can be returned are taken from the enumeration OLEMISC:

typedef enum tagOLEMISC

{

    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_ACTIVATEWHENVISABLE           = 256,

    OLEMISC_RENDERINGISDEVICEINDEPENDENT  = 512,

} OLEMISC;

These values have the following meanings:

OLEMISC Value	Description
OLEMISC_RECOMPOSEONRESIZE	If true, signifies that when the size that the container allocates to the object changes, the object should have the opportunity to recompose  its picture. When resize occurs, the object is likely to do something other than scale its picture. The container should force the object to run so it can call IOleObject::GetExtent.
OLEMISC_ONLYICONIC	This object has no useful content view other than its icon. From the user's perspective, the Display As Icon checkbox (in the Paste Special dialog box) for this object should always be checked. Note that such an object should still have a drawable content aspect; it will look the same as its icon view.
OLEMISC_INSERTNOTREPLACE	Indicates that this is the kind of object that when inserted into a document should be inserted beside the selection instead of replacing it. An object which linked itself to the selection with which it was initialized would set this bit. Containers should examine this bit after they have initialized the object with the selection. See IOleObject::InitFromData.
OLEMISC_STATIC	Indicates that this object is a static object. See OleCreateStaticFromData.
OLEMISC_CANTLINKINSIDE	Indicates that this is the kind of object that should not be the link source that when bound to runs the object. That is, if when the object is selected, its container wishes to offer the Link Source format in a data transfer, then the link, when bound, must connect to the outside of the object. The user would see the object selected in its container, not open for editing. Some objects that do not want to implement being the source of a link when they are embedded may want to set this bit.
OLEMISC_CANLINKBYOLE1	Indicates that this object can be linked to by OLE 1 containers. This bit is used in the dwStatus field of the OBJECTDESCRIPTOR structure transferred with the Object and Link Source Descriptor formats. An object can be linked by OLE 1 if it is not an embedded object or pseudo object contained within an embedded object.
OLEMISC_ISLINKOBJECT	This object is a link object. This bit is significant to OLE 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_ACTIVATEWHEN VISIBLE	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_RENDERINGIS DEVICEINDEPENDENT	This object does not pay any attention to target devices. Its presentation data will be the same in all cases.
Example

Two different implementations of IOleObject::GetMiscStatus are provided below. The first implementation is from an object application. It returns information about whether the object can be linked to by an OLE 1 container and the type of in-place activation the object prefers. 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.

HRESULT IOleObject_GetMiscStatus(

        IOleObject *            pThis,

        DWORD                   dwAspect,

        DWORD *                 pdwStatus)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

LPAPPDOC lpAppDoc = (LPAPPDOC)lpServerDoc;

*pdwStatus = 0;

if (lpAppDoc->m_docInitType == DOCTYPE_NEW ||

lpAppDoc->m_docInitType == DOCTYPE_FROMFILE)

*pdwStatus |= OLEMISC_CANLINKBYOLE1;

if (dwAspect == DVASPECT_CONTENT)

*pdwStatus |= (OLEMISC_INSIDEOUT | OLEMISC_ACTIVATEWHENVISIBLE);

return NOERROR;

}

The second implementation is also from an object application and illustrates how to delegate to the default handler's implementation.

HRESULT IOleObject_GetMiscStatus(

        IOleObject              pThis,

        DWORD                   dwAspect,

        DWORD *                 pdwStatus)

{

LPSERVERDOC lpServerDoc =

((struct CDocOleObjectImpl *)pThis)->lpServerDoc;

*pdwStatus = 0;

// Tell OLE to get information from registry

return OLE_S_USEREG;

}


IOleObject::SetColorScheme

HRESULT IOleObject::SetColorScheme(pLogpal)
LOGPALETTE *pLogpal

IOleObject::SetColorScheme specifies the color palette that the object application should use when it edits the specified object.

Parameters

pLogpal

[in] Points to a LOGPALETTE structure that specifies the recommended palette.

Return Values

Value	Meaning
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

IOleObject::SetColorScheme sends the object's application the color palette recommended by the container, although object applications are not required to use the recommended palette. The object application should:

1.	Allocate and fill in it own LOGPALETTE structure that contains the colors passed by means of lpLogPal.

2.	Create a palette from the application-defined LOGPALETTE structure using CreatePalette. 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 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. When there is an uneven number of entries, the server should round up to the fill colors; that is, if there are five entries, the first three should be interpreted as fill colors and the last two as line and text colors.


IRunnableObject Interface

The IRunnableObject interface, implemented by object handlers and DLL object applications, is one way that handlers and DLL object applications find out when to transition into the running state and when to become a contained object. Although the IRunnableObject interface is not specifically required for in-process servers outside of linking to embeddings, it is useful to objects to initialize information about the running state before other calls occur. This interface also supports the handling of silent updates.

In the running state, the DLL object application must keep a lock on the container (by calling IOleContainer::LockContainer), just as the default object handler does. This is necessary to make linking to embedded objects work correctly.

The IRunnableObject interface contains the following methods:

DECLARE_INTERFACE_(IRunnableObject, IUnknown)

{

HRESULT (GetRunningClass) (LPCLSID pclsid);

HRESULT (Run) (LPBC lpbc);

BOOL (IsRunning) ();

HRESULT (LockRunning) (BOOL flock, BOOL fLastUnlockCloses);

HRESULT (SetContainedObject) (BOOL fContained);

}


IRunnableObject::GetRunningClass

HRESULT IRunnableObject::GetRunningClass(pClsid)
LPCLSID lpClsid;

IRunnableObject::GetRunningClass returns the CLSID of the embedded object.

Parameters

pClsid

[out] Points to where to return the CLSID of the object.

Return Values

Value	Meaning
S_OK	CLSID was returned successfully.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

In cases where the object is being emulated as a different object class (through a call to CoTreatAsClass), the CLSID returned is that of the object that is actually running. For example, suppose that ABC is emulating an XYZ object; calling IRunnableObject::GetRunningClass on XYZ returns the CLSID of ABC.

See Also

CoTreatAsClass


IRunnableObject::Run

HRESULT IRunnableObject::Run(pbc)
LPBINDCTX pbc

IRunnableObject::Run puts an object in its running state.

Parameters

pbc

[in] Points to the bind context for the run operation; may be NULL.

Return Values

Value	Meaning
S_OK	The object was successfully put into its running state.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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.

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 with a server EXE. In-process server object implementations (objects implemented by means of a server DLL) must explicitly put a lock on their embedding container. This is done by first calling IOleClientSite::Getcontainer to get a pointer to an IOleContainer and then calling IOleContainer::LockContainer to place a lock on the container. This lock must be released when IOleObject::Close is called.

When called on a linked object, IRunnableObject::Run may return OLE_E_CLASSDIFF in cases when the link has been made to an object that has been converted to a new class of object since the link was last activated. In this case, if the client wants to continue, it should call IOleLink::BindToSource before proceeding.

OleRun is a helper function that wraps the functionality offered by this method. Its implementation is to call QueryInterface, asking for IRunnableObject, followed by a call to IRunnableObject::Run.

See Also

OleRun


IRunnableObject::IsRunning

BOOL IRunnableObject::IsRunning()

IRunnableObject::IsRunning determines whether an object is currently in the running state.

Return Values

Value	Meaning
TRUE	The object is in its running state.
FALSE	The object is not in its running state.
Remarks

OleIsRunning is a helper function that wraps the functionality offered by this method. Its implementation is to call QueryInterface, asking for IRunnableObject, followed by a call to IRunnableObject::IsRunning.

See Also

OleIsRunning


IRunnableObject::LockRunning

HRESULT IRunnableObject::LockRunning(fLock, fLastUnlockCloses)
BOOL fLock
BOOL fLastUnlockCloses

IRunnableObject::LockRunning locks an already-running object into its running state or unlocks it from its running state.

Parameters

fLock

[in] TRUE locks the object into its running state, FALSE unlocks the object from its running state.

fLastUnlockCloses

[in] Specifies how the object should be closed. TRUE closes the object if this is the last unlock on the object; FALSE leaves the object in its locked state.

Return Values

Value	Meaning
S_OK	The object was either locked or unlocked, depending on the value of fLock.
E_FAIL	The object was not running.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Most implementations of IRunnableObject::LockRunning will call CoLockObjectExternal.

See Also

CoLockObjectExternal


IRunnableObject::SetContainedObject

HRESULT IRunnableObject::SetContainedObject(fContained)
BOOL fContained

IRunnableObject::SetContainedObject indicates that the object is contained as an embedding in an OLE container.

Parameters

fContained

[in] TRUE specifies that the object is contained in an OLE container; FALSE that it is not.

Return Values

Value	Meaning
S_OK	Object has been marked as a contained embedding.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

This method indicates that the object is contained as an embedded object. The method is called with fContained set to TRUE by embedding containers after calling OleLoad, or OleCreate. Generally, embedding containers call IRunnableObject::SetContainedObject with fContained set to TRUE once and never call it again, even before they close. Consequently, the use of this method with fContained set to FALSE is rare.

By default, all external connections to an object are strong reference locks in that the external connection controls the lifetime of the object. Calling IRunnableObject::SetContainedObject on a strong external connection transitions the strong lock to a weak lock, allowing other connections to determine the fate of the object while it is invisible. When an object is visible, it is controlled by OleNoteObjectVisible(pUnk, TRUE) or CoLockObjectExternal.

OleSetContainedObject is a helper function that wraps the functionality offered by this method.

See Also

OleSetContainedObject, OleNoteObjectVisible

CoLockObjectExternal


Compound Document Functions

The functions described in this section are used by container and object applications to manage and send OLE notifications and to control the state of a compound document object. These functions are defined as follows:

HRESULT  CreateOleAdviseHolder(LPOLEADVISEHOLDER * ppOAHolder);

BOOL  OleIsRunning(LPOLEOBJECT pOleObject);

HRESULT  OleRun(LPUNKNOWN pUnk);

HRESULT  OleLockRunning(LPUNKNOWN pUnk, BOOL fLock, BOOL fLastUnlockCloses);

HRESULT  OleNoteObjectVisible(LPUNKNOWN pUnk, BOOL fVisible);

HRESULT  OleSetContainedObject(LPUNKNOWN pUnk, BOOL fContained);


CreateOleAdviseHolder

HRESULT CreateOleAdviseHolder(ppOAHolder)
LPOLEADVISEHOLDER  *ppOAHolder

CreateOleAdviseHolder returns an instance of an OLE-provided implementation of the IOleAdviseHolder interface.

Parameters

ppOAHolder

Points to where to return the new IOleAdviseHolder instance.

Return Values

Value	Meaning
S_OK	IOleAdviseHolder instance returned successfully.
E_OUTOFMEMORY	Cannot return the IOleAdviseHolder instance because there is not enough memory.
Remarks

CreateOleAdviseHolder is called to return an IOleAdviseHolder instance. If the call fails, E_OUTOFMEMORY is returned. If the call succeeds, S_OK is returned. As in 16-bit OLE, the IOleAdviseHolder instance returned by CreateOleAdviseHolder does not support the IOleAdviseHolder::EnumAdvise method.

Example

The following example illustrates a typical use of CreateOleAdviseHolder. In an object application's implementation of IOleObject::Advise.

hrErr = CreateOleAdviseHolder(&lpObj->m_lpOleAdviseHldr);

if (hrErr != NOERROR)

return E_OUTOFMEMORY;

else

return S_OK;


OleIsRunning

BOOL OleIsRunning(pObject)
LPOLEOBJECT pObject

OleIsRunning determines whether an object is running.

Parameters

pObject

Points to the object in question.

Return Values

Value	Meaning
TRUE	Object is running.
FALSE	Object is not running.
Remarks

OleIsRunning is a helper function that wraps the functionality offered by IRunnableObject::IsRunning and can be called in place of that method.

See Also

IRunnableObject::IsRunning


OleRun

HRESULT OleRun(pUnknown)
LPUNKNOWN pUnknown

OleRun places an object into the running state if it is not already running.

Parameters

pUnknown

Points to the object that should be made running.

Return Values

Value	Meaning
S_OK	The object was successfully placed in the running state.
OLE_E_CLASSDIFF	Source of an OLE link has been converted.
Remarks

Calling OleRun can be expensive if the object is not running, because an object application must be launched. If the object is already running, a call to OleRun has no effect.

When invoked on an OLE link object, OleRun may return OLE_E_CLASSDIFF, which indicates that the link source has been converted while the link has been passive. The caller can invoke IOleLink::BindToSource to proceed in this case. To do so, the caller must call IOleLink::BindToSource with the flags set to OLELINKBIND_EVEN IFCLASSDIFF. This means calling QueryInterface on the default link object for IOleLink manually.

OleRun is a helper function that wraps the functionality offered by IRunnableObject::Run and can be called in place of that method.

See Also

IRunnableObject::Run


OleLockRunning

HRESULT OleLockRunning(pUnknown, fLock, fLastUnlockCloses)
LPUNKNOWN pUnknown
BOOL fLock
BOOL fLastUnlockCloses

OleLockRunning locks an object into its running state or unlocks it from its running state.

Parameters

pUnknown

Points to the object that is to be locked or unlocked.

fLock

TRUE locks the object into its running state, FALSE unlocks the object from its running state.

fLastUnlockCloses

Specifies how the object should be closed. TRUE closes the object if this is the last unlock on the object; FALSE leaves the object in its locked state.

Return Values

Value	Meaning
S_OK	The object was either locked or unlocked, depending on the value of fLock.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

OleLockRunning is a helper function that wraps the functionality offered by IRunnableObject::LockRunning and can be called in place of that method.

See Also

IRunnableObject::LockRunning


OleNoteObjectVisible

HRESULT OleNoteObjectVisible(pUnknown, fVisible)
LPUNKNOWN pUnknown
BOOL fVisible

OleNoteObjectVisible locks a visible object such that its reference count cannot decrement to zero; and releases such a lock.

Parameters

pUnknown

Points to the object that is to be locked or unlocked.

fVisible

Indicates whether the object is visible. If TRUE, OLE holds the object visible and alive regardless of external or internal AddRef and Release operations, registrations, or revokes. If FALSE, OLE releases its hold and the object can lose visibility.

Return Values

Value	Meaning
S_OK	Object locked or unlocked successfully.
E_OUTOFMEMORY	Lock/unlock operation could not be completed due to lack of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	Lock/unlock operation could not be completed for reasons other than lack of memory or invalid arguments.
Remarks

OleNoteObjectVisible is a wrapper for a call to CoLockObjectExternal(pUnk, fVisible, TRUE). It is provided as a separate API to reinforce the need to lock an object when it becomes visible to the user and to release the object when it becomes invisible. Sophisticated objects may exercise the option to call CoLockObjectExternal directly.

See Also

CoLockObjectExternal


OleSetContainedObject

HRESULT OleSetContainedObject(pUnknown, fContained)
LPUNKNOWN pUnknown
BOOL fContained

OleSetContainedObject indicates that the object is contained as an embedding and facilitates linking to embedded objects.

Parameters

pUnknown

Points to the object that is a contained embedding.

fContained

TRUE if the object is an embedded object; FALSE otherwise.

Return Values

Value	Meaning
S_OK	Object has been marked as a contained embedding.
E_OUTOFMEMORY	Object could not be marked as a contained embedding due to lack of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error happened.
Remarks

Usually, containers call OleSetContainedObject with fContained set to TRUE one time, either after creating the object initially or after loading it, and never call it again, even before they close. Consequently, the use of OleSetContainedObject with fContained set to FALSE is rare.

Calling OleSetContainedObject 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 OleSetContainedObject. However, if your application does support linking to embedded objects, it is necessary to call this function. If the container application does not call this function on the object, the container will not shut down properly when another linking client does a silent update, such as when the user chooses Update Now in the Links dialog box. The reference from the container to the embedded object provided by OleSetContainedObject will keep the object alive and not let it shut down during a silent update.

Any external connection(s) to the embedded object start out strong (locked). Becoming a contained object weakens (unlocks) the connection(s) so that other connections can determine the fate of the object while it is visible.

OleSetContainedObject is a helper function that wraps the functionality offered by IRunnableObject::SetContainedObject and can be called in place of that method.

See Also

IRunnableObject::SetContainedObject


Object Creation Functions

OLE provides several functions that create or aid in the creation of compound document objects of different types. These functions are defined as follows:

HRESULT  OleCreate(REFCLSID rclsid, REFIID riid, DWORD renderopt, LPFORMATETC pFormatetc,

    LPOLECLIENTSITE pClientSite,  LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateDefaultHandler(REFCLSID rclsid, LPUNKNOWN pUnkOuter, REFIID riid,

    LPVOID * ppvObj);

HRESULT  OleCreateEmbeddingHelper(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD flags,

    LPCLASSFACTORY pCF, REFIID riid, LPVOID * ppvObj);

HRESULT  OleCreateFromData(LPDATAOBJECT pDataObject, REFIID riid, DWORD renderopt,

    LPFORMATETC pFormatetc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateFromFile(REFCLSID rclsid, LPCOLESTR lpszFileName, REFIID riid, DWORD renderopt,

    LPFORMATETC lpFormatetc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateLink(LPMONIKER pmk, REFIID riid, DWORD renderopt, LPFORMATETC lpFormatetc,

    LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateLinkFromData(LPDATAOBJECT pDataObject, REFIID riid, DWORD renderopt,

    LPFORMATETC pFormatetc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateLinkToFile(LPCOLESTR lpszFileName, REFIID riid,  DWORD renderopt,

    LPFORMATETC lpFormatetc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleCreateStaticFromData(LPDATAOBJECT pDataObject, REFIID iid, DWORD renderopt,

    LPFORMATETC pFormatetc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID * ppvObj);

HRESULT  OleQueryLinkFromData(LPDATAOBJECT pDataObject);

HRESULT  OleQueryCreateFromData(LPDATAOBJECT pDataObject);


Object Creation Parameters

Many of the object creation functions have parameters in common. The following table defines these parameters as they are used in the majority of the functions. For each function where the usage is different, additional comments are included with the description of the function.

Parameter	Description
renderopt	Indicates the caching that the container wants in the newly created object.
pDataObject	Points to the data transfer object from which a compound document object is to be created.
pFormatetc	Points to the data formats and medium information that control the caching to be done in the newly created object.
riid	Contains the interface to be used to communicate with the new object. The most common value is IID_IOleObject.
pClientSite	Points to the client site. May be NULL. If NULL, the caller must call IOleObject::SetClientSite before attempting any operations. If non-NULL, OleCreate calls IOleObject::SetClientSite before returning.
ppvObj	Points to the place at which the newly created instance of riid should be returned.
pStg	Points to the storage that will be used for the object. May not be NULL.
Valid values for the renderopt parameter are taken from the enumeration OLERENDER:

typedef enum tagOLERENDER

{

    OLERENDER_NONE   = 0,

    OLERENDER_DRAW   = 1,

    OLERENDER_FORMAT = 2,

    OLERENDER_ASIS   = 3

} OLERENDER;

For most of the creation functions, these values have the following semantics:

Value	Meaning
OLERENDER_NONE	The container is not requesting any locally cache drawing or data retrieval capabilities in the object. pFormatetc is ignored for this option.
OLERENDER_DRAW	The container will draw the content of the object on the screen (a NULL target device) using IViewObject::Draw. The object determines the data formats that need to be cached. 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 container 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 container is not requesting any locally cache drawing or data retrieval capabilities in the object. pFormatetc is ignored for this option. The difference between this and OLERENDER_NONE  is important in other helper functions such as OleCreateFromData and OleCreateLinkFromData.
Some of the API functions have different or additional semantics for these values. Appropriate comments are included with each function description.


OleCreate

HRESULT OleCreate(rclsid, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj)
REFCLSID rclsid
REFIID riid
DWORD renderopt
LPFORMATETC pFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreate creates an embedded object of a specified class. This method is typically used in implementing the Insert New Object scenario.

Parameters

rclsid

Identifies the class of the embedded object to be created.

The riid, renderopt, pFormatetc, pClientSite, lpStg, and ppvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
S_OK	Embedded object created successfully.
E_OUTOFMEMORY	Embedded object cannot be created due to lack of memory.
Remarks

When OleCreate returns, the object is blank and in the loaded state. Containers typically then call OleRun or IOleObject::DoVerb to show the object for initial editing.

The cache is not necessarily filled by the time that OleCreate returns. Instead, the cache is filled as appropriate for the passed renderopt and pFormatetc the first time the object enters the running state. Between the return of OleCreate and a subsequent running of the object, the caller may add additional cache nodes with a call to IOleCache::Cache.


OleCreateDefaultHandler

HRESULT OleCreateDefaultHandler(clsid, pUnkOuter, riid, lplpvObj)
REFCLSID clsid
LPUNKNOWN pUnkOuter
REFIID riid
LPVOID * lplpvObj

OleCreateDefaultHandler creates a new instance of the default handler to service the specified rclsid.

Parameters

clsid

ID of object class to be loaded.

pUnkOuter

Points to the controlling IUnknown if the handler is to be aggregated; NULL if it is not to be aggregated.

The riid and lplpvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
S_OK	Default handler created successfully.
E_OUTOFMEMORY	Default handler cannot be created due to lack of memory.
Remarks

Calling OleCreateDefaultHandler is the same as calling CoCreateInstance(rclsid, pUnkOuter, CLSCTX_INPROC_HANDLER, riid, ppvObj) if the given class does not have a special object class handler.

OleCreateDefaultHandler is used internally when the rclsid is not registered and by handler writers that want to use the services of the default handler.


OleCreateEmbeddingHelper

HRESULT OleCreateEmbeddingHelper(clsid, pUnkOuter, flags, pCF, riid, lplpvObj)
REFCLSID clsid
LPUNKNOWN pUnkOuter
DWORD flags
LPCLASSFACTORY pCF
REFIID riid
LPVOID * lplpvObj

OleCreateEmbeddingHelper creates an 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.

Parameters

clsid

Specifies the CLSID of the object class to be helped.

pUnkOuter

Points to the controlling IUnknown if the handler is to be aggregated; NULL if it is not to be aggregated.

flags

Specifies the role and creation context for the embedding helper; see the following comments for legal values.

pCF

Points to the application's IClassFactory instance; can be NULL.

riid

Specifies the interface ID desired by the caller.

lplpvObj

Points to where to return the newly created embedding helper.

Return Values

Value	Meaning
S_OK	Embedding helper was created successfully.
E_NOINTERFACE	Interface not supported by the object.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

OleCreateEmbeddingHelper is used to create a new instance of the OLE default handler that can be used to support objects in various roles. The caller passes to OleCreateEmbeddingHelper a pointer to its IClassFactory implementation; this object and the default handler are then aggregated to create the new embedding helper object.

OleCreateEmbeddingHelper is usually used to support one of the following implementations:

•	An object application is being used as both a container and a server, and the application supports inserting objects into itself. In this case, the application will register its CLSID for different contexts (CoRegisterClassObject (Clsid, lpUnk, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER, REGCLS_MULTI_SEPARATE, lpdwRegister)). 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.

•	An in-process object handler. In this case, the DLL creates the embedding helper by passing in a private class factory 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	EMBDHLP_INPROC_HANDLER creates an embedding helper that is used with an EXE object application; specifying this flag is the same as calling OleCreateDefaultHandler.
EMBDHLP_INPROC_SERVER	EMBDHLP_INPROC_SERVER 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_CREATENOW	EMBDHLP_CREATENOW creates the application-supplied piece immediately.
EMBDHLP_DELAYCREATE	EMBDHLP_DELAYCREATE delays creating the application-supplied piece until the object is put into its running state.
Calling

OleCreateEmbeddingHelper
(rclsid, pUnkOuter, EMBDHLP_INPROC_HANDLER | EMBDHLP_CREATENOW,
 	NULL, riid, ppvObj)

is the same as calling

OleCreateDefaultHandler(rclsid, pUnkOuter, riid, ppvObj)

See Also

OleCreateDefaultHandler


OleCreateFromData

HRESULT OleCreateFromData(pSrcDataObj, riid, renderopt, pFormatetc, pClientSite, pStg, ppvObj)
LPDATAOBJECT pSrcDataObj
REFIID riid
DWORD renderopt
LPFORMATETC pFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateFromData creates an embedded object from a data transfer object retrieved either from the Clipboard or as part of a drag-and-drop operation.

Parameters

pSrcDataObj

Points to the data transfer object that holds the data from which the object will be created.

riid

Identifies the interface that the caller is to use to communicate with the new object.

renderopt

Indicates the type of caching desired for the newly created object. The use of renderopt is as defined in the Object Creation Parameters section, with the following additional comments that relate to how the new object's cache is initialized:

Value	Meaning
OLERENDER_DRAW and OLERENDER_FORMAT	If the format to be cached is currently present in the appropriate cache initialization pool (the old object for the Embedded Object format, the other formats in pDataObject for Embed Source), then it is used. If the format is not present, then 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	The newly created object has nothing cached. In the Embedded Object case, any copied existing cached data is removed.
OLERENDER_ASIS	In the Embedded Object case, the new object has exactly those things cached within itself that the original old source object had cached within itself. In the Embed Source case, the newly created object has nothing cached. The idea is that this option is to be used by more sophisticated containers. After this call, such containers will call IOleCache::Cache and IOleCache::Uncache calls to set up exactly what they want cached. In the Embed Source case, they will then also call IOleCache::InitCache.
The riid, pClientSite, pStg, pFormatetc, and ppvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
S_OK	Embedded object created successfully.
OLE_E_STATIC	OLE can only create a static object.
DV_E_FORMATETC	No acceptable formats are available for object creation.
Remarks

The renderopt and pFormatetc parameters can be used by the caller to control the caching that is done in the newly created object. The interaction between renderopt and pFormatetc in determining what is to be cached is described in the OleCreate function. OleCreateFromData makes use of either the Embedded Object or the Embed Source data formats in pDataObject to create the object. (For more information on these Clipboard formats, refer Data Transfer/Caching Interfaces and Functions.)

The main difference between these two formats is where the appropriate cache-initialization data lies. In the Embedded Object case, the source is an existing embedded object; the cache inside the object itself has the appropriate data. In the Embed Source case, the formats available in pDataObject, other than Embed Source, should initialize the cache.

If either the FileName or FileNameW Clipboard format is present in the data transfer object, OleCreateFromData creates a package containing the indicated file. These formats are placed on the Clipboard by the Microsoft Windows NT File Manager when the user selects the File Copy To menu command. If a package is not made, OleCreateFromData tries to create an object using the Embedded Object format if it is available. If not, use the Embed Source format. If neither of these formats is available and the data transfer object supports the IPersistStorage interface, OleCreateFromData calls IPersistStorage::Save to ask the object to save itself.


OleCreateFromFile

HRESULT OleCreateFromFile(rclsid, lpszFileName, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj)
REFCLSID rclsid
LPCOLESTR lpszFileName
REFIID riid
DWORD renderopt
LPFORMATETC pFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateFromFile creates an embedded object of the indicated class from the contents of a named file. The newly created object is not shown to the user for editing.

Parameters

rclsid

Reserved for future use. Must be CLSID_NULL.

lpszFile

Specifies the full path of the file from which the object should be initialized.

The riid, renderopt, pFormatEtc, pClientSite, pStg, and ppvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
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.
E_OUTOFMEMORY	OLE libraries have not been properly initialized.
Remarks

OleCreateFromFile is used in the Insert File operation of the Insert Object dialog box. It creates either a package if the ProgID in the registry contains the PackageOnFileDrop key or an OLE 2 embedded object as appropriate for the class of the file (see GetClassFile).

Unless it can manage with no conversion of representation, this function uses IPersistFile::Load on a new instance of the class rclsid object to open the file. It then calls QueryInterface to access IPersistStorage and calls IPersistStorage::Save(..., false, true) to indicate to the object that it should convert representations.


OleCreateLink

HRESULT OleCreateLink(pmkLinkSrc, riid, render opt, pFormatetc, pClientSite, pStg, ppvObj)
LPMONIKER pmkLinkSrc
REFIID riid
DWORD renderopt
LPFORMATETC lpFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateLink creates an OLE compound-document link object. The source of the link is initialized with pmk.

Parameters

pmk

Points to an IMoniker interface instance indicating the linked object's source.

The riid, renderopt, lpFormatEtc, pClientSite, pStg, and ppvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
S_OK	The compound-document link object was created successfully.
OLE_E_CANT_BINDTOSOURCE	Not able to bind to source. Binding is necessary to get cache's initialization data.
Remarks

When a container creates a linked object, it should assign a moniker to the newly created link and inform the link object of the moniker using IOleObject::SetMoniker (OLEWHICHMK_OBJREL, ...). If the container doesn't already have a moniker, it should obtain one by requesting its client site with IOleClientSite::GetMoniker (OLEGETMONIKER_FORCEASSIGN, ...). Once obtained, the container should inform the link of this moniker with IOleObject::SetMoniker (OLEWHICHMK_CONTAINER, ...).

In the event that the client site fails to give a moniker to the link's container, the container should assign a moniker to the link object that is relative to the link's container. This moniker is needed for link tracking to work: all link objects need to have monikers assigned to generate relative moniker paths.


OleCreateLinkFromData

HRESULT OleCreateLinkFromData(pSrcDataOb, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj)
LPDATAOBJECT pSrcDataObj
REFIID riid
DWORD renderopt
LPFORMATETC pFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateLinkFromData creates a linked object from a data transfer object retrieved either from the Clipboard or as part of a drag-and-drop operation.

Parameters

pSrcDataObj

Points to the data transfer object from which the linked object is to be created.

The riid, renderopt, pFormatEtc, pClientSite, pStg, and ppvObj parameters are described in the Object Creation Parameters section.

Return Values

Value	Meaning
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 object's moniker.
OLE_E_CANT_BINDTOSOURCE	Not able to bind to source. Binding is necessary to get cache's initialization data.
Remarks

OleCreateLinkFromData is similar in function to OleCreateFromData in that it creates a compound document object from a data transfer object. However, where OleCreateFromData uses the Embedded Object or Embed Source formats from the data transfer object, OleCreateLinkFromData uses the Link Source format. From the perspective of cache initialization, the Link Source format behaves like Embed Source.

If the FileName format is available, OleCreateLinkFromData creates a package containing a link to the indicated file. If a package cannot be made, OleCreateLinkFromData looks for the Link Source format with which to create a linked object.

See Also

OleCreateLink


OleCreateLinkToFile

HRESULT OleCreateLinkToFile(lpszFileName, riid, renderopt, pFormatetc, pClientSite, pStg, ppvObj)
LPCOLESTR lpszFileName
REFIID riid
DWORD renderopt
LPFORMATETC lpFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateLinkToFile creates an object that is linked to a file.

Parameters

lpszFileName

Points to the source file to be linked to.

The riid, renderopt, lpFormatEtc, pClientSite, pStg, and ppvObj parameters are described in Object Creation Parameters.

Return Values

Value	Meaning
S_OK	The object was created successfully.
STG_E_FILENOTFOUND	Filename is invalid.
OLE_E_CANT_BINDTOSOURCE	Not able to bind to source.
Remarks

OleCreateLinkToFile can create links to files that can't detect OLE, using the Windows Packager.


OleCreateStaticFromData

HRESULT OleCreateStaticFromData(pSrcDataObj, riid, renderopt, pFormatEtc, pClientSite, pStg, ppvObj)
LPDATAOBJECT pSrcDataObj
REFIID riid
DWORD renderopt
LPFORMATETC pFormatEtc
LPOLECLIENTSITE pClientSite
LPSTORAGE pStg
LPVOID * ppvObj

OleCreateStaticFromData creates a static object from a data transfer object.

Parameters

pSrcDataObj

Points to the data transfer object from which the object is to be created.

The riid, renderopt, pFormatEtc, pClientSite, pStg, and ppvObj parameters are described in Object Creation Parameters. However, it is an error to pass OLERENDER_NONE and OLERENDER_ASIS in renderopt.

Return Values

Value	Meaning
S_OK	The static object was created successfully.
Remarks

Any object that provides an IDataObject interface can be converted to a static object using OleCreateStaticFromData. OleCreateStaticFromData is useful in implementing the Convert To Picture option for a link.

Static objects can be created only if the source supports one of the OLE rendered formats (ENHANCED METAFILE / METAFILEPICT / DIB / BITMAP).

Static objects can be pasted from the clipboard using OleCreateStaticFromData. OleQueryCreateFromData will return OLE_S_STATIC if either CF_METAFILEPICT, CF_DIB, or CF_BITMAP is present and an OLE format is not present. But OleCreateFromData will not automatically create the static object in this case, if the container wants to paste a static object it should call OleCreateStaticFromData.

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 will set the OLEMISC_STATIC and OLE_CANTLINKINSIDE bits returned from IOleObject::GetMiscStatus. The static object will have the aspect DVASPECT_CONTENT and an lindex of -1.

The pDataObject will still be 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.

See Also

OleCreateFromData


OleQueryCreateFromData

HRESULT OleQueryCreateFromData(pSrcDataObject)
LPDATAOBJECT pSrcDataObject

OleQueryCreateFromData checks whether a container application can create an embedded object from the given data transfer object.

OleQueryCreateFromData tests for the presence of the following formats in the data object:

•	Embedded Object
Embed Source
cfFileName
CF_METAFILEPICT
CF_DIB
CF_BITMAP

Parameters

pSrcDataObject

Points to the data transfer object that will be queried.

Return Values

Value	Meaning
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

Containers should use OleQueryCreateFromData with the data transfer object retrieved with OleGetClipboard as part of the process of deciding to enable or disable their Edit/Paste or Edit/Paste Special... commands. OleCreateFromData or OleCreateStaticFromData is used to actually create the object.

OleQueryCreateFromData returns OLE_S_STATIC when either CF_METAFILEPICT, CF_BITMAP, or CF_DIB are available and none of the other OLE formats are offered.

OleQueryCreateFromData does not test for the presence of these formats on the clipboard; it looks for the specified formats in the data transfer object. A successful return from OleQueryCreateFromData does not guarantee a subsequent successful return from OleCreateFromData or OleCreateStaticFromData.


OleQueryLinkFromData

HRESULT OleQueryLinkFromData(pSrcDataObject)
LPDATAOBJECT pSrcDataObject

OleQueryLinkFromData operates in a manner similar to OleQueryCreateFromData, but determines whether a linked object (rather than an embedded object) can be created from the data transfer object.

Parameters

pSrcDataObject

Points to the data transfer object that is to be created.

Return Values

Value	Meaning
S_OK	Formats that support linked object creation are present.
S_FALSE	Formats that support embedded object creation are not present.


OLE Initialization Functions

The following functions are used to manage the OLE2.DLL:

DWORD OleBuildVersion();	// Obsolete in 32-bit OLE

HRESULT OleInitialize(LPMALLOC pMalloc);

void OleUninitialize(void);


OleBuildVersion

DWORD OleBuildVersion()

Obsolete in 32-bit OLE. In 16-bit OLE, OleBuildVersion returns the major and the minor version number of the OLE library, OLE2.DLL.

Return Values

Value	Meaning
32-bit integer	The 16 high-order bits are the major build number; the 16 low-order bits are the minor build number.
Remarks

As stated above, this function is obsolete in 32-bit OLE. However, 16-bit applications can be compiled to run against only one released major build version of the OLE library while they are using any of the incremental minor build versions. For example, an application that was compiled against build version 17.0 of the library would safely run against 17.0 and any of the minor build version releases — provided the minor build versions are greater than or equal to the minor version that the application was compiled against. However, if the library was updated to major build version 18.0 or later and the application had not been compiled against that build version, then the application should fail to initialize the library.

For any given release of the 16-bit OLE library there is a file called OLExVER.H, which contains the major and minor build versions, defined as rmm and rup. To get the build versions, applications call OleBuildVersion, which returns the major and minor build version numbers from this file. It is unlikely that the major build version would change between releases, but applications should still ensure compatibility by calling OleBuildVersion. If the major version number is different than that expected by the application, the application must not call OleInitialize.

OleBuildVersion will always be the very first function that a 16-bit application calls in the OLE2.DLL library.

#define rmm	21

#define rup	373

DWORD dwBuildVersion = OleBuildVersion();

// fail if different major version or older minor version.

if (HIWORD(dwBuildVersion) != rmm || LOWORD(dwBuildVersion) < rup)

return ResultFromScode(E_FAIL);	//Do not initialize OLE

else

OleInitialize(NULL);

See Also

OleInitialize, CoBuildVersion


OleInitialize

HRESULT OleInitialize(pvReserved)
LPVOID pvReserved

OleInitialize initializes the OLE library so that it can be used.

Parameters

pvReserved

In 32-bit OLE, this parameter must be NULL. The 32-bit version of OLE does not support applications replacing OLE's allocator and if the parameter is not NULL, OleInitialize returns E_INVALIDARG. For information related to 16-bit OLE applications, see the following Remarks section.

Return Values

Value	Meaning
S_OK	The library was initialized successfully.
S_FALSE	OLE library is already initialized; pMalloc not used.
OLE_E_WRONGCOMPOBJ	compobj.dll is the wrong version for ole2.dll.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	An invalid argument.
E_UNEXPECTED	An unexpected error occurred.
Remarks

OleInitialize must be called by applications before any other function in the OLE library.

Calls to OleInitialize must be balanced by corresponding calls to OleUninitialize. Typically, OleInitialize is called only once by the process that wants to use the OLE library, although multiple calls can be made. Subsequent calls to OleInitialize return S_FALSE. The OLE libraries are uninitialized when OleUninitialize has been called for each OleInitialize call that returned a success HRESULT (including S_FALSE).

In 16-bit OLE only, the pvReserved parameter is identified as pMalloc and points to the memory allocator to be used for task memory by the OLE library and by object handlers. To use the OLE implementation of IMalloc, pass NULL as this parameter. Because OleInitialize retains the pMalloc pointer beyond the duration of the call in 16-bit OLE, it calls pMalloc->AddRef. Applications can release this pointer by calling pMalloc->Release.

See Also

CoGetMalloc, OleUninitialize, CoInitialize


OleUninitialize

void OleUninitialize()

OleUninitialize uninitializes the OLE library, freeing any resources that it maintains.

Remarks

OleUninitialize should be called on application shutdown, as the last call made to the OLE library. OleUninitialize internally calls CoUninitialize to uninitialize the Component Object Library.

Calls to OleUninitialize must be balanced with a previous corresponding call to OleInitialize; only the OleUninitialize call that corresponds to the OleInitialize call that actually did the initialization will uninitialize the library.

See Also

OleInitialize, CoUninitialize


Data Transfer/Caching Interfaces and Functions

Overview of Data and Presentation Transfer

Data and Presentation Transfer Structures and Enumerations

OLE Clipboard Formats

IDataObject Interface

CreateDataAdviseHolder

IDataAdviseHolder Interface

IEnumFORMATETC Interface

IEnumSTATDATA Interface

IViewObject Interface

IViewObject2 Interface

IOleCache Interface

IOleCache2 Interface

IOleCacheControl Interface

Clipboard Functions

Data Transfer, Caching, and Drawing Functions

Icon Extraction Helper Functions


Overview of Data and Presentation Transfer

OLE provides a generalized data- and presentation-transfer mechanism that can be used in a wide range of situations, including those related to compound documents. Data can be copied to the Clipboard or dragged and then pasted or dropped into the same document, a different document, or a different application. Delayed rendering, a technique whereby data is not actually transferred until it is needed for rendering, is an integral part of data transfer. Only information about the impending transfer, such as data formats and mediums, is initially made available at copy or cut time. A source application creates a data-transfer object that holds a copy of the selected data and exposes methods for retrieving the data and receiving change notifications.

When a paste operation occurs, the receiving application makes a call to get the actual data from the source application. It can request both the data format and the medium across which the data should be transferred. The receiving application's ability to select formats and mediums at run time allows objects to be transferred in the most efficient manner. Bitmaps and metafiles (GDI objects) can only be transferred as GDI objects, but all other data can be transferred on any flat medium (hGlobal or file). Only structured data can be transferred on a structured medium (TYMED_ISTORAGE). A large object can be transferred using a storage object or a file, for example, while a smaller object can be transferred as a GDI object by using the Clipboard.

It is important to note that OLE Clipboard APIs replace all the functionality of Win32 Clipboard APIs. To use both usually creates redundancy and is unnecessary. The only exception to this occurs when you are setting yourself up in the clipbook viewer chain.

OLE supports the caching of presentation data in the container's storage so that a compound-document object can be shown in its container without running the object's application. Multiple presentations can also be cached; the container can maintain presentations for multiple target devices, such as the screen and the printer.


Data and Presentation Transfer Structures and Enumerations

This section discusses the data structures and enumerations that play a central role in the transfer and caching of data and presentation information.

The FORMATETC structure is a generalized Clipboard format, 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.

The STGMEDIUM structure holds data being transferred between applications that participate in OLE's uniform data-transfer model. It allows data to be passed in a range of formats, including a global memory handle, a GDI handle, IStorage, and IStream. Where one might expect to find a global memory handle involved in a data transfer, OLE uses a STGMEDIUM structure instead.

The DVTARGETDEVICE structure contains enough information about a Windows target device so that you can create a handle to a device context (hDC) using the Windows CreateDC function.

The ADVF enumeration is a set of advisory flags that specifies values for controlling advisory connections and caching.


FORMATETC Data Structure

The FORMATETC structure is used by the data and presentation interfaces as the means for passing information. For example, in IDataObject::GetData a FORMATETC structure is used to indicate exactly what kind of data the caller is requesting. For more information on the IDataObject interface, see IDataObject Interface.

The FORMATETC data structure is defined as follows:

typedef struct STRUCT tagFORMATETC

{

    CLIPFORMAT          cfFormat;

    DVTARGETDEVICE * ptd;

    DWORD               dwAspect;

    LONG                lindex;

    DWORD               tymed;

} FORMATETC, * LPFORMATETC;

The cfFormat member indicates the particular Clipboard format of interest. OLE recognizes three types of formats:

•	standard interchange formats, such as CF_TEXT,

•	private application formats understood only by the application offering the format, or by other applications similar in functionality, and

•	OLE formats.

The OLE formats are used to create linked or embedded objects and are described in the following sections. The concept of Clipboard formats should already be familiar to Windows programmers. For more information on Clipboard formats and related functions, such as RegisterClipboardFormat, see the Microsoft Win32 Software Development Kit.

The ptd member points to a DVTARGETDEVICE data structure containing information about the target device for which the data is being composed. A NULL value is used whenever the data format is insensitive to the target device or when the caller doesn't require a specific device. In the latter case, if the data requires a target device, the object should pick an appropriate default device (often the display for visual objects). 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.

The dwAspect member lets the caller request multiple aspects, roles, or views of the object using a single Clipboard format. 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.

Values for dwAspect are taken from the enumeration DVASPECT:

typedef enum tagDVASPECT

{

    DVASPECT_CONTENT       = -1,

    DVASPECT_THUMBNAIL     = -1,

    DVASPECT_ICON          = -1,

    DVASPECT_DOCPRINT      = anything,

} DVASPECT;

Even though DVASPECT values are individual flag bits, dwAspect can only represent one value. That is, dwAspect cannot contain the result of DVASPECT values that have been OR-ed together. DVASPECT values have the following meaning:

Value	Meaning
DVASPECT_CONTENT	Provides a representation so the object can be displayed as an embedded object inside its container; this is the most common value for compound-document objects. It is appropriate to use DVASPECT_CONTENT to get a presentation of the embedded object for rendering either on the screen or on a printer; DVASPECT_DOCPRINT, by contrast, indicates the look of the object as though it were printed.
DVASPECT_THUMBNAIL	Provides a thumbnail representation so that the object can be displayed in a browsing tool. The thumbnail is approximately a 120 by 120 pixel 16-color (recommended) device-independent bitmap wrapped in a metafile.
DVASPECT_ICON	Provides an iconic representation of the object.
DVASPECT_DOCPRINT	Represents the object as though it were printed using the Print command from the File menu. The described data represents a sequence of pages.
The value of lindex depends on the value of dwAspect. If dwAspect is either DVASPECT_CONTENT, DVASPECT_ICON, or DVASPECT_THUMBNAIL, the lindex member must be -1. For a dwAspect of DVASPECT_DOCPRINT, lindex represents the part of the aspect that is of interest.

The tymed member indicates the means by which data is conveyed in a particular data-transfer operation. In addition to being passed through global memory, data can be passed either through a disk file or an instance of one of the OLE storage-related interfaces.

Each Clipboard format has a natural expression as either a flat format, a structured, hierarchical format, or a GDI format. All standard formats, such as CF_TEXT, are expressed as flat formats. The OLE embedded object formats, Embedded Object and Embed Source, transfer data using the structured format. Three types of medium are used to transfer formats designated as flat formats: hglobal, stream, and file; and a single type of structured media: storage.

It is always appropriate to ask for a particular format on either a flat or a structured medium, as appropriate for the natural expression of the format. In addition, it is plausible to ask that a format whose natural expression is a structured format be provided on a flat format: the structured-to-flat mapping is provided by the compound file implementation of the structured storage model. However, it is not appropriate to ask for a flat format on a structured medium. For example, CF_TEXT cannot be passed on TYMED_ISTORAGE.


TYMED Enumeration

Valid values for tymed are taken from the following enumeration TYMED. These values may be OR-ed together to represent a composite value.

typedef 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;

The TYMED values have specific meanings and required release behavior. However, for any of the TYMED values, if pUnkForRelease is non-NULL, pUnkForRelease->Release is always called. The following table describes each of the TYMED values and their mechanisms for release:

Value	Meaning	Release Mechanism
TYMED_HGLOBAL	Pass the data in a global memory handle, much as data is passed using DDE. All global handles must be allocated with the GMEM_SHARE flag.	GlobalFree(hGlobal)
TYMED_FILE	Pass the data in the contents of a file on the disk.	OpenFile(lpszFile, &of, OF_DELETE)
TYMED_ISTREAM	Pass the data using an instance of the IStream interface. The passed data is available through calls to the IStream::Read.	pStm->Release()
TYMED_ISTORAGE	Pass the data using an instance of the IStorage interface; the passed data are the streams and storage objects nested beneath the IStorage.	pStg->Release()
TYMED_GDI	Pass the data as a GDI object (bitmap or palette only).	DeleteObject ((HGDI)hGlobal)
TYMED_MFPICT	Pass the data as a CF_METAFILEPICT, which contains a nested global handle.	LPMETAFILEPICT pMF; pMF=GlobalLock(hGlobal); DeleteMetaFile(pMF->hMF); GlobalUnlock(hGlobal); GlobalFree(hGlobal);
TYMED_ENHMF	Pass CF_ENHMETAFILE as HENHMETAFILE	Delete EnhMetafile(hEnhMetaFile)
TYMED_NULL	This is not actually a medium; it indicates that no data is being passed.
ReleaseStgMedium should be called any time a medium is to be released. It uses the appropriate release mechanism for any valid value.


STGMEDIUM Data Structure

The STGMEDIUM structure describes a medium of transfer and is defined as follows:

typedef struct tagSTGMEDIUM {

    DWORD          tymed;

    union {

    HBITMAP        hBitmap;

    HMETAFILEPICT  hMetaFilePict;

    HENHMETAFILE   hEnhMetaFile;

....HGLOBAL        hGlobal;

    LPOLESTR       lpszFileName;

    ISTREAM       *pstm;

    ISTORAGE      *pstg;

};

    IUNKNOWN      *pUnkForRelease;

} STGMEDIUM;

A STGMEDIUM structure is a tagged union whose tymed member corresponds to the TYMED enumeration. Each type of medium specified in FORMATETC::tymed has a matching STGMEDIUM tymed member through which occurrences of that medium are passed.

STGMEDIUM can be set to NULL by calling ReleaseStgMedium. The tymed member TYMED_NULL indicates a NULL STGMEDIUM.

The pUnkForRelease member provides flexibility of medium ownership. It is helpful to have the following choices:

•	The callee owns the medium, freeing all resources when finished.

•	The callee does not own the medium and informs the caller when finished so that resources can then be freed.

The provider of the medium indicates its choice of ownership scenarios in the value it provides in pUnkForRelease. A NULL value 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. For a detailed explanation of how storage mediums are freed and pUnkForRelease is used, see ReleaseStgMedium.


DVTARGETDEVICE Data Structure

The DVTARGETDEVICE data structure, used to describe a target device, is defined as follows:

typedef struct tagDVTARGETDEVICE

{

    DWORD     tdSize;

    WORD      tdDriverNameOffset;

    WORD      tdDeviceNameOffset;

    WORD      tdPortNameOffset;

    WORD      tdExtDevmodeOffset;

    BYTE      tdData[1];

} DVTARGETDEVICE;

The tdSize member specifies the size of DVTARGETDEVICE structure in bytes. The initial size is included so that the structure can be copied more easily.

The next four members specify the offset, in bytes, from the beginning of the DVTARGETDEVICE structure to the device-driver name, the device name, the port name, and the DEVMODE structure retrieved by calling ExtDeviceMode. The strings indirectly indicated by tdDeviceNameOffset, tdDriverNameOffset, and tdPortNameOffset should be NULL-terminated. A NULL device or port name can be specified by setting the appropriate offset fields to zero.

The tdData member specifies an array of bytes containing data for the target device. It is not necessary to include empty strings in tdData.

Some OLE 1 client applications incorrectly construct target devices by allocating only DEVMODE.dmSize number of bytes in their OLETARGETDEVICE for the DEVMODE structure. The number of bytes to be allocated should be the sum of DEVMODE.dmSize + DEVMODE.dmDriverExtra. When a call is made to CreateDC 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.

The following example, from the sample user library function OleStdCreateTargetDevice, illustrates how to construct a target device structure correctly. Applications can call OleStdCreateTargetDevice to handle the target device construction completely.

DVTARGETDEVICE * OleStdCreateTargetDevice(LPPRINTDLG lpPrintDlg)

{

DVTARGETDEVICE * ptd=NULL;

LPDEVNAMES lpDevNames, pDN;

LPDEVMODE lpDevMode, pDM;

UINT nMaxOffset;

LPOLESTR pszName;

DWORD dwDevNamesSize, dwDevModeSize, dwPtdSize;

if ((pDN = (LPDEVNAMES)GlobalLock(lpPrintDlg->hDevNames)) == NULL) {

return ptd;

}

if ((pDM = (LPDEVMODE)GlobalLock(lpPrintDlg->hDevMode)) == 	NULL) {

GlobalUnlock(lpPrintDlg->hDevNames);

return ptd;

}

nMaxOffset =  (pDN->wDriverOffset > pDN->wDeviceOffset) ?

pDN->wDriverOffset : pDN->wDeviceOffset ;

nMaxOffset =  (pDN->wOutputOffset > nMaxOffset) ?

pDN->wOutputOffset : nMaxOffset ;

pszName = (LPOLESTR)((BYTE *) pDN + nMaxOffset);

dwDevNamesSize = (DWORD)(nMaxOffset+wcslen * 	sizeof(OLECHAR) + 1);

dwDevModeSize = (DWORD) (pDM->dmSize + pDM->dmDriverExtra);

dwPtdSize = sizeof(DWORD) + dwDevNamesSize + dwDevModeSize;

if ((ptd = (DVTARGETDEVICE *)OleStdMalloc(dwPtdSize)) != NULL) {

// copy in the info

ptd->tdSize = (UINT)dwPtdSize;

lpDevNames = (LPDEVNAMES) &ptd->tdDriverNameOffset;

_fmemcpy(lpDevNames, pDN, (size_t)dwDevNamesSize);

lpDevMode=(LPDEVMODE)
((BYTE *)&ptd->tdDriverNameOffset+dwDevNamesSize);

 _fmemcpy(lpDevMode, pDM, (size_t)dwDevModeSize);

ptd->tdDriverNameOffset += 4 ;

ptd->tdDeviceNameOffset += 4 ;

ptd->tdPortNameOffset   += 4 ;

ptd->tdExtDevmodeOffset = (UINT)dwDevNamesSize + 4 ;

}

return ptd;

}


ADVF Enumeration

The values defined in the ADVF enumeration are used to specify information about connections of the types shown below:

•	data advisories

•	view advisories

•	cache connections

The valid values for ADVF, which can be OR-ed together to form a composite value, are defined as follows:

typedef enum tagADVF

{

    ADVF_NODATA             =   1,

    ADVF_PRIMEFIRST         =   2,

    ADVF_ONLYONCE           =   4,

    ADVF_DATAONSTOP         =   64,

    ADVFCACHE_NOHANDLER     =   8,

    ADVFCACHE_FORCEBUILTIN  =  16,

    ADVFCACHE_ONSAVE        =  32

} ADVF;

Depending on the way the ADVF value is used, the values have different meanings. Some of the methods use only a subset of the values; other methods use all of them.

ADVF_NODATA, when passed to IDataObject::DAdvise, is a request to avoid sending data with subsequent IAdviseSink::OnDataChange calls. TYMED_NULL is passed as the storage medium. The recipient of the data change notification can later retrieve the latest data by calling IDataObject::GetData. ADVF_NODATA however, is just a request. The data object may provide the data anyway, especially when more than one advisory connection has been made specifying the same FORMATETC data structure.

When ADVF_NODATA is passed to IViewObject::SetAdvise, it returns E_INVALIDARG.

When ADVF_NODATA is passed to IOleCache::Cache, it is an indication that the cache should not be updated by changes made to the running object. Instead, the container can update the cache by explicitly calling IOleCache::SetData IDataObject::SetData, or IOleCache2::UpdateCache with the flags UPDFCACHE_ALL or UPDFCACHE_NODATACACHE. This situation typically occurs when the iconic aspect of an object is being cached.

ADVF_PRIMEFIRST requests that a data- or view-change notification be sent or the cache updated immediately without waiting for a change in the current data or view.

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 ID is returned if the connection is established so the caller can use it to delete the connection. For data-change notifications, the combination of ADVF_ONLYONCE | 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.

ADVF_DATAONSTOP is meaningful for data-change notifications only when ADVF_NODATA is also given. This value indicates that just before the advisory connection shuts down, a call to IAdviseSink::OnDataChange should be made that provides the data with it. Without this value, by the time an OnDataChange call without data reaches the sink, the source might have shut down and the data might not be accessible. Sinks that specify this value should, in OnDataChange, accept data if it is being passed because they may not get another chance to retrieve it.

For cache connections, ADVF_DATAONSTOP updates the cache as part of object closure. ADVF_DATAONSTOP is not applicable to view-change notifications.

ADVFCACHE_NOHANDLER, ADVFCACHE_FORCEBUILTIN, and ADVFCACHE_ONSAVE are applicable only to the caching methods. The ADVFCACHE_NOHANDLER value is reserved for future use.

ADVFCACHE_FORCEBUILTIN forcefully 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, as appropriate. By specifying this value, the container ensures that the data can be retrieved even when the object or handler code is not available. 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.

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 re-running the object).


OLE Clipboard Formats

Three types of formats are made available by using the FORMATETC structure when an application offers data to be placed on the Clipboard. They include the following:

•	Standard Windows formats, such as CF_TEXT

•	Private application formats understood only by the application offering the format or by other applications that are similar in functionality

•	OLE-defined formats

The OLE-defined formats are of particular interest here. They are used to describe data that can become an embedded or linked compound-document object. The formats are made available in order of their fidelity of description of the data. In other words, the format that best represents the data selection is made available first. This intentional ordering encourages a receiving application to use the first format if possible.

There are six OLE-defined formats. The formats with the term "Source" in their names are used to create embedded or linked objects; the Embedded Object format can be used to create either object type. The "Descriptor" formats are used to describe the data selection on the Clipboard and are made available in conjunction with the other formats. To get the CLIPFORMAT for the OLE Clipboard formats, an application must call RegisterClipboardFormat with the actual name shown in the following table (such as "Embed Source." RegisterClipboardFormat then returns the number of the format. These formats are summarized below and described in detail in subsequent sections:

OLE Format	Usage
Embed Source	Creates an embedded object from an object application's native data.
Link Source	Creates a standard new linked object.
Custom Link Source	Creates a custom new linked object.
Embedded Object	Creates an embedded or linked object from a container's existing embedded object.
Object Descriptor	Describes the data being transferred and is made available with all transfers.
Link Source Descriptor	Describes the link being transferred in the link source.
OLE provides helper functions to render and query for each of the OLE formats in its sample code library. The following table describes the available helper functions:

Sample Library Helper Function	Description
OleStdGetOleObjectData	Renders Embedded Object and Embed Source data.
OleStdGetLinkSourceData	Renders Link Source data.
OleStdGetObjectDescriptor	Renders Object Descriptor and Link Source Descriptor data.
OleStdQueryOleObjectData	Queries for Embedded Object and Embed Source data.
OleStdQueryLinkSourceData	Queries for Link Source data.
OleStdQueryObjectDescriptorData	Queries for Object Descriptor and Link Source Descriptor data.


Embed Source Format

The Embed Source format is offered when the data selection can be the source of a new embedded object in a container. Embed Source data is arranged exactly like it is in the usual persistent representation of an embedded object. Only the normal native data of the object is placed inside the transferred IStorage; presentations of the object are not to be passed.

When an embedded object is created for the first time, as is the case with Embed Source, the formats available with the data transfer object can be used to initialize the cache for the newly created embedded object. When the transfer operation is done using the Clipboard, OleSetClipboard can also offer these other formats to non-OLE-aware applications.


Link Source Format

The Link Source format is offered when the data selection can be the source of a new linked object in a container, although TYMED_ISTREAM is preferred. Link Source data contains a class identifier (CLSID) and a moniker intended to represent the link source.

Data in Link Source format is always passed on a flat storage medium. This means that a medium of type TYMED_ISTORAGE is never appropriate. The contents of the medium are a serialized CLSID immediately followed by the serialized data of the moniker. The CLSID is retrievable with ReadClassStm, and the moniker can be de-serialized with IPersistStream::Load. A link can be created from the resulting moniker using OleCreateLink.

The cache in the new linked object is initialized with the formats available only from the data-transfer object.


Custom Link Source Format

The Custom Link Source format is offered by applications that implement a custom link object to fully or partially replace the default link object provided by OLE. You may want applications to use a custom link object for objects that have special rendering needs.

The custom link object is a DLL object and is registered as an INPROC_SERVER in the registry. The custom link object implements the following interfaces:

Interface	Usage
IPersistStorage	Compound-document storage
IDataObject	Data transfer
IOleCache2	Compound-document cache support
IViewObject2	Compound-document view support
IRunnableObject	Required by all in-process servers.
IExternalConnection	Required by all in-process servers.
IOleLink	Compound-document link management
IOleObject	Compound-document object support
The typical custom link object used for special rendering will implement IPersistStorage, IDataObject, IOleCache2, and IViewObject2 and then aggregate OLE's standard link object implementation for the remaining interfaces.

To use a custom link object, objects must register a special key in the registry:

HKEY_CLASSES_ROOT\CLSID\{...}\UseCustomLink

When the Link Source format is available and a user initiates a paste link, the link source application is asked whether or not it supports Custom Link Source. If Custom Link Source is supported, the link source application writes the CLSID of the custom link object into a specified IStorage. When the linked object is instantiated, the CLSID is read from the storage and passed to CoCreateInstance, which uses it to create the custom link object. The moniker passed with the Link Source data is given to the custom link object so it can bind the newly created linked object when necessary.


Embedded Object Format

The Embedded Object format is offered when the data selection is an embedded object and indicates that either a linked or embedded object can be created. Embedded Object should be first in the list of available formats.

The Embedded Object data is arranged exactly like the Embed Source data. To create an embedded object from the Embedded Object format, a receiving application copies the contents of the data selection's storage object into a new storage object. Before a copy can be safely made, it is necessary to check whether the object has changed since it was last saved. If it has, the object can be saved directly into the new storage. If no change has been made, the method IStorage::CopyTo can be called to copy the contents from the original storage to the new storage. The following code illustrates this logic:

lpObject->QueryInterface(IID_IPersistStorage, &lpPersistStorage);

if (lpPersistStorage->IsDirty() == NOERROR)  {

OleSave(lpPersistStorage, lpNewStorage, FALSE);

lpPersistStorage->SaveCompleted(lpNewStorage);

}

else  {

lpStorage->CopyTo(lpNewStorage);

}

The main difference between Embed Source and Embedded Object lies in the way that the cache for the new compound-document object is initialized. When an embedded object is created for the first time, as is the case with Embed Source, only the formats available with the data-transfer object can be used to initialize the cache for the newly created embedded object. Because the embedded object has saved the cache information, it is simply loaded again. With the Embedded Object format, the presentations in the cache of the embedded object itself are available as well.


Object Descriptor Format

The Object Descriptor format is offered whenever data is copied. Object Descriptor data is made available in an instance of an OBJECTDESCRIPTOR data structure. The OBJECTDESCRIPTOR structure consists of the following elements:

ULONG               cbSize

CLSID               clsid

DWORD               dwDrawAspect

SIZEL               sizel

POINTL              pointl

DWORD               dwStatus

DWORD               dwFullUserTypeName

DWORD               dwSrcOfCopy

The cbSize is the size of the OBJECTDESCRIPTOR structure in bytes.

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 value of the dwDrawAspect field is typically 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).

The sizel field contains the true extents from the object, available through a call to IOleObject::GetExtent. Setting the sizel field is optional; its value can be (0, 0) for applications that do not draw the object being transferred.

The pointl field is the offset in HIMETRIC units from the upper-left corner of the object where a drag operation was initiated. This field is only meaningful for a drag-and-drop transfer operation. The value is (0,0) for other transfer situations, such as a Clipboard copy and paste.

The dwStatus field contains miscellaneous status flags for the object. These flags are defined by the OLEMISC enumeration and are returned by calling IOleObject::GetMiscStatus.

The dwFullUserTypeName field is the offset, in bytes, from the beginning of the data structure to the null-terminated string that specifies the full user type name of the object. The value is zero if the string is not present.

The dwSrcOfCopy field is the 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.


Link Source Descriptor Format

The Link Source Descriptor format, like Object Descriptor, contains information about the source of a compound-document object. Link Source Descriptor is always offered when Link Source is offered. The same data is included for both formats. However, with Link Source Descriptor, the value of dwSrcOfCopy can represent either a pseudo object in the source application, referred to as an outside link, or the running embedded object, referred to as an inside link. For example, consider the case in which a word processor links to a drawing that is a linked object within a spreadsheet program. If the new link is an inside link, the draw program is launched to edit the drawing when a word-processor user double-clicks. The link source is the original object application used to create the drawing. If the new link is an outside link, the spreadsheet program becomes the link source and is used for editing.

When a link is copied, the source field will show the display name of the link source and the list box in the Paste dialog box will show "Linked <object full type>." The resulting text and image will not be changed because they typically apply to both linking or embedding. It is the responsibility of the copying application to add the "Linked" to the full user type name given in the Link Source Descriptor data.


Copying to the Clipboard

Applications that support Clipboard copy typically have a standard set of formats they always offer and another set to offer selectively, depending on the data being copied. This standard set of formats is application-specific. For example, a pure container that does not support linking to its embedded objects would offer a different set of formats than an object application that supports linking to pseudo objects. Applications that are to maintain compatibility with OLE 1 must offer CF_METAFILEPICT.

The following table suggests possible format offerings by application type. In all cases except for the container copying native data or multiple compound-document objects, it is assumed that the data selection can be the source of a linked object. The placement of the picture format relative to the other formats in the table is somewhat arbitrary. It is up to the application to determine where the picture format belongs. Otherwise, the ordering is intentional. Containers that do not draw their objects (use them pictorially) do not need to offer a picture format.

Type of Application	Data Selection	Clipboard Formats
Container application	single compound-document object	High Fidelity application-specific formats Embedded Object Object Descriptor Picture Low Fidelity application-specific formats Link Source (inside or outside moniker) Link Source Descriptor (inside or outside moniker)
Container application	native data or multiple compound-document objects	High Fidelity application-specific formats Picture  Low Fidelity application-specific
Object application	native data	High Fidelity application-specific formats Embedded Source Object Descriptor Picture Low Fidelity application-specific formats Link Source (outside moniker) Link Source Descriptor (outside moniker)
Container/object application	single compound-document object	High Fidelity application-specific formats  Object Descriptor Picture  Low Fidelity application-specific formats Link Source (inside or outside moniker) Link Source Descriptor (inside or outside moniker)
Container/object application	native data or multiple compound-document objects	High Fidelity application-specific formats  Embed Source (not for multiple objects) Object Descriptor (not for multiple objects) Picture  Low Fidelity application-specific formats Link Source (outside moniker) Link Source Descriptor (outside moniker)
A consideration when offering Link Source for an embedded object in an unsaved container document is whether the object is running or merely loaded. If the object is not running, and a link is created before the container document is saved, no OnRename notification will be sent when the document is saved. Thus, the link will be broken. To ensure that links are valid whenever possible, containers should only permit linking to data in saved documents.

A few conditions can change the set of offered formats. Link Source and Link Source Descriptor should be removed from the data-transfer object if either a moniker cannot be provided to represent the data selection, or, in the case of a copy, the data has changed since the copy was initiated.


Pasting From the Clipboard

An application implementing paste operations retrieves the data-transfer object from the Clipboard and looks for an acceptable format among the list of formats that are available. If the first acceptable format is one that the application can edit, the selection is typically treated as data that is native to the application. A linked or embedded object is not created, thus the extra overhead involved in compound-document object creation is avoided. For example, if the data selection is a paragraph from a word-processor application that offers CF_TEXT as its first available format, and the receiving application is also a word processor, the receiving application can integrate the paragraph directly into its document. A user will be able to edit the pasted data in the same way as the rest of the document.

If only ENHMETAFILE, CF_METAFILEPICT, CF_DIB, or CF_BITMAP is available, a static object can be created from the data by calling OleCreateStaticFromData. Static objects are pictures for which OLE provides a compound-document object wrapping, making it possible for containers to treat them as though they were linked or embedded objects. A static object has a class ID, such as CLSID_StaticMetafile or CLSID_StaticDib and the aspect DVASPECT_CONTENT. Static objects cannot be edited.

To copy data from one storage object into another storage object in the nonlocal case, a container application can create a temporary document. The data is loaded from the source storage object and copied into the temporary document. The contents of the temporary document are then copied into the real container document and the temporary document is destroyed.


IDataObject Interface

The IDataObject interface plays a central role in the transferring and caching of data and presentations. IDataObject contains methods that retrieve, store, and enumerate data, and handle data-change notifications. All applications that transfer data implement IDataObject regardless of whether they are containers or object applications. All applications that receive data use IDataObject. IDataObject contains the following methods:

DECLARE_INTERFACE_(IDataObject, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG  Release (THIS);

    // *** IDataObject methods ***

  HRESULT GetData (THIS_ FORMATETC * pformatetcIn,

    STGMEDIUM * pmedium);

  HRESULT GetDataHere (THIS_ FORMATETC * pformatetc,

    STGMEDIUM *pmedium);

  HRESULT QueryGetData (THIS_ FORMATETC * pformatetc );

  HRESULT GetCanonicalFormatEtc (THIS_ FORMATETC * pformatetcIn,

    FORMATETC * pFormatetcOut);

  HRESULT SetData (THIS_ FORMATETC * pformatetc, STGMEDIUM * pmedium,

    BOOL fRelease);

  HRESULT EnumFormatEtc (THIS_ DWORD dwDirection,

    IEnumFORMATETC ** ppenumFormatEtc );

  HRESULT DAdvise (THIS_ FORMATETC * pformatetc, DWORD advf,

    IAdviseSink pAdvSnk, DWORD * pdwConnection);

  HRESULT DUnadvise (THIS_ DWORD dwConnection);

  HRESULT EnumDAdvise (THIS_ IEnumSTATDATA ** ppenumAdvise );

};


IDataObject::GetData

HRESULT IDataObject::GetData(pformatetcIn, pmedium)
FORMATETC *pformatetcIn
STGMEDIUM *pmedium

IDataObject::GetData retrieves data in a specified format using a specified storage medium.

Parameters

pformatetcIn

[in] Points to the format to use for returning the data.

pmedium

[out] Points to the storage medium to use for returning the data. This is the storage medium provided by the callee. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Data was successfully retrieved and placed in the storage medium provided.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC	Invalid value for pFormatetcIn.
DV_E_TYMED	Invalid tymed value.
DV_E_DVASPECT	Invalid dwAspect value.
OLE_E_NOTRUNNING	Object application is not running.
Remarks

It is valid to ask GetData to return data in a specified format on one of a specified group of mediums. Multiple mediums can be specified by OR-ing together the tymed values in the FORMATETC structure passed to GetData. The callee allocates the medium and decides how the resources associated with that medium are to be released. GetData determines the best medium, given its selection, and attempts to transfer the data. Only one medium can be returned. If this initial transfer fails, GetData might try one of the other mediums specified before returning an error.

Conceptually, the pmedium parameter is an out parameter, implying that the caller allocates the STGMEDIUM structure and the callee fills it in. The callee determines who is responsible for releasing the medium's resources and sets pmedium->pUnkForRelease to the appropriate value.

Supporting transfer of data with the IStorage medium requires special handling. Because it is not possible to transfer ownership of a root IStorage object from one process to another, the callee must retain ownership of the data by setting the pUnkForRelease field in the STGMEDIUM structure passed in by the caller. As an alternative, callers should be able to consider using IDataObject::GetDataHere because it is more efficient.

Data transferred across a stream extends from position zero of the stream pointer through to the position immediately before the current stream pointer (the stream-pointer position upon exit).

Example

The following implementation of IDataObject::GetData is from an object application that supports the standard metafile format and the Embed Source, Link Source, and Object Descriptor OLE formats. IDataObject_GetData first checks for CF_METAFILEPICT with a tymed value of TYMED_MFPICT and the content and print aspects. Any other aspects or mediums are not supported for CF_METAFILEPICT. If the CF_METAFILEPICT format is requested and memory can be allocated for the data, the data is returned. Otherwise, the value of m_fDataTransferDoc is examined to determine whether this document is being used for data transfer. If the document is not a data-transfer object, the only supported format is metafile. If the document is a data-transfer object, IDataObject_GetData renders Object and Link Source Descriptor, Embed Source, and Link Source. For each format, the medium is checked to ensure it is appropriate and one of the sample helper functions is called to render the data.

HRESULT IDataObject_GetData (

        LPSERVERDOC       lpServerDoc,

        LPFORMATETC       pFormatetc,

        LPSTGMEDIUM       pmedium

)

{

LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;

LPOBJDOC lpObjDoc = (LPOBJDOC)lpServerDoc;

LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;

LPOLEAPP lpOleApp = (LPOLEAPP)lpServerApp;

LPOBJAPP lpObjApp = (LPOBJAPP)lpServerApp;

HREULT hrErr;

lpMedium->pUnkForRelease = NULL;

pmedium->pUnkForRelease = NULL;

pmedium->tymed = TYMED_NULL;

pmedium->u.hGlobal = NULL;

if(pFormatetc->cfFormat==lpObjApp->m_cfObject){

if(!(lpformatetc->tymed & TYMED_HGLOBAL)){

hrErr = DV_E_FORMATETC;

goto error;

}

lpMedium->u.hGlobal = ObjDoc_GetOutlineData(lpObjDoc,NULL);

if(!lpMedium->u.hGlobal){

hrErr = E_OUTOFMEMORY;

goto error

 }

 lpMedium->tymed = TYMED_HGLOBAL;

 return NOERROR;

} else if (pFormatetc->cfFormat == CF_METAFILEPICT &&

   (pFormatetc->dwAspect & (DVASPECT_ICON)){

CLSID clsid;

// Verify caller asked for correct medium

if (!(pFormatetc->tymed & TYMED_MFPICT)) {

hrErr = DV_E_FORMATETC;

goto error;

         }

   if(ServerDoc_GetClassID(lpServerDoc, (LPCLSID)&clsid) !=

NOERROR){

hrErr = DV_EFORMATETC;

gotoerror;

}

pmedium->u.hGlobal = GetIconOfClass(g_lpApp->m_hInst,

(REFCLSID)&clsid, NULL, FALSE);

if (! pmedium->u.hGlobal){

hrErr = E_OUTOFMEMORY;

goto error;

     }

pmedium->tymed = TYMED_MFPICT;

return NOERROR;

}

else if (pFormatetc->cfFormat ==CF_TEXT){

// Verify caller asked for correct medium

if(!(pFormatetc->tymed & TYMED_GLOBAL)){

hrErr = DV_E_FORMATETC;

goto error;

}

lpMedium->u.hGlobal = ObjDoc_GetTextData(

(LPOBJDOC)lpserverDoc,

NULL);

if(!lpMedium->u.hGlobal){

hrErr = E_OUTOFMEMORY;

goto error;

}

pMedium->tymed = TYMED_HGLOBAL;

return NOERROR;

}

}

if(!lpObjDoc->m_fDataTransferDoc){

hrErr = DV_E_FORMATETC;

goto error;

}

if (pFormatetc->cfFormat == lpApp->m_cfObjectDescriptor ||

(pFormatetc->cfFormat == lpApp->m_cfLinkSrcDescriptor &&

        lpDoc->m_fLinkSourceAvail)) {

// Verify caller asked for correct medium

if (!(pFormatetc->tymed & TYMED_HGLOBAL)){

hrErr = DV_E_FORMATETC;

goto error;

}

pMedium->u.hglobal = OleDoc_GetObjectDescriptorData(

(LPOLEDOC)lpServerDoc,

NULL);

if (! pmedium->u.hGlobal){

return E_OUTOFMEMORY;

goto error;

}

pmedium->tymed = TYMED_HGLOBAL;

return NOERROR;

} else if (pFormatetc->cfFormat == lpApp->m_cfEmbedSource) {

hrErr = OleStdGetOleObjectData

((LPPERSISTSTORAGE)&lpDoc->m_PersistStorage,

  pFormatetc, pmedium, FALSE);

if(hrErr != NOERROR){

goto error;

}

} else if (pFormatetc->cfFormat == lpOleApp->m_cfLinkSource) {

if (lpOleDoc->m_fLinkSourceAvail) {

LPMONIKER lpmk;

lpmk = ServerDoc_GetSelFullMoniker((LPSERVERDOC)

lpOleDoc->m_lpSrcDocOfCopy,

&lpServerDoc->m_lrSrcSelOfCopy,

GETMONIKER_FORCEASSIGN);

if (lpmk)  {

hrErr = OleStdGetLinkSourceData(lpmk,

(LPCLSID)&CLSID_APP, pFormatetc, pmedium);

OleStdRelease(LPUNKNOWN)lpmk);

if (hrErr != NOERROR){

goto error;

}

} else{

hrErr = E_FAIL;

goto error;

}

return NOERROR;

} else{

hrErr = DV_E_FORMATETC;

}

error:

return hrErr;

}

See Also

IDataObject::SetData, IDataObject::GetDataHere, IEnumFORMATETC, ReleaseStgMedium


IDataObject::GetDataHere

HRESULT IDataObject::GetDataHere(pformatetc, pmedium)
FORMATETC *pformatetc
STGMEDIUM *pmedium

IDataObject::GetDataHere retrieves data in a specified format using a storage medium provided by the caller.

Parameters

pformatetc

[in] Points to the format to use for returning the data.

pmedium

[in, out] Points to the storage medium to use for returning the data. This is the storage medium provided by the caller.

Return Values

Value	Meaning
S_OK	Data was successfully retrieved and placed in the storage medium provided.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Invalid value for lindex; currently only -1 is supported.
DV_E_FORMATETC	Invalid FORMATETC structure pointed to by pformatetc.
DV_E_TYMED	Invalid tymed value.
DV_E_DVASPECT	Invalid dwAspect value.
OLE_E_NOTRUNNING	Object application is not running.
DATA_E_FORMATETC	Cannot support the requested media.
STG_E_MEDIUMFULL	The caller-provided medium is not large enough.
Remarks

GetDataHere is like GetData except that the caller allocates the medium across which the data is to be transferred. GetDataHere is called when the Clipboard selection or data being dragged is an embedded object.

It is not reasonable to pass storage mediums to IDataObject::GetDataHere other than those that can be filled in by the callee. For example, the callee can write data to an IStream, create child streams within an IStorage, write data into a specified file, or copy data into an HGLOBAL. It is not possible for a callee to put data into a caller-provided metafile, bitmap, or GDI object. If the caller tries to use a TYMED other then TYMED_STORAGE, TYMED_STREAM, TYMED_HGLOBAL, or TYMED_FILE, IDataObject::GetDataHere should return DV_E_TYMED.

Note  The callee will always get pUnkForRelease as NULL in the STGMEDIUM even though the caller might set it to some IUnknown.

When the transfer medium is a stream, assumptions are made 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 (the position on exit). For GetDataHere, the data returned is from the stream position on entry through just before the position on exit. Note that some mediums are not appropriate using GetDataHere, including GDI types such as metafiles.

The callee must fill in the actual medium provided by the caller in the hGlobal case. That is, the callee cannot resize the Global, nor can the a new hGlobal be called. It must put the data in the medium provided by the caller. The caller always sets the field pmedium->tymed to the value of pformatetc->tymed; it lets you know what to do next.

Example

The following example of IDataObject_GetDataHere is from an object application. After checking that the current document is a data-transfer object, OleStdGetOleObjectData is called to transfer Embed Source data with the medium supplied by the caller, a storage object.

HRESULT ServerDoc_GetDataHere (

      LPSERVERDOC             lpServerDoc,

      LPFORMATETC             lpformatetc,

      LPSTGMEDIUM             lpMedium

)

{

   LPOLEDOC        lpOleDoc = (LPOLEDOC)lpServerDoc;

   LPDOC           lpDoc = (LPDOC)lpServerDoc;

   LPSERVERAPP     lpServerApp = (LPSERVERAPP)g_lpApp;

   LPOLEAPP        lpOleApp = (LPOLEAPP)lpServerApp;

   LPAPP           lpApp = (LPAPP)lpServerApp;

   HRESULT         hrErr;

   if (! lpDoc->m_fDataTransferDoc) {

      hrErr = DV_E_FORMATETC;

      goto error;

   }

   if (lpformatetc->cfFormat == lpOleApp->m_cfEmbedSource) {

      hrErr = OleStdGetOleObjectData(

            (LPPERSISTSTORAGE)&lpServerDoc->m_PersistStorage,

            lpformatetc,

            lpMedium,

            FALSE

      );

      if (hrErr != NOERROR) {

goto error;

      }

      return NOERROR;

   } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource) {

      if (lpOleDoc->m_fLinkSourceAvail) {

         LPMONIKER lpmk;

         lpmk = ServerDoc_GetSelFullMoniker(

               (LPSERVERDOC)lpOleDoc->m_lpSrcDocOfCopy,

               &lpServerDoc->m_lrSrcSelOfCopy,

               GETMONIKER_FORCEASSIGN

         );

         if (lpmk) {

            hrErr = OleStdGetLinkSourceData(

                  lpmk,

                  (LPCLSID)&CLSID_APP,

                  lpformatetc,

                  lpMedium

            );

            OleStdRelease((LPUNKNOWN)lpmk);

            if (hrErr != NOERROR) {

goto error;

            }

            return NOERROR;

         } else {

            hrErr = E_FAIL;

            goto error;

         }

      } else {

         hrErr = DV_E_FORMATETC;

         goto error;

      }

   } else {

      hrErr = DV_E_FORMATETC;

      goto error;

   }

   return NOERROR;

error:

   return hrErr

}

See Also

IDataObject::GetData


IDataObject::QueryGetData

HRESULT IDataObject::QueryGetData(pformatetc)
FORMATETC *pformatetc

IDataObject::QueryGetData determines whether a call to IDataObject::GetData would succeed if it were passed pformatetc.

Parameters

pformatetc

[in] Points to the format to use for transferring data.

Return Values

Value	Meaning
S_OK	Data in the specified format would be successfully returned.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Invalid value for lindex; currently only -1 is supported.
DV_E_FORMATETC	Invalid FORMATETC structure.
DV_E_TYMED	Invalid tymed value.
DV_E_DVASPECT	Invalid dwAspect value.
OLE_E_NOTRUNNING	Object application is not running.
Example

IDataObject_QueryGetData is a container's implementation; several formats and mediums are supported. For the private formats, the helper function OleStdQueryFormatMedium is called to determine whether the specified FORMATETC structure includes TYMED_HGLOBAL, because this format can only be rendered using memory. Therefore, the call is delegated to the object's IDataObject_QueryGetData implementation. For all other formats, the appropriate sample library helper function is called.

HRESULT IDataObject_QueryGetData (

        IDataObject *  lpThis,

        LPFORMATETC    pFormatetc

)

{

LPAPP lpApp = (LPAPP)g_lpApp;

IDataObject  lpDataObj = NULL;

LPDOC lpDoc = ((struct CDocDataObjectImpl *)lpThis)->lpDoc;

LPCLISITE lpCliSite;

HRESULT hresult;

if (lpDoc->m_fEmbeddedObjectAvail) {

lpDataObj = (IDataObject *)GetSingleOleObject(lpDoc,

                    &IID_IDataObject, (LPCLISITE *)&lpCliSite);

    }

if (pFormatetc->cfFormat == lpApp->m_cfEmbeddedObject &&

lpDoc->m_fEmbeddedObjectAvail ) {

hresult = OleStdQueryOleObjectData(pFormatetc);

    } else if (pFormatetc->cfFormat == lpApp->m_cfLinkSource &&

            lpDoc->m_fLinkSourceAvail) {

hresult = OleStdQueryLinkSourceData(pFormatetc);

    } else if((pFormatetc->cfFormat == (lpApp)->m_cfPrivate ||

            pFormatetc->cfFormat == CF_TEXT) ) {

hresult = OleStdQueryFormatMedium(pFormatetc,

TYMED_HGLOBAL);

} else if (pFormatetc->cfFormat == lpApp->m_cfEmbeddedObject &&

lpDoc->m_fEmbeddedObjectAvail ) {

hresult = OleStdQueryOleObjectData(pFormatetc);

} else if (pFormatetc->cfFormat == lpApp->m_cfLinkSource &&

lpDoc->m_fLinkSourceAvail) {

hresult = OleStdQueryLinkSourceData(pFormatetc);

} else if ( pFormatetc->cfFormat == lpApp->m_cfObjectDescriptor ||

(pFormatetc->cfFormat == lpApp->m_cfLinkSrcDescriptor &&

 lpDoc->m_fLinkSourceAvail) ) {

hresult = OleStdQueryObjectDescriptorData(pFormatetc);

} else if (pFormatetc->cfFormat == CF_METAFILEPICT &&

lpDoc->m_fEmbeddedObjectAvail && lpCliSite &&

(pFormatetc->dwAspect & lpCliSite->m_dwDrawAspect)) {

hresult = OleStdQueryFormatMedium(pFormatetc,

 TYMED_MFPICT);

} else if (lpDataObj) {

hresult = lpDataObj->lpVtbl->QueryGetData(lpDataObj,

pFormatetc);

} else {

hresult = DATA_E_FORMATETC;

}

if (lpDataObj)

(LPUNKNOWN)lpDataObj->lpVtbl->Release ((LPUNKNOWN)lpDataObj);

return hresult

}

See Also

IDataObject::GetData, FORMATETC


IDataObject::GetCanonicalFormatEtc

HRESULT IDataObject::GetCanonicalFormatEtc(pformatetcIn, pformatetcOut)
FORMATETC *pformatetcIn
FORMATETC *pformatetcOut

IDataObject::GetCanonicalFormatEtc communicates to the caller which FORMATETC data structures produce the same output data.

Parameters

pformatetcIn

[in] Points to the format and medium in which the caller wants to obtain the returned data.

pformatetcOut

[out] Points to where to return the canonical equivalent of pformatetcIn. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The returned FORMATETC structure is different than the one that was passed.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DATA_S_SAMEFORMATETC	The FORMATETC structures are the same and no value needs to be put in pformatetcOut.
DV_E_LINDEX	Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC	Invalid FORMATETC structure.
OLE_E_NOTRUNNING	Object application is not running.
Remarks

Often, a given data object returns the same data for more than one requested FORMATETC structure. This is especially true for target devices. The returned data is often insensitive to the particular target device in question. So that callers can avoid caching duplicate sets of data, IDataObject::GetCanonicalFormatEtc provides the means by which objects can indicate to the caller which FORMATETC structures produce the same results.

The callee should pick a canonical representative of the set of FORMATETC structures equivalent to the one passed by the caller in pformatetcIn and return that through pformatetcOut. pformatetcOut is allocated by the caller and filled in by the callee. The tymed member of both FORMATETC structure pointers (pformatetcIn->tymed and pformatetcOut->tymed) is not significant and should be ignored.

The simplest implementation of IDataObject::GetCanonicalFormatEtc returns DATA_S_SAMEFORMATETC and sets pformatetcOut->ptd to NULL. This type of implementation implies that the callee, usually a more sophisticated application, is sensitive to target devices.

Examples

The following implementation of IDataObject::GetCanonicalFormatEtc checks that the outgoing FORMATETC pointer is valid before initializing its ptd member to NULL. If either of the FORMATETC pointers are invalid, E_INVALIDARG is returned. A call to IDataObject::QueryGetData determines whether the format information supplied in pformatetc is supported. If the information is supported, the contents of pformatetcOut is set to the contents of pformatetcIn. When pformatetc->ptd is NULL, DATA_S_SAMEFORMATETC is returned as an indication that the callee is not target-device sensitive. When pformatetc->ptd is not NULL, GetCanonicalFormatEtc sets it to NULL and returns NOERROR.

HRESULT IDataObject_GetCanonicalFormatEtc(IDataObject lpThis,

LPFORMATETC pFormatetc, LPFORMATETC pFormatetcOut);

{

HRESULT hrErr;

if (!pFormatetcOut)

return E_INVALIDARG;

pFormatetcOut->ptd = NULL;

if (!pFormatetc)

return E_INVALIDARG;

if((hrErr = lpThis->lpVtbl->QueryGetData

(lpThis,pFormatetc)) != NOERROR)

return hrErr;

*pFormatetcOut = *pFormatetc;

if (pFormatetc->ptd == NULL)

return DATA_S_SAMEFORMATETC;

else {

pFormatetcOut->ptd = NULL;

return NOERROR;

}

}

See Also

FORMATETC


IDataObject::SetData

HRESULT IDataObject::SetData(pformatetc, pmedium, fRelease)
FORMATETC *pformatetc
STGMEDIUM *pmedium
BOOL fRelease

IDataObject::SetData sends data in a specified format.

Parameters

pformatetc

[in] Points to the format to use when interpreting the data contained in the storage medium.

pmedium

[in] Points to the storage medium (an in parameter only) containing the actual data.

fRelease

[in] Indicates ownership of the storage medium after completing the method. If fRelease returns TRUE, the callee takes ownership, freeing the medium after it has been used. If fRelease returns FALSE, the caller retains ownership and the callee uses the storage medium for the duration of the call only.

Return Values

Value	Meaning
S_OK	Data was successfully used.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Currently only lindex -1 is supported. Passing any other lindex results in an error.
DV_E_FORMATETC	Invalid FORMATETC structure.
DV_E_TYMED	Invalid tymed value.
DV_E_DVASPECT	Invalid dwAspect value.
OLE_E_NOTRUNNING	Object application is not running.
Remarks

The callee does not take ownership of the data until it has successfully used it. That is, DATA_E_FORMATETC or another error code is not returned. If the callee does take ownership, it must free the medium by calling ReleaseStgMedium.

The type of medium (TYMED) in pformatetc and pmedium must be equal. For example, one cannot be an hGlobal and the other a stream.

Example

Data-transfer objects do not support IDataObject::SetData, so an initial check for document type is made. If the check succeeds and the requested format is supported, the data is pasted and S_OK is returned. Otherwise, either E_FAIL or DATA_E_FORMATETC is returned. E_FAIL indicates the document represents a data-transfer object; DV_E_FORMATETC indicates an unsupported format was specified.

HRESULT IDataObject_SetData (

        LPDATAOBJECT    lpThis,

        LPFORMATETC     pFormatetc,

        LPSTGMEDIUM     pmedium,

        BOOL            fRelease

)

{

LPOLEDOC lpOleDoc =

((struct CDocDataObjectImpl *)lpThis)->lpOleDoc;

LPDOC lpDoc = (LPDOC)lpOleDoc;

LPAPP lpApp = (LPAPP)g_lpApp;

HRESULT hrErr = S_OK;

// Data transfer objects cannot accept data

if (lpDoc->m_fDataTransferDoc){

hrErr = E_FAIL;

goto error;

}

if (pFormatetc->cfFormat == lpApp->m_cfPrivate) {

SetRedraw ( lpDoc, FALSE );

ClearAllLines(lpDoc);

PastePrivateData(lpDoc,pmedium->u.hGlobal,-1);

SetRedraw ( lpDoc, TRUE );

} else if (pFormatetc->cfFormat == CF_TEXT) {

SetRedraw ( lpDoc, FALSE );

ClearAllLines(lpDoc);

PasteTextData(lpDoc,pmedium->u.hGlobal,-1);

SetRedraw ( lpDoc, TRUE );

} else

hrErr = DV_E_FORMATETC;

}

if (hrErr == S_OK && fRelease)

ReleaseStgMedium(pmedium);

return hrErr;

}

See Also

IDataObject::GetData, ReleaseStgMedium


IDataObject::EnumFormatEtc

HRESULT IDataObject::EnumFormatEtc(dwDirection, ppenumFormatEtc )
DWORD dwDirection
IEnumFORMATETC ** ppenumFormatEtc

IDataObject::EnumFormatEtc enumerates the formats that can be used to store data obtained with IDataObject::GetData or sent with IDataObject::SetData.

Parameters

dwDirection

[in] Indicates the set of formats to be enumerated; valid values are taken from the enumeration DATADIR:

typedef enum tagDATADIR

{

    DATADIR_GET = 1,

    DATADIR_SET = 2,

} DATADIR;

DATADIR_GET enumerates formats that can be passed to IDataObject::GetData. DATADIR_SET enumerates formats that can be passed to IDataObject::SetData.

ppenumFormatEtc

[out] Points to where to return the instantiated enumerator. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator returned successfully.
E_INVALIDARG	One or more parameters are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_NOTIMPL	The direction indicated by dwDirection is not supported.
OLE_S_USEREG	Request that OLE enumerate the formats from the registry.
Remarks

The enumeration returned by IDataObject::EnumFormatEtc is not a guarantee of support, because the formats can change over time. Accordingly, applications should treat the enumeration as a hint to the format types that can be passed.

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.

Formats can be registered statically in the registry or dynamically during application initialization. If an application has an unchanging list of formats and these formats are registered in the registry, it can ask OLE to perform the enumeration using the registry by calling OleRegEnumFormatEtc or by returning OLE_S_USEREG. OLE_S_USEREG instructs the default handler to call OleRegEnumFormatEtc. OLE_S_USEREG should be used only for local servers, but it is preferable, even in those cases, to call OleRegEnumFormatEtc. Object applications that are implemented as DLL object applications cannot return OLE_S_USEREG; they 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 2 objects, 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, calling EnumFormatEtc with DATADIR_GET enumerates only the native and metafile formats. Calling EnumFormatEtc with 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) but applications should always check, freeing it if it is non-NULL.

Note  FORMATETC::tymed often indicates that more than one kind of storage medium is acceptable. It should always be masked and tested by using OR values.

Example

The following object-application implementation of IDataObject::EnumFormatEtc enumerates the static list of formats that are registered in the registry if this document represents a user document. User documents (such as a nondata-transfer object) need not enumerate the OLE formats.

If the document is a data-transfer object, the offered formats might or might not include Link Source and Link Source Descriptor, depending on whether a moniker is available. If a moniker is not available, the number of formats is decremented by 2 and Link Source and Link Source Descriptor are removed from the list. The sample code library provides an implementation of IEnumFormatEtc that is used in IDataObject::EnumFormatEtc. Calling OleStdEnumFmtEtc_Create returns an instance of the enumerator. Notice that dwDirection must be DATADIR_GET; no other options are supported.

HRESULT IDataObject_EnumFormatEtc(

        LPDATAOBJECT            lpThis,

        DWORD                   dwDirection,

        IEnumFORMATETC **       ppenumFormatEtc

)

{

LPDOC lpDoc=((struct CDocDataObjectImpl *)lpThis)->lpDoc;

HRESULT hrErr;

LPAPP  lpApp = (LPAPP)g_lpApp;

int nActualFormats;

HRESULT hrErr = S_OK;

*lplpenumFormatEtc = NULL;

if (! ((LPDOC)lpDoc)->m_fDataTransferDoc)

return OLE_S_USEREG;

if (dwDirection == DATADIR_GET) {

nActualFormats = lpApp->m_nDocGetFmts;

// Moniker not available - exclude CF_LINKSOURCE and

// CF_LINKSRCDESCRIPTOR. These formats deliberately listed

// last in the array of possible "Get" formats.

if (! lpDoc->m_fLinkSourceAvail)

nActualFormats == 2;

// Call sample library enumerator method

*ppenumFormatEtc = OleStdEnumFmtEtc_Create(

nActualFmts, lpApp->m_arrDocGetFmts);

if (*ppenumFormatEtc == NULL)

hrErr = E_OUTOFMEMORY;

else

return S_OK;

} else if (dwDirection == DATADIR_SET) {

return E_NOTIMPL;

    } else {

return E_INVALIDARG;

    }

See Also

IDataObject::SetData, IDataObject::GetData, OleRegEnumFormatEtc


IDataObject::DAdvise

HRESULT IDataObject::DAdvise(pformatetc, advf, pAdvSink, pdwConnection)
FORMATETC * pformatetc
DWORD advf
IAdviseSink *pAdvSink
DWORD * pdwConnection

IDataObject::DAdvise creates a connection between the data-transfer object and an advisory sink through which the sink can be informed when the object's data changes.

Parameters

pformatetc

[in] Points to the format to advise about.

advf

[in] Contains a group of flags from the enumeration ADVF that specify information about the advisory connection.

pAdvSink

[in] Points to the advisory sink that should be informed of changes.

pdwConnection

[out] Points to where to return the token that can later be passed to IDataObject::DUnadvise to remove an advisory connection.

Return Values

Value	Meaning
S_OK	The connection was successfully created.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Invalid value for lindex; currently only -1 is supported.
DV_E_FORMATETC	Cannot support the requested media.
OLE_E_ADVISENOTSUPPORTED	Advisory notifications are not supported.
Remarks

Data-transfer object implementations of IDataObject typically do not support advisory notifications and return OLE_E_ADVISENOTSUPPORTED from IDataObject::DAdvise.

Callers register through this method to be notified when data in the format and medium specified in pformatetc changes. When a change occurs, a call is made to pAdvSnk->OnDataChange. Those who implement IDataObject::DAdvise can use the functionality provided in the IDataAdviseHolder interface. OLE supplies an IDataAdviseHolder implementation, accessible by calling CreateDataAdviseHolder.

If an advisory connection is successfully established, the object returns a nonzero value through pdwConnection; if a connection fails to be established, pdwConnection is set to zero and returns an error code zero. The caller can remove the established connection by passing the nonzero value of pdwConnection back to the object in a call to IDataObject::DUnadvise.

Containers of linked objects can set up advisory connections directly with the bound link source or indirectly via 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 it. 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 that 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 just 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.

Example

The following example of IDataObject::DAdvise illustrates how to establish a wildcard advise and how to use the default implementation of IDataAdviseHolder. After initializing the out parameter, lpdwConnection, and checking that this implementation is not being exposed by a data-transfer object that does not support data-change notification, IDataObject_DAdvise determines whether the caller is setting up an advise for a format and medium that is supported. If so, CreateDataAdviseHolder is called to return an IDataAdviseHolder interface pointer so that IDataAdviseHolder::Advise can be called.

HRESULT IDataObject_DAdvise(

        IDataObject *       lpThis,

        FORMATETC *         pFormatetc,

        DWORD               advf,

        IAdviseSink *       pAdvise,

        DWORD *             lpdwConnection

)

{

    LPDOC lpDoc=((struct CDocDataObjectImpl *)lpThis)->lpDoc;

HRESULT hrErr;

    *lpdwConnection = 0;

    if (lpDoc->m_fDataTransferDoc) {

        hrErr = OLE_E_ADVISENOTSUPPORTED;

        goto error

    }

if ( !( pFormatetc->cfFormat == NULL && pFormatetc->ptd == NULL

&& pFormatetc->dwAspect == -1L && pFormatetc->lindex == -1L

&& pFormatetc->tymed == -1L)

&& (hrErr = IDataObject_QueryGetData

(lpThis, pFormatetc)) != NOERROR) {

goto error;

}

if (lpDoc->m_lpDataAdviseHldr == NULL &&

CreateDataAdviseHolder(&lpDoc->m_lpDataAdviseHldr) != NOERROR) {

hrErr = E_OUTOFMEMORY;

goto error

}

return ( lpDoc->m_lpDataAdviseHldr->lpVtbl->Advise(

                lpDoc->m_lpDataAdviseHldr,

                (IDataObject)&lpDoc->m_DataObject,

                pFormatetc,

                advf,

                pAdvise,

                lpdwConnection ));

}

error:

return hrErr;

}

See Also

IDataAdviseHolder interface, IAdviseSink::OnDataChange, IDataObject::DUnadvise


IDataObject::DUnadvise

HRESULT IDataObject::DUnadvise(dwConnection)
DWORD dwConnection

IDataObject::DUnadvise deletes an advisory connection previously established by IDataObject::DAdvise.

Parameters

dwConnection

[in] Specifies a nonzero value previously returned from IDataObject::DAdvise.

Return Values

Value	Meaning
S_OK	The connection was successfully deleted.
E_FAIL	The pdwConnection value does not indicate a valid connection.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_ADVISENOTSUPPORTED	This IDataObject implementation does not support data advises.
OLE_E_NOCONNECTION	There is no connection for this connection identifier.
Remarks

The dwConnection parameter is a nonzero value returned through pdwConnection in IDataObject::DAdvise. If this value does not actually indicate a valid connection, E_FAIL is returned. If the advisory connection being deleted was initially set up using IDataAdviseHolder::Advise, IDataAdviseHolder::Unadvise should be called to delete it.

Example

After determining that this method is not part of a data-transfer object implementation and that the interface pointer to IDataAdviseHolder is still valid, IDataObject_DUnadvise deletes the connection specified by dwConnection by calling IDataAdviseHolder::Unadvise.

HRESULT IDataObject_DUnadvise(IDataObject lpThis, DWORD dwConnection)

{

    LPDOC lpDoc=((struct CDocDataObjectImpl *)lpThis)->lpDoc;

HRESULT hrErr;

if (lpDoc->m_fDataTransferDoc){

hrErr = OLE_E_ADVISENOTSUPPORTED;

goto error;

}

if (lpDoc->m_lpDataAdviseHldr == NULL){

hrErr = E_FAIL;

goto error;

}

hrErr = lpDoc->m_lpDataAdviseHldr->lpVtbl->Unadvise

(lpDoc->m_lpDataAdviseHldr, dwConnection);

return hrErr;

}

See Also

IDataObject::DAdvise


IDataObject::EnumDAdvise

HRESULT IDataObject::EnumDAdvise(ppenumAdvise )
IEnumSTATDATA ** ppenumAdvise

IDataObject::EnumDAdvise enumerates the advisory connections currently established on an object.

Parameters

ppenumAdvise

[out] Points to where the new enumerator should be returned. If NULL is returned, there are either no connections or enumeration of them is not supported by this data object. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator returned successfully.
E_FAIL	Enumerator cannot be returned.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_ADVISENOTSUPPORTED	Advisory notifications not supported.
Remarks

The returned enumerator enumerates data stored in STATDATA structure format. The STATDATA structure is defined as follows:

typedef struct tagSTATDATA

{

    FORMATETC formatetc;

    DWORD advf;

    LPADVISESINK pAdvSink;

    DWORD dwConnection;

} STATDATA;

While an enumeration is in progress, registering or revoking advisory connections on what is later enumerated is undefined. If there are no connections on the object, NULL is returned through ppenumAdvise.

When a STATDATA structure returned by the IEnumSTATDATA enumerator is released, the pAdvSink member variable should be released because its reference count will have been incremented by the implementation of the enumerator.

Example

IDataObject::EnumDAdvise below is implemented much like IDataObject::DUnadvise. Checks are made for an implementation that supports data-change notifications and a valid IDataAdviseHolder interface pointer after which the corresponding IDataAdviseHolder method is called to return the enumerator.

HRESULT IDataObject_EnumDAdvise(

        IDataObject *  pThis,

        IEnumSTATDATA ** ppenumAdvise

)

{

LPOLEDOC lpOleDoc=((struct CDocDataObjectImpl *)pThis)->lpOleDoc;

LPDOC lpDoc = (LPDOC)lpDOC;

HRESULT hrErr;

*ppenumAdvise = NULL;

if (lpDoc->m_fDataTransferDoc){

hrErr = OLE_E_ADVISENOTSUPPORTED;

goto error;

}

#if defined( OLE_SERVER)

{

LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;

if (lpServerDoc->m_lpDataAdviseHldr == NULL){

hrErr = E_FAIL;

goto error;

}

hrErr = lpServerDoc->m_lpDataAdviseHldr->lpVtbl->EnumAdvise(

lpserverDoc->m_lpDataAdviseHldr,

lplpenumAdvise

);

error:

return hrErr;

}


CreateDataAdviseHolder

HRESULT CreateDataAdviseHolder(ppDAHolder)
LPDATAADVISEHOLDER  * ppDAHolder

CreateDataAdviseHolder returns an instance of the IDataAdviseHolder interface. The IDataAdviseHolder methods are used by objects that support data-advisory connections to manage registration and notification; IDataAdviseHolder is described in the next section.

Parameters

ppDAHolder

Points to where to return the new interface instance.

Return Values

Value	Meaning
S_OK	IDataAdviseHolder instance successfully returned.
E_OUTOFMEMORY	IDataAdviseHolder instance could not be returned due to lack of memory.


IDataAdviseHolder Interface

The IDataAdviseHolder interface keeps track of the set of IDataObject::DAdvise calls and sends data-change notifications when appropriate.

Object applications and handlers implementing IDataObject::DAdvise, DUnadvise, and EnumDAdvise can forward these calls to the corresponding methods in IDataAdviseHolder. The examples shown in the discussion of these IDataObject methods illustrates how to do this.

An implementation of the IDataAdviseHolder interface is provided by OLE and may be used by object applications. It contains the following methods:

DECLARE_INTERFACE_(IDataAdviseHolder, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppv);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IDataAdviseHolder methods ***

  HRESULT Advise (THIS_ IDataObject pDataObject, FORMATETC * pFetc,

    DWORD advf, IAdviseSink * pAdvise, DWORD * pdwConnection);

  HRESULT Unadvise (THIS_ DWORD dwConnection);

  HRESULT EnumAdvise (THIS_ IEnumSTATDATA** ppenumAdvise);

  HRESULT SendOnDataChange (THIS_ IDataObject * pDataObject, DWORD dwReserved,

    DWORD advf);

};


IDataAdviseHolder::Advise

HRESULT IDataAdviseHolder::Advise(pDataObject, pFetc, advf, pAdvise, pdwConnection)
IDataObject *pDataObject
FORMATETC * pFetc
DWORD advf
IAdviseSink *pAdvise
DWORD * pdwConnection

IDataAdviseHolder::Advise creates a connection between the data object and an advisory sink through which the sink can be informed when the object's data changes.

Parameters

pDataObject

[in] Points to the source of the data.

pFetc

[in] Points to the format to use when reporting changes to the specified sink.

advf

[in] Contains a group of flags from the enumeration ADVF that specify information about the advisory connection. For more information, see ADVF enumeration.

pAdvise

[in] Points to the advisory sink that should be informed of changes.

pdwConnection

[out] Points to where to return the token that can later be used to delete an advisory connection by passing it to IDataAdviseHolder::Unadvise.

Return Values

Value	Meaning
S_OK	The advisory connection was created.
E_INVALIDARG	One or more arguments are invalid.
DV_E_LINDEX	Invalid value for lindex; currently only -1 is supported.
Remarks

Callers register to be notified when data in the format and medium specified in pFetc changes. A call is made to pAdvise->OnDataChange when a change occurs.

If an advisory connection is successfully established, the object returns a nonzero value through pdwConnection. If a connection fails to be established, it returns zero. The caller can delete the established connection by passing the nonzero value of pdwConnection back to the object in a call to IDataAdviseHolder::Unadvise.

See Also

IDataObject::DUnadvise, CreateDataAdviseHolder


IDataAdviseHolder::Unadvise

HRESULT IDataAdviseHolder::Unadvise(dwConnection)
DWORD	dwConnection

IDataAdviseHolder::Unadvise deletes an advisory connection previously established by IDataAdviseHolder::Advise.

The dwConnection parameter here is a nonzero value returned through the pdwConnection parameter in IDataAdviseHolder::Advise. If this value does not actually indicate a valid connection, E_FAIL is returned.

Parameters

dwConnection

[in] Specifies a nonzero value previously returned from IDataAdviseHolder::Advise.

Return Values

Value	Meaning
S_OK	The previously established advisory connection was successfully deleted.
OLE_E_NOCONNECTION	The dwConnection parameter does not represent a valid connection.


IDataAdviseHolder::EnumAdvise

HRESULT IDataAdviseHolder::EnumAdvise(ppenumAdvise )
IEnumSTATDATA ** ppenumAdvise

IDataAdviseHolder::EnumAdvise enumerates the advisory connections currently established on an object.

Parameters

ppenumAdvise

[out] Points to where the new enumerator should be returned. NULL indicates there are presently no connections. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator successfully returned.
E_OUTOFMEMORY	Enumerator could not be returned due to lack of memory.
Remarks

While an enumeration is in progress, the effect of registering or revoking advisory connections on what is later enumerated is undefined. If there are no connections to the object, NULL is returned through ppenumAdvise.

See Also

IDataObject::EnumDAdvise


IDataAdviseHolder::SendOnDataChange

HRESULT IDataAdviseHolder::SendOnDataChange(pDataObject, dwReserved, advf)
IDataObject *pDataObject
DWORD dwReserved
DWORD advf

IDataAdviseHolder::SendOnDataChange calls IAdviseSink::OnDataChange for all advisory sinks currently registered with the data-advise holder according to the advf flag parameter.

Parameters

pDataObject

[in] Points to the source of the data to be passed in the IAdviseSink::OnDataChange call(s). This is the object in which the data change has just occurred.

dwReserved

[in] Reserved, must be zero.

advf

[in] Contains a group of flags from the enumeration ADVF that specify information about the notification to be sent.

Return Values

Value	Meaning
S_OK	Data change notification successfully sent.
E_OUTOFMEMORY	Notification could not be sent due to lack of memory.
Remarks

Unless the ADVF_NODATA flag is specified when the advisory connection is initially set up, data is passed with the IAdviseSink::OnDataChange call. This data is obtained by calling pDataObject->GetData.

The value for advf is typically NULL. The only exception occurs when the object is shutting down and is sending the last data-change notification for one or more clients that request only one data notification when the object shuts down. In this case, the value ADVF_DATAONSTOP should be passed for advf. None of the other ADVF flags are meaningful for SendOnDataChange. For more information, see ADVF enumeration.


IEnumFORMATETC Interface

The IEnumFORMATETC interface enumerates arrays of FORMATETC data structures. IEnumFORMATETC is one of the standard enumerator interfaces described in Component Object Interfaces and Functions. Applications can implement the IEnumFORMATETC interface to use in IDataObject::EnumFormatEtc to enumerate the entries in a data-transfer object's FORMATETC structure.

When releasing a FORMATETC returned by IEnumFORMATETC free any memory pointed to by FORMATECT.ptd.


IEnumSTATDATA Interface

The IEnumSTATDATA interface enumerates through a set of data-change notifications. IEnumSTATDATA is one of the standard enumerator interfaces described in Component Object Interfaces and Functions. Applications can implement IEnumSTATDATA to use in IDataObject::EnumDAdvise or take advantage of the implementation provided in IDataAdviseHolder::EnumAdvise.

The IEnumSTATDATA interface contains the following methods:

DECLARE_INTERFACE_(IEnumSTATDATA, IUnknown)

{

    // *** IUnknown methods ***

HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppv);

ULONG, AddRef (THIS);

ULONG, Release (THIS);

    // *** IEnumSTATDATA methods ***

HRESULT Next (THIS_ ULONG celt, STATDATA * rgelt, ULONG *
 pceltFetched);

HRESULT Skip (THIS_ ULONG celt);

HRESULT Reset (THIS);

HRESULT Clone (THIS_ IEnumSTATDATA ** ppenum);

};


IViewObject Interface

IViewObject supports the display and printing of data and the registration of view-change notifications.

The IViewObject interface lets an object be drawn on a caller-provided device context. The caller can also ask the object to compose a picture for a target device that is potentially different than the device context in which the object will actually be drawn. This is particularly useful for implementing print preview. Different object representations can also be selected. For example, a caller can ask for an embedded object's content or iconic representation.

Unlike most interfaces, IViewObject is never accessed remotely. Architectural considerations prohibit the remote access of an instance of the IViewObject interface between processes. Servers that have special drawing requirements (such as drawing PostScript data) should implement a custom drawing handler for containers to use. For more information, see OLE: Custom Interfaces.The IViewObject interface contains the following methods:

DECLARE_INTERFACE_(IViewObject, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IViewObject methods ***

  HRESULT Draw (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DVTARGETDEVICE * ptd, HDC hicTargetDev,

    HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,

    BOOL (CALLBACK * pfnContinue) (DWORD), DWORD dwContinue);

  HRESULT GetColorSet (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DVTARGETDEVICE * ptd, HDC hicTargetDev,

    LPLOGPALETTE ** ppColorSet);

  HRESULT Freeze (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DWORD * pdwFreeze);

  HRESULT Unfreeze (THIS_ DWORD dwFreeze);

  HRESULT SetAdvise (THIS_ DWORD aspects, DWORD advf,

    IAdviseSink * pAdvSnk);

  HRESULT GetAdvise (THIS_ DWORD * pAspects, DWORD * pAdvf,

    IAdviseSink ** ppAdvSnk);

};


IViewObject::Draw

HRESULT IViewObject::Draw(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev, hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue)
DWORD dwDrawAspect
LONG  lindex
void * pvAspect
DVTARGETDEVICE * ptd
HDC hicTargetDev
HDC hdcDraw
LPCRECTL lprcBounds
LPCRECTL lprcWBounds
BOOL (CALLBACK * pfnContinue) (DWORD)
DWORD dwContinue

IViewObject::Draw draws a pictorial representation of an object on a device context.

Parameters

dwDrawAspect

[in] Specifies the requested view of the object. This parameter contains only one value taken from the enumeration DVASPECT. For more information, see the description of FORMATETC.

lindex

[in] Indicates the piece of the object that is of interest. Currently, only -1 is supported. Any other value results in an error.

pvAspect

[in] Currently, this pointer must be NULL.

ptd

[in] Points to the target device for which the picture should be rendered. If this value is NULL, the picture should be rendered for a default target device, usually a display device.

hicTargetDev

[in] Specifies the information context on the target device indicated by ptd. This can be a device context, but is not necessarily. If ptd is NULL, hicTargetDev must also be NULL.

hdcDraw

[in] Specifies the device context on which the drawing should actually be created.

lprcBounds

[in] Points to a RECT structure that indicates the rectangle on which the object should be drawn. This parameter controls the positioning and stretching of the object.

lprcWBounds

[in] A NULL pointer unless hdcDraw is a metafile device context. If it is non-NULL, lprcWBounds is used in the enumeration of the metafile records to adjust the records' coordinates. This parameter points to a RECT structure defining the bounding rectangle of the metafile underneath hdcDraw. The rectangle indicated by lprcBounds is nested inside this rectangle and they are in the same coordinate space. The left and top members of the RECT structure specify the window origin, but the right and bottom members specify the window extents (not the bottom-right corner point as in a true RECT structure).

pfnContinue

[in] Points to a callback function that the view object should call periodically during a lengthy drawing operation to determine whether the operation should be canceled. The callback function should be exported; otherwise, a GP fault occurs if it attempts to access a global variable.

If the callback function returns FALSE, drawing should be terminated and the IViewObject::Draw implementation should return E_ABORT.

dwContinue

[in] Contains a value that should be passed back as the argument to the function in pfnContinue.

Return Values

Value	Meaning
S_OK	Object successfully drawn.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_BLANK	No data to draw from.
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 dwDrawAspect.
OLE_E_INVALIDRECT	Invalid rectangle.
Remarks

The hicTargetDev parameter is typically an information context on the ptd target device. Objects almost always need an information context for the target device. However, callees should be aware that it might be a full device context instead.

For IViewObject::Draw, there is a relationship between the dwDrawAspect value and 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 aspect ratio. The container can suggest a size via IOleObject::SetExtent. 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 lprcBounds.

Note  Object handlers (such as the default handler) that implement IViewObject:Draw by playing a metafile 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—they must be set to the background palette. Use EnumMetaFile to do this. Enhanced metafiles are always recorded with background palette so there is no need to do it manually.


IViewObject::GetColorSet

HRESULT IViewObject::GetColorSet(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev, ppColorSet)
DWORD dwDrawAspect
LONG lindex
void * pvAspect
DVTARGETDEVICE * ptd
HDC hicTargetDev
LOGPALETTE ** ppColorSet

IViewObject::GetColorSet returns the set of colors that would be used by a call to IViewObject::Draw with the corresponding parameters.

Parameters

dwDrawAspect

[in] Specifies the requested view of the object. This parameter contains only one value taken from the enumeration DVASPECT. For more information, see the description of FORMATETC.

lindex

[in] Indicates the piece of the object that is of interest. Currently only -1 is supported. Any other value results in an error.

pvAspect

[in] Currently, this pointer must be NULL.

ptd

[in] Points to the target device for which the picture should be rendered. If this value is NULL, the picture should be rendered for a default target device, usually a display device.

hicTargetDev

[in] Specifies the information context on the target device indicated by ptd. This parameter can be a device context, but is not necessarily. If ptd is NULL, hicTargetDev must be also.

ppColorSet

[out] Points to where to return the set of colors that would be used. If the object does not return the color set, NULL is returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Set of colors successfully returned.
S_FALSE	Set of colors is empty or the object will not give the information out.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
DV_E_LINDEX	Invalid value for lindex; currently only -1 is supported.
DV_E_DVASPECT	Invalid value for dwDrawAspect.
Remarks

IViewObject::GetColorSet 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 the colors it needs to set the overall color palette.

The default handler implements IViewObject::GetColorSet by looking at the data it has on hand to draw the picture. If the drawing format is a DIB, the palette found in the DIB is used. For a regular bitmap, no color information is returned. If the drawing format is a metafile, the handler enumerates the metafile, looking for a CreatePalette metafile record. If one is found, the handler uses it as the color set.

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 handler returns S_FALSE.


IViewObject::Freeze

HRESULT IViewObject::Freeze(dwDrawAspect, lindex, pvAspect, pdwFreeze)
DWORD dwDrawAspect
LONG lindex
void * pvAspect
DWORD * pdwFreeze

IViewObject::Freeze informs an object that it should not change its drawn representation until a subsequent IViewObject::Unfreeze is called.

Parameters

dwDrawAspect

[in] Specifies the requested view of the object. This parameter contains only one value taken from the enumeration DVASPECT.

lindex

[in] Indicates the piece of the object that is of interest. Currently only -1 is supported. Any other value results in an error.

pvAspect

[in] Currently, this pointer must be NULL.

pdwFreeze

[out] Points to where to return the key that is later passed to IViewObject::Unfreeze. This key is an index that the default cache uses to keep track of which object is frozen.

Return Values

Value	Meaning
S_OK	Presentation successfully frozen.
VIEW_S_ALREADYFROZEN	Presentation has already been frozen.
OLE_E_BLANK	Nothing in the cache.
DV_E_LINDEX	Invalid value for lindex; currently, only -1 is supported.
DV_E_DVASPECT	Invalid value for dwDrawAspect.
Remarks

After the call to IViewObject::Freeze, successive calls to IViewObject::Draw (using the same parameters) produce the same picture, until IViewObject::Unfreeze is called. The most common use of this method is for banded printing.

While the presentation is in a frozen state, view notifications are not sent. Pending view notifications are deferred to the subsequent call to IViewObject::Unfreeze.

See Also

IViewObject::Unfreeze


IViewObject::Unfreeze

HRESULT IViewObject::Unfreeze(dwFreeze)
DWORD dwFreeze

IViewObject::Unfreeze unfreezes a previously frozen drawing by calling IViewObject::Freeze.

Parameters

dwFreeze

[in] Contains a key previously returned from IViewObject::Freeze. This key determines which object to unfreeze.

Return Values

Value	Meaning
S_OK	Drawing was successfully unfrozen.
OLE_E_NOCONNECTION	Error in the unfreezing process or the object is currently not frozen.
See Also

IViewObject::Freeze


IViewObject::SetAdvise

HRESULT IViewObject::SetAdvise(aspects, advf, pAdvSink)
DWORD aspects
DWORD advf
IAdviseSink *pAdvSink

IViewObject::SetAdvise sets up an advisory connection between the view object and an advisory sink through which the sink can be informed of changes made to an object's drawings.

Parameters

aspects

[in] Specifies the view of the object for which the advisory connection is to be established. This parameter contains only one value taken from the enumeration DVASPECT.

advf

[in] Contains a group of flags from the enumeration ADVF that specify information about the advisory connection.

pAdvise

[in] Points to the advisory sink that is to be informed of changes. A NULL value deletes any existing advisory connection.

Return Values

Value	Meaning
S_OK	The advisory connection was successfully established.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_ADVISENOTSUPPORTED	Advisory notifications are not supported.
DV_E_DVASPECT	Invalid value for aspects.
Remarks

Callers register with this method to be notified when an object's view changes. When a change does occur, a call is made to pAdvSnk->OnViewChange. At any time, a given IViewObject instance can support only one advisory connection. An existing advisory connection can be deleted by calling IViewObject::SetAdvise with pAdvSnk set to NULL.

See Also

IViewObject::GetAdvise, IAdviseSink::OnDataChange


IViewObject::GetAdvise

HRESULT IViewObject::GetAdvise(pAspects, pAdvf, ppAdvSink)
DWORD * pAspects
DWORD * pAdvf
IAdviseSink ** ppAdvSink

IViewObject::GetAdvise retrieves the existing advisory connection, if any, on the object.

Parameters

pAspects

[out] Points to where to return the most recent SetAdvise(dwAspect, ...) parameter. It can be NULL, indicating that the caller does not want this value returned.

pAdvf

[out] Analogous to pAspect, but with respect to the advf parameter to SetAdvise. Can be NULL.

ppAdvSink

[out] Analogous to pdwAspect, but with respect to the pAdvSnk parameter to SetAdvise. Can be NULL. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The existing advisory connection was retrieved.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
Remarks

IViewObject::GetAdvise returns the values passed in the most recent call to IViewObject::SetAdvise.

See Also

IViewObject::SetAdvise


IViewObject2 Interface

The IViewObject2 interface is an extension to IViewObject implemented by DLL object applications and object handlers. A default implementation is provided with the OLE libraries. The interface is used by containers and object handlers to get the view extents of an object. IViewObject2 contains the following methods:

DECLARE_INTERFACE_(IViewObject2, IViewObject)

{

    // *** IUnknown methods ***

    HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

    ULONG AddRef (THIS) ;

    ULONG Release (THIS);

    // *** IViewObject methods ***

  HRESULT Draw (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DVTARGETDEVICE * ptd, HDC hicTargetDev,

    HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,

    BOOL (CALLBACK * pfnContinue) (DWORD), DWORD dwContinue);

  HRESULT GetColorSet (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DVTARGETDEVICE * ptd, HDC hicTargetDev,

    LPLOGPALETTE ** ppColorSet);

  HRESULT Freeze (THIS_ DWORD dwDrawAspect, LONG lindex,

    void * pvAspect, DWORD * pdwFreeze);

  HRESULT Unfreeze (THIS_ DWORD dwFreeze);

  HRESULT SetAdvise (THIS_ DWORD aspects, DWORD advf,

    IAdviseSink * pAdvSink);

  HRESULT GetAdvise (THIS_ DWORD * pAspects, DWORD * padvf,

    IAdviseSink ** ppAdvSink);

};

    // *** IViewObject2 methods ***

    HRESULT GetExtent (THIS_ DWORD dwDrawAspect, LONG lindex,

                    DVTARGETDEVICE * ptd, LPSIZEL lpsizel);

};


IViewObject2::GetExtent

IViewObject2::GetExtent(dwDrawAspect, lindex, ptd, lpsizel)
DWORD dwDrawAspect
DWORD lindex
DVTARGETDEVICE *ptd
LPSIZEL lpsizel

IViewObject2::GetExtent retrieves the view extent of an object, returning the size of the object in the cache.

Parameters

dwDrawAspect

[in] Specifies the requested view of the object. This parameter contains only one value taken from the enumeration DVASPECT. For more information, see the description of FORMATETC.

lindex

[in] Indicates the piece of the object that is of interest. Currently, only -1 is supported. Any other value results in an error.

ptd

[in] Points to the target device for which the object's extent should be returned.

lpsizel

[out] Points to where the object's extent is to be returned.

Return Values

Value	Meaning
S_OK	The object's extent was successfully returned.
OLE_E_BLANK	Appropriate cache is not available.
E_OUTOFMEMORY	Insufficient memory.
Remarks

OLE provides a default implementation of IViewObject2::GetExtent that searches only the cache for the extent. This method is never remoted to the object application. To prevent the object from being run if it isn't already running, containers call IViewObject2::GetExtent rather than IOleObject::GetExtent to determine the extents of the presentation to be drawn.

See Also

IOleObject::GetExtent


IOleCache Interface

The IOleCache interface lets objects control what data gets cached inside the container's process on behalf of the object. This determines the data that will be available to the container when the object is not running or is unavailable.

The IOleCache interface is implemented by OLE and is used by container applications. IOleCache contains the following methods:

DECLARE_INTERFACE_(IOleCache, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IOleCache methods ***

  HRESULT Cache (THIS_ FORMATETC * pformatetc, DWORD advf,

    DWORD *pdwConnection);

  HRESULT Uncache (THIS_ DWORD dwConnection);

  HRESULT EnumCache (THIS_ IEnumSTATDATA ** ppenumSTATDATA);

  HRESULT InitCache (THIS_ IDataObject * pDataObject);

  HRESULT SetData (THIS_ FORMATETC *pformatetc, STGMEDIUM * pmedium,

    BOOL fRelease);

};


IOleCache::Cache

HRESULT IOleCache::Cache(pformatetc, advf, pdwConnection)
FORMATETC *pformatetc
DWORD advf
DWORD * pdwConnection

IOleCache::Cache specifies the formats and other data to be cached inside an embedded object.

Parameters

pformatetc

[in] Points to the data formats that are to be cached.

advf

[in] Contains a group of flags from the enumeration ADVF that specify information about the cache connection. Valid values for IOleCache::Cache are:

ADVF_NODATA
ADVF_ONLYONCE
ADVF_PRIMEFIRST
ADVFCACHE_FORCEBUILTIN
ADVFCACHE_ONSAVE

More detailed information about the ADVF enumeration can be found at the beginning of this section.

pdwConnection

[out] Points to a returned token value that can later be used to turn caching off. The OLE implementation of caching always uses nonzero numbers for connection identifiers.

Return Values

Value	Meaning
S_OK	Requested data or view successfully cached.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
CACHE_S_FORMATETC_ NOTSUPPORTED	Indicates the cache was created, but the object application does not support the specified format. Cache creation will succeed even if the format is not supported, allowing the caller to fill the cache. If the caller does not need to keep the cache, IOleCache::UnCache should be called.
CACHE_S_SAMECACHE	Indicates a cache already exists for the FORMATETC passed to IOleCache::Cache. In this case, the new advise flags are assigned to the cache, and the previously assigned connection identifier is returned. The new advise flags are assigned to the cache, and the previously assigned connection identifier are to be  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 static object and it already has a cache node.
Remarks

IOleCache::Cache can specify either data caching by passing a valid data format in pformatetc or view (presentation) caching by passing a zero data format in pformatetc as follows:

pformatetc->cfFormat == 0

With view caching, the object itself decides on the format to cache.

A custom object handler is not required to store data in a given format. Instead, it can synthesize it on demand when requested.

The advf value of ADVFCACHE_FORCEBUILTIN ensures that presentation data can be retrieved after the container document has been moved where the object application or object handler is not available.

When IOleCache::Cache is called with pformatetc->dwAspect set to DVASPECT_CONTENT, pformatetc->lindex set to -1, and pformatetc->ptd set to NULL for static objects, no cache is created and pdwConnection will be NULL. S_OK is returned, however, because the caching code is able to draw from the native data.

When an IOleCache::Cache call is made to cache either CF_DIB or CF_BITMAP, the OLE cache implementation will implicitly cache the other format. Thus, the enumerator (IOleCache::EnumCache) will have two entries (CF_DIB and CF_BITMAP) for the one cached format. Both of these entries have the same connection ID, so one call to IOleCache::Uncache will remove both. CF_DIB and CFBITMAP may also be explicitly cached separately (by two calls to IOleCache::Cache.

See Also

IOleCache::Uncache


IOleCache::Uncache

HRESULT IOleCache::Uncache(dwConnection)
DWORD dwConnection

IOleCache::Uncache deletes a cache connection created with IOleCache::Cache.

Parameters

dwConnection

[in] Contains the nonzero value previously returned through the pdwConnection parameter in a call to IOleCache::Cache.

Return Values

Value	Meaning
S_OK	The cache connection was deleted.
OLE_E_NOCONNECTION	No cache connection exists for dwConnection.
See Also

IOleCache::Cache


IOleCache::EnumCache

HRESULT IOleCache::EnumCache(ppenumSTATDATA)
IEnumSTATDATA ** ppenumSTATDATA

IOleCache::EnumCache enumerates presently established cache connections.

Parameters

ppenumSTATDATA

[out] Points to where to return the new enumerator. NULL is a legal value and indicates that there are currently no connections. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The cache enumerator successfully returned.
E_OUTOFMEMORY	The cache enumerator could not be returned due to lack of memory.
See Also

IOleCache::Cache


IOleCache::InitCache

HRESULT IOleCache::InitCache(pDataObject)
IDataObject * pDataObject

IOleCache::InitCache fills the cache using the data provided by the data object, pDataObject.

Parameters

pDataObject

[in] Points to the data object from which the cache is to be initialized.

Return Values

Value	Meaning
S_OK	The cache was filled using the data provided.
E_INVALIDARG	The value is not valid for pDataObject.
E_OUTOFMEMORY	The cache could not be initialized due to lack of memory.
OLE_E_NOTRUNNING	The cache is not running.
CACHE_E_NOCACHE_UPDATED	Indicates none of the caches were updated.
CACHE_S_SOMECACHES_NOTUPDATED	Indicates only some of the existing caches were updated.
Remarks

IOleCache::InitCache is usually called when an object is created with Embed Source or Link Source data, either by OleCreateFromData, OleCreateLinkFromData or containers that do not use either of these helper functions. Containers that do not use helper functions to create objects will usually want to use IOleCache::Cache to set up the cache entries which are then filled by InitCache.

See Also

IOleCache::Cache


IOleCache::SetData

HRESULT IOleCache::SetData(pformatetc, pmedium, fRelease)
FORMATETC * pformatetc
STGMEDIUM * pmedium
BOOL fRelease

IOleCache::SetData fills the cache using the data contained in the storage medium.

Parameters

pformatetc

[in] Points to the format of the data being set.

pmedium

[in] Points to the storage medium (an in parameter only) that contains the data.

fRelease

[in] Indicates ownership of the storage medium after completion of the method. If fRelease is TRUE, the callee 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 callee can only use the storage medium for the duration of the call.

Return Values

Value	Meaning
S_OK	The cache was filled.
E_OUTOFMEMORY	Ran out of memory.
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.
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. SetData is similar to InitCache; the difference is that SetData initializes the cache using a single format and InitCache uses all of the formats provided by the data object.

See Also

IOleCache::Cache, IOleCache::InitCache


IOleCache2 Interface

The IOleCache2 interface is an extension of the IOleCache interface that adds the ability for a client of an object to update each of the maintained caches. An implementation of the IOleCache2 interface is provided by OLE and may be used by containers and DLL object applications to update one or more caches created with IOleCache::Cache. IOleCache2 contains the following methods:

DECLARE_INTERFACE_(IOleCache2, IOleCache)

{

    // *** IUnknown methods ***

    HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

    ULONG AddRef (THIS) ;

    ULONG  Release (THIS);

    // *** IOleCache methods ***

  HRESULT Cache (THIS_ FORMATETC * pformatetc, DWORD advf,

    DWORD *pdwConnection);

  HRESULT Uncache (THIS_ DWORD dwConnection);

  HRESULT EnumCache (THIS_ IEnumSTATDATA ** ppenumSTATDATA);

  HRESULT InitCache (THIS_ IDataObject * pDataObject);

  HRESULT SetData (THIS_ FORMATETC *pFormatetc, STGMEDIUM * pmedium,

    BOOL fRelease);

    // *** IOleCache2 methods ***

    HRESULT UpdateCache (THIS_ LPDATAOBJECT pDataObject, DWORD dwgrfUpdf,

LPVOID pReserved);

    HRESULT DiscardCache (THIS_ DWORD dwDiscardOptions);

};

Even though a default implementation is provided with OLE, a DLL object application might need to provide its own implementation, depending on how it caches data.


IOleCache2::UpdateCache

HRESULT IOleCache2::UpdateCache(pDataObject, grfUpdf, pReserved)
LPDATAOBJECT pDataObject
DWORD grfUpdf
LPVOID pReserved

IOleCache2::UpdateCache updates the cache(s) according to the value of the grfUpdt parameter.

Parameters

pDataObject

[in] Points to the object that is the source for updating the cache; can be NULL.

grfUpdf

[in] Specifies the type of cache to update. See the Remarks section below.

pReserved

[in] Reserved parameter; must be NULL.

Return Values

Value	Meaning
S_OK	The cache(s) were updated according to the value specified in grfUpdt.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
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.
NOERROR	No nodes are to be updated, or any number of existing nodes were updated successfully.
Remarks

IOleCache2::UpdateCache can be called by containers or DLL object applications requiring more control over caching.

The pDataObject points to the source for updating the cache(s). Typically, non-NULL values are passed by handlers and DLL object applications. Containers most often pass a NULL pDataObject, in which case, the source is obtained from the running object.

The grfUpdt parameter controls the caches that get updated. 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 UPDFCACHE_IFBLANK | UPDFCACHE_ONSAVECACHE.
UPDFCACHE_ALL	Updates all caches.
UPDFCACHE_ ALLBUTNODATACACHE	Updates all caches except those created with ADVF_NODATA in the call to IOleCache::Cache.
See Also

IOleCache, IOleCacheControl


IOleCache2::DiscardCache

HRESULT IOleCache2::DiscardCache(dwDiscardOptions)
DWORD dwDiscardOptions

IOleCache2::DiscardCache flushes the cache(s) in memory. The cache will satisfy subsequent IDataObject::GetData calls by reverting to disk-based data.

Parameters

dwDiscardOptions

[in] Indicates whether data is to be saved prior to the discard. Valid values are from the enumeration DISCARDCACHE:

typedef enum tagDISCARDCACHE {

DISCARDCACHE_SAVEIFDIRTY 	= 0

DISCARDCACHE_NOSAVE 	= 1

} DISCARDCACHE;

DISCARDCACHE_SAVEIFDIRTY indicates that all of the dirty data in the cache(s) should be saved before the discard occurs. Containers that have drawn a large object and need to free memory may want to specify DISCARDCACHE_SAVEIFDIRTY so that the newest presentation is saved for the next time the object must be drawn.

DISCARDCACHE_NOSAVE indicates that no save is necessary. Containers that have activated an embedded object, made some changes, and then called IOleObject::Close(OLECLOSE_NOSAVE) to rollback the changes may specify this option to ensure that the native and presentation data are not out of sync.

Return Values

Value	Meaning
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 dirty data in the cache.
Remarks

IOleCache2::DiscardCache is useful when a low memory condition exists. It is intended to tell the cache to discard memory

Concerning DISCARDCACHE_NOSAVE, it is possible for the screen presentation (drawn from the old data) to be out of sync with the cache's presentation. This is because the screen presentation was discarded and the only data available, if any, would be on disk.

See Also

IOleCache, IOleCacheControl


IOleCacheControl Interface

The IOleCacheControl interface is used almost exclusively by object handlers or DLL object applications as the means by which the cache part of the handler is connected to the running object's IDataObject implementation. This allows the cache to receive notifications from the running object. Containers have no need for this interface; they should use IRunnableObject or OleRun instead.

IOleCacheControl contains the following methods:

DECLARE_INTERFACE_(IOleCacheControl, IUnknown)

{

    // *** IUnknown methods ***

    HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

    ULONG AddRef (THIS) ;

    ULONG  Release (THIS);

    // *** IOleCacheControl methods ***

    HRESULT OnRun (THIS_ LPDATAOBJECT pDataObject);

    HRESULT OnStop (THIS);

};


IOleCacheControl::OnRun

HRESULT IOleCacheControl::OnRun(pDataObject)
LPDATAOBJECT pDataObject

IOleCacheControl::OnRun notifies the cache that the compound-document object that is its data source has entered its running state.

Parameters

pDataObject

Points to the object that is entering the running state.

Return Values

Value	Meaning
S_OK	The cache was notified and pDataObject is valid.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

When OnRun is called, the cache sets up advisory notifications as necessary. If the cache is already running, OnRun returns S_OK.

Some DLL object applications might use the cache in a passive manner 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 create a reference count on pDataObject. It is the responsibility of the caller of OnRun to ensure that the lifetime of pDataObject lasts until OnStop is called.

Calling IOleCacheControl::OnRun creates a data advisory sink between the running object and the cache. The sink is destroyed when IOleCacheControl::OnStop is called.

See Also

IOleCacheControl::OnStop, IOleCache2::UpdateCache


IOleCacheControl::OnStop

HRESULT 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.

Return Values

Value	Meaning
S_OK	The cache was notified and the advise sink was successfully removed.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The data advisory sink between the running object and the cache is destroyed as part of calling IOleCacheControl::OnStop.

See Also

IOleCacheControl::OnRun


Clipboard Functions

The following functions are used in conjunction with Clipboard- and other data- transfer operations.

OleSetClipboard(LPDATAOBJECT pDataObj);

OleGetClipboard(LPDATAOBJECT * ppDataObj);

OleFlushClipboard(void);

OleIsCurrentClipboard(LPDATAOBJECT pDataObj);


OleSetClipboard

HRESULT OleSetClipboard(pDataObj)
LPDATAOBJECT pDataObj

OleSetClipboard puts the indicated IDataObject instance on the Clipboard.

Parameters

pDataObj

Points to an IDataObject instance from which the data to be presented to the Clipboard can be obtained.  This parameter can be NULL, in which case the Clipboard is emptied.

Return Values

Value	Meaning
S_OK	pDataObj was placed on the Clipboard.
CLIPBRD_E_CANT_OPEN	OpenClipboard failed.
CLIPBRD_E_CANT_EMPTY	EmptyClipboard failed.
CLIPBRD_E_CANT_CLOSE	CloseClipboard failed.
Remarks

The reference count of the data object passed in will be incremented on success by OleSetClipboard. It will be released when one of the following events occurs:

•	OleSetClipboard is called using NULL.

•	OleFlushClipboard is called.

•	A call to EmptyClipboard is made, usually by another application copying data to the Clipboard.

All of the formats available from the data object will be offered on the Clipboard by means of delayed rendering. Note, however, that Clipboard data transfers are slightly more restrictive than generic IDataObject transfers. Data on the Clipboard consists of <Clipboard format, data> tuples, whereas data from an IDataObject is specified by a FORMATETC data structure (in which the cfFormat, ptd, and dwAspect members uniquely specify a piece of data). Therefore, data offered through the given data object should have a NULL target device and an aspect of DVASPECT_CONTENT. For more information, refer to the See Also section below.

Passing NULL for pDataObj is legal and it empties the Clipboard.

Note  If an application must leave data on the Clipboard after shutting down, it should call OleFlushClipboard. This will cause all of the data originally offered by the data object to actually be copied onto the Clipboard. If the application shuts down without calling OleFlushClipboard, any data it placed there with a call to OleSetClipboard will be lost.

See Also

OleGetClipboard


OleGetClipboard

HRESULT OleGetClipboard(ppDataObj)
LPDATAOBJECT  * ppDataObj

OleGetClipboard retrieves the instance of the IDataObject from the Clipboard that was placed there by using OleSetClipboard. If there is no data object on the Clipboard (either because the Clipboard owner called OleFlushClipboard or the Clipboard owner is not an OLE app), OleGetClipboard returns an OLE-provided instance of IDataObject that wraps the contents of the Clipboard.

Parameters

ppDataObj

Points to where to return the IDataObject instance.

Return Values

Value	Meaning
S_OK	The IDataObject instance was successfully retrieved.
E_OUTOFMEMORY	Ran out of memory.
CLIPBRD_E_CANT_OPEN	OpenClipboard failed.
Remarks

There are three interesting cases for OleGetClipboard:

1.	The app that called OleSetClipboard is still running.

2.	The app that called OleSetClipboard has called OleFlushClipboard.

3.	There is data from a non-OLE app on the Clipboard.

In the first case, OleGetClipboard will return an IDataObject pointer (potentially marshalled) directly back to the source process.

In the second case, OLE creates a default data object and returns it to the user. However, because the data on the Clipboard originated from an OleSetClipboard call, OLE has more accurate information about the type of data on the Clipboard. In particular, we know the original medium (TYMED) on which the data was offered. Thus, if a data-source application offers cfFOO on a storage and calls OleFlushClipboard, the storage is copied into an hglobal and the hglobal is put on the Clipboard. In the data object created by OleGetClipboard, the hglobal again becomes a storage. Furthermore, the FORMATETC enumerator and IDataObject::QueryGetData would all correctly indicate that cfFOO is available on a storage.

In the third case, OLE still creates a default data object, but we have no special information about the data in the Clipboard formats (particularly for application-defined Clipboard formats). Thus, if an hglobal-based storage were put on the Clipboard directly by a call to SetClipboardData, the FORMATETC enumerator and IDataObject::QueryGetData would not indicate that the data was available on a storage. A call to IDataObject::GetData for TYMED_ISTORAGE would succeed, however. Because of these limitations, it is strongly recommended that OLE-aware apps interact with the Clipboard using OLE Clipboard API's.

The data object created by OleGetClipboard (the second and third cases) has a fairly extensive IDataObject implementation. In particular, 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. Simple applications may take advantage of this OLE-provided functionality by calling OleFlushClipboard after calling OleSetClipboard. If the data to be placed on the Clipboard is large, this may cause a performance penalty because the data will be kept entirely in memory.

As a reminder, 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.

See Also

OleSetClipboard, OleFlushClipboard


OleFlushClipboard

HRESULT OleFlushClipboard()

OleFlushClipboard carries out the Clipboard shutdown sequence, copying all of the data from the data object given in OleSetClipboard onto the Clipboard and finally removing the IDataObject instance.

Return Values

Value	Meaning
S_OK	The Clipboard has been flushed.
CLIPBRD_E_CANT_OPEN	OpenClipboard failed.
CLIPBRD_E_CANT_CLOSE	CloseClipboard failed.
Remarks

For every Clipboard format offered by the data object, OleFlushClipboard will attempt to get data in a format that can be set on the Clipboard (namely, an hglobal or GDI object such as a metafile or bitmap). If the data object only offers a format on a storage or stream, for example, OleFlushClipboard copies the data onto a memory-based storage or stream and places the underlying global on the Clipboard. The data object returned by OleGetClipboard will be able to reconstruct a storage or stream from the hglobal on the Clipboard.

As noted in OleSetClipboard, the Windows Clipboard keeps data in <Clipboard format, data> tuples. For this reason, OleFlushClipboard will only ask the source data object for data with DVASPECT_CONTENT and a NULL target device.

See Also

OleGetClipboard


 OleIsCurrentClipboard

HRESULT OleIsCurrentClipboard(pDataObj)
LPDATAOBJECT pDataObj

OleIsCurrentClipboard determines whether the pDataObject put on the Clipboard by the caller using OleSetClipboard(pDataObject) is still on the Clipboard.

Parameters

pDataObj

Points to an IDataObject instance.

Return Values

Value	Meaning
S_OK	Indicates that pDataObj is currently on the Clipboard and the caller is the owner of the Clipboard.
S_FALSE	Indicates that pDataObj is not on the Clipboard.
See Also

OleSetClipboard, OleGetClipboard


Data Transfer, Caching, and Drawing Functions

The following functions are used in conjunction with drawing and caching operations.

OleDraw (LPUNKNOWN pUnknown, DWORD dwAspect, HDC hdcDraw,
     LPCRECT lprcBounds);

OleDuplicateData (HANDLE hSrc, CLIPFORMAT cfFormat, UINT uiFlags);
CreateDataCache (LPUNKNOWN pUnkOuter, REFCLSID rclsid, REFIID riid,
     LPVOID  * ppvLP);

ReleaseStgMedium (LPSTGMEDIUM pmedium);


OleDraw

HRESULT OleDraw(pUnknown, dwAspect, hdcDraw, lprcBounds)
LPUNKNOWN pUnknown
DWORD dwAspect
HDC	hdcDraw
LPCRECT lprcBounds

OleDraw is a helper function that can be used to draw objects.

Parameters

pUnknown

Points to the object to be drawn.

dwAspect

Contains a value taken from the enumeration DVASPECT, described earlier in this section.

hdcDraw

Specifies the device context onto which the drawing should actually be done. Cannot be a metafile device context.

lprcBounds

Points to a RECT structure that indicates the rectangle on which the object should be drawn. This parameter controls the positioning and stretching of the object.

Return Values

Value	Meaning
S_OK	Object was successfully drawn.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_BLANK	There is 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

OleDraw is a helper function that calls IViewObject::Draw (obtained from pUnknown via QueryInterface).

OleDraw should not be used to draw into a metafile because it does not specify the lprcWBounds parameter. This value is required for drawing into metafiles.

See Also

IViewObject::Draw


OleDuplicateData

HANDLE OleDuplicateData(hSrc, cfFormat, uiFlags)
HANDLE hSrc
CLIPFORMAT cfFormat
UINT uiFlags

OleDuplicateData is a helper function that duplicates metafiles, enhanced metafiles, bitmaps, and global memory and returns a new handle of the same type.

Parameters

hSrc

Specifies the handle of the source data.

cfFormat

Specifies the Clipboard format of the source data.

uiFlags

Specifies the flags to be used in global memory allocation. If the value of uiFlags is NULL, GMEM_MOVEABLE is used as a default.

Return Values

Value	Meaning
Handle to the duplicated data.	Data was successfully duplicated.
NULL	Error duplicating data.
Remarks

The CF_METAFILEPICT, CF__ENHMETAFILE, CF_PALETTE, and CF_BITMAP formats receive special handling; all other formats are duplicated byte-wise. For the formats that are duplicated byte-wise, hSrc must be a global memory handle.


CreateDataCache

HRESULT CreateDataCache(pUnkOuter, rclsid, riid, ppv)
LPUNKNOWN pUnkOuter
REFCLSID rclsid
REFIID riid
LPVOID  * ppv

CreateDataCache creates a cache object and returns a pointer to the interface identified by riid. The returned object supports IOleCache, IOleCache2, and IOleCacheControl for controlling the cache, IPersistStorage for reading and writing to persistent storage, IDataObject without advise support, IViewObject, and IViewObject2. CreateDataCache can be used with DLL object applications and handlers that do not use the OLE default handler as an aggregate object.

Parameters

pUnkOuter

Specifies the controlling unknown if this cache object is to be instantiated as part of an aggregate. Can be NULL, indicating that no aggregation will occur.

rclsid

Specifies the CLSID that is used only to generate default icon labels; most often, this will be CLSID_NULL.

riid

The interface that the caller is to use to communicate with the newly created cache, typically, this is IID_IOleCache.

ppv

Pointer to where to return the cache object.

Return Values

Value	Meaning
S_OK	A new instance of the OLE-provided cache implementation was returned.
E_INVALIDARG	One or more arguments are invalid.
E_OUTOFMEMORY	Ran out of memory.
E_NOINTERFACE	The interface represented by riid is not supported by the object; ppv is set to NULL.
See Also

IOleCache, IDataObject, IPersistStorage


ReleaseStgMedium

void ReleaseStgMedium(pmedium)
LPSTGMEDIUM pmedium

ReleaseStgMedium frees the specified storage medium.

Parameters

pmedium

Points to the storage medium that is to be freed.

Remarks

After this call, the storage medium is invalid and can no longer be used.

Sometimes the original provider of the medium wishes to maintain control of the freeing of the medium by specifying a non_NULL pUnkForRelease in the medium. If this is the case and the medium is TYMED_HGLOBAL, TYMED_MFPICT, or TYMED_GDI, ReleaseStgMedium does nothing. If the medium is TYMED_FILE, ReleaseStgMedium does nothing to the actual disk file. The filename string, however, is freed using the standard memory management mechanism. For TYMED_ISTREAM, ReleaseStgMedium calls IStream::Release; for TYMED_ISTORAGE, IStorage::Release is called. Following all these actions, pmedium->pUnkForRelease->Release is invoked.

If the receiving of the medium is to free it, as is indicated by pUnkForRelease being set to NULL, ReleaseStgMedium does the following:

Medium	ReleaseStgMedium Action
TYMED_HGLOBAL	Calls GlobalFree on the handle.
TYMED_GDI	Calls DeleteObject on the handle.
TYMED_MFPICT	The hMF that it contains is deleted with DeleteMetaFile, then the handle itself is passed to GlobalFree.
TYMED_ENHMF	Calls DeleteEnhMetaFile on the hEnhMetaFile handle in STGMEDIUM.
TYMED_FILE	Frees the disk file by deleting it. Frees the filename string by using the standard memory-management paradigm.
TYMED_ISTREAM	Calls IStream::Release.
TYMED_ISTORAGE	Calls IStorage::Release.


Icon Extraction Helper Functions


OleGetIconOfFile

HGLOBAL OleGetIconOfFile(lpszPath, fUseFileAsLabel)
LPOLESTR lpszPath
BOOL fUseFileAsLabel

OleGetIconOfFile returns a handle to a METAFILEPICT containing an icon and label (filename) for the specified filename.

Parameters

lpszPath

Points to the path, including the filename, to use to locate the file.

fUseFileAsLabel

Determines whether the icon's label is to be the filename (TRUE) or the short user type name (FALSE).

Return Values

Value	Meaning
HGLOBAL	METAPICT containing the icon and label. If lpszPath is NULL, NULL is returned.


OleGetIconOfClass

HGLOBAL OleGetIconOfClass(rclsid, lpszLabel, fUseTypeAsLabel)
REFCLSID rclsid
LPOLESTR lpszLabel
BOOL fUseTypeAsLabel

OleGetIconOfClass returns a hMetaPict containing an icon and human-readable label for the specified class.

Parameters

rclsid

Points to the CLSID to use.

lpszLabel

Points to the label to use for the icon.

fUseTypeAsLabel

Indicates whether to use the CLSID's user type name as the label for the icon (TRUE) or not (FALSE).

Return Values

Value	Meaning
HGLOBAL	hMetaPict containing the icon and label. If the CLSID is not found in the registry, NULL is returned.


OleMetafilePictFromIconAndLabel

HGLOBAL OleMetafilePictFromIconAndLabel(hIcon, lpszLabel, lpszSourceFile, iIconIndex)
HICON hIcon
LPOLESTR lpszLabel
LPOLESTR lpszSourceFile
UINT iIconIndex

OleMetafilePictFromIconAndLabel creates a METAFILEPICT structure (defined in WINDOWS.H) that contains a metafile in which the icon and label are drawn.

Parameters

hIcon

The handle to the icon that is to be drawn into the metafile.

lpszLabel

Points to the string that is to be used as the label for the icon.

lpszSourceFile

Points to the string, as obtained from the user or the registry, that contains the local path of the icon.

iIconIndex

Provides the index into lpszSourceFile from where the icon was obtained.

Return Values

Value	Meaning
HGLOBAL	Global memory handle containing a METAFILEPICT structure, where the metafile uses the MM_ANISOTROPIC mapping mode. The extents reflect both the icon and the label.


Linking Interfaces and Functions

This section describes the interfaces and functions that make linking possible. Most of these interfaces are implemented by OLE applications, and the following table provides a brief summary of the interfaces that support linking.

Interface Name	Implemented By	Used By
IMoniker	OLE and some object applications	Used by OLE to manage the binding of monikers to their sources.
IEnumMoniker	OLE	Used by OLE, certain containers, and object applications to enumerate items that are monikers.
IOleLink	OLE	Used by containers to manage monikers inside of links and to manage the link's update options.
IBindCtx	OLE	Used by OLE to accumulate the set of objects that are bound during an operation and that should be released when the operation is completed.
IRunningObjectTable	OLE	Used by OLE and object applications to access the running object table.
IParseDisplayName	Containers and object applications	Used by OLE to transform the display name of an object into a moniker.


What is a Moniker

In OLE 2, a moniker is a mechanism for linking and naming. It is a conceptual handle to a link at its source that can be stored with the linked object's consumer. A moniker's main operation is to bind, or connect, to the object to which it points. The binding process invokes whatever algorithms are necessary to locate and run the link source object.

Two types of monikers are stored for each linked object: one that represents the absolute path to the link source and one that provides a relative path. When a linked object is activated, the relative moniker is used first to bind to the link source, using the absolute moniker only if the relative one fails. Using a relative moniker in addition to the absolute moniker supports the following link-tracking situations:

•	The link source and the link consumer (the linked object) have been copied or moved but retain the same relative structure, such as two documents in the same directory. Another case is a link between two objects, both embedded in a third document. In these situations, the relative moniker succeeds in binding.

•	The link source does not move, but the link consumer does move, and in such a way that the relative structure is not maintained. In this case, the relative moniker fails to bind, but the absolute moniker does bind.


Monikers and Linked Objects

There are two architecturally distinct types of compound-document objects: linked and embedded. The difference between them is where the native representation of the object is persistently stored. An embedded object's native data is stored inside the container document, which also contains the presentation of the object. A linked object's native data is not kept with the linked object itself, but is kept with the source of the data, also known as the link source.

Compound-document applications can support different levels of linking. Object applications can support linking to file-based documents, as well as linking to ranges of items (pseudo objects) in the document.

Applications that support linking must create and register a file moniker for their documents. Existing documents typically use the filename for the file moniker; new documents create a unique, temporary name that is replaced when the document is saved. Applications call CreateFileMoniker to create the file moniker, passing it the moniker name and where to return the pointer to the IMoniker instance.

File monikers for existing documents are registered in the running object table, a global table that stores information about running link sources. Registering a document's file moniker ensures that the document can be accessed by its linking container(s). When a container attempts to bind to the linked object, the moniker within the linked object first checks the running object table to see whether the object is already running. If the moniker for the object is registered, the container is connected to the running instance of the object; otherwise, the object handler is loaded and the object is placed in the running state.

Applications that support linking to documents that have not yet been saved will register the new document's temporary moniker. Linking to unsaved documents can cause broken links if the container closes before the new document is saved and a permanent file moniker registered.

Note  Registering a file moniker in the running object table puts a weak reference lock on the document object. Although this lock is not strong enough to hold the document alive should the user decide to close it, the object should still revoke the moniker's registration as part of its close routine.


IMoniker Interface

The IMoniker interface is typically implemented and used by OLE. However, some advanced applications that do not organize their data in files, such as database-oriented applications, may need to implement a new moniker class. IMoniker contains the following methods:

DECLARE_INTERFACE_(IMoniker, IPersistStream)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IPersist methods ***

  HRESULT GetClassID (THIS_ CLSID ClassID);

    // *** IPersistStream methods ***

  HRESULT IsDirty (THIS);

  HRESULT Load (THIS_ lpSTREAM pStm);

  HRESULT Save (THIS_ LPSTREAM pStm, BOOL fClearDirty);

  HRESULT GetSizeMax (THIS_ ULARGE_INTEGER  * pcbSize);

    // *** IMoniker methods ***

  HRESULT BindToObject (THIS_ IBindCntext * pbc, IMoniker * pmkToLeft, REFIID riidResult,

    VOID ** ppvResult);

  HRESULT BindToStorage (THIS_ IBindContext * pbc, IMoniker pmkToLeft, REFIID riid, void ** ppvObj);

  HRESULT Reduce (THIS_ IBindContext * pbc, DWORD dwReduceHowFar, IMoniker ** ppmkToLeft,

    IMoniker ** ppmkReduced);

  HRESULT ComposeWith (THIS_ IMoniker ** pmkRight, BOOL fOnlyIfNotGeneric,

    IMoniker ** ppmkComposite);

  HRESULT Enum (THIS_ BOOL fForward, IEnumMoniker ** ppenumMoniker);

  HRESULT IsEqual (THIS_ IMoniker * pmkOtherMoniker);

  HRESULT Hash (THIS_ DWORD * pdwHash);

  HRESULT IsRunning (THIS_ IBindContext * pbc, IMoniker * pmkToLeft, IMoniker * pmkNewlyRunning);

  HRESULT GetTimeOfLastChange (THIS_ IBindContext * pbc, IMoniker * pmkToLeft, FILETIME * pFileTime);

  HRESULT Inverse (THIS_ IMoniker ** ppmk);

  HRESULT CommonPrefixWith (THIS_ IMoniker * pmkOther, IMoniker ** ppmkPrefix);

  HRESULT RelativePathTo (THIS_ IMoniker * pmkOther, IMoniker ** ppmkRelPath)

  HRESULT GetDisplayName (THIS_ IBindContext * pbc, IMoniker * pmkToLeft, LPOLESTR *

    ppszDisplayName);

  HRESULT ParseDisplayName (THIS_ IBindContext * pbc, IMoniker * pmkToLeft,

    LPOLESTR pszDisplayName, ULONG * pchEaten, IMoniker ** ppmkOut);

  HRESULT IsSystemMoniker (THIS_ DWORD pdwMksys);

};

The IMoniker interface contains a group of methods to control and access monikers. Different classes of monikers can exist; currently OLE supports these classes: generic composite, file, item, pointer, and anti- monikers. Supporting a moniker class means providing a unique implementation of the IMoniker interface for that class.

Generic composite (or simply composite) monikers are a sequenced collection of other monikers. In general, each type of moniker is designed to be part of a composite moniker that specifies the complete path to an object. The composite moniker here is generic in that it has no information about the pieces involved other than that they are monikers.

Each moniker class can store arbitrary data in its persistent representation and can run arbitrary code at binding time. If there is an identifiable piece of persistent storage in which the object is stored, IMoniker::BindToStorage can be used to gain access to it. Many objects (all OLE embedded objects, for example) have such identifiable storage. Others, such as the objects that are the ranges on a spreadsheet, do not.

The most basic operation in the IMoniker interface is IMoniker::BindToObject, which binds a moniker to the object to which it points. IMoniker::BindToObject runs whatever algorithm is necessary to locate the object and returns a pointer to a specified interface type to the caller.

Most monikers have a textual representation that is meaningful to the user and can be retrieved with IMoniker::GetDisplayName. The function MkParseDisplayName turns a textual display name into the appropriate moniker. Using this function is usually as expensive as actually binding to the object.

Monikers can compare themselves to other monikers using IMoniker::IsEqual. A hash value useful for storing monikers in lookup tables is available through IMoniker::Hash. The earliest time after which the object that the moniker points to is known not to have changed can be obtained with IMoniker::GetTimeOfLastChange.

A moniker can be requested to rewrite itself into an equivalent moniker by calling IMoniker::Reduce. This function returns a new moniker that binds to the same object, but does so in a more efficient way.

Pointers to instances of the IMoniker interface can be marshalled to other processes, just as with any other interface pointer. Despite the statement in the preceding paragraph, many monikers cannot be changed once they have been created; nor do they maintain an object state outside themselves. Item monikers are an example of a class of such monikers. These monikers, which can be replicated any time, usually support custom marshalling (see IMarshal interface) so they can serialize and deserialize themselves in the destination context (see IPersistStream regarding serialization).


IMoniker::BindToObject

HRESULT IMoniker::BindToObject(pbc, pmkToLeft, riidResult, ppvResult)
IBindContext *pbc
IMoniker *pmkToLeft
REFIID riidResult
VOID ** ppvResult

IMoniker::BindToObject locates and loads the object referenced by a given moniker.

Parameters

pbc

[in] Points to the bind context, an instance of the IBindCtx interface, to be used for this binding operation.

pmkToLeft

[in] Points to the moniker of the object to the left of this moniker.

riidResult

[in] Identifies the interface by which the caller wants to connect to the object.

ppvResult

 On a successful return, points to the instantiated object, unless BINDFLAGS_JUSTTESTEXISTENCE was specified in the binding options. If BINDFLAGS_JUSTTESTEXISTENCE was specified, NULL can be returned instead. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The binding operation was successful.
MK_E_NOOBJECT	Some, possibly intermediate, object could not be found during an operation such as binding or parsing a display name.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time limit specified by the bind context.
MK_E_CONNECTMANUALLY	During some process (binding, parsing a display name, and so on) OLE was unable to connect to a network device. The application receiving this error should call IBindCtx::GetObjectParam with the key "ConnectManually" to retrieve the moniker of the network device, get the display name,  put up a dialog box asking the user for a password, and so on.
MK_E_INTERMEDIATE INTERFACENOTSUPPORTED	An object was found but did not support an interface required for an operation. (During binding, for example, a container is expected to support the IOleItemContainer interface, and during parsing of display names a container is expected to support the IParseDisplayName interface.)
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Ran out of memory.
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

Because each type of moniker is designed to be part of a composite moniker, any given piece has a prefix of the composite to its left, and a suffix of the composite to its right. If IMoniker::BindToObject is called on a given piece, the IMoniker::BindToObject function usually requires the services of the object indicated by the prefix to its left. An item moniker, for example, requires the IOleItemContainer interface of the object to its left.

The bind context contains a time limit in which the caller wants the binding process to be completed. If the process is not completed within the specified time, it fails and returns MK_E_EXCEEDEDDEADLINE. The moniker of the object for which the deadline was exceeded can be retrieved by calling IBindCtx::GetObjectParam with the keys ExceededDeadline, ExceededDeadline1, ExceededDeadline2, and so on. (This capability is not often used with IMoniker::BindToObject; it is more often used with other IMoniker methods such as IMoniker::GetTimeOfLastChange.)

The running object table is accessible from the bind context using the IBindCtx::GetRunningObjectTable method.

Note  Binding to the right of the present piece of a composite moniker may involve binding to other objects. Consequently, binding a moniker can be an expensive operation since the composite moniker often needs to start object applications and open files. To avoid loading the object and releasing it, then loading it again later, IMoniker::BindToObject can use the bind context passed through the pbc parameter until the binding process is complete. For more information on bind contexts, see IBindCtx Interface.

See Also

IBindCtx::GetBindOptions, IBindCtx::GetRunningObjectTable, IBindCtx::RegisterObjectBound, IMoniker::GetTimeOfLastChange


IMoniker::BindToStorage

HRESULT IMoniker::BindToStorage(pbc, pmkToLeft, riid, ppvObj)
IBindContext * pbc
IMoniker pmkToLeft
REFIID riid
void ** ppvObj

IMoniker::BindToStorage returns access to the persistent storage of the receiver (that is, the location that contains the presentation of the information) by using the specified interface, rather than access to the object itself.

Parameters

pbc

[in] Points to the bind context to be used during this binding operation.

pmkToLeft

[in] Points to the moniker of the object to the left of this moniker.

riid

[in] Identifies the interface to bind to the persistent storage. Common interfaces passed here include IStorage, IStream, and ILockBytes.

ppvObj

[out] Points to where the instantiated storage is placed. NULL can be returned if BINDFLAGS_JUSTTESTEXISTENCE was specified in the binding options. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The binding operation was successful.
MK_E_NOSTORAGE	An attempt was made to access or bind to the storage of an object that does not have storage.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time specified by the bind context.
MK_E_CONNECTMANUALLY	During some process (binding, parsing a display name, and so on), OLE was unable to connect to a network device. For more information, see IMoniker::BindToObject.
MK_E_INTERMEDIATE INTERFACENOTSUPPORTED	An object was found but did not support an interface required for an operation. (For example, during binding, a container is expected to support the IOleItemContainer interface, and during parsing of display names a container is expected to support the IParseDisplayName interface.)
E_OUTOFMEMORY	Ran out of memory.
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

For example, a composite moniker that refers to a spreadsheet embedded in a word-processing document could look like the following:

Calling IMoniker::BindToObject on this composite allows the caller to communicate with the spreadsheet; calling IMoniker::BindToStorage allows the caller to communicate with the IStorage instance in which the spreadsheet resides.

IMoniker::BindToStorage is usually called during the right-to-left recursion process of IMoniker::BindToObject when it has been invoked on a generic composite moniker. Sometimes, monikers at the tail of the composite don't require access to the object on their left; they merely require access to its persistent storage. These monikers can be bound more efficiently by not binding to the objects of the monikers to their left.

Some objects do not have an identifiable piece of storage. Attempting to call IMoniker::BindToStorage on a moniker with this type of object(s) fails with the error MK_E_NOSTORAGE.

Using the bind context in IMoniker::BindToStorage is the same as in IMoniker::BindToObject.

See Also

IMoniker::BindToObject


IMoniker::Reduce

HRESULT IMoniker::Reduce(pbc, dwReduceHowFar, ppmkToLeft, ppmkReduced)
IBindContext * pbc
DWORD dwReduceHowFar
IMoniker ** ppmkToLeft
IMoniker ** ppmkReduced

IMoniker::Reduce returns a more efficient or equally efficient moniker that refers to the same object.

Parameters

pbc

[in] Points to the bind context to use.

dwReduceHowFar

[in] Specifies how this moniker should be reduced.

ppmkToLeft

[out] On entry, ppmkToLeft points to the moniker that prefixes this one within the composite, that is, the moniker to the left of the current moniker. On exit, the pointer will be NULL or non-NULL. Non-NULL indicates that the previous prefix should be disregarded and the moniker returned through ppmkToLeft should be used as the prefix in its place (this is not usual). NULL indicates that the prefix should not be replaced. Most monikers will NULL out this parameter before returning. The ppmkToLeft parameter is an in/out parameter and it must be released before NULLing out. If an error is returned, this parameter must be set to NULL. For more information on in/out parameters, see the discussion of parameter types in the section on Memory Management.

ppmkReduced

[out] Points (on exit) to the reduced form of the moniker; it can be NULL. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The reduction of the moniker was successful.
MK_S_REDUCED_TO_SELF	The IMoniker::Reduce operation output is the same as the input, and further calls to Reduce will have no effect.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time specified by the bind context.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Insufficient memory.
Remarks

Most monikers simply reduce to themselves (because they cannot be reduced any further). A moniker that reduces to itself passes itself through ppmkReduced and then returns MK_S_REDUCED_TO_SELF. A moniker that reduces to nothing should pass NULL through ppmkReduced and return S_OK.

Reducing a moniker that is a composite of other monikers returns the composite of the reduced pieces.

The dwReduceHowFar parameter controls the reduction process. It has the following values:

typedef enum tagMKREDUCE {

MKRREDUCE_ONE        = 3<<16,

MKRREDUCE_TOUSER     = 2<<16,

MKRREDUCE_THROUGUSER = 1<<16,

MKRREDUCE_ALL        = 0

} MKRREDUCE;

These values have the following meanings:

Value	Meaning
MKRREDUCE_ONE	Performs only one reduction step on the moniker. In general, the caller must have specific information about the particular class of moniker to be able to take advantage of this option.
MKRREDUCE_TOUSER	Reduce the moniker to a form that the user identifies as a persistent object. If no such point exists, this option should be treated as MKRREDUCE_ALL.
MKRREDUCE_THROUGHUSER	Reduce the moniker to where any further reduction would prevent the user from identifying it as a persistent object. Often, this is the same stage as MKRREDUCE_TOUSER.
MKRREDUCE_ALL	Reduce the moniker until it is reduced to itself.
Reducing a moniker to a display name recognizable to the user is an important facet of moniker reduction. Paths in the file system, bookmarks in word-processing documents, and range names in spreadsheets are all examples of user identities. In contrast, a macro or an alias encapsulated in a moniker is not a user identity.

The bind context parameter is used as it is in IMoniker::BindToObject. Implementations of IMoniker::Reduce should note the time limit imposed by the caller and the reporting of the object moniker which, had it been running, would have allowed the reduction to progress further. For more information, see IBindCtx Interface.

See Also

IBindCtx Interface, IMoniker::BindToObject


IMoniker::ComposeWith

HRESULT IMoniker::ComposeWith(pmkRight, fOnlyIfNotGeneric, ppmkComposite)
IMoniker *pmkRight
BOOL fOnlyIfNotGeneric
IMoniker ** ppmkComposite

IMoniker::ComposeWith returns a new composite moniker formed from the moniker on the left and the moniker pointed to by pmkRight. This operation uses the pieces of the path to an object to form the full path.

Parameters

pmkRight

[in] Points to the moniker to compose onto the end of the pmkComposite.

fOnlyIfNotGeneric

[in] Controls what should be done in the case where using a generic composite is the way to form a composite.

ppmkComposite

[out] Points (on exit) to the resulting composite moniker, which may be NULL. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The composite moniker was returned successfully.
MK_E_NEEDGENERIC	This error is returned when IMoniker::ComposeWith is called with fOnlyIfNotGeneric equal to TRUE, but the moniker only supports generic composition.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

There are two kinds of composite monikers: those that have no information about their pieces other than that they are monikers, and those that have more. The former is often referred to as a generic composite moniker. A generic composite moniker is a moniker made up of other simpler monikers stored in a left-to-right order. An example of the second type of moniker might be the result of composing a file moniker containing a relative path onto the end of another file moniker. The result could be a new file moniker containing the complete path.

Applications can call the function CreateGenericComposite, to create a generic composite moniker. Non-generic composite monikers can collapse a path within a storage domain to a more efficient representation in a subsequent IMoniker::Reduce operation. However, none of the monikers provided by OLE are capable of this.

In general, each moniker class should have a set of special monikers (possibly empty) that can be added to the end of a moniker of a given class in a nongeneric way. The moniker class understands enough of the semantics of these special monikers to recognize about them that they are monikers. Each IMoniker::ComposeWith implementation examines pmkRight to see whether it is a special moniker for this implementation. Often, pmkRight is asked for its class, but other possibilities exist, such as using QueryInterface. A common use of such special monikers are anti monikers, which remove the last piece of a generic moniker when the antimoniker is joined to the end of it.

If pmkRight is special, the IMoniker::ComposeWith implementation does whatever is appropriate for that special case. If not, fOnlyIfNotGeneric controls what should occur. If fOnlyIfNotGeneric is true, NULL should be passed back through ppmkComposite and the status, MK_E_NEEDGENERIC, returned. If fOnlyIfNotGeneric is FALSE, a generic composite should be returned using CreateGenericComposite. Most callers of ComposeWith should set fOnlyIfNotGeneric to FALSE.

In any situation where pmkRight completely negates the receiver (irrespective of fOnlyIfNotGeneric), and the resulting composite is empty, NULL should be passed back through ppmkComposite and the status S_OK returned.

The pieces of a moniker that have been composed can be enumerated using IMoniker::Enum. On a generic composite, this enumerates the monikers contained within it. On other monikers, the particular pieces returned are implementation specific.

Composition of monikers is an associative operation. That is, if A, B, and C are monikers, then

(A  (°)  B)  (°)  C

is always equal to

A  (°)  (B  (°)  C)

where  (°)  represents the composition operation. Each implementation of IMoniker::ComposeWith must maintain this invariant.


IMoniker::Enum

HRESULT IMoniker::Enum(fForward, ppenumMoniker)
BOOL fForward
IEnumMoniker ** ppenumMoniker

IMoniker::Enum enumerates the composite pieces of a generic composite moniker.

Parameters

fForward

[in] Specifies the enumeration order. TRUE enumerates the monikers in the usual order; FALSE causes a reverse enumeration order.

ppenumMoniker

[out] Points (on exit) to the returned enumerator. Can be NULL to indicate there is nothing to enumerate. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The enumerator was returned successfully.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IMoniker::Enum operates differently with different moniker classes. For example, enumerating a file moniker might enumerate the internally stored path into its components, even though they are not stored internally as separate monikers. If the moniker has no discernible internal structure, it simply passes back NULL through ppenumMoniker instead of an enumerator.

The returned enumeration is of the type IEnumMoniker, defined as:

typedef Enum<IMoniker*> IEnumMoniker;

This is shorthand for:

interface IEnumMoniker : IUnknown

{

  virtual SCODE 	Next(ULONG celt, IMoniker* rgelt,
    ULONG* pceltFetched) = 0;

  virtual SCODE 	Skip(ULONG celt) = 0;

  virtual SCODE 	Reset() = 0;

  virtual SCODE 	Clone(IEnumMoniker** ppenumMoniker) = 0;

};

See Also

IEnumUnknown Interface, IEnumString Interface


IMoniker::IsEqual

HRESULT IMoniker::IsEqual(pmkOtherMoniker)
IMoniker* pmkOtherMoniker

IMoniker::IsEqual compares two monikers and indicates whether they are equal.

Parameters

pmkOtherMoniker

[in] Points to the moniker to use for comparison.

Return Values

Value	Meaning
S_OK	The two monikers are alike.
S_FALSE	The two monikers are not alike.
Remarks

IMoniker::IsEqual is used to implement the running object table.

The moniker entries in the running object table indicate which objects are running, providing a way for link consumers to connect to the appropriate running object.

Two monikers that can compare as equal in either order must also hash to the same value.

See Also

IMoniker::Hash


IMoniker::Hash

HRESULT IMoniker::Hash(pdwHash)
DWORD *pdwHash

IMoniker::Hash returns a 32-bit integer associated with this moniker. The returned integer can be used to maintain tables of monikers. The moniker can be hashed to determine a hash bucket in the table, then compared against all the monikers in the hash bucket by calling IMoniker::IsEqual.

Parameters

pdwHash

[out] Points to where to return the hash value.

Return Values

Value	Meaning
S_OK	The integer was returned successfully.
Remarks

Two monikers that compare as equal in either order hash to the same value. Implementations of IsEqual and Hash are closely related and must always be written together.

The value returned by IMoniker::Hash is invariant under marshalling: if a moniker is marshalled to a new context, Hash invoked on the unmarshalled moniker in the new context returns the same value as Hash invoked on the original moniker. This is the only way that a global table of monikers such as the running object table can be maintained in shared space, yet accessed from many processes. Thus, implementations of IMoniker::Hash should not rely on the memory address of the moniker, but only its internal state.

See Also

IMoniker::IsEqual


IMoniker::IsRunning

HRESULT IMoniker::IsRunning(pbc, pmkToLeft, pmkNewlyRunning)
IBindContext * pbc
IMoniker * pmkToLeft
IMoniker * pmkNewlyRunning

IMoniker::IsRunning determines whether the specified moniker is running.

Parameters

pbc

[in] Points to the usual bind context.

pmkToLeft

[in] Points to the moniker to the left of this one in the composite in which it is found.

pmkNewlyRunning

[in] If non-NULL, this is the moniker most recently added to the running object table, and IMoniker::IsRunning can assume that without this moniker in the running object table, IsRunning would return S_FALSE. Can be NULL.

Return Values

Value	Meaning
S_OK	The moniker is running.
S_FALSE	The moniker is not running.
E_UNEXPECTED	An unexpected error occurred.
Remarks

This moniker gains access to the running object table, which is used to determine if the object is running, by calling IBindCtx::GetRunningObjectTable. pmkToLeft is the moniker to the left of this object in the generic composite in which it is found, if any. pmkNewlyRunning, if non-NULL, is the moniker most recently added to the running object table. IsRunning can assume that without pmkNewlyRunning in the running object table, IsRunning would have returned S_FALSE. Thus, the only way this moniker can now be running is if the newly running moniker is itself running. This allows for some n (2)  to n reductions in algorithms that use monikers. Implementations of this method in various kinds of moniker classes are roughly as follows:

Generic composite moniker implementation:

if (pmkToLeft != NULL)

return (pmkToLeft->ComposeWith(this))->IsRunning(pbc, NULL,
 pmkNewlyRunning);

if (pmkNewlyRunning != NULL) {

if (pmkNewlyRunning->IsEqual(this) == NOERROR)

return NOERROR;

}

else if (pRunningObjectTable -> IsRunning(this) == NOERROR)

return NOERROR;

// otherwise, forward it on to my last element.

return this->Last()->IsRunning(pbc, this->AllButLast(),
pmkNewlyRunning);

For moniker classes that do not support wildcard matching:

if (pmkToLeft == NULL) {

if (pmkNewlyRunning != NULL)

return pmkNewlyRunning->IsEqual(this);

else

return pRunningObjectTable->IsRunning(this);

else

return ResultFromScode(S_FALSE);

For moniker classes that support a wildcard entry that always matches any instance of the moniker class (all instances of the moniker class with the same moniker to their left are deemed to be running if the wildcard is present):

if (pmkToLeft == NULL) {

if (pmkNewlyRunning != NULL)

return pmkNewlyRunning->IsEqual(this) == NOERROR ||
pmkNewlyRunning->IsEqual(my wild card moniker) == NOERROR

if (pRunningObjectTable->IsRunning(this) == NOERROR)

return NOERROR;

return pRunningObjectTable->IsRunning(my wild card moniker);

}

else

return pmkToLeft->ComposeWith(my wild card moniker)->IsRunning

(pbc, NULL, pmkNewlyRunning);

Some moniker classes specifically, item monikers have a wildcard entry that matches only those objects that are currently running.

if (pmkToLeft == NULL) {

if (pmkNewlyRunning != NULL) {

if (pmkNewlyRunning->IsEqual(this) == NOERROR)

return NOERROR;

if (pmkNewlyRunning->IsEqual(wild card moniker) !=

NOERROR)

return ResultFromScode(S_FALSE);

goto TestBind:

return pRunningObjectTable -> IsRunning;

}

}

if (pmkToLeft->ComposeWith(my wild card moniker)->IsRunning
(pbc, NULL, pmkNewlyRunning) != NOERROR)

return ResultFromScode(S_FALSE);

TestBind:

// In general, connect to the container and ask if object
// is running. The use of IOleItemContainer is specific to

// item monikers, but the theme is a general one.

IOleItemContainer *pcont;

pmkToLeft->BindToObject(pbc, NULL, IID_IOleItemContainer,

&pcont);

return pcont->IsRunning(szItemString);


IMoniker::GetTimeOfLastChange

HRESULT IMoniker::GetTimeOfLastChange(pbc, pmkToLeft, pFileTime)
IBindContext * pbc
IMoniker *pmkToLeft
FILETIME * pFileTime

IMoniker::GetTimeOfLastChange provides date and time information needed to determine whether a linked object, or an embedded object containing linked objects, is up-to-date.

pbc

[in] Points to the bind context to be used for this operation.

pmkToLeft

[in] Points to the moniker to the left of this one in the composite in which it is found.

pFileTime

[out] Points to where to return the time of the last change.

Return Values

Value	Meaning
S_OK	The date/time information was returned successfully.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time specified by the bind context.
MK_E_CONNECTMANUALLY	During some process (binding, parsing a display name, and so on), OLE was unable to connect to a network device. For more information, see IMoniker::BindToObject.
MK_E_UNAVAILABLE	The time of the change is unavailable, and will not be available no matter what deadline is used.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If the objects in a document are not up-to-date, the user should be prompted to update them. Updating out-of-date objects causes them to be bound and to retrieve a new presentation. IMoniker::GetTimeOfLastChange does not bind to the object to determine whether it is out of date. Instead, it returns the best available answer based on the time-of-last-change information for objects already running. Many monikers point to an object contained in the object denoted by the moniker to the left. Implementations of IMoniker::GetTimeOfLastChange can often take advantage of the fact that objects cannot have changed at a date later than the object in which they are contained. That is, these monikers can simply forward the call onto the moniker to their left.

The returned time of change is reported using a FILETIME structure, defined as:

typedef struct STRUCT tagFILETIME

{

DWORD dwLowDateTime;

DWORD dwHighDateTime;

} FILETIME;

Time is indicated in units of 100 nanoseconds, which allows the times of fast-changing data to be recorded with precision. For information about converting between DOS date and time structures and OLE's FILETIME structure, see the descriptions of the CoDosDateTimeToFileTime and CoFileTimeToDosDateTime functions.

If the time of last change is unavailable, for example, because the deadline was exceeded, a time of FILETIME_MAX should be passed back. If the deadline was exceeded, the status MK_E_EXCEEDEDDEADLINE should be returned. If the time of change is unavailable, and would not be available no matter what deadline were used, MK_E_UNAVAILABLE should be returned; otherwise, S_OK should be returned.

If pmkToLeft is NULL, IMoniker::GetTimeOfLastChange should first check for a recorded change-time in the running object table with IRunningObjectTable::GetTimeOfLastChange before proceeding with other strategies. Moniker classes that support wildcards must consider exactly what gets put in the running object table and look for the appropriate item. Because generic composite monikers have no information about wildcards, they may even need to do that in the non-NULL pmkToLeft case.

See Also

IMoniker::IsRunning, IRunningObjectTable Interface


IMoniker::Inverse

HRESULT IMoniker::Inverse(ppmk)
IMoniker ** ppmk

IMoniker::Inverse returns a moniker that destroys this moniker when it is composed to the end of this moniker.

Parameters

ppmk

[out] Points to where to return the inverse moniker. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The inverse moniker has been returned successfully.
MK_E_NOINVERSE	A moniker class that does not have inverses was asked for one.
E_OUTOFMEMORY	Ran out of memory.
Remarks

IMoniker::Inverse is needed in implementations of the IMoniker::RelativePathTo member function, which is important for supporting monikers that track object movement.

IMoniker::Inverse is an abstract generalization of the ".." operation in MS-DOS file systems. For example, a file moniker representing the path "a\b\c\d" would have as its inverse a moniker containing the path "..\..\..\..", because "a\b\c\d" composed with "..\..\..\.." yields nothing.

The inverse of a moniker does not destroy just that particular moniker, but all monikers with a similar structure. Thus, the inverses are of a generic composite moniker is the reverse composite, or the inverse of its pieces. Nongeneric composite monikers (such as the preceding file moniker example) also have nontrivial inverses. However, there are many kinds of monikers whose inverses are trivial. As the moniker adds one more piece to an existing structure; its inverse is a moniker with the last piece of the existing structure removed. A moniker that removes the last piece of a generic moniker when it is composed onto the end of that generic moniker is called an anti moniker. An OLE-provided implementation of an anti moniker can be created by calling the CreateAntiMoniker helper function. A moniker with no internal structure can return an anti moniker as its inverse.

Not all monikers have inverses. Anti monikers, for example, do not. Neither do the inverses of most monikers that are themselves inverses. Because moniker inverses are used in constructing relative paths, monikers that have no inverse can cause IMoniker::RelativePathTo to fail in certain situations.

See Also

CreateAntiMoniker, IMoniker::RelativePathTo


IMoniker::CommonPrefixWith

HRESULT IMoniker::CommonPrefixWith(pmkOther, ppmkPrefix)
IMoniker * pmkOther
IMoniker ** ppmkPrefix

IMoniker::CommonPrefixWith returns the longest common prefix that the receiver shares with the moniker pointed to by pmkOther.

pmkOther

[in] Points to the (other) moniker whose common prefix is to be returned.

ppmkPrefix

[out] Points to where to return the common prefix moniker. If the common prefix does not exist, NULL is returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The common prefix exists but is neither the receiver nor pmkOther.
MK_S_NOPREFIX	No common prefix exists.
MK_S_HIM	The other moniker is the prefix of the receiver moniker.
MK_S_US	The two monikers (the receiver and the other moniker) are equal.
MK_S_ME	The receiver moniker (the moniker whose CommonPrefixWith method is called) is a prefix of the other moniker.
MK_E_NOTBINDABLE	IMoniker::CommonPrefixWith was called on a relative moniker. The moniker cannot be bound to something until it is composed with a container moniker.
E_OUTOFMEMORY	Ran out of memory.
This functionality is useful in constructing relative paths and in performing some of the calculations on monikers needed by the Edit/Links dialog box.

IMoniker::CommonPrefixWith should only be called on absolute monikers; for example, a file moniker that starts with a share or drive name, followed by items. It should not be called on relative monikers.


IMoniker::RelativePathTo

HRESULT IMoniker::RelativePathTo(pmkOther, ppmkRelPath)
IMoniker *pmkOther
IMoniker **ppmkRelPath

IMoniker::RelativePathTo returns the relative path to a moniker that will yield pmkOther when composed on to the end of this moniker, or a moniker with a similar structure.

Parameters

pmkOther

[in] Points to the moniker to which a relative path should be taken.

ppmkRelPath

[out] Points to where to return the relative path; cannot be NULL.

Return Values

Value	Meaning
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	IMoniker::RelativePathTo was called on a relative moniker. The moniker cannot be bound to something until it is composed with a container moniker.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Conceptually, implementations of this function determine the longest prefix that the receiver and pmkOther have in common. This breaks the receiver and pmkOther into two parts each, say (P,T (me) ) and (P,T (him) ) respectively, where P is the maximal common prefix. The correct relative path result is then:

T (me)  -1  (°)  T (him)

That is, inverse T (me)   (°)  T (him) .

For any given implementation of IMoniker::RelativePathTo, the same pmkOther monikers are usually treated in a special manner, as they would be with IMoniker::ComposeWith. For example, file monikers might treat other file monikers in a special manner in both cases.

IMoniker::RelativePathTo should be called only on absolute monikers; for example, a file moniker that starts with a share or drive name, followed by items. It should not be called on relative monikers.

See Also

IMoniker::GetDisplayName


IMoniker::GetDisplayName

HRESULT IMoniker::GetDisplayName(pbc, pmkToLeft, ppszDisplayName)
IBindContext * pbc
IMoniker * pmkToLeft
LPOLESTR * ppszDisplayName

IMoniker::GetDisplayName returns the current display name for this moniker If none exists, it is NULL.

Parameters

pbc

[in] Points to the bind context for this operation.

pmkToLeft

[in] Points to the moniker to the left of this one in the composite in which it is found. Most monikers do not require this when calling IMoniker::GetDisplayName.

ppszDisplayName

[out] Points (on exit) to the current display name for this moniker. It is NULL if the moniker does not have a display name or if the time limit was exceeded. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The display name was returned successfully.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time specified by the bind context. The moniker of the object with the exceeded deadline can be retrieved
E_OUTOFMEMORY	Ran out of memory.
E_NOTIMPL	There is no display name.
Remarks

To obtain the current display name of a moniker, IMoniker::GetDisplayName may have to access the storage of the object to which it refers, if not the object itself, which can be an expensive operation. As with other IMoniker functions, the pbc parameter specifies, as part of the overall bind context, a time limit within which the operation should be completed or fail, returning MK_E_EXCEEDEDDEADLINE if it is unable to do so.

Callers have the tendency to cache and use the last successful result obtained when they do not have quick access to the display name of a moniker (the standard implementation of OLE link does this).

Display names should be thought of as an annotation on the moniker that helps distinguish one moniker from another. Although rare, there might be more than one moniker with the same display name.

There is no guarantee that a display name obtained from a moniker will parse back into that moniker when calling MkParseDisplayName with the display name, though failure to do so also is rare.

A moniker that is a prefix of another has a display name that is a (string) prefix of the display name of the second moniker.

Monikers that are designed to be part of a generic composite must include any preceding delimiter as part of their display name. Many monikers take a parameter for this delimiter in their instance creation functions.

See Also

MkParseDisplayName


IMoniker::ParseDisplayName

HRESULT IMoniker::ParseDisplayName(pbc, pmkToLeft, pszDisplayName, pchEaten, ppmkOut)
IBindContext *pbc
IMoniker * pmkToLeft
LPOLESTR pszDisplayName
ULONG * pchEaten
IMoniker ** ppmkOut

IMoniker::ParseDisplayName parses the composite moniker's remaining display name. The pszDisplayName parameter is the yet-to-be-parsed tail of the display name. The IMoniker::ParseDisplayName function parses as much of the remaining tail as is appropriate for a display name within the object identified by (pmkToLeft ° (the receiver)) and returns the corresponding moniker.

Parameters

pbc

[in] Points to the bind context that is to be used to accumulate bound objects.

pmkToLeft

[in] Points to the moniker to the left of this one in the so-far-parsed display name.

pszDisplayName

[in] Points to the display name to be parsed.

pchEaten

[out] Points to the number of characters of the input name this parse consumed.

ppmkOut

[out] Points to the resulting moniker. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The parse operation was completed successfully.
MK_E_SYNTAX	An error in the syntax of a filename was encountered while parsing a display name or while creating a file moniker.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
IMoniker::BindToObject errors.	Parsing display names may cause binding. Thus, any error associated with this function may be returned.
Remarks

The main loop of MkParseDisplayNamefinds the next moniker piece by calling IMoniker::ParseDisplayName and passing NULL through pmkToLeft. If the moniker that has been parsed to this point is a generic composite, the composite forwards this information onto its last piece, passing the prefix of the composite to the left of the piece in pmkToLeft.

Some moniker classes are able to parse their display names themselves because they are designed to designate only certain kinds of objects. Other monikers need to bind to the object that they designate to do the parsing. These objects should not be released by IMoniker::ParseDisplayName, but instead should be transferred to the bind context for later release.

If a syntax error occurs, NULL is returned through ppmkOut. The number of characters that were successfully parsed in the display name is returned through pchEaten.

See Also

MkParseDisplayName


IMoniker::IsSystemMoniker

HRESULT IMoniker::IsSystemMoniker(pdwMksys)
DWORD pdwMksys

IMoniker::IsSystemMoniker determines whether this moniker is a class of moniker whose implementation semantics are conceptually important to the binding process.

pdwMksys

[out] Points to where to return the enumerated moniker type; may not be NULL.

Return Values

Value	Meaning
S_OK	The moniker is a system moniker.
S_FALSE	The moniker is not a system moniker.
Remarks

The values returned through pdwMksys are taken from the following enumeration:

typedef enum tagMKSYS {

MKSYS_NONE = 0,

MKSYS_GENERICCOMPOSITE = 1,

MKSYS_FILEMONIKER = 2,

MKSYS_ANTIMONIKER = 3,

MKSYS_ITEMMONIKER = 4,

MKSYS_POINTMONIKER = 5,

} MKSYS;

All user implementations of this function should simply return MKSYS_NONE through the pdwMksys parameter.

Note  New values to the MKSYS enumeration may be defined in the future; therefore, callers of IMoniker::IsSystemMoniker should explicitly test against the return values relevant to them, because the returned value may not be one of those listed here.


IParseDisplayName Interface

The IParseDisplayName interface transforms the display name of an object into a moniker. IParseDisplayName is implemented by container and object applications and is used by OLE. It contains the following method:

DECLARE_INTERFACE_(IParseDisplayName, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IParseDisplayName method ***

  HRESULT ParseDisplayName (THIS_ IBindContext * pbc, LPOLESTR pszDisplayName,

    ULONG * pchEaten, IMoniker ** ppmkOut);

};

IParseDisplayName::ParseDisplayName


IParseDisplayName::ParseDisplayName

HRESULT IParseDisplayName::ParseDisplayName(pbc, pszDisplayName, pchEaten, ppmkOut)
IBindContext * pbc
LPOLESTR pszDisplayName
ULONG * pchEaten
IMoniker ** ppmkOut

IParseDisplayName::ParseDisplayName parses an object's display name into a moniker to the object.

Parameters

pbc

[in] Points to the bind context to be used to accumulate bound objects.

pszDisplayName

[in] Points to the display name to be parsed.

pchEaten

[out] Points to the number of characters parsed from the display name.

ppmkOut

[out] Points to the resulting moniker; NULL on error. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The parse operation was successful.
MK_E_SYNTAX	An error in the syntax of a filename was encountered while parsing a display name or while creating a moniker.
MK_E_NOOBJECT	Some, possibly intermediate, object could not be found during some operation, such as binding or parsing a display name.
MK_E_INTERMEDIATEINTERFACE NOTSUPPORTED	An object was found but did not support an interface required for an operation. For example, during binding, a container is expected to support the IOleItemContainer interface, and during parsing of display names a container is expected to support the IParseDisplayName interface.
E_UNEXPECTED	Failure of unknown cause.
E_OUTOFMEMORY	Ran out of memory.
IMoniker::BindToObject errors.	MK_E_EXCEEDEDDEADLINE and MK_E_CONNECTMANUALLY.
Remarks

The pszDisplayName parameter is the yet-to-be-parsed tail of the display name.

If a syntax error occurs, NULL should be returned through ppmkOut. The number of characters that were successfully parsed should be returned through pchEaten.

See Also

MkParseDisplayName


IBindCtx Interface

The IBindCtx interface, typically implemented and used by OLE, is passed to many moniker-management operations. The primary purpose of the IBindCtx instance is to accumulate the set of objects that get bound during an operation, but that should be released when the operation is complete. This is particularly useful in binding generic composite monikers because using the bind context in this way avoids binding an object and releasing it, only to have it bound again when the operation moves to another piece of the composite.

In addition, the bind context passes a group of parameters that do not change as an operation moves from one piece of a generic composite to another. Some of the binding options have a related return value in certain error conditions; the bind context provides the means by which they can be returned. The bind context is also the only means through which moniker operations should access contextual information about their environment.

All calls that query or set the state of the environment are funneled through the bind context. (Doing this allows for future enhancements that can dynamically modify binding behavior.) In OLE 2, the most important piece of contextual information that moniker operations need to access is the running object table. Monikers should always access this table indirectly through IBindCtx::GetRunningObjectTable, rather than using the function GetRunningObjectTable.

The IBindCtx interface allows for future extensions to the passed-in contextual information in the form of the ability to maintain a string-keyed table of objects. For more information, see IBindCtx::RegisterObjectParam and related functions.

The IBindCtx interface contains the following methods:

DECLARE_INTERFACE_(IBindCtx, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IBindCtx methods ***

  HRESULT RegisterObjectBound (THIS_ IUnknown * pUnk);

  HRESULT RevokeObjectBound (THIS_ IUnknown * pUnk);

  HRESULT ReleaseBoundObjects (THIS);

  HRESULT SetBindOptions (THIS_ BIND_OPTS * pbindopts);

  HRESULT GetBindOptions (THIS_ BIND_OPTS * pbindopts);

  HRESULT GetRunningObjectTable (THIS_ IRunningObjectTable ** pprot);

  HRESULT RegisterObjectParam (THIS_ LPOLESTR pszKey, IUnknown * punk);

  HRESULT GetObjectParam (THIS_ LPOLESTR lszKey, IUnknown ** ppunk);

  HRESULT EnumObjectParam (THIS_ IEnumString ** ppenum);

  HRESULT RevokeObjectParam (THIS_ LPOLESTR pszKey);

};


IBindCtx::RegisterObjectBound

HRESULT IBindCtx::RegisterObjectBound(punk)
IUnknown * punk

IBindCtx::RegisterObjectBound registers an object as one that should be released when the moniker binding operation is completed.

punk

[in] Points to the object being registered as one that should be released.

Return Values

Value	Meaning
S_OK	The object was registered successfully.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

A call to IBindCtx::RegisterObjectBound causes the bind context to create an additional reference to the passed-in object (with IUnknown::AddRef). The caller must still release its own copy of the pointer.

Calling IBindCtx::RegisterObjectBound twice with the same object requires two calls to IBindCtx::RevokeObjectBound to completely remove the registration of the object within the bind context.

See Also

IBindCtx::RevokeObjectBound


IBindCtx::RevokeObjectBound

HRESULT RevokeObjectBound(punk)
IUnknown * punk

IBindCtx::RevokeObjectBound removes an object previously registered in the bind context.

punk

[in] Points to the object to be removed from the bind context.

Return Values

Value	Meaning
S_OK	The object was removed successfully.
MK_E_NOTBOUND	IBindCtx::RevokeObjectBound was called for an object that was not registered by IBindCtx::RegisterObjectBound.
E_OUTOFMEMORY	Ran out of memory.
E_INVALIDARG	The argument is invalid.
Remarks

The IBindCtx::RevokeObjectBound function undoes the effect of IBindCtx::RegisterObjectBound by removing one occurrence of the object from the registered set in the bind context.

Applications usually release objects as a set by calling IBindCtx::ReleaseBoundObjects and do not typically call IBindCtx::RevokeObjectBound.

See Also

IBindCtx::RegisterObjectBound, IBindCtx::ReleaseBoundObjects


IBindCtx::ReleaseBoundObjects

HRESULT IBindCtx::ReleaseBoundObjects()

IBindCtx::ReleaseBoundObjects releases all objects currently registered in the bind context.

Return Values

Value	Meaning
S_OK	The objects were released successfully.
Holding a bind context without calling IBindCtx::ReleaseBoundObjects prevents the reference count form going to zero for all objects bound during the binding process. This includes OLE item containers and the link sources themselves.


IBindCtx::SetBindOptions

HRESULT IBindCtx::SetBindOptions(pbindopts)
BIND_OPTS * pbindopts

IBindCtx::SetBindOptions stores a block of bind options in the bind context so they can be used in other IMoniker operations using the same bind context.

pbindopts

[in] Points to the block of bind options to store in the bind context. These can be retrieved later with IBindCtx::GetBindOptions.

Return Values

Value	Meaning
S_OK	The parameters were stored successfully.
E_OUTOFMEMORY	Ran out of memory.
Using IBindCtx::SetBindOptions to store a block of data is an alternative to passing parameters to a function. Because bind options are common to most IMoniker methods and the bind options do not change as the binding progresses, it is more efficient to store the block of bind options with IBindCtx::SetBindOptions than to pass the options to each IMoniker method.

BIND_OPTS is defined as follows:

typedef struct tagBIND_OPTS

{

DWORD	cbStruct;	// size in bytes of BIND_OPTS.

DWORD	grfFlags;

DWORD	grfMode;

DWORD	dwTickCountDeadline;

} BIND_OPTS;

The grfFlags member is a group of Boolean flags. Legal values that can be OR-ed together are taken from the enumeration BINDFLAGS (see below). Moniker implementations should ignore any bits in this field that are not relevant to them.

The grfMode member is a group of flags that indicates the caller's intended use for the object received from the associated moniker binding operation. Constants for this member are taken from the STGM enumeration. For more information, see Storage Access Mode Flags described in Persistent Storage Interfaces and Functions.When applied to a IMoniker::BindToObject operation, the most significant flag values are: STGM_READ, STGM_WRITE, and STGM_READWRITE. Some binding operations may make use of other flags, particularly STGM_DELETEONRELEASE or STGM_CREATE, but such cases would be quite esoteric.

When applied to the IMoniker::BindToStorage operation, most STGM values are potentially useful.

The default value for grfMode is STGM_READWRITE | STGM_SHARE_EXCLUSIVE.

The dwTickCountDeadline member indicates when the operation is to be completed according to the caller. This parameter allows the caller to limit the execution time of an operation when it is more important that the operation perform quickly rather than accurately. This capability is most often used with IMoniker::GetTimeOfLastChange, though it can be usefully applied to other operations as well.

This 32-bit unsigned value is a time in milliseconds on the local clock maintained by the GetTickCount function. A value of zero indicates "no deadline;" however, because zero is also a valid return value from GetTickCount, callers should handle this case. Callers should also handle clock wrapping cases. If the value in this variable is less than the current time by more than 231 milliseconds, it should be interpreted as indicating a time in the future of its indicated value plus 232 milliseconds.

Typical deadlines allow for a few hundred milliseconds of execution. Each function should try to complete its operation by this time on the clock, or fail with the error MK_E_EXCEEDEDDEADLINE if it cannot. Functions are not required to be absolutely accurate in this regard, because it is almost impossible to predict how long execution might take (thus, callers cannot rely on the operation being completed by the deadline), but operations that exceed their deadline excessively will usually cause intolerable delays in the operation of their callers. In practice, the use of deadlines is a heuristic that callers can impose on the execution of moniker operations.

If a moniker operation exceeds its deadline because one or more objects it uses are not running and, if one of these had been running, the operation would have completed more of its execution, the monikers of these objects should be recorded in the bind context. This is done by using IBindCtx::RegisterObjectParam under the parameter names "ExceededDeadline," "ExceededDeadline1," "ExceededDeadline2," and so on. Use the first currently used name to indicate to the caller when to try the operation again.

The enumeration BINDFLAGS, which contains the legal values for the bit field BIND_OPTS::grfFlags, is defined as follows:

typedef enum tagBINDFLAGS

{

BIND_MAYBOTHERUSER      =  1,

BIND_JUSTTESTEXISTENCE  =  2,

} BINDFLAGS;

These flags have the following meanings:

BINDFLAGS Value	Purpose
BIND_MAYBOTHERUSER	If present, this sort of interaction is permitted. If not present, the operation to which the bind context containing this parameter is applied 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, an operation can elect to use a different algorithm that does not require user interaction, or it can fail with the error MK_MUSTBOTHERUSER.
BIND_JUSTTESTEXISTENCE	If present, this value indicates that the caller of the moniker operation to which this flag is being applied is not actually 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 allows the caller to 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. See individual routine descriptions for details of exactly what status is returned.


IBindCtx::GetBindOptions

HRESULT IBindCtx::GetBindOptions(pbindopts)
BIND_OPTS * pbindopts

IBindCtx::GetBindOptions returns the current binding options stored in this bind context.

pbindopts

[in, out] Points to the structure of binding options to be filled in (the caller-supplied BIND_OPTS structure).

Return Values

Value	Meaning
S_OK	The binding options were returned successfully.
E_INVALIDARG	The argument is invalid. For example, the size is not correct or the parameter is not readable.
Remarks

The caller must set the cbStruct member correctly in the BIND_OPTS structure before calling IBindCtx::GetBindOptions.

The stored parameters can be retrieved with IBindCtx::GetBindOptions. Using a block of parameters is an alternative way to pass parameters that are common to IMoniker operations and that do not change as the operation moves from one piece of a generic composite to another.

The pbindopts parameter is of type BIND_OPTS, which is defined as:

typedef struct tagBIND_OPTS

{

    DWORD   cbStruct;

    DWORD   grfFlags;

    DWORD   grfMode;

    DWORD   dwTickCountDeadline;

} BIND_OPTS;

See Also

IBindCtx::SetBindOptions


IBindCtx::GetRunningObjectTable

HRESULT IBindCtx::GetRunningObjectTable(pprot)
IRunningObjectTable ** pprot

IBindCtx::GetRunningObjectTable returns access to the running object table relevant to this binding process.

pprot

[out] Points to where to return the running object table. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The pointer was returned successfully.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Moniker implementations should get access to the running object table by using IBindCtx::GetRunningObjectTable, rather than the global API GetRunningObjectTable. The appropriate running object table is determined implicitly when the bind context is created.

See Also

GetRunningObjectTable


IBindCtx::RegisterObjectParam

HRESULT IBindCtx::RegisterObjectParam(pszKey, punk)
LPOLESTR pszKey
IUnknown * punk

IBindCtx::RegisterObjectParam registers an object pointer in an internally maintained table of object pointers.

pszKey

[in] Points to the name under which the object is being registered.

punk

[in] Points to the object to be registered.

Return Values

Value	Meaning
S_OK	The object was registered successfully.
E_OUTOFMEMORY	Ran out of memory.
Remarks

IBindCtx::RegisterObjectParam registers the object under the name specified in the pszKey parameter. The table of pointers is used to pass contextual information to the binding process. String keys are compared on a case-sensitive basis.

Like IBindCtx::RegisterObjectBound, IBindCtx::RegisterObjectParam creates an additional reference to the passed-in object using IUnknown::AddRef. Calling IBindCtx::RegisterObjectParam a second time with a different object (using the same pszKey), replaces the object stored in the first call to the method.

See Also

IBindCtx::RegisterObjectBound, IUnknown::AddRef


IBindCtx::GetObjectParam

HRESULT IBindCtx::GetObjectParam(pszKey, ppunk)
LPOLESTR pszKey
IUnknown ** ppunk

IBindCtx::GetObjectParam finds the specified object pointer in the internally maintained table of contextual object pointers and returns the corresponding object, if one exists.

Parameters

pszKey

[in] Points to the key under which to look for an object.

ppunk

[out] Points to where to return the object's interface pointer. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The object was returned successfully.
E_OUTOFMEMORY	Ran out of memory.
See Also

IBindCtx::EnumObjectParam


IBindCtx::EnumObjectParam

HRESULT IBindCtx::EnumObjectParam(ppenum)
IEnumString ** ppenum

IBindCtx::EnumObjectParam returns an enumeration of the internally maintained table of contextual object pointers.

ppenum

[out] Points to where to return the string enumerator. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The enumerator was returned successfully.
E_OUTOFMEMORY	Ran out of memory.
See Also

IEnumString Interface


IBindCtx::RevokeObjectParam

HRESULT IBindCtx::RevokeObjectParam(pszKey)
LPOLESTR	pszKey

IBindCtx::RevokeObjectParam revokes the registration of the object currently found under this key in the internally maintained table of contextual object pointers, if any such key is currently registered.

pszKey

[in] Points to the key whose registration is to be revoked.

Return Values

Value	Meaning
S_OK	The object registration was revoked successfully.
E_FAIL	Key not present.
See Also

IBindCtx::RegisterObjectParam


IEnumMoniker Interface

The IEnumMoniker interface is implemented by OLE and used by OLE, object applications, and certain containers to iterate though items that are monikers. It is used specifically in the implementation of IRunningObjectTable::EnumRunning.

The IEnumMoniker interface contains the following methods, as do all enumerators:

DECLARE_INTERFACE_(IEnumMoniker, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IEnumMoniker methods ***

  HRESULT Next (THIS_ ULONG celt, IMoniker ** rgelt, ULONG * pceltFetched);

  HRESULT Skip (THIS_ ULONG celt);

  HRESULT Reset (THIS);

  HRESULT Clone (THIS_ IEnumMoniker ** ppenum);

};

For more information, see the discussion of enumerators in IEnumX Interface in "Component Object Interfaces and Functions."


IOleLink Interface

The main architectural difference, from a container's perspective, between an embedded object and a linked object is that the linked object supports the IOleLink interface. IOleLink contains functionality by which the moniker inside the linked object and the linked object's update options are manipulated. OLE provides an implementation of IOleLink that is used by containers.

The IOleLink interface contains the following methods:

DECLARE_INTERFACE_(IOleLink, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleLink methods ***

  HRESULT SetUpdateOptions (THIS_ DWORD dwUpdateOpt);

  HRESULT GetUpdateOptions (THIS_ DWORD* pdwUpdateOpt);

  HRESULT SetSourceMoniker (THIS_ IMoniker * pmk, REFCLSID rclsid);

  HRESULT GetSourceMoniker (THIS_ IMoniker ** ppmk);

  HRESULT SetSourceDisplayName (THIS_ LPCOLESTR pszStatusText);

  HRESULT GetSourceDisplayName (THIS_ LPOLESTR * ppszDisplayName);

  HRESULT BindToSource (THIS_ DWORD bindflags, IBindContext * pbc);

  HRESULT BindIfRunning (THIS);

  HRESULT GetBoundSource (THIS_ IUnknown ** ppUnk);

  HRESULT UnbindSource (THIS);

  HRESULT Update (THIS_ IBindContext * pbc);

};


IOleLink::SetUpdateOptions

HRESULT IOleLink::SetUpdateOptions(dwUpdateOpt)
DWORD dwUpdateOpt

IOleLink::SetUpdateOptions sets the link-update options for the linked object.

dwUpdateOpt

[in] Specifies when the data and presentation cache on the link consumer is updated.

Return Values

Value	Meaning
S_OK	The operation was completed successfully.
E_INVALIDARG	The argument is invalid.
Remarks

The dwUpdateOpt parameter controls when the data and/or presentation cache on the link consumer is updated. dwUpdateOpt is taken from the enumeration OLEUPDATE, defined as follows:

typedef enum tagOLEUPDATE {

OLEUPDATE_ALWAYS = 1,

OLEUPDATE_ONCALL = 3,

} OLEUPDATE;

The values in OLEUPDATE are used as follows:

Value	Purpose
OLEUDPATE_ALWAYS	Update the link object whenever possible. This option supports the Automatic link-update option in the Links dialog box.
OLEUPDATE_ONCALL	Update the link object only when the IOleObject::Update member function is called. This option supports the Manual link-update option in the Links dialog box.


IOleLink::GetUpdateOptions

HRESULT IOleLink::GetUpdateOptions(pdwUpdateOpt)
DWORD * pdwUpdateOpt

IOleLink::GetUpdateOptions retrieves link-update options previously set with IOleLink::SetUpdateOptions.

pdwUpdateOpt

[out] Points to where to return the flags taken from the OLEUPDATE enumeration.

Return Values

Value	Meaning
S_OK	The operation was completed successfully.
Remarks

OLEUPDATE is defined in IOleLink::SetUpdateOptions.

See Also

IOleLink::SetUpdateOptions


IOleLink::SetSourceMoniker

HRESULT IOleLink::SetSourceMoniker(pmk, rclsid)
IMoniker *pmk
REFCLSID rclsid

IOleLink::SetSourceMoniker stores a moniker that indicates the source of the link inside of the linked object.

Parameters

pmk

[in] Points to the new moniker; may be NULL.

rclsid

[in] Specifies the CLSID of the moniker.

Return Values

Value	Meaning
S_OK	The operation was completed successfully.
Remarks

The stored moniker becomes part of the persistent state of the object. Setting the source moniker also allows an application to set the expected class of the object without binding to it. To support link-source tracking, linked objects also store a relative moniker, which is computed as

pmkContainer->RelativePathTo(pmkLinkSource)

When a linked object is running (the source moniker has been bound and connected), it registers to receive rename notifications from its link source. When a notification is received, the linked object updates its source moniker to the new name. It registers itself for notifications to handle situations in which a link is made to a newly created document that has never been saved. For example, newly created Excel spreadsheets are named "SHEET1," "SHEET2," and so on. Only when they are saved for the first time do they acquire a persistent identity that is appropriate to store in the links to them. As long as the source document is saved before its link consumer is closed, the linked object will be able to track the source of the link.

The NULL option is used for the Cancel Link option of the Edit Links dialog box to indicate a static link. A static link continues to load and draw out of its cache, but cannot implement other verbs because the connection to its link source has been broken.


IOleLink::GetSourceMoniker

HRESULT IOleLink::GetSourceMoniker(ppmk)
IMoniker ** ppmk

IOleLink::GetSourceMoniker retrieves the moniker for the current link source.

ppmk

[out] Points to the address where the moniker currently in the link should be placed. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The operation was completed successfully.
MK_E_UNAVAILABLE	The moniker is unavailable, and would not be available no matter what deadlines were used.


IOleLink::SetSourceDisplayName

HRESULT IOleLink::SetSourceDisplayName(pszStatusText)
LPCOLESTR pszStatusText

IOleLink::SetSourceDisplayName parses a linked object's display name into a moniker, and stores the moniker in the linked object.

pszStatusText

[in] The display name of the new link source; may not be NULL.

Return Values

Value	Meaning
S_OK	The operation completed successfully.
MK_E_SYNTAX	An error in the syntax of a filename was encountered while parsing a display name or creating a file moniker.
E_OUTOFMEMORY	Ran out of memory.
Remarks

Monikers used to indicate the source of linked objects have a display name that can be shown to the user in dialog boxes. Display names can be parsed into monikers using the MkParseDisplayName function. Most often, the location of a link source is provided directly in a moniker, such as the moniker passed through the Clipboard in a Copy/Paste Link operation. Less frequently, the moniker originates in a textual form, such as in the text box in the Edit Links dialog box, provided with the OLE user-interface library.

Monikers originating in textual form need to be parsed into monikers before they can be stored as the source of a linked object. If the caller is to do the parsing, it calls MkParseUserName and then passes the returned moniker to IOleLink::SetSourceMoniker. To let the linked object do the parsing, the caller should call IOleLink::SetSourceDisplayName. In this case, the display name is parsed and the resulting moniker stored in its place before the linked object is bound for the first time.

See Also

MkParseDisplayName


IOleLink::GetSourceDisplayName

HRESULT IOleLink::GetSourceDisplayName(ppszDisplayName)
LPOLESTR  * ppszDisplayName

IOleLink::GetSourceDisplayName retrieves the display name of the linked object's source.

ppszDisplayName

[out] Points to where to return the name of the link source; cannot be NULL.

Return Values

Value	Meaning
S_OK	The operation completed successfully.
MK_E_EXCEEDEDDEADLINE	During some process (binding, parsing a display name, etc.) a deadline was exceeded.
E_OUTOFMEMORY	Ran out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The display name of a linked object's moniker can be obtained from its source moniker using IMoniker::GetDisplayName. However, the display name might either be unavailable or might not be obtained quickly enough to suit the caller, because it could require that object applications be run. So that a reasonable representation of the link source is always available to the callers, linked objects keep a cache of their source's display name, which is updated each time the linked object connects to its source.

See Also

IMoniker::GetDisplayName


IOleLink::BindToSource

HRESULT IOleLink::BindToSource(bindflags, pbc)
DWORD bindflags
IBindContext * pbc

IOleLink::BindToSource binds the moniker contained within the linked object.

Parameters

bindflags

[in] The bindflags parameter controls whether or not the binding operation proceeds; it contains values taken from the enumeration OLELINKBIND, described in the Remarks section.

pbc

[in] Points to the bind context.

Return Values

Value	Meaning
S_OK	The binding operation was successful.
MK_E_NOOBJECT	Some, possibly intermediate, object could not be found during an operation such as binding or parsing a display name.
MK_E_EXCEEDEDDEADLINE	The process was not completed within the time specified by the bind context.
MK_E_CONNECTMANUALLY	During some process (binding, parsing a display name, and so on), OLE was unable to connect to a network device. The application receiving this error should call IBindCtx::GetObjectParam with the key "ConnectManually" to retrieve the moniker of the network device, get the display name, and put up a dialog box asking the user for a password, and so on.
MK_E_INTERMEDIATE INTERFACENOTSUPPORTED	An object was found but it did not support an interface required for an operation. During binding, for example, a container is expected to support the IOleItemContainer interface, and during parsing of display names a container is expected to support the IParseDisplayName interface.)
E_NOINTERFACE	Interface not supported.
E_OUTOFMEMORY	Ran out of memory.
OLE_E_CLASSDIFF	The bind operation failed because the class is different than the last time the object was successfully bound.
STG_E_ACCESSDENIED	Incorrect permission for accessing this storage object.
E_UNEXPECTED	Failure of unknown cause.
IMoniker::BindToObject errors.	All IMoniker::BindToObject errors.
Remarks

When a user activates a linked object and the object application must be located, IOleLink::BindToSource is invoked to connect the object to the object application. If the object cannot be found, MK_E_NOOBJECT is returned.

The time limit specified in the bind context identifies the amount of time within which the binding operation must be completed. A value of zero specifies an infinite time. For more information, see the BIND_OPTS structure in the description for IBindCtx::SetBindOptions.

When binding a linked object, the current class of the link source might not be the same as it was when the linked object tried to connect. For example, the class of a link to a Lotus spreadsheet object that the user subsequently converted (using the Convert dialog box) to an Excel sheet would now be different.

The bindflags parameter contains values taken from the enumeration OLELINKBIND:

typedef enum tagOLELINKBIND

{

OLELINKBIND_EVENIFCLASSDIFF = 1,

} OLELINKBIND:

If OLELINKBIND_EVENIFCLASSDIFF is not provided, IOleLink::BindToSource returns OLE_E_CLASSDIFF when the class is different from when the linked object was last successfully bound. If OLELINKBIND_EVENIFCLASSDIFF is given, the bind process proceeds even if the class has changed.

See Also

IBindCtx::SetBindOptions


IOleLink::BindIfRunning

HRESULT IOleLink::BindIfRunning()

IOleLink::BindIfRunning binds the link to the link source, using IOleLink::BindToSource(NULL, NULL), if the source is currently running.

Return Values

Value	Meaning
S_OK	The link was bound to the link source.
S_FALSE	The link source is not running.
E_OUTOFMEMORY	Memory allocation failure occurred.
E_FAIL	The link does not have a moniker identifying a link source.
Remarks

IOleLink::BindIfRunning tries only the relative moniker in the running object table, when both a relative and absolute moniker are present. It tries the absolute moniker only if no relative moniker is present, otherwise it fails.

If the link source is not running, this method returns S_FALSE. If the link source is running, this method calls IOleLink::BindToSource. If this is successful, S_OK is returned; otherwise, whatever error returned BindToSource is returned. Accordingly, if the error is something other than S_FALSE or E_FAIL, fully binding to the object application to help resolve these errors is inexpensive because the object application is already running.

See Also

IOleLink::BindToSource


IOleLink::GetBoundSource

HRESULT IOleLink::GetBoundSource(ppunk)
IUnknown ** ppunk

IOleLink::GetBoundSource retrieves the object to which this link source is currently connected, if any source is present.

ppunk

[out] Points to where to return the currently-connected source of this object. It cannot be NULL. If no source is currently connected, NULL is returned.

Return Values

Value	Meaning
S_OK	The object was retrieved successfully.
E_FAIL	No link source is currently connected.
E_UNEXPECTED	Failure of unknown cause.
Remarks

If there is no source currently connected, IOleLink::GetBoundSource returns E_FAIL and NULL is returned through *ppunk.


IOleLink::UnbindSource

HRESULT IOleLink::UnbindSource()

IOleLink::UnbindSource unbinds a currently linked object from the link source.

Return Values

Value	Meaning
S_OK	The unbind operation was completed successfully.
IOleObject::Closecalls IOleLink::UnbindSource for linked objects.

See Also

IOleObject::Close, IOleLink::BindToSource


IOleLink::Update

IOleLink::Update(pbc)
IBindContext * pbc

IOleLink::Update updates a linked object with the latest data from the link source. This function finds the link source if necessary and gets a new presentation from the source. This process may require the running of one or more object applications.

pbc

[in] Points to the bind context to be used for the update.

Return Values

Value	Meaning
S_OK	All caches 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.
MK_E_NOOBJECT	The bind operation failed. Some, possibly intermediate, object could not be found during an operation such as binding or parsing a display name.
MK_E_EXCEEDEDDEADLINE	The bind operation failed so the process was not completed within the time specified by the bind context.
MK_E_CONNECTMANUALLY	The bind operation failed. OLE was unable to connect to a network device. The application receiving this error should call IBindCtx::GetObjectParam with the key "ConnectManually" to retrieve the moniker of the network device, get the display name, and put up a dialog box asking the user for a password, and so on.
MK_E_INTERMEDIATEINTERFACE NOTSUPPORTED	The bind operation failed. An object was found but did not support an interface required for binding. (A container is expected to support the IOleItemContainer interface, and during parsing of display names a container is expected to support the IParseDisplayName interface.)
E_NOINTERFACE	Interface not supported. The bind operation failed.
E_OUTOFMEMORY	Ran out of memory. The bind operation failed.
OLE_E_CLASSDIFF	The bind operation failed because the class is different from the last time the object was successfully bound.
STG_E_ACCESSDENIED	Incorrect permissions for accessing this storage object.
OLE_E_CANT_BINDTOSOURCE	Unable to bind to the source.
E_UNEXPECTED	An unexpected error occurred. The bind operation failed.
Remarks

IOleLink::Update ensures that the linked object has the latest data from the link source. This function is useful for automatic links, when the link is not bound, and for all cases involving manual links.

The pbc parameter is used when the linked object has to bind to the link source. The link source is locked for the duration of the update. This function does not call IOleObject::Update on the link source; it just gets the necessary data by using IDataObject::GetData.

See Also

IOleObject Interface


IRunningObjectTable Interface

The IRunningObjectTable interface provides access to the running object table, a globally accessible lookup table that keeps track of the objects currently running on the workstation. When a container tries to bind to a running object, it can look in the running object table to determine whether an instance is currently running and if it is, bind to that instance instead of loading the object a second time. The table contains a series of data pairs, of the form:

(pmk, pUnk)

The pmk element points to the moniker that, if successfully bound, connects to the running object. The pUnk element points to the available running object's implementation of IUnknown. During the binding process, monikers consult the pmk entries in the running object table to see whether an object is already running.

Clients of objects access the running object table by calling GetRunningObjectTable or IBindCtx::GetRunningObjectTable, which return an instance of the IRunningObjectTable interface.

The IRunningObjectTable interface, implemented by OLE and used by OLE and object applications, contains the following methods:

DECLARE_INTERFACE_(IRunningObjectTable, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IRunningObjectTable methods ***

  HRESULT Register (THIS_ DWORD grfFlags, IUnknown * punkObject, IMoniker * pmkObjectName,

    DWORD * pdwRegister);

  HRESULT Revoke (THIS_ DWORD dwRegister);

  HRESULT IsRunning (THIS_ IMoniker * pmkObjectName);

  HRESULT GetObject (THIS_ IMoniker * pmkObjectName, IUnknown ** ppUnkObject);

  HRESULT NoteChangeTime (THIS_ DWORD dwRegister, FILETIME * pfiletime);

  HRESULT GetTimeOfLastChange (THIS_ IMoniker * pmkObjectName, FILETIME * pfiletime);

  HRESULT EnumRunning (THIS_ IEnumMoniker ** ppenumMoniker );

};


IRunningObjectTable::Register

HRESULT IRunningObjectTable(grfFlags, punkObject, pmkObjectName, pdwRegister)
DWORD grfFlags
IUnknown *punkObject
IMoniker *pmkObjectName
DWORD *pdwRegister

IRunningObjectTable::Register registers a running object in the running object table.

Parameters

grfFlags

[in] Specifies the strength or weakness of the registration. Zero specifies weak registration and one specifies strong registration. Weak registration enables an object to be connected to and released only once. Strong registration enables multiple clients to connect to and release an active object multiple times. Strong registration can be specified using the constant ROTFLAGS_REGISTRATIONKEEPSALIVE = 1

punkObject

[in] Points to the object that has just entered the running state.

pmkObjectName

[in] Points to the moniker that is to bind to the newly running object.

pdwRegister

[out] Points to where to return a value that can later be used to revoke the registration. It cannot be NULL. Zero is never returned as a valid registration value, so, unless IRunningObjectTable::Register fails, pdwRegister is never NULL on exit.

Return Values

Value	Meaning
S_OK	The object was registered successfully.
MK_S_MONIKERALREADYREGISTERED	An attempt was made to register a given pmkObjectName a second time.
E_OUTOFMEMORY	Ran out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	Failure of an unknown cause.
Remarks

If the moniker pointed to by pmkObjectName is bound to during registration, the object pointed to by punkObject should be used as the result of the bind operation (with an appropriate QueryInterface).

The moniker pmkObjectName should be fully reduced before registration. For information on reducing monikers, see IMoniker::Reduce described earlier. If an object is known by more than one fully reduced moniker, it should register itself under all such monikers.

OLE compound-document objects should call IRunningObjectTable::Register as soon as the following conditions are all true simultaneously:

1.	The object is in the running state.

2.	The object knows its full moniker (see the IOleObject::SetMoniker member function description.)

a.	A moniker has been assigned to the object that is relative to the object's container (this is part of the persistent state of the object).

b.	The object knows the current moniker of its container, almost always obtained by the object's container calling IOleObject::SetMoniker.

3.	A link is made to the object or something it contains.

OLE objects register themselves as running by using their full moniker (see also IOleObject::GetMoniker). Generally, if a link has ever been made to an object, the object must assume that the link might still exist. For example, the consumer of the link might be on a floppy disk somewhere and later reappear. (The exceptions are rare situations where a link is created but almost immediately destroyed before the link source is saved.)

Attempting to register an object under the same moniker returns MK_S_MONIKERALREADYREGISTERED. In most cases, the application revokes the pending registration (in some rare cases, multiple objects may want to register under the same moniker).

With weak registration only, the registration entry becomes invalid as soon as the last external connection to the object is released. Presumably the object will release itself as a result of the loss of connections. In the process of doing so, it revokes its registration. An object that registers itself should always revoke itself from the running object table by calling IRunningObjectTable::Revoke.

See Also

IRunningObjectTable::Revoke, IOleObject::SetMoniker


IRunningObjectTable::Revoke

HRESULT IRunningObjectTable::Revoke(dwRegister)
DWORD dwRegister

IRunningObjectTable::Revoke revokes an object's registration from the running object table.

dwRegister

[in] Specifies a value previously returned from the IRunningObjectTable::Register member function.

Return Values

Value	Meaning
S_OK	The object registration has been successfully undone.
E_INVALIDARG	The object is not registered as running.
E_UNEXPECTED	Failure of unknown cause.
Remarks

IRunningObjectTable::Revoke revokes a registration made previously through IRunningObjectTable::Register, presumably because the object is about to quit running.

Whenever any of the conditions that put an object in the running state cease to be true, the object should revoke its registration(s).

See Also

IRunningObjectTable::Register


IRunningObjectTable::IsRunning

HRESULT IRunningObjectTable::IsRunning(pmkObjectName)
IMoniker *pmkObjectName

IRunningObjectTable::IsRunning determines whether an object with this moniker is currently registered as running.

pmkObjectName

[in] Points to the moniker to compare against entries in the running object table to determine whether the object is running.

Return Values

Value	Meaning
S_OK	The object is registered as running.
S_FALSE	The object is not registered as running.
Remarks

In general, this function should only be called by implementations of IMoniker::IsRunning. Clients of monikers should invoke this on their monikers rather than asking the running object table directly.

The running object table compares monikers by sending IMoniker::IsEqual to the monikers already in the table with pmkOjbectName on the right as an argument.

See Also

IMoniker::IsEqual


IRunningObjectTable::GetObject

HRESULT IRunningObjectTable::GetObject(pmkObjectName, ppUnkObject)
IMoniker *pmkObjectName
IUnknown ** ppUnkObject

IRunningObjectTable::GetObject returns the running object designated by pmk.

Parameters

pmkObjectName

[in] Points to the moniker of the object to return.

ppUnkObject

[out] Points to where to return the pointer to the object. A NULL value indicates the object is not registered. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The object has been successfully returned.
MK_E_UNAVAILABLE	The object is not registered as running.
Remarks

Moniker implementations use IRunningObjectTable::GetObject to determine whether an object is already running, and, if so, to get the pointer to the object.

The running object table compares monikers by sending IMoniker::IsEqual to the monikers already in the table with pmkObjectName on the right as an argument.

See Also

IRunningObjectTable::IsRunning


IRunningObjectTable::NoteChangeTime

HRESULT IRunningObjectTable::NoteChangeTime(dwRegister, pfiletime)
DWORD dwRegister
FILETIME * pfiletime

IRunningObjectTable::NoteChangeTime makes a note of the time that a particular object changed so that IMoniker::GetTimeOfLastChange can report an appropriate time-of-last-change.

Parameters

dwRegister

[in] Identifies which object has changed. This is the value returned by the IRunningObjectTable::Register call. When an object is registered in the running object table (using IRunningObjectTable::Register), a dwRegister value is returned that is used to identify that object in other running object table methods. Here it is used to denote which object has changed.

pfiletime

[in] Points (on entry) to the time at which the object changed.

Return Values

Value	Meaning
S_OK	The object has been successfully returned.
E_INVALIDARG	No such connection exists.
Remarks

For each object, OLE needs to know a time past which the object has not changed. This is used, for example, to determine whether links are up to date. While an object is on disk, this time comes from the file time stamp, but when the object is in the running state, the change times are recorded in the running object table. When an object application changes an object, it should call this method with the file time corresponding to the current instant, which can be retrieved by calling CoFileTimeNow.

See Also

CoFileTimeNow, IRunningObjectTable::Register


IRunningObjectTable::GetTimeOfLastChange

HRESULT IRunningObjectTable::GetTimeOfLastChange(pmkObjectName, pfiletime)
IMoniker *pmkObjectName
FILETIME * pfiletime

IRunningObjectTable::GetTimeOfLastChange reports the time of change recorded for the given moniker.

Parameters

pmkObjectName

[in] Points to the moniker whose time of last change is being checked.

pfiletime

[out] On exit, points to the time at which the object last changed.

Return Values

Value	Meaning
S_OK	The time of change has been successfully reported.
MK_E_UNAVAILABLE	Either no time has been recorded for the moniker, or the object is not registered as running.
Remarks

When invoked with pmkToLeft = NULL, IMoniker::GetTimeOfLastChange calls IRunningObjectTable::GetTimeOfLastChange as its first task.

The running object table compares monikers by sending IMoniker::IsEqual to the monikers already in the table with pmkObjectName on the right as an argument.

IRunningObjectTable::GetTimeOfLastChange is not typically used by applications. Applications instead call IMoniker::GetTimeOfLastChange. In most cases, if the object is registered in the running object table, the answer is the same, but in some cases, such as when the moniker contains wildcard items, IMoniker::GetTimeOfLastChange returns the correct result while IRunningObjectTable::GetTimeOfLastChange might fail to find the moniker in the running object table. IMoniker::GetTimeOfLastChange also returns the correct result when the object is passive (on disk).

See Also

IMoniker::GetTimeOfLastChange, IRunningObjectTable::NoteChangeTime


IRunningObjectTable::EnumRunning

HRESULT IRunningObjectTable::EnumRunning(ppenumMoniker)
IEnumMoniker ** ppenumMoniker

IRunningObjectTable::EnumRunning allows applications to enumerate the monikers of all the objects registered as running in the running object table.

ppenumMoniker

[out] Points to the location that is to receive the pointer to the returned moniker enumerator. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	Enumerator successfully returned.
E_OUTOFMEMORY	Ran out of memory.
Remarks

The monikers that have been passed to IRunningObjectTable::Register are enumerated. The returned enumerator is of type IEnumMoniker. For more information, see the description of IEnumMoniker Interface.


Linking Functions

BindMoniker(LPMONIKER pmk, DWORD grfOpt, REFIID iidResult, LPVOID  * ppvResult);

CreateAntiMoniker(LPMONIKER  * ppmk);

CreateBindCtx(DWORD reserved, LPBC  * ppbc);

CreateFileMoniker(LPCOLESTR lpszPathName, LPMONIKER  * ppmk);

CreateItemMoniker(LPCOLESTR lpszDelim, LPCOLESTR lpszItem,

    LPMONIKER  * ppmk);

CreatePointerMoniker(LPUNKNOWN punk, LPMONIKER  * ppmk);

CreateGenericComposite(LPMONIKER pmkFirst, LPMONIKER pmkRest, LPMONIKER  * ppmkComposite);

GetRunningObjectTable( DWORD reserved, LPRUNNINGOBJECTTABLE * pprot);

MkParseDisplayName(LPBC pbc, LPOLESTR szUserName, ULONG  * pchEaten, LPMONIKER  * ppmk);

MonikerRelativePathTo(LPMONIKER pmkSrc, LPMONIKER pmkDest,

   LPMONIKER  * ppmkRelPath, BOOL dwReserved);

MonikerCommonPrefixWith(LPMONIKER pmkThis, LPMONIKER pmkOther,

    LPMONIKER  * ppmkCommon);

BindMoniker

CreateAntiMoniker

CreateBindCtx

CreateFileMoniker

CreateGenericComposite

CreateItemMoniker

CreatePointerMoniker

GetRunningObjectTable

MkParseDisplayName

MonikerRelativePathTo

MonikerCommonPrefixWith


BindMoniker

HRESULT BindMoniker(pmk, grfOpt, iidResult, ppvResult)
LPMONIKER pmk
DWORD grfOpt
REFIID iidResult
LPVOID  * ppvResult

BindMoniker binds a moniker with the specified interface and returns the result.

Parameters

pmk

Points to the moniker to be bound.

grfOpt

Reserved for future use; must be zero.

iidResult

Identifies the interface to connect to the object.

ppvResult

Points to the address to place the resulting object.

Return Values

Value	Meaning
S_OK	The bind operation was successful.
MK_E_NOOBJECT	Some, possibly intermediate, object could not be found during some operation, such as binding or parsing a display name.
E_OUTOFMEMORY	Ran out of memory.
IMoniker::BindToObject errors.	All IMoniker::BindToObject errors.
BindMoniker is a helper function that packages the following functionality:

IBindCtx pbc;

CreateBindCtx(0, &pbc);

pmk->BindToObject(pbc, NULL, riid, ppvObj);

pbc->Release();

See Also

IMoniker::BindToObject


CreateAntiMoniker

HRESULT CreateAntiMoniker(ppmk)
LPMONIKER  * ppmk

CreateAntiMoniker creates and returns a new anti moniker.

ppmk

Pointer to where to return the new anti moniker.

Return Values

Value	Meaning
S_OK	The anti moniker was created successfully.
E_OUTOFMEMORY	Ran out of memory.
Remarks

An anti moniker is a moniker that when joined to the end of a generic moniker removes the last piece. Anti monikers provide no functional use; they exist solely to support implementations of the IMoniker::Inverse member function.

See Also

IMoniker::Inverse


CreateBindCtx

HRESULT CreateBindCtx(reserved, ppbc)
DWORD reserved
LPBC  * ppbc

CreateBindCtx allocates and initializes a new bind context using an OLE-supplied implementation.

Parameters

dwReserved

Reserved for future use; must be zero.

ppbc

Points to where to store the new bind context.

Return Values

Value	Meaning
S_OK	The bind context was allocated and initialized successfully.
E_OUTOFMEMORY	Ran out of memory.
Remarks

The default values for the following members of the BIND_OPTS structure in the newly created bind context are:

grfFlags = 0

grfMode = STGM_READWRITE

dwTickCountDeadline = 0

See Also

IBindCtx::SetBindOptions


CreateFileMoniker

HRESULT CreateFileMoniker(lpszPathName, ppmk)
LPCOLESTR lpszPathName
LPMONIKER  * ppmk

CreateFileMoniker creates a moniker from a path.

Parameters

lpszPathName

Points to the path of the needed file.

ppmk

Points to the newly created moniker.

Return Values

Value	Meaning
S_OK	The moniker was created successfully.
MK_E_SYNTAX	An error in the syntax of a path was encountered while creating a moniker.
E_OUTOFMEMORY	Ran out of memory.
Remarks

The path can be absolute or relative. In the latter case, the resulting moniker will need to be composed onto another file moniker before it can be bound. In either case, it is often necessary that other monikers are composed onto the end of this moniker to access the subpieces of the document stored in the file.


CreateItemMoniker

HRESULT CreateItemMoniker(lpszDelim, lpszItem, ppmk)
LPCOLESTR lpszDelim
LPCOLESTR lpszItem
LPMONIKER  * ppmk

CreateItemMoniker allocates and returns a new item moniker.

Parameters

lpszDelim

Points to a string that will prefix lpszItem in the display name of this moniker. Often an exclamation mark: "!".

lpszItem

Points to the item name to pass to IOleItemContainer::GetObject.

ppmk

Points to where to put the new item moniker.

Return Values

Value	Meaning
S_OK	The moniker was created successfully.
E_OUTOFMEMORY	Ran out of memory.
Remarks

The resulting moniker will be joined to the end of a second moniker that binds to an object supporting the IOleItemContainer interface. When bound, the resulting composite moniker extracts the object of the indicated name from within the object container.

The lpszItem parameter is the item name that will later be passed to IOleItemContainer::GetObject. The lpszDelim parameter is simply another string that can prefix lpszItem in the display name of the item moniker.

See Also

IOleItemContainer::GetObject


CreatePointerMoniker

HRESULT CreatePointerMoniker(punk, ppmk)
LPUNKNOWN punk
LPMONIKER  * ppmk

CreatePointerMoniker creates a pointer moniker from the specified interface pointer.

Parameters

punk

Points to the IUnknown interface pointer from which the pointer moniker will be created.

ppmk

Points to the location that will contain a pointer to the new pointer moniker.

Return Values

Value	Meaning
S_OK	The pointer moniker was created successfully.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Ran out of memory.
Remarks

A pointer moniker is essentially a wrapping of a pointer so that it can be passed to those interfaces that require monikers. The result of binding a pointer moniker is the pointer itself. Pointer monikers cannot be loaded and saved.

Think of pointers as a referencing mechanism into the "active space," that is, a process's memory. Most moniker implementations are, by contrast, references into "passive space," that is, the representation of an object on disk. Pointer monikers provide a means by which a given use of a moniker can reference either active or passive space transparently. IMoniker methods treat pointer monikers slightly differently, as described in the following table.

Method	Treatment
BindToObject	Turns into a QueryInterface on the pointer.
BindToStorage	Returns MK_E_NOSTORAGE.
Reduce	Reduces the moniker to itself.
ComposeWith	Always does a generic composition.
Enum	Returns NULL.
IsSystemMoniker	Returns MKSYS_POINTERMONIKER.
IsEqual	Uses the identity test paradigm on pointers after first checking the other moniker for the right class.
Hash	Returns a constant.
GetTimeOfLastChange	Returns MK_E_UNAVAILABLE.
Inverse	Returns an anti moniker.
RelativePathTo	Returns the other moniker.
GetDisplayName	Returns NULL.
ParseDisplayName	Binds to the punk pointer using the IParseDisplayName interface and works from there.
Instances of pointer monikers cannot be serialized; that is, IPersistStream::Save will return an error. These monikers can, however, be marshalled to a different process in an RPC call; internally, OLE marshals and unmarshals the pointer using the standard paradigm for marshalling interface pointers.


CreateGenericComposite

HRESULT CreateGenericComposite(pmkFirst, pmkRest, ppmkComposite)
LPMONIKER pmkFirst
LPMONIKER pmkRest
LPMONIKER  * ppmkComposite

CreateGenericComposite allocates and returns a new composite moniker.

Parameters

pmkFirst

Points to the first element(s) in the new composite; cannot be NULL. pmkFirst can point to any type of moniker, including a generic composite.

pmkRest

Points to the last element(s) in the new composite; cannot be NULL. pmkRest can point to any type of moniker, including a generic composite.

ppmkComposite

Points to where to return the new composite.

Return Values

Value	Meaning
S_OK	The composite moniker was allocated successfully.
E_OUTOFMEMORY	Ran out of memory.
Remarks

Most monikers are a composite, that is, made up of other moniker pieces, and referred to as a generic composite moniker. All generic composite monikers are instances of the GenericCompositeMoniker class, whose implementation is provided with OLE—there is no need for two generic composite moniker classes.

The OLE-provided implementations of the IMoniker::Reduce and IMoniker::BindToObject functions for the GenericCompositeMoniker class are particularly important, as they manage the interactions between the various elements of the composite and, as a consequence, define the semantics of binding to the moniker.

Generic composite monikers of size zero or of size one are never exposed outside of OLE's internal GenericCompositeMoniker method implementations. From a client perspective, a generic composite moniker always contains at least two elements.

Generic composites get flattened into their component pieces before being put into the new composite. CreateGenericComposite will be called by implementations of IMoniker::ComposeWith when they need to do a generic compose operation.

See Also

IMoniker::ComposeWith


GetRunningObjectTable

HRESULT GetRunningObjectTable(reserved, pprot)
DWORD reserved
LPRUNNINGOBJECTTABLE  * pprot

GetRunningObjectTable returns a pointer to the running object table for the caller's context.

Parameters

reserved

Reserved for future use.

pprot

Points to where to return the running object table pointer.

Return Values

Value	Meaning
S_OK	The operation completed successfully.


MkParseDisplayName

HRESULT MkParseDisplayName(pbc, szUserName, pchEaten, ppmk)
LPBC pbc
LPCOLESTR szUserName,
ULONG  * pchEaten
LPMONIKER  * ppmk

Given a string, MkParseDisplayName returns a moniker of the object that szUserName refers to. This operation is known as parsing.

Parameters

pbc

Points to the bind context in which to accumulate bound objects.

szUserName,

Points to the display name to be parsed.

pchEaten

Points, on exit, to the number of characters of the display name that were successfully parsed.

ppmk

Points to the resulting moniker.

Return Values

Value	Meaning
S_OK	The parse operation was successful and the moniker was created.
MK_E_SYNTAX	An error in the syntax of a filename was encountered while parsing a display name or while creating a moniker.
E_OUTOFMEMORY	Ran out of memory.
IParseDisplayName errors.	Any IParseDisplayName errors.
Remarks

When a display name is parsed into a moniker, it is resolved into its component moniker pieces.

If a syntax error occurs, the number of successfully parsed characters is returned in pchEaten and NULL is returned through ppmk. Otherwise, the value returned through pchEaten indicates the size of the display name.

In general, parsing a display name is as expensive as binding to the object that it refers to because various name-space managers need to be connected to by the parsing mechanism. Objects are not released by the parsing operation itself, but are instead handed over to the passed-in bind context. Thus, if the moniker resulting from the parse is immediately bound using this bind context, the redundant loading of objects is minimized.

Most compound document links are created by a user choosing Copy and then Paste Link rather than typing in the name of the link source. This link is created programmatically with no need for an intermediate form that is a human-readable textual representation. The primary use of MkParseDisplayName lies instead in textual programming languages that permit remote references as syntactic elements. The expression language of a spreadsheet is a good example of such a language. MkParseDisplayName is also used in the implementation of the standard Edit Links dialog. The parsing process is an inductive one; an initial step gets the process going, then an inductive step is repeatedly applied. At any point after the beginning of the parse, a certain prefix of szUserName has been parsed into a moniker, and a suffix of the display name remains not understood. This is illustrated in the following figure.

Intermediate stage in parsing a display name into a moniker.

The inductive step, using IMoniker::ParseDisplayName, asks the moniker-so-far to use as much as it needs of the remaining suffix and then return the corresponding moniker and new suffix. The moniker is composed on to the end of the existing moniker-so-far, and the process repeats. Implementations of IMoniker::ParseDisplayName vary in exactly where the knowledge of how to carry out the parsing is kept. Some monikers are only used in particular kinds of containers. It is likely that these monikers know the legal display name syntax within the objects that they denote, and can carry out the processes completely within IMoniker::ParseDisplayName.

Typically, however, the moniker-so-far is generic, meaning that it is not specific to one kind of container, and thus cannot know the legal syntax for the elements within the container, as is the case with file monikers and item monikers.

Generic monikers employ the following strategy to carry out parsing. First, the moniker connects to the class of object that it currently denotes, asking for IParseDisplayName interface (see the CoGetClassObject function). If that succeeds, it then uses the obtained interface pointer to attempt to carry out the parse. If the class refuses to handle the parse, then the moniker binds to the object it denotes, asking again for IParseDisplayName. If this fails, the parse is aborted. The effect is that ultimately an object gets to be in control of the syntax of the elements it contains.

Certain objects can carry out parsing more efficiently by having a moniker or their class do the parsing on their behalf. Notice that the OLE 2 parsing machinery knows nothing of the legal syntax of display names (with the exception of the initial parsing step). (See the following code sample). It will help the user if display names in different contexts do not have unnecessarily different syntax. While some rare circumstances call for special-purpose syntax, that the syntax for display names should be the same as that of the native file system syntax. Most importantly, the delimiters used to separate one part of the display name from the next should be the same ones used by the native file system. It is less important that the piece of the path between the delimiters conform to the syntax of the file system; this is recommended unless there are clear reasons to do otherwise. Specifically, the recommended syntax for display names on MS-DOS is:

displayName	::=	initialPart [path]

initialPart	::=	drive ':'  |  '\\' server '\' share

path	::=	delim pathPiece

delim	::=	'\' | '/' | ! | '['

pathPiece	::=	identifier

drive	::=	identifier

server	::=	identifier

share	::=	identifier

identifier	::=	(any character but a delim)*

To accommodate existing practice, the parsing process should treat an exclamation mark as a delimiter. However, in IMoniker::GetDisplayName, such exclamation marks should be converted to the normal '\' delimiter. Unless there is a compelling reason for doing so, applications should use only the system-defined delimiters listed above. If, for example, other delimiters are used, the result of calling MkParseDisplayName may be incorrect.

Monikers and objects that have implementations on more than one platform, such as file monikers, should always parse according to the syntax of the platform on which they are currently running. When asked for their display name, monikers should also show delimiters appropriate to the platform on which they are currently running, even if they were originally created on a different platform. Users should always see delimiters appropriate for the host platform.

In order to accommodate monikers that are not file monikers or composite monikers that begin with a file moniker, the moniker must begin with an '@' sign followed by a ProgID. Following the ProgID can be anything that would normally follow a filename.

For example, to parse a string such as "@Realtime\MSFT", where "Realtime" is a ProgID stored in the registry, the parsing process first looks for a file named "@Realtime" in the current directory. (Note that '@'is a legal filename character.) IF that fails, the parsing process looks for the ProgID "Realtime" in the registry. If found, it looks up the CLSID and then tries the IParseDisplayName interface first from the class factory, if that fails, it creates an instance of the class and tries to get the IParseDisplayName interface. If one of these succeeds, the full string, "@Realtime\MSFT" is passed to the ParseDisplayName method and a moniker is returned.

The initial step of the parsing process needs to determine the initial moniker-so-far. MkParseDisplayName knows the syntax that the display name of a moniker can legally begin with, and it uses this knowledge to choose the initial moniker. In OLE 2, this syntax is fixed because a legal display name must begin with a filename. However, that filename can be drive absolute, drive relative, working-directory relative, or begin with an explicit network share name.

The initial moniker is determined by trying the following strategies, in order, using the first to succeed.

1.	All prefixes of szDisplayName that consist solely of valid filename characters are consulted as file monikers in the running object table. This is attempted in order to support documents that are as yet unsaved.

2.	The maximal prefix of szDisplayName, which consists solely of valid filename characters, is checked to see if an OLE1 document is registered by that name (this is a DDE broadcast or two). In this case, the returned moniker is an internal moniker provided by the OLE1 compatibility layer of OLE2.

3.	The file system is consulted to check whether a prefix of szDisplayName matches an existing file. If so, the filename may 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 longest string immediately following it that conforms to the legal ProgID syntax is determined. This is converted to a CLSID with CLSIDFromProgID. If this is an OLE2 class, the class object is so designated and an instance of it is asked in turn for the IParseDisplayName interface. The resulting IParseDisplayName interface is then given the whole string to parse, starting with the '@'. If the CLSID is an OLE1 class, then the string following the ProgID is considered as an OLE1 / DDE filename!item syntax link designator.

See Also

IMoniker::ParseDisplayName, GetDisplayName


MonikerRelativePathTo

HRESULT MonikerRelativePathTo(pmkSrc, pmkDest, ppmkRelPath, dwReserved)
LPMONIKER pmkSrc
LPMONIKER pmkDest
LPMONIKER  *ppmkRelPath
BOOL dwReserved

MonikerRelativePathTo creates a moniker specifying the relative path from pmkSrc to pmkDest.

Parameters

pmkSrc

The moniker pointing to the start of the relative path.

pmkDest

The moniker to be expressed relative to pmkSrc.

ppmkRelPath

The location to be filled with the relative moniker.

dwReserved

Reserved for future use, must be FALSE.

Return Values

Value	Meaning
S_OK	The parse operation was completed successfully.
MK_S_HIM	The other moniker is the prefix of the receiver moniker.
MK_E_NOTBINDABLE	MonikerRelativePathTo was called on a relative moniker. The moniker cannot be bound to something until it is composed with a container moniker.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Ran out of memory.
Remarks

MonikerRelativePathTo is not called by any code except implementations of moniker classes and can be ignored unless you are implementing a new class of monikers.

If you are implementing a class of monikers, you should implement IMoniker::RelativePathTo as indicated by the following pseudo code:

HRESULT CMyMoniker::RelativePathTo (

IMoniker * pmkOther,

IMoniker **ppmkRelPath  )

{

Does pmkOther point to a moniker of a type treated as special
by this class?

If so, execute special case code.

Otherwise,

return MonikerRelativePathTo( this, pmkOther, ppmkRelPath,
0);

}

This function is necessary to handle those cases where pmkOther points to a generic composite moniker.

MonikerRelativePathTo should only be called on absolute monikers, meaning that a file moniker that starts with a share or drive name and followed by items. It should not be called on relative monikers.

See Also

IMoniker::RelativePathTo


MonikerCommonPrefixWith

HRESULT MonikerCommonPrefixWith(pmkThis, pmkOther, ppmkCommon)
LPMONIKER pmkThis
LPMONIKER pmkOther
LPMONIKER  * ppmkCommon

MonikerCommonPrefixWith returns the longest common prefix that the moniker pointed to by pmkThis shares with the moniker pointed to by pmkOther.

Parameters

pmkThis

The starting moniker for computing the longest common prefix.

pmkOther

The other moniker to use for computing the longest common prefix.

ppmkCommon

The place at which the moniker of the longest common prefix common to pmkThis and pmkOther is to be returned. Cannot be NULL.

Return Values

Value	Meaning
S_OK	The prefix operation was completed successfully.
MK_S_HIM	The other moniker is the prefix of the receiver moniker.
MK_S_ME	The receiver moniker, the moniker whose CommonPrefixWith method is called, is a prefix of the other moniker.
MK_S_US	The receiver and the other moniker are equal.
MK_E_NOPREFIX	The monikers have no common prefix.
MK_E_NOTBINDABLE	MonikerCommonPrefixWith was called on a relative moniker. The moniker cannot be bound to something until it is composed with a container moniker.
E_UNEXPECTED	An unexpected error occurred.
E_OUTOFMEMORY	Ran out of memory.
MonikerCommonPrefixWith is intended solely for the use of moniker implementors. Clients should instead compute the common prefix between two monikers using pmkSrc->CommonPrefixWith(pmkOther, ppmkPrefix).

Implementations of IMoniker::CommonPrefixWith call MonikerCommonPrefixWith as part of their internal processing. Such a method should first check whether the other moniker is a type it recognizes and handles in a special way. If not, it should call MonikerCommonPrefixWith, passing itself as pmkSrc and the other moniker as pmkDest. MonikerCommonPrefixWith will handle the generic composite cases correctly.

MonikerCommonPrefixWith should only be called on absolute monikers, that is, a file moniker that starts with a share or drive name, followed by items. It should not be called on relative monikers.

See Also

IMoniker::CommonPrefixWith


Persistent Storage Interfaces and Functions

This section describes the interfaces, APIs, and helper functions that support the OLE structured-storage model, the implementation of which is commonly referred to as compound files.

The following table provides a brief summary of the interfaces that support OLE's storage model.

Interface	Implementation	Use
IStorage	Implemented by OLE.	Used by containers to instantiate a directory-like collection of storage and stream objects in which an embedded object's native data is saved. Object applications are given a valid pStg for embedded objects while an object is in the running state.
IStream	Implemented by OLE.	Used by container and object applications to read and write the underlying bytes of data that comprise an IStorage object. IStream instances are analogous to files.
IRootStorage	Implemented by OLE.	Used by containers to switch the underlying disk file that IStorage objects are saved to.
ILockBytes	Implemented by OLE.	Generally, used only by OLE to manipulate the byte array that underlies a compound file.
IEnumSTATSTG	Implemented by OLE.	Used to enumerate IStorage objects. An instance of the enumeration is available by calling IStorage::EnumElements.
IPersist	Implemented by object handlers and object applications.	Used to obtain an object's CLSID. The base interface from which the three other persistence-related interfaces derive: IPersistStorage, IPersistStream, and IPersistFile.
IPersistStorage	Implemented by object handlers and object applications.	Used by containers to read and write a compound-document object's native data to and from its IStorage.
IPersistStream	Implemented by OLE.	Used by containers to save and reload objects stored in a simple serial stream rather than in an IStorage object (such as a moniker).
IPersistFile	Implemented by object applications and optionally by container applications to support linking to embedded objects and to file-based objects.	Used by OLE to load an outer-level document object residing in a file, as opposed to one that is embedded inside a compound document.

Note  Any of the storage-related interfaces or APIs can return a system error code. This information, if returned, is contained within the FACILITY_STORAGE facility field of the returned status code and can be extracted using the SCODE_FACILITY macro. For more information, see Error Handling Functions and Macros.


Structured Storage Overview

Objects are saved to their containing documents using the IStorage instance passed to the object by its container. Usually, the container opens a child IStorage object within its own IStorage instance. Each IStorage instance can contain many levels of nested storage objects — objects contained within objects. Having obtained an IStorage object for itself by calling IStorage::CreateStorage, an object has the streams and substorages available for saving its data.

An object can use as many streams as needed to save its native data. In addition to the native data stream, OLE uses one or more streams to store information about the object, including the object's cached presentation, identifying information about the object application, and link connectivity information. (This information is later used to load an object into memory using the OleLoad function and to convert objects from one type to another.)

To better understand the concept of storing objects, consider the word-processing document shown in the following figure. The document contains an embedded worksheet object, which in turn has a chart embedded in it.

Word processing document with nested levels of objects.

The word-processing document is saved and opened as the outermost root IStorage object. The embedded worksheet and chart objects are saved as child storage objects. Inside these storage objects, IStream objects are created in which the object's actual native data is saved (streams are also created for saving OLE information about each object). Once an application has obtained a valid IStorage instance (by calling the StgCreateDocfile or StgOpenStorage functions), the needed data streams are created by calling IStorage::CreateStream. From this root IStorage object, child IStorage objects can be created to save the document's contained objects. These child IStorage elements are created by calling IStorage::CreateStorage.  To open an existing IStorage object, applications call the IStorage::OpenStorage function. Child IStorage objects and their collective IStreams can be arbitrarily nested to any level in an open root IStorage object.

Note  Child storage objects and streams must always be opened in STG_SHARE_EXCLUSIVE access mode.

It is the container application's responsibility to open the IStorage instances for its embedded objects in the appropriate access mode. Most often, this is transacted read-write/deny-write mode, but the exact choice depends on how the container is allocating the IStorage object. The object and OLE must be free to read from and write to the IStream objects inside the IStorage object.

The following access modes are available when opening IStorage objects:

Access Mode	Purpose
STGM_TRANSACTED | STGM_READWRITE	Using this mode, an object can freely write to its IStorage instance. OLE takes care of the transaction, freeing the container from the task. This is the most common mode.
STGM_TRANSACTED | STGM_READ | STGM_SHARE_DENY_ WRITE	In transacted mode, write permissions are enforced at the time of the commit operation, not as changes are being made. Thus, this mode does not prevent the object from being able to write freely to its IStorage instance.
STGM_DIRECT | STGM_READWRITE	From the object's view, this mode behaves like transacted read and write. However, in this mode the container is responsible for keeping the changes to the object separate from the original version of the object when the container was loaded (and to which the object should revert if the container is closed without saving).
Although an embedded object can write freely to its own IStorage object, it cannot commit or revert changes. Because of this, the container is responsible for committing changes to its root IStorage object, which it does by calling IStorage::Commit on the open IStorage object.

An OLE object's IStorage instance can be put into one of three states: normal, no-scribble, and hands-off, as described in the following table:

Storage State	Description
Normal	Upon being created or loaded into memory, all OLE 2 embedded objects are given an IStorage instance they hold while they are either loaded or running. While in the running state, objects are free to write or "scribble" into their open IStorage instance. Unless IPersistStorage::Save or IPersistStorage::HandsOffStorage is called on the object, this is the normal, default state for an object.
No-scribble	The state of an object between the time IPersistStorage::Save finishes and IPersistStorage::SaveCompleted is called. In no-scribble mode, the object can read from its storage, but it cannot write to it until IPersistStorage:: SaveCompleted is called, which puts the object back in normal state.
Hands-off	The state of an object when IPersistStorage::HandsOffStorage is called on the object. The object is unable to read or write to its storage until IPersistStorage::SaveCompleted is called, which puts the object and any of its child storages back in the normal state. There are two variations to the hands-off state, hands-off from normal and hands-off after save, as described in the following sections.
The following diagram shows the storage-state transitions. Unmarked transitions are illegal.

Transitions between the OLE storage states.

The object should not clear its dirty bit if IPersistStorage::HandsOffStorage is called before IPersistStorage::Save and IPersistStorage::SaveCompleted being called. To ensure that the object is saved correctly, these follow recommendations. For more information, see the example for IPersistStorage::SaveCompleted.

1.	In the application's initialization code, create the following variables:

m_fSaveWithSameAsLoad = FALSE

m_fNoScribbleMode = FALSE;

2.	In the IPersistStorage::Save code, set the variables to the following values:

m_fSaveWithSameAsLoad = fSameAsLoad

m_fNoScribbleMode = TRUE;

3.	In the IPersistStorage::SaveCompleted code, compare the object's IStorage pointer to m_fSaveWithSameAsLoad and then clear the dirty bit as appropriate:

if (pStg != NULL || m_fSaveWithSameAsLoad)

{

if (m_fNoScribbleMode) {

clear dirty bit

send on save

}

m_fSaveWithSameAsLoad = FALSE;

}

m_fNoScribbleMode = FALSE;

The following tables describe the storage states and the transitions as various IPersistStorage methods are called. The state is not changed if an error occurs. Notice the relationship between clearing the dirty flag and calling IAdviseSink::OnSave to send notifications to connected advise sinks: the object calls OnSave precisely at the moment it changes from dirty to nondirty.

Uninitialized State

In the uninitialized state, the object has been created, but not yet initialized.

IPersistStorage Method Called	Action to Take	State Entered
InitNew (pStg)	Create a new object containing default data as defined by the object application.	Normal
Load (pStg)	Initialize the object from the data found in pStg.	Normal
GetClassID	Error: E_UNEXPECTED.	Uninitialized
Other IPersistStorage methods	Error: E_UNEXPECTED.	Uninitialized
Normal State

In the normal state, the object has been initialized and has a valid pointer to its storage. In this state, the object can freely read from and write to its storage. This is the default state for an embedded object, and is the state in which the object is found except when it is uninitialized or is undergoing a save operation.

IPersistStorage Method Called	Action to Take	State Entered
Save(pStgSave, TRUE)	Recursively save any dirty embeddings. Write data as appropriate into pStgSave. This call is used in the low- memory save sequence and must not fail for lack of memory.	No-scribble
Save(pStgNew, FALSE)	Recursively save any dirty embeddings. Write data as appropriate into pStgNew.	No-scribble
HandsOffStorage	Recursively invoke HandsOffStorage on any loaded embeddings. Release pStgCur and any storage elements opened from there.	Hands-off from normal
SaveCompleted	Error: E_UNEXPECTED. No other change of state.	Normal
InitNew or Load	Error: E_UNEXPECTED. No other change of state.	Normal
Other IPersistStorage methods	Error: E_UNEXPECTED.	Uninitialized
No-Scribble State

In the no-scribble state, the object has a valid IStorage and can read from it, but is not allowed to write to it. This state gives the object's container a chance to save the object's data. Most containers just call IStorage::Commit, but some, such as databases, use other techniques for saving.

This state is parameterized by a Boolean value, fSavedToCurrentStorage, that indicates whether the object was saved into pStgCur or a separate storage. This is used to control the clearing of the dirty flag on error.

IPersistStorage Method Called	Action to Take	State Entered
SaveCompleted(NULL)	If fSavedToCurrentStorage is TRUE, then if the dirty flag is set, clear it and call IAdviseSink::OnSave to notify connected advise sinks.	Normal
SaveCompleted(pStgNew)	Do the equivalent of HandsOffStorage followed by SaveCompleted(pStgNew).	Normal or hands-off after save; see below.
HandsOffStorage	Recurse on any loaded embeddings. Release pStgCur and any subelements opened.	Hands-off after save
Save	This transition indicates a bug on the container's part: it has erroneously omitted an intervening call to SaveCompleted. The object can either fail (E_UNEXPECTED) or carry out the save a second time and succeed.	No-scribble
InitNew or Load	Error: E_UNEXPECTED.	No-scribble
Other IPersistStorage methods	Error: E_UNEXPECTED.	Uninitialized
Hands-Off

In the hands-off state, the object just saved its internal state into a storage, and was then told not to access its internal storage (this happens in the low-memory save sequence). When the object eventually gets back a new storage, it is guaranteed to get back a storage containing exactly the bits that were just saved.

IPersistStorage Method Called	Action to Take	State Entered
SaveCompleted(pStgNew)	(Re)open the streams and storages needed. The bits in pStgNew are guaranteed to be the same as those last saved. Notice that if Save(pStgSave, false), were used, bits in pStgNew are different than those in the pStgCur being held and that were revoked in HandsOffStorage. If this function is successful, then if the dirty flag is set, clear it and call IAdviseSink::OnSave to notify connected advise sinks.	Normal, if able to reconnect to the new storage. Otherwise, if unsuccessful, fail with E_OUTOFMEMORY and remain in hands-off after save.
SaveCompleted(NULL)	Error: E_INVALIDARG or E_UNEXPECTED.	Hands-off after save
HandsOffStorage	Error: E_UNEXPECTED.	Hands-off after save
Save	Error: E_UNEXPECTED.	Hands-off after save
InitNew or Load	Error: E_UNEXPECTED.	Hands-off after save
Other IPersistStorage methods	Error: E_UNEXPECTED.	Uninitialized
Hands-off From Normal

In the hands-off from normal state, the container temporarily prevents the object from accessing its storage until the container is done with it. When the container releases the storage back to the object, it returns a storage with exactly the same bits as were there when the object was in the hands-off mode. Thus, the object does not need to completely re-initialize its internal state. Instead, the object can simply reconnect to the new storage by opening the appropriate streams and/or storages.

IPersistStorage Method Called	Action to Take	State Entered
SaveCompleted(pStgNew)	pStgNew is the new replacement IStorage for the one revoked by HandsOffStorage. The bits in the storage are guaranteed to be the same as those that were in the revoked storage. (Re)open the streams and storages needed. Do not clear the dirty flag even if this function is successful. Do not call IAdviseSink::OnSave.	If able to reconnect to new storage, enter normal(pStgNew). If unsuccessful (for example, unable to reopen data streams), fail (E_OUTOFMEMORY, and so on) and remain in hands-off from normal.
SaveCompleted(NULL)	Error: E_INVALIDARG or E_UNEXPECTED.	Hands-off from normal
HandsOffStorage	Error: E_UNEXPECTED.	Hands-off from normal
Save	Error: E_UNEXPECTED.	Hands-off from normal
InitNew or Load	Error: E_UNEXPECTED.	Hands-off after save
Other IPersistStorage methods	Error: E_UNEXPECTED.	Uninitialized


Saving Objects to Storage

To save its contained embeddings, an object's top-level container performs the following tasks:

1.	Call OleSave on the IStorage object of each immediate embedded object, passing the IStorage object a pointer to where the object is to be saved. The object then recursively calls OleSave on any of its nested embeddings.

The OleSave function first calls the WriteClassStg function to save the object's CLSID before calling the object handler's IPersistStorage::Save method. (Usually, the object handler is the OLE-provided default handler.) The default handler's implementation of the IPersistStorage::Save first saves the object's presentation cache and then calls the object application's IPersistStorage::Save method to save the object's native data.

Finally, OleSave calls IStorage::Commit on the IStorage object to commit the changes back one level to the transaction state of the root IStorage object.

2.	After OleSave returns, the top-level container must call IPersistStorage::SaveCompleted on the immediate embedded objects to return them and any of their nested embeddings to the normal storage mode. Unless this is done, the object is left in the no-scribble mode it entered when OleSave called IPersistStorage::Save.

Note  IPersistStorage::SaveCompleted is always called on the IStorage object containing the object. It is a call that recurses to all currently loaded or running embedded objects. Should IPersistStorage::SaveCompleted fail, the objects do not return to normal storage mode. In this case, the container should back out any changes that have been committed and try instead to save the objects following the recommendations for saving an object in low-memory situations. For more information, see Saving Objects in Low Memory,.

Saving a linked object is not different from saving an embedded object. Saving a linked object is not different from saving an embedded object. To save a linked object that is not loaded or running, use the IStorage::CopyTo method. When an object is in the loaded or running state, OleSave should be called because it saves all changes.

Note  Static objects are saved into a stream called CONTENTS. Static metafile data gets saved in "placeable metafile format" and static DIB data gets saved in "DIB file format." These formats are the OLE standards for metafile and DIB. All data transferred using an IStream 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.

The OleSave and IPersistStorage::Save functions use the fSameAsLoad flag to determine whether the storage object that the object is being saved to is the same one from which the object was loaded. The value of fSameAsLoad can be combined with the value of the pStg passed to these functions to determine the routine used to save the compound document, as shown in the following table:

Value of fSameAsLoad	Value of pStg passed to IPersistStorage:: SaveCompleted	Save Routine to call
TRUE	NULL	File Save
FALSE	pStg	File Save As
FALSE	NULL	File Save Copy As
A container should save both the document's native data stream and any information on behalf of the compound-document object, including the following data:

ata Saved on Behalf of a Compound-Document Object	Description
szStgName	Stores the name of the object's persistent IStorage element.
fMonikerAssigned	Keeps track of whether or not a moniker was assigned to the compound-document object.
dwDrawAspect	Determines how to display the object when it is later loaded into memory: as an embedded object, a thumbnail representation (appropriate for browsing tools), an iconic representation, or as a sequence of pages (as if the object had been printed).


Opening Storage Objects

IStorage objects are handled differently, depending on whether the application is an object application or a container and whether the File New or File Open command was selected. The following sections discuss the different approaches.

Note  However, 16-bit OLE compound files require that range locking be enabled. This is done through the MS-DOS SHARE.EXE command. On Windows for Workgroups and in 32-bit OLE, range locking is part of the system and need not be installed.

File New

When opening a new untitled document, a container creates a temporary root IStorage object in which to create child IStorage objects for saving compound-document objects. This outermost root storage object forms the base from which all object-storage elements are created in the container.

A temporary root IStorage object is created with a call to the StgCreateDocfile function, specifying NULL for the document filename (pwcsName) and STG_DELETEONRELEASE as one of the values to the grfMode parameter. Additional values for the grfMode parameter determine the access mode (transacted or direct) in which to open the IStorage object.

In transacted mode, the changes to an object are buffered in temporary files until the application commits (saves) or reverts (destroys) the changes. In direct mode, the changes are immediately saved to the open IStorage object with no chance of undoing the changes. By default, if no mode value is specified, the IStorage object is opened in direct mode.

Containers must provide scribble-enabled storage to the objects they contain. However, sometimes there is a need to instantiate an object in read-only mode. If the object's storage is read only, it must also be opened in transacted mode, because transacted mode is the only scribble-enabled read-only mode. An object can call IStorage::Stat to determine is storage mode.

A root IStorage object that is opened in direct mode and that can be written to must always specify STGM_SHARE_EXCLUSIVE for its access mode.

File Open

When opening a file-based document, an application must be able to open the document as an IStorage object and read the document's data. Object applications typically read in the data and then close the IStorage object until it is time to save the document. Containers, on the other hand, must keep their root storage objects open at all times to ensure that compound-document objects have access to their storage.

To open an existing IStorage object, an application calls StgOpenStorage, passing it the IStorage filename and the access mode to use.


Loading Objects from Storage

An application that saves its application data to IStorage objects—after opening an existing IStorage—proceeds to initialize the document object with the contents of the IStream object(s). To read the contents of an IStorage object, an application calls IStorage::OpenStream, which opens an existing named IStream instance using the access mode specified by the grfMode parameter. To read the contents of an open data stream into memory, an application calls IStream::Read.

An IStorage object can have nested child IStorage objects, which are accessed through IStorage::OpenStorage.

As part of opening and reading IStorage objects, applications should test the compatibility of the document with the current version of the application. If the document is incompatible, the application should display an error message and open an untitled document instead. Store the version number of the application as part of its native data to make compatibility testing easy.


Storage Access Modes Flags
Storage Access Mode Flags

All IStorage objects and their contained streams can be created and opened in one or more access modes. The access modes for a given object are specified using a collection of flags divided into groups according to use. These flags are defined as follows:

/* creation */

#define STGM_CREATE

#define STGM_CONVERT

#define STGM_FAILIFTHERE

/* temporary creation */

#define STGM_DELETEONRELEASE

/* transaction support */

#define STGM_DIRECT

#define STGM_TRANSACTED

/* assigning permissions */

#define STGM_READ

#define STGM_WRITE

#define STGM_READWRITE

/* File sharing */

#define STGM_SHARE_DENY_NONE

#define STGM_SHARE_DENY_READ

#define STGM_SHARE_DENY_WRITE

#define STGM_SHARE_EXCLUSIVE

/* priority access */

#define STGM_PRIORITY

Transaction support for IStorage and IStream objects can be combined with the ability to read and/or write to the objects and to deny others the right to do the same. When new IStorage and IStream objects are created or existing objects are opened, the correct flags for creation, permissions, sharing, and transaction support are specified. Flags for temporary file creation and priority mode are also an option.

The flags are OR-ed together, usually in the grfMode parameter, which is passed to methods in the IStorage and IStream interfaces. These flags have the same semantics as the flags used with the Windows OpenFile function and not all of the flags apply in each method. In addition, the STGM_* flags have the same binary value as the corresponding OF_* flag.

Only one value from each of the six groups of flags can be specified at any one time. For example, the following is a common combination of flags for an IStorage object opened in transacted mode with full read/write capabilities. To protect the storage object's integrity, others are forbidden from writing to it.

STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_DENY_WRITE

The following sections discuss each of these flags by group in detail.


Storage Creation Flags

STGM_CREATE, STGM_CONVERT, and STGM_FAILIFTHERE specify the action taken when trying to create a new IStorage or IStream object when an object by the specified name already exists. Only one of these flags can be used in a given creation call. STGM_FAILIFTHERE is the default.

None of the flags should be passed to calls that open existing IStorage or IStream objects.


STGM_CREATE

STGM_CREATE indicates an existing IStorage or IStream object should be removed before the new one replaces it. A new object is created when this flag is specified only if the existing IStorage or IStream has been successfully removed.


STGM_CONVERT

This flag is applicable only to the creation of IStorage objects. The STGM_CONVERT flag is used in three situations:

•	When an application is trying to create an IStorage object on a disk where a file of that name already exists.

•	Inside another IStorage object where one of the data streams already has the specified name.

•	Inside an ILockBytes instance.

STGM_CONVERT allows the creation to proceed while preserving existing data. The old data is saved to a stream named CONTENTS containing the same data that was in the old IStorage, IStream, or ILockBytes instance. In the IStorage and ILockBytes cases, the data is flattened to a stream regardless of whether the existing file or ILockBytes currently contains a layered storage object.


STGM_FAILIFTHERE

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.

STGM_FAILIFTHERE applies to both IStorage and IStream objects.


Temporary Storage Creation Flag

The STGM_DELETEONRELEASE flag indicates that the underlying file is to be automatically destroyed when the root IStorage object is released. This capability is most useful for creating temporary files.


Transaction Flags

OLE's structured-storage model supports both transacted and direct mode. In transacted mode, all changes are buffered and the buffered changes are written to disk or discarded only when an explicit commit or revert request occurs. A snapshot of the compound file is taken only when STGM_SHARE_DENY_NONE is specified so the original version of the file can be maintained.

In direct mode, storage code writes each change to disk and reverting cannot be done. Direct mode STGM_DIRECT is the default access mode, and is implied by the absence of STGM_TRANSACTED


STGM_DIRECT

STGM_DIRECT is always specified for IStream objects. STGM_TRANSACTED is not supported in the compound file implementation of IStream. Other implementations can choose to support it.

For root storage objects, direct mode is only supported in the following combinations of access permissions flags:

•	STGM_READ | STGM_SHARE_EXCLUSIVE | STGM_READWRITE | 	 	STGM_SHARE_DENY_WRITE

•	STGM_PRIORITY | STGM_READ

•	STGM_READ |STGM_SHARE_DENY_WRITE

•	STGM_READWRITE | STGM_SHARE_EXCLUSIVE

The priority mode is independent of sharing modes; that is, normal rules apply except that changes cannot be committed during a priority open operation.


STGM_TRANSACTED

The transaction for each open object is nested in the transaction for its parent storage object. Therefore, committing changes at the child level is dependent on committing changes in the parent and a commit of the root storage object (top-level parent) is necessary before changes to an object are actually written to disk. The changes percolate upward: inner objects publish changes to the transaction of the next object outwards. Outermost objects publish changes permanently into the file system.

Transacted mode is not required on the parent storage object in order to use transacted mode on a contained object.

The scope of changes that are buffered in transacted mode is very broad. A storage or stream object can be opened, have arbitrary changes made to it, and then have the changes reverted, preserving the object as it was when it was first opened. The creation and destruction of elements within a storage object are scoped by its transaction.


Priority Flag

The STGM_PRIORITY flag allows an IStorage object to be opened so that a subsequent copy operation can be done at reduced cost. STGM_PRIORITY allows an application to read certain streams from storage before opening the storage object in a mode that would require a snapshot copy to be made.

Priority mode has exclusive access to the committed version of the IStorage object. While a compound file is open in priority mode, no other opening of the compound file can commit changes — even one that was opened before the priority mode opening. Therefore, applications should keep IStorage objects open in priority mode for as short a time as possible.

Calling StgOpenStorage with a non-NULL ppStgOpen parameter takes a storage object currently open in priority mode to normal operating mode. It does so without the possibility of losing access to the file to another process during an otherwise-necessary close-and-reopen operation.

When using STGM_PRIORITY, the following rules apply:

•	STGM_DIRECT and STGM_READ must be specified with STGM_PRIORITY.

•	STGM_WRITE and STGM_READWRITE must not be specified.


Access Permission Flags

The permission flags specify the degree of access applications have to an object as well as the access others have. The following are general rules regarding permissions:

•	Children of storage objects opened in direct mode must have permissions at least as restrictive as those of the parent.

•	Children of storage objects opened in transacted mode can have any permissions. For read permission to be allowed on a child storage opened in transacted mode, its parent storage must also have been opened in transacted mode.

•	Storage objects opened in transacted, read-only (STGM_READ) mode can be modified, but the changes cannot be committed.

•	Opening a child storage or stream with write permission requires either write permissions or transacted mode on a parent storage.


File Sharing Privileges

STGM_READ

STGM_WRITE

STGM_READWRITE

STGM_SHARE_DENY_READ

STGM_SHARE_DENY_WRITE

STGM_SHARE_EXCLUSIVE

STGM_SHARE_DENY_NONE


STGM_READ

When applied to an IStream object, STGM_READ enables applications to successfully call IStream::Read. If STGM_READ is omitted, IStream::Read will return an error.

When applied to an IStorage, STGM_READ allows the enumeration of the storage object's elements and enables applications to open these elements in read mode. Parents of storage objects to be opened in read mode must also have been opened in read mode; otherwise, an error is returned.


STGM_WRITE

STGM_WRITE lets an object commit changes to the storage. Specifically, unless this flag has been given, IStorage::Commit and IStream::Commit will fail. An open object whose changes cannot be committed can save its changes by copying the storage or stream with IStorage::CopyTo or IStream::CopyTo.

In direct mode, changes are committed after every change operation. Thus, write permissions are needed to call any function that causes a change. On streams, this includes IStream::Write and IStream::SetSize.

If a parent storage is opened in direct mode without write permission, any attempt to open a child stream within it in direct mode with write permission will fail, because it causes an implicit commit to be made on the storage. Similarly, trying to create or destroy a contained element in this storage object also causes an implicit commit, resulting in an error.


STGM_READWRITE

STGM_READWRITE is the logical combination of the STGM_READ and STGM_WRITE. However, the defined value of STGM_READWRITE is not equal to (STGM_READ | STGM_WRITE).


STGM_SHARE_DENY_READ

When successfully applied to a root IStorage, the STGM_SHARE_DENY_READ flag prevents others from opening the object in read mode. The open call fails and returns an error if the object is presently open in deny-read mode.

STGM_SHARE_DENY_READ is most useful when opening root storage objects. Deny modes on inner elements are still useful if some component is coordinating the opening of these inner elements, as might happen in a Copy/Paste operation. However, inner elements always require STGM_SHARE_EXCLUSIVE.

Opening a parent storage object with STGM_SHARE_DENY_READ applies only to that opening, not the network-wide set of openings of the parent.


STGM_SHARE_DENY_WRITE

When successfully applied the STGM_SHARE_DENY_WRITE flag prevents subsequent openings of either a storage or a stream from specifying write mode. The open call fails and returns an error if the storage or stream is presently open in write mode. For information about the interaction of this flag with nested openings, see the earlier discussion about STGM_SHARE_DENY_READ.

STGM_SHARE_DENY_WRITE can be used to avoid unnecessary snapshot copy operations. Attempting to open a compound file storage object without specifying STGM_SHARE_DENY_WRITE is expensive because a snapshot copy of the storage object must be created. Note that only a root storage can be opened without denying writes. In that case, a snapshot is taken.

Applications that have an "all in memory" model of dealing with persistent storage can reduce the expense of the snapshot copy by specifying STGM_PRIORITY. STGM_PRIORITY allows applications to read some of the elements before use. The application then has the option to exclude them from a snapshot copy during reopen. The exclusion does not occur automatically.


STGM_SHARE_EXCLUSIVE

The compound files implementation requires that all inner elements be opened STGM_SHARE_EXCLUSIVE. It is the logical combination of the STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE. All root storage objects, as well as child storage and stream objects must be opened with STGM_SHARE_EXCLUSIVE.


STGM_SHARE_DENY_NONE

STGM_SHARE_DENY_NONE indicates that neither read access nor write access should be denied to subsequent openings. This is the default sharing mode and if no STGM_SHARE_* flag is explicitly given, STGM_SHARE_DENY_NONE is implied. This differs from the Windows OpenFile function in which the default is a "compatibility mode" indicated by OF_SHARE_COMPAT. OLE does not support compatibility mode.


Storage Naming Conventions

Many storage-related functions take a string name as a parameter to specify a particular element of interest within some container. Depending on which component actually stores these names, different conventions and restrictions apply. The two components of relevant interest are the underlying file system and implementations of storage objects.

Names of root IStorage objects are actual names of files in the underlying file system. Thus, they obey the conventions and restrictions the file system imposes. Filename strings passed to storage-related functions are passed on uninterpreted and unchanged to the file system.

Names of elements contained within storage objects are managed by the implementation of the particular storage object. All implementations of storage objects must support element names 32 characters in length (including the NULL terminator). Some implementations might choose to 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 in either situation. OLE compound files support names up to 32 characters in length. Note that they do not do case conversion.

The following naming conventions apply to elements inside an IStorage instance:

•	The characters "." and ".." are reserved.

•	Element names cannot contain the characters "\", "/", ":", or "!".

In addition, the name space in an IStorage instance is partitioned into different areas of ownership. Different pieces of code can create elements in each area of the name space.

•	Element names beginning with characters other than \0x01 through \0x1F (decimal 1 through decimal 31) are for use by the embedded object stored in the IStorage instance. Conversely, the embedded object must not use element names beginning with these characters.

•	Element names beginning with a \0x01 and \0x02 are for the exclusive use of the OLE libraries.

•	Element names beginning with a \0x03 are for the exclusive use of the container in which the object is embedded. The container can use this space to persistently store information associated with the object.

•	Element names beginning with a \0x04 are for the exclusive use of the structured storage implementation. For example, they are useful if the implementation supports interfaces besides IStorage that need persistent state.

•	All other names beginning with \0x05 through \0x1F are reserved for future use by the system.


String Name Block (SNB)

A string name block (SNB) is a pointer to an array of pointers to strings, that ends in a NULL pointer. It is defined as:

typedef OLESTR **SNB;

String name blocks are used to exclude particular contained storages or streams in storage object open calls. 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 marshalling of a string name block is based on the assumption that the SNB passed in was created this way. Although it could be stored in other ways, the SNB created in this manner has the advantage of requiring only one allocation and one freeing of memory for all the strings.


STATSTG Data Structure
STATSTG Structure

The STATSTG structure is used in IEnumSTATSTG::Next, and the Stat methods in IStorage, IStream, and ILockBytes.

The STATSTG structure is defined as follows:

typedef struct STRUCT tagSTATSTG{

OLECHAR  * pwcsName;

DWORD type;

ULARGE_INTEGER cbSize;

FILETIME mtime;

FILETIME ctime;

FILETIME atime;

DWORD grfMode;

DWORD grfLocksSupported;

CLSID clsid;

DWORD grfStateBits;

DWORD Reserved;

} STATSTG;

The pwcsName member is the name of the storage element. pwcsName is allocated and filled in by the callee and later freed by the caller. The following code illustrates how to free pwcsName using the standard allocator:

LPMALLOC pMalloc;

CoGetMalloc(MEMCTX_TASK, &pMalloc);

pMalloc->Free(statstg.pwcsName);

pMalloc->Release();

In some cases, such as when a name does not exist for a byte array, NULL is returned in pwcsName.

The type member identifies the type of the element. Legal values are found in the enumeration STGTY:

typedef enum tagSTGTY

{

    STGTY_STORAGE   = 1,	//Storage object

    STGTY_STREAM    = 2,	`	//Stream object

    STGTY_LOCKBYTES = 3,	//Byte array object

} STGTY;

The cbSize member is the size of the element. It is the byte count for stream objects and byte arrays, and it is undefined for storage objects.

The next three members provide the date and time information. The modification date and time is returned in mtime, the creation date and time is returned in ctime, and the closest approximation of the time of last access is returned in atime.

Note  The information supplied by these three members applies only to storages; streams do not have time stamps so these members always return zero.

The modification time differs depending on whether the storage element uses transacted or direct mode. Transacted mode elements return the time of the last commit operation invoked on the element. Direct mode elements return the later of the following:

•	The time of the last explicit commit operation invoked on the element (using IStorage::Commit or IStream::Commit). In direct mode, committing changes has the effect of flushing the buffer.

•	The time of the last release operation (using IStorage::Release or IStream::Release) in a situation where changes have been made because the stream or storage was opened or the last set of changes were committed. In this case, the release operation flushes the buffers internally.

In the compound file implementation, the time returned in atime is the same as mtime for internal elements. Whatever is supported in the underlying file system or exposed by ILockBytes::Stat for root elements (on MS-DOS FAT, is the same as mtime).

The grfMode member is the mode in which the element was opened. It is valid only on Stat method calls.

The grfLocksSupported member is a group of Boolean flags relevant only for stream objects and byte arrays. Legal values are combinations of values from the enumeration LOCKTYPE. For each lock type, grfLocksSupported indicates whether a call to IStream::LockRegion or ILockBytes::LockRegion will ever be worthwhile.

typedef enum tagLOCKTYPE

{

    LOCK_WRITE      = 1,

    LOCK_EXCLUSIVE  = 2,

    LOCK_ONLYONCE   = 4

} LOCKTYPE;

For each flag, a FALSE (zero) value indicates that the specified type of write locking is not supported. A TRUE value indicates write locking can be supported, but there is no guarantee. Any other value returns the error STG_E_INVALIDFUNCTION.

The clsid member is relevant only for storage objects and represents the CLSID associated with the storage, if any. The clsid is CLSID_NULL for newly created storages.

The grfStateBits member is relevant only for storage objects and is the value most recently set with IStorage::SetStateBits.

The Reserved member is reserved for future use.


IEnumSTATSTG Interface

The IEnumSTATSTG interface enumerates items of type STATSTG. An instance of the IEnumSTATSTG interface is returned by calling IStorage::EnumElements.

The IEnumSTATSTG interface contains the following methods, as do all enumerators:

DECLARE_INTERFACE_(IEnumSTATSTG, IUnknown)

{

...

  HRESULT Next (ULONG celt, STATSTG  * rgelt[], ULONG *pceltFetched);

  HRESULT Skip (ULONG celt);

  HRESULT Reset ();

  HRESULT Clone (LPENUMSTATSTG  * ppenumStatStg);

};

For more information on enumerator objects, see IEnumX in Component Object Interfaces and Functions.


IStorage Interface

IStorage contains the following methods:

DECLARE_INTERFACE_(IStorage, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IStorage methods ***

  HRESULT CreateStream (THIS_ const OLECHAR * pwcsName, DWORD grfMode,

    DWORD reserved1, DWORD reserved2, IStream ** ppstm);

  HRESULT OpenStream (THIS_ const OLECHAR * pwcsName, void * reserved1,

    DWORD grfMode, DWORD reserved2, IStream ** ppstm);

  HRESULT CreateStorage (THIS_ const OLECHAR * pwcsName, DWORD grfMode,

    DWORD dwStgFmt, DWORD reserved2, LPSTORAGE * ppStg);

  HRESULT OpenStorage (THIS_ const OLECHAR * pwcsName, IStorage pstgPriority,

    DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage ** ppstg);

  HRESULT CopyTo (THIS_ DWORD ciidExclude, IID const * rgiidExclude,

    SNB snbExclude, IStorage * pstgDest);

  HRESULT MoveElementTo (THIS_ const OLECHAR * pwcsName, IStorage * pstgDest,

    const OLECHAR * pwcsNewName, DWORD grfFlags);

  HRESULT Commit (THIS_ DWORD grfCommitFlags);

  HRESULT Revert (THIS);

  HRESULT EnumElements (THIS_ DWORD reserved1, void * reserved2,

    DWORD reserved3, IEnumSTATSTG ** ppenumstatstg);

  HRESULT DestroyElement (THIS_ const OLECHAR * pwcsName);

  HRESULT RenameElement (THIS_ const OLECHAR * pwcsOldName,

    const OLECHAR * pwcsNewName);

  HRESULT SetElementTimes (THIS_ const OLECHAR * pwcsName, FILETIME const * pctime,

    FILETIME const * patime, FILETIME const * pmtime);

  HRESULT SetClass (THIS_ REFCLSID clsid);

  HRESULT SetStateBits (THIS_ DWORD grfStateBits, DWORD grfMask);

  HRESULT Stat (THIS_ STATSTG * pstatstg, DWORD grfStatFlag);

};

The IStorage interface is implemented by OLE and is used by containers to instantiate a directory-like collection of storage and stream objects in which an embedded object's native data is saved. An application can also save its native data to IStorage. For an overview of saving application documents, see Programming Considerations.The IStorage interface contains methods for managing root IStorage objects, child IStorage objects, and IStream objects. IStorage instance pointers can be marshalled to other processes, just as any other interface pointer can. When an IStorage interface pointer is remoted to a process that has shared memory access with the original process, the custom marshalling facilities of OLE are used to create a remote version of the original object. This remote version does not need to communicate with the original process to carry out its functions.

An OLE application must release any owned IStorage instance pointers regardless of whether a higher-level IStorage has caused the pointers to become invalid. This is necessary to de-allocate memory. When a parent storage releases its last reference or reverts, all child storages are invalidated.

IStorage::Release is used to close an open IStorage object, making the IStorage invalid. In addition, Release reverts any logical uncommitted changes to the IStorage.

Notice that IStorage::Release does not return any error status that might arise from the cleanup. Applications that require error status during cleanup should explicitly call IStorage::Revert or IStorage::Commit as appropriate before calling Release. This is similar to closing file handles in traditional file systems in that once the file close has been attempted, no reasonable error handling is possible.

See IUnknown::Release for possible return values.


IStorage::CreateStream

HRESULT IStorage::CreateStream (pwcsName, grfMode, reserved1, reserved2, ppstm)
const OLECHAR * pwcsName
DWORD grfMode
DWORD reserved1
DWORD reserved2
IStream ** ppstm

IStorage::CreateStream creates and immediately opens a new IStream object contained in this storage object.

Parameters

pwcsName

[in] Points to the name by which the newly created stream can later be reopened.

grfMode

[in] Defines the access mode to use when opening the newly created stream. For more information, see the following comments.

reserved1

[in] Reserved for future use; must be set to zero by the caller.

reserved2

[in] Reserved for future use; must be set to zero by the caller.

ppstm

[out] Points to where the opened IStream interface is returned. Valid only if the operation is successful. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The stream was created successfully.
STG_E_ACCESSDENIED	Insufficient permissions to create stream.
STG_E_FILEALREADYEXISTS	The stream with the specified name already exists and grfMode is set to STGM_FAILIFTHERE.
STG_TOOMANYOPENFILES	There are too many open files.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDFLAG	Unsupported value(s) specified in grfMode.
STG_E_INVALIDPOINTER	A bad pointer was passed in.
STG_E_INVALIDPARAMETER	Invalid parameter.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDNAME	Invalid value for pwcsName.
Remarks

If pwcsName specifies a stream that does not exist, IStorage::CreateStream creates a new stream. If a stream already exists with the name specified in pwcsName, the STGM_CREATE or STGM_FAILIFTHERE access modes (defined by the grfMode parameter) indicate how to create the stream. If STGM_CREATE is specified, any existing stream is destroyed and then replaced with a newly created stream. If STGM_FAILIFTHERE is specified, the function fails and returns the error STG_E_FILEALREADYEXISTS. Any destruction and creation of the stream is subject to the transaction mode of the parent storage.

All elements within a storage object — including both streams and other storage objects — are kept in the same name space.

See Also

IStorage::OpenStream


IStorage::OpenStream

HRESULT IStorage::OpenStream(pwcsName, reserved1, grfMode, reserved2, ppstm)
const OLECHAR * pwcsName
void * reserved1
DWORD grfMode
DWORD reserved2
IStream * ppstm

IStorage::OpenStream opens an existing named stream according to the access mode grfMode.

Parameters

pwcsName

[in] Points to the name of the stream to open.

reserved1

[in] Reserved for future use; must be set to NULL by the caller.

grfMode

[in] Defines the access mode to use when opening the stream. For more information, see the following comments.

reserved2

[in] Reserved for future use; must be set to zero by the caller.

ppstm

[out] Points to where the opened IStream is returned. Valid only if the operation is successful. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The stream was opened successfully.
STG_E_ACCESSDENIED	Insufficient permissions to open the stream.
STG_E_FILENOTFOUND	The stream with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDFLAG	Unsupported value(s) specified in grfMode.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

The permissions on this storage object impose some restrictions on the permissions that can be given in grfMode. For more information about permissions, see Storage Access Mode Flags.

Opening streams in transacted mode is not supported by OLE. Opening the same stream more than once from the same open IStorage instance is also not supported. This behavior is enforced by requiring the caller of IStorage::OpenStream to specify STGM_SHARE_EXCLUSIVE in grfMode. The call fails with STG_E_INVALIDFUNCTION if these flags are not specified.

OLE's compound file implementation does not support passing STG_DELETEONRELEASE to IStorage::OpenStream, although other implementations of IStorage might have this capability.

See Also

IStorage::CreateStream


IStorage::CreateStorage

HRESULT IStorage::CreateStorage(pwcsName, grfMode, reserved1, reserved2, ppstg)
const OLECHAR * pwcsName
DWORD grfMode
DWORD reserved1
DWORD reserved2
IStorage ** ppstg

IStorage::CreateStorage creates and opens a new IStorage object within this storage object.

Parameters

pwcsName

[in] Points to the name by which the newly created storage can later be (re)opened.

grfMode

[in] Defines the access mode to use when opening the newly created storage object.

reserved1

[in] Reserved for future use; must be set to zero by the caller.

reserved2

[in] Reserved for future use; must be set to zero by the caller.

ppstg

[out] Points to where the opened storage is returned. Valid only if the operation is successful. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The storage object was created successfully.
STG_E_ACCESSDENIED	Insufficient permissions to create storage object.
STG_E_FILEALREADYEXISTS	The specified file already exists.
STG_E_TOOMANYOPENFILES	Too many files are open.
STG_S_CONVERTED	The existing stream with the specified name was replaced with a new storage object containing a single stream called CONTENTS.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDFLAG	Invalid flag value(s) were specified in grfMode.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

If a storage object does not exist with the name specified by pwcsName, it is created. If a storage object already exists with the name specified by pwcsName, the following values for grfMode indicate how to proceed:

Storage Access Flag	Meaning
STGM_CREATE	If a storage object with the specified name already exists, remove it and replace it with a new storage object.
STGM_CONVERT	If such an element exists, and is an IStream, the stream is to be replaced with a new storage object that contains one stream named "CONTENTS." This stream contains exactly the data that was in the old stream before the conversion.
STGM_FAILIFTHERE	Do not create a new storage object if one already exists; return STG_E_FILEALREADYEXISTS.
STGM_DELETEONRELEASE	Delete the root IStorage object when it receives it final release.
See Also

IStorage::OpenStorage


IStorage::OpenStorage

HRESULT IStorage::OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg)
const OLECHAR * pwcsName
IStorage pstgPriority
DWORD grfMode
SNB snbExclude
DWORD reserved
IStorage ** ppstg

IStorage::OpenStorage opens an existing IStorage object contained within this storage object according to the access mode grfMode.

Parameters

pwcsName

[in] Points to the name of the storage element to open. The pwcsName parameter is ignored if pstgPriority is non-NULL. In this case, the name of the element is taken from pstgPriority.

pstgPriority

[in] Typically NULL. A non-NULL value points to a previous opening of the storage object, usually one that was opened in priority mode. When pstgPriority is non-NULL, the container should close and reopen the storage object without admitting the possibility that some concurrent process could steal away access permissions already obtained.

grfMode

[in] Defines the access mode to use when opening the storage object; see the following comments.

snbExclude

[in] Specifies a non-NULL value that points to a block of stream names in this IStorage instance which are to be emptied as the IStorage is opened. This exclusion happens independently of a snapshot copy. Can be NULL.

For more information on snbExclude and the pstgPriority parameters, see the StgOpenStorage function.

reserved

[in] Reserved for future use; must be zero.

ppstg

[out] Points to where the opened IStorage instance is returned. Valid only if the operation is successful. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The storage object was opened successfully.
STG_E_ACCESSDENIED	Insufficient permissions to open IStorage object.
STG_E_FILENOTFOUND	The storage object with the specified pwcsName does not exist.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDFLAG	Invalid flag value(s) specified in grfMode.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

After the function returns, the IStorage instance contained in pstgPriority is invalid, and can no longer be used. Use the one in ppstg instead.

The compound file implementation of storage objects does not support including STGM_PRIORITY in grfMode; that is, it does not support opening nonroot storage objects in priority mode. For more information on access permissions, see the warning in the description of DENY_WRITE related to opening a storage object read and/or write mode without specifying DENY_WRITE.

Root IStorage 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 IStorage objects.

The compound file implementation does not support simultaneous openings of a storage object from the same open IStorage instance. This behavior is enforced by requiring the caller to specify STGM_SHARE_EXCLUSIVE in grfMode. IStorage::OpenStorage fails with the error STG_E_INVALIDFUNCTION if this flag is not specified.

See Also

StgOpenStorage


IStorage::CopyTo

HRESULT IStorage::CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest)
DWORD ciidExclude
IID const * rgiidExclude
SNB snbExclude
IStorage * pstgDest

IStorage::CopyTo copies the entire contents of an open IStorage object into the storage object pStgDest.

Parameters

ciidExclude

[in] Specifies the number of elements in the array pointed to by rgiidExclude. If rgiidExclude is NULL, ciidExclude is ignored.

rgiidExclude

[in] Specifies an array of interface identifiers the caller takes responsibility for copying from the source to the destination. It can be NULL, indicating that all other objects are to be copied. If non-NULL, an array length of zero indicates that no other objects are to be copied, only the state exposed by the IStorage object.

snbExclude

[in] Points to a block of named elements in this IStorage that are not to be copied to the destination. It can be NULL. If IID_IStorage is the rgiidExclude array, this parameter is ignored.

pstgDest

[in] Points to the open storage object where this open storage object is to be copied.

Return Values

Value	Meaning
S_OK	The storage object was successfully copied.
STG_S_DESTLACKSINTERFACE	The destination lacks an interface of the source object requested to be copied.
STG_E_ACCESSDENIED	The destination storage object is a child of the source storage object.
STG_E_MEDIUMFULL	The storage medium is full; cannot create new storage object.
STG_E_TOOMANYOPENFILES	Too many open files.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

The pstgDest parameter can be a different implementation of the IStorage interface than the source. Thus, IStorage::CopyTo can only use the publicly available functionality of pstgDest.

If pstgDest is transacted, it can be reverted by calling IStorage::Revert.

The copy operation merges elements contained in the source IStorage with those already present in the destination. The copy process is recursive, invoking IStorage::CopyTo and IStream::CopyTo on the elements nested inside the source. If an attempt is made to copy a stream on top of an existing stream with the same name, the existing destination stream is first removed and then replaced with the source stream. Attempting to copy an IStorage on top of an existing destination IStorage does not remove the existing storage. Thus, after the copy operation, the IStorage contains older elements if they were not replaced by newer ones.

If the destination IStorage is a (transitive) child of the source, IStorage::CopyTo fails and returns STG_E_ACCESSDENIED.

The rgiidExclude parameter specifies an array of interface identifiers the caller knows about and does not want copied from source to destination. Presumably, the caller takes care of copying the state behind these interfaces. It is not an error for this array to include an interface this IStorage does not support; such interfaces are simply ignored. The IStorage interface is explicitly allowed to be in this array. In the event the destination lacks an interface of the source that was requested to be copied, this does not affect the other interfaces copied and STG_S_DESTLACKSINTERFACE is returned.

The snbExclude parameter specifies a block of named elements of the IStorage to be excluded from the copy operation. Elements of these names are not created or otherwise touched in the destination.

A consequence of these rules is that:

pStg->CopyTo(0, 0, 0, pstgDest)

copies everything possible from the source to the destination. This is the most commonly used form of this operation.

See Also

IStorage::MoveElementTo, IStorage::Revert


IStorage::MoveElementTo

HRESULT IStorage::MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags)
const OLECHAR * pwcsName
IStorage * pstgDest
const OLECHAR * pwcsNewName
DWORD grfFlags

IStorage::MoveElementTo moves an IStorage element to the indicated new destination container.

Parameters

pwcsName

[in] Points to the name of the element of this IStorage to be moved.

pstgDest

[in] Points to the destination container into which the element is to be placed.

pwcsNewName

[in] Points to the new name to give to the element in its new container.

grfFlags

[in] Specifies how the move is to happen. For more information, see the following comments.

Return Values

Value	Meaning
S_OK	The storage element was moved successfully.
STG_E_ACCESSDENIED	Insufficient permissions.
STG_E_FILENOTFOUND	The element could not be found.
STG_E_FILEALREADYEXISTS	The specified file already exists.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_TOOMANYOPENFILES	Too many open files.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDFLAG	Invalid flag.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

Unless both this IStorage and the destination IStorage have some special information about each other's implementation (for example, they could be different instances of the same implementation), then IStorage::MoveElementTo is similar to an IStorage::CopyTo on the indicated element, followed by a removal of the original element.

The grfFlags parameter controls how the move is to happen. Its values are taken from the enumeration STGMOVE:

typedef enum tagSTGMOVE {

STGMOVE_MOVE = 0,

STGMOVE_COPY = 1,

} STGMOVE;

These flags have the following meanings:

Values for grfFlags	Meaning
STGMOVE_MOVE	Carry out the move operation, as expected.
STGMOVE_COPY	Carry out the first part of the move operation but do not remove the original element. With this flag, the behavior resulting from copying an element on top of itself (that is, pstgDest is the same as the source IStorage, and pwszNewName = pwszName) is undefined.
See Also

IStorage::CopyTo


IStorage::Commit

HRESULT IStorage::Commit(grfCommitFlags)
DWORD grfCommitFlags

IStorage::Commit commits any changes made to an IStorage object since it was opened or last committed to persistent storage.

Parameters

grfCommitFlags

[in] Controls how the object is to be committed to IStorage. For more information, see the following comments.

Return Values

Value	Meaning
S_OK	The commit operation was successful.
STG_E_NOTCURRENT	Another opening of the storage object has committed changes. Indicates the possibility of overwriting changes.
STG_E_MEDIUMFULL	No space left on device to commit.
STG_E_TOOMANYOPENFILES	Too many open files.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDFLAG	Invalid flag.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

Calling IStorage::Commit has no effect if the storage object was opened in direct mode, except when invoked on a direct mode root storage object. It acts much like a traditional flush operation, ensuring that any internal memory buffers have been written to disk (or to the underlying ILockBytes, as appropriate).

In transacted mode, IStorage::Commit causes the changes that have been made since the storage object was opened or since the last commit was done to be permanently reflected in the object's persistent image. The commit is subject to the transaction of the object's parent storage. If the parent reverts at a later time, the changes currently being committed are rolled back. Calling IStorage::Commit has no effect on currently opened storages or streams in this object. Changes to these nested storages and streams are not automatically committed but they are still valid and can be used. Committing involves informing the parent storage of the changes the object is currently informed about. Thus, this object has to be informed of changes from inner transactions before it can publish changes to its parent.

The grfCommitFlags parameter consists of the OR-ing of elements from the enumeration STGC:

typedef enum tagSTGC

{

    STGC_DEFAULT = 0,

    STGC_OVERWRITE  = 1,

    STGC_ONLYIFCURRENT  = 2,

    STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4

} STGC;

The caller can specify STGC_DEFAULT or any combination of the other elements.

STGC_OVERWRITE allows new data to overwrite the old data, reducing space requirements. STGC_OVERWRITE is not recommended for usual operation, but it could be useful in the following situations:

•	A commit was tried and STG_E_MEDIUMFULL was returned.

•	The user has somehow specified a willingness to risk overwriting the old data.

•	A low-memory save sequence is being used to end up with a smaller file.

The commit operation obtains all the disk space it needs before attempting to store the new data, whether or not STGC_OVERWRITE is specified. If space requirements prohibit the save, the old data will be intact, including all uncommitted changes.

However, if the commit fails due to reasons other than lack of disk space and STGC_OVERWRITE was specified, it is possible that neither the old nor the new version of the data will be intact.

STGC_ONLYIFCURRENT prevents multiple users of an IStorage from overwriting the other's changes. STGC_ONLYIFCURRENT commits changes only if no one else has made changes since the last time this user opened the IStorage or committed. If other changes have been made, STG_E_NOTCURRENT is returned. If the caller chooses to overwrite the changes, IStorage::Commit can be called with grfCommitFlags set to STGC_DEFAULT.

STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE commits the changes, but does not save them to the disk cache.

When committing root storage objects, care must be taken to ensure that the operation is successfully completed or that, when failing, the old committed contents of the IStorage are still intact. In the OLE compound file implementation, unless grfCommitFlags is set to STGC_OVERWRITE, a two-phase commit process is used. First, all new data is written to unused space in the underlying file, growing it as necessary. Once this has been successfully completed, a table in the file is updated using a single sector write to indicate 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.

If the storage object was opened with some of its elements excluded, the caller is responsible for rewriting them before calling Commit. The storage must have been opened in write mode in order for the commit to succeed.

Unless multiple simultaneous writers on the same storage object are prohibited, applications usually specify at least STGC_ONLYIFCURRENT in grfCommitFlags to prevent the changes made by one writer from inadvertently overwriting the changes made by another.

See Also

IStorage::Revert, IStream::Commit


IStorage::Revert

HRESULT IStorage::Revert()

IStorage::Revert discards all changes made in or made visible to this storage object by nested commits since the storage object was opened or last committed.

Return Values

Value	Meaning
S_OK	The revert operation was successful.
STG_E_INSUFFICIENTMEMORY	The revert operation failed due to lack of memory.
STG_E_TOOMANYOPENFILES	There are too many open files.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
Remarks

After IStorage::Revert finishes, any existing elements that were opened from the reverted IStorage object are invalid and can no longer be used. The error STG_E_REVERTED is returned on all calls except IStorage::Release using these openings.

Because commits happen implicitly and immediately, calling IStorage::Revert on a storage opened in direct mode only invalidates any child openings.


IStorage::EnumElements

HRESULT IStorage::EnumElements(reserved1, reserved2, reserved3, ppenumstatstg)
DWORD reserved1
void * reserved2
DWORD reserved3
IEnumSTATSTG ** ppenumstatstg

IStorage::EnumElements enumerates the elements immediately contained within this storage object.

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.

ppenumstatstg

[out] Points to where to return the enumerator from which the elements of this storage can be obtained. If an error is returned, must be set to NULL. See the description of IEnumSTATSTG in this section.

Return Values

Value	Meaning
S_OK	The enumeration was successful.
STG_E_INSUFFICIENTMEMORY	The enumeration failed due to lack of memory.
E_OUTOFMEMORY	The enumeration failed due to lack of memory.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

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 on which it was opened (not necessarily the state at which the enumerator was initialized), depends on the IStorage implementation. The OLE compound file implementation makes a snapshot.

See Also

IEnumSTATSTG


IStorage::DestroyElement

HRESULT IStorage::DestroyElement(pwcsName)
const OLECHAR * pwcsName

IStorage::DestroyElement removes an element from this storage, subject to the transaction mode in which the storage object was opened.

Parameters

pwcsName

[in] Points to the element to remove.

Return Values

Value	Meaning
S_OK	The element was successfully removed.
STG_E_ACCESSDENIED	Insufficient permissions.
STG_E_FILENOTFOUND	The element could not be found.
STG_E_TOOMANYOPENFILES	Too many open files.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

The existing open instance of this element from this parent instance becomes invalid after IStorage::DestroyElement is called.


IStorage::RenameElement

HRESULT IStorage::RenameElement(pwcsOldName, pwcsNewName)
const OLECHAR * pwcsOldName
const OLECHAR * pwcsNewName

IStorage::RenameElement renames an element contained in storage, subject to the transaction state of the IStorage object.

Parameters

pwcsOldName

[in] Points to the old name of the element.

pwcsNewName

[in] Points to the new name of the element.

Return Values

Value	Meaning
S_OK	The element was successfully renamed.
STG_E_ACCESSDENIED	The element named pwcsNewName already exists.
STG_E_FILENOTFOUND	The element could not be found.
STG_E_FILEALREADYEXISTS	The specified file already exists.
STG_E_TOOMANYOPENFILES	Too many open files.
STG_E_INSUFFICIENTMEMORY	Not enough memory to rename the element.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

An IStorage element cannot be renamed while it is open. IStorage::RenameElement is not guaranteed to work in low memory conditions.


IStorage::SetElementTimes

HRESULT IStorage::SetElementTimes(pwcsName, pctime, patime, pmtime)
const OLECHAR * pwcsName
FILETIME const * pctime
FILETIME const * patime
FILETIME const * pmtime

IStorage::SetElementTimes sets the creation, access, and modification times of the indicated element of this storage object.

Parameters

pwcsName

[in] Points to the name of the element to change.

pctime

[in] Points to the new creation time.

patime

[in] Points to the new access time.

pmtime

[in] Points to the new modification time.

Return Values

Value	Meaning
S_OK	The time values were successfully set.
STG_E_ACCESSDENIED	The element named pwcsNewName already exists.
STG_E_FILENOTFOUND	The element could not be found.
STG_E_FILEALREADYEXISTS	The specified file already exists.
STG_E_TOOMANYOPENFILES	There are too many open files.
STG_E_INSUFFICIENTMEMORY	Not enough memory to rename the element.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPOINTER	Invalid pointer.
STG_E_INVALIDNAME	Invalid name.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

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. IStorage::SetElementTimes sets the times that can be set and ignores the rest.

The compound file implementation maintains modification and change times for internal storage objects. For root storage objects, whatever is supported by the underlying file system (or ILockBytes) is used. The compound file implementation does not maintain any time stamps for internal streams. Unsupported time stamps are reported as zero, enabling the caller to test for support.


IStorage::SetClass

HRESULT IStorage::SetClass(clsid)
REFCLSID clsid

IStorage::SetClass persistently stores the object's CLSID.

Parameter

clsid

[in] Specifies the CLSID to be associated with this IStorage.

Return Values

Value	Meaning
S_OK	The CLSID was successfully stored.
STG_E_ACCESSDENIED	Unable to access the storage object.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_MEDIUMFULL	Not enough space was left on the device to complete the operation.
Remarks

The current CLSID for an IStorage can be retrieved with IStorage::Stat. When initially created, IStorage instances have an associated CLSID of CLSID_NULL.

See Also

IStorage::Stat


IStorage::SetStateBits

HRESULT IStorage::SetStateBits(grfStateBits, grfMask)
DWORD grfStateBits
DWORD grfMask

IStorage::SetStateBits stores up to 32 bits of state information in this IStorage.

Parameters

dwgrfStateBits

[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.

dwgrfMask

[in] Specifies a binary mask indicating which bits in dwgrfStateBits are significant in this call.

Return Values

Value	Meaning
S_OK	The state was successfully set.
STG_E_ACCESSDENIED	The caller cannot change state information.
STG_E_INVALIDFLAG	Invalid flag specified in dwgrfStateBits or dwgrfMask.
STG_E_INVALIDPARAMETER	Invalid parameter.
Remarks

The current value of this state is retrievable with IStorage::Stat. When a storage object is initially created, this state value is zero.

See Also

IStorage::Stat


IStorage::Stat

HRESULT IStorage::Stat(pstatstg, grfStatFlag)
STATSTG *pstatstg
DWORD grfStatFlag

IStorage::Stat returns statistics about this open storage object.

Parameters

pstatstg

[out] Points to a STATSTG structure filled by the callee with the statistics of this storage object. If an error is returned, this parameter must be set to NULL.

grfStatFlag

[in] Controls the level of statistics returned about the storage object. For more information, see the following comments.

Return Values

Value	Meaning
S_OK	The statistics were successfully returned.
STG_E_ACCESSDENIED	The storage object cannot be accessed.
STG_E_INSUFFICIENTMEMORY	Not enough memory to rename the element.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDFLAG	Invalid flag specified in dwgrfStatflag.
STG_E_INVALIDPOINTER	Invalid pointer.
Remarks

With grfStatFlag set to STATFLAG_NONAME, pstastg->pwcsName is not returned (that is, it is returned as NULL), thus saving an alloc/free operation.

The grfStatFlag values are taken from the enumeration STATFLAG, which is defined as follows:

typedef enum tagSTATFLAG

{

STATFLAG_DEFAULT = 0,

STATFLAG_NONAME  = 1

} STATFLAG;

See Also

ReadClassStg, IEnumSTATSTG


IStream Interface

The IStream interface contains the following methods:

DECLARE_INTERFACE_(IStream, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS) ;

  ULONG Release (THIS);

    // *** IStream methods ***

  HRESULT Read (THIS_ void * pv, ULONG cb, ULONG * pcbRead);

  HRESULT Write (THIS_ VOID const * pv, ULONG cb, ULONG * pcbWritten);

  HRESULT Seek (THIS_ LARGE_INTEGER dlibMove, DWORD dwOrigin,

    ULARGE_INTEGER * plibNewPosition);

  HRESULT SetSize (THIS_ ULARGE_INTEGER libNewSize);

  HRESULT CopyTo (THIS_ IStream * pStm, ULARGE_INTEGER cb,

    ULARGE_INTEGER * pcbRead, ULARGE_INTEGER * pcbWritten);

  HRESULT Commit (THIS_ DWORD grfCommitFlags);

  HRESULT Revert (THIS);

  HRESULT LockRegion (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb,

    DWORD dwLockType);

  HRESULT UnlockRegion (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb,

    DWORD dwLockType);

  HRESULT Stat (THIS_ STATSTG * pstatstg, DWORD grfStatFlag);

  HRESULT Clone)(THIS_ IStream ** ppstm);

};

The IStream interface is implemented by OLE and used by containers to read and write the underlying bytes of data comprising an IStorage object. IStream instances are analogous to files. IStream is the interface to which the stream elements of a IStorage object conform. By design, instances of this interface present almost exactly the same semantics as those of MS-DOS FAT file handles, except that range locking is not supported by the compound file implementation.

The IStream interface supports streams up to 2 (64)  bytes in length and it requires a 64-bit value to represent their seek pointers. OLE compound files can only support streams up to 2 (32)  bytes in length and, therefore, read-and-write operations (using IStream::Read and IStream::Write, respectively) are always limited to 2 (32)  bytes at a time.

Note  Be aware of the following if you are creating a stream object that is larger than the heap in your machine's memory. When a stream object is created on an HGLOBAL, it calls GlobalRealloc internally whenever it needs to grow the HGLOBAL. Because GlobalRealloc always copies data from the source to the destination, growing a stream from 20M to 25M, 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 45M of memory on the machine because of disk swapping.

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.

One alternative is to do a SetSize on the stream object to enlarge the memory allocation in advance. This is not, however, as efficient as the solution presented in the preceding paragraph.

Pointers to instances of the IStream interface can be marshalled to other processes, just as any other interface pointer can. When the destination is another process that has shared memory access with the original process, the compound file implementation of IStream uses OLE's custom marshalling facilities to create a remote version of the original stream. This stream does not need to communicate with the original process to carry out its functions. The remote version of a stream shares the same seek pointer as the original stream. However, if this functionality is not needed, IStream::Clone can be called to create a clone of the stream, which is then passed to the remote process.

Releasing an IStream invalidates the stream and, as a result, the stream can no longer be used for any operations.


IStream::Read

HRESULT IStream::Read(pv, cb, pcbRead)
void * pv
ULONG cb
ULONG * pcbRead

IStream::Read reads data from the stream starting at the current seek pointer.

Parameters

pv

[out] Points to the buffer into which the stream data should be stored. If an error is returned, this parameter must be set to NULL.

cb

[in] Specifies the number of bytes of data to read from the stream.

pcbRead

[out] Points (after the call) to the number of bytes actually read from the stream. It can be NULL, indicating the caller does not require this value.

Return Values

Value	Meaning
S_OK	The data was successfully read from the stream.
S_FALSE	The data could not be read from the stream.
STG_E_ACCESSDENIED	Insufficient access.
STG_E_INVALIDPOINTER	A bad pointer was passed in for pv or pcbRead.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

To read from the stream, STGM_READ mode is required on the stream. The number of bytes actually read is returned in pcbRead, even if an error is returned. The seek pointer is adjusted for the number of bytes actually read.

If the end of the stream was reached during the read, the number of bytes read might be fewer than requested. In this case, an error is not returned and the number of bytes actually read is merely returned through pcbRead. This is the same end-of- file behavior presently found in MS-DOS FAT file systems.

Some implementations might choose to return S_FALSE instead of S_OK when fewer than the requested number of bytes are returned. Callers must be prepared to deal with this (typically, by comparing *pcbRead on exit against the number of bytes requested for the read.)

See Also

IStream::Write


IStream::Write

HRESULT IStream::Write(pv, cb, pcbWritten)
void const * pv
ULONG cb
ULONG * pcbWritten

IStream::Write attempts to write cb bytes from the buffer pointed to by pv into the stream starting at the current seek pointer.

Parameters

pv

[in] Points to the buffer containing the data to be written to the stream.

cb

[in] Defines the number of bytes of data to write into the stream.

pcbWritten

[out] After the call, points to the number of bytes actually written into the stream. It can be NULL, indicating the caller does not require this value.

Return Values

Value	Meaning
S_OK	The data was successfully written into the stream.
STG_E_MEDIUMFULL	No space left on device.
STG_E_ACCESSDENIED	Insufficient access.
STG_E_CANTSAVE	Data cannot be written for reasons other than no access or space.
STG_E_INVALIDPOINTER	A bad pointer was passed in for pv or pcbWritten.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

Writing a count of zero bytes has no effect. For a nonzero byte count, if the seek pointer is currently past the end of the stream, the size of the stream is increased so the seek pointer can be reached. The fill bytes written to the stream are not initialized to any particular value. Except for writing a count of zero bytes past the end-of-file (for which IStream::SetSize can be used), this is the same end-of-file behavior as in the MS-DOS FAT file system.

In the compound file implementation, IStreams are not sparse. Any fill bytes are eventually allocated on the disk and assigned to the stream.

The number of bytes actually written is always returned in pcbWritten, even if an error is returned. The seek pointer is adjusted for the number of bytes actually written.

See Also

IStream::Read


IStream::Seek

HRESULT IStream::Seek(dlibMove, dwOrigin, plibNewPosition)
LARGE_INTEGER dlibMove
DWORD dwOrigin
ULARGE_INTEGER * plibNewPosition

IStream::Seek adjusts the location of the seek pointer on the stream.

Parameters

dlibMove

[in] Specifies the displacement to be added to the location indicated by dwOrigin. If dwOrigin is STREAM_SEEK_SET, this is interpreted as an unsigned value rather than a signed value.

dwOrigin

[in] Specifies the seek mode; the origin with respect to which dlibMove should be interpreted; see the following comments.

plibNewPosition

[out] Points (after this call is completed) to the beginning of the stream. It can be NULL, indicating the caller does not require this value.

Return Value

Value	Meaning
S_OK	The seek pointer has been successfully adjusted.
STG_E_INVALIDPOINTER	A bad pointer was passed in for plibNewPosition.
STG_E_INVALIDFUNCTION	dwOrigin contains invalid value.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

It is an error to seek before the beginning of the stream, but it is not an error to seek past the end of the stream. Seeking past the end of the stream is useful for subsequent writes, because the stream will be extended at that time to the seek position immediately before the write is done.

The dwOrigin parameter can have one of the following values:

typedef enum tagSTREAM_SEEK

{

    STREAM_SEEK_SET = 0,

    STREAM_SEEK_CUR = 1,

    STREAM_SEEK_END = 2

} STREAM_SEEK;

These values have the following effects:

Values for dwOrigin	Meaning
STREAM_SEEK_SET	Sets the seek position relative to the beginning of the stream; dlibMove is the new seek position.
STREAM_SEEK_CUR	Sets the seek position relative to the current position of the stream; dlibMove is the (signed) displacement to be made.
STREAM_SEEK_END	Sets the seek position relative to the current end of the stream; dlibMove is the (signed) displacement to be made.
Calling Seek with dlibMove set to zero and dwOrigin set to STREAM_SEEK CUR returns the current seek position through plibNewPosition.


IStream::SetSize

HRESULT IStream::SetSize(libNewSize)
ULARGE_INTEGER libNewSize

IStream::SetSize changes the size of the stream.

Parameters

libNewSize

[in] Specifies the new size of the stream.

Return Value

Value	Meaning
S_OK	The stream size was successfully changed.
STG_E_MEDIUMFULL	Lack of space prohibited change of stream size.
STG_E_INVALIDFUNCTION	High DWORD of libNewSize != 0.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

The seek pointer is not affected by the change in stream size. If libNewSize is larger than the current stream size, the stream is extended with fill bytes of undefined value, much as is done in an IStream::Write call if the seek pointer is past the current end-of-stream. If libNewSize is smaller than the current stream, the stream is truncated to the indicated size.

In most stream implementations, calling IStream::SetSize is an effective way of trying to obtain a large chunk of contiguous space. However, no guarantee is made that the space will be contiguous.

See Also

IStream::Write


IStream::CopyTo

HRESULT IStream::CopyTo(pStm, cb, pcbRead, pcbWritten)
IStream *pStm
ULARGE_INTEGER cb
ULARGE_INTEGER * pcbRead
ULARGE_INTEGER * pcbWritten

IStream::CopyTo copies data from one stream to another stream, starting at the current seek pointer in each stream.

Parameters

pStm

[in] Points to the stream into which the data should be copied (pstm can be a clone of the source stream).

cb

[in] Specifies the number of bytes to read from the source stream.

pcbRead

[out] Contains the number of bytes actually read from the source stream. It can be NULL, indicating the caller does not require this value.

pcbWritten

[out] Contains the number of bytes actually written into the destination stream. It can be NULL, indicating the caller does not require this value.

Return Value

Value	Meaning
S_OK	The stream was successfully copied.
STG_E_MEDIUMFULL	Lack of space prohibited copy.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDPOINTER	A bad pointer for pstm, pcbRead, or pcbWritten.
STG_E_READFAULT	Disk error during read.
	Can also return any error codes from IStream::Read, IStream::Write, and IStream::Seek.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors	Any ILockBytes or system errors.
Remarks

IStream::CopyTo is equivalent to reading cb bytes into memory using IStream::Read and then immediately writing them to the destination stream using IStream::Write. Copying a stream onto a clone of itself is always safe.

Setting cb to its maximum large-integer value causes the remainder of the source stream to be copied.

The number of bytes actually read or written is always returned in pcbRead and pcbWritten respectively. The seek pointer in each stream instance is adjusted for the number of bytes read or written.

If IStream::CopyTo returns an error, no assumptions can be made about the seek positions of either the source or destination streams and the values in pcbRead and pcbWritten are not meaningful. One result of this is that if IStream::CopyTo returns successfully, pcbRead and pcbWritten are equal.

See Also

IStream::Read, IStream::Write, IStream::Seek


IStream::Commit

HRESULT IStream::Commit(grfCommitFlags)
DWORD grfCommitFlags

IStream::Commit commits any changes made to the IStorage object containing the stream.

Parameters

grfCommitFlags

[in] Controls how the object is to be committed to storage. The grfCommitFlags parameter consists of the OR-ing of elements from the enumeration STGC. For more information about grfCommitFlags, see IStorage::Commit.

Return Values

Value	Meaning
S_OK	The stream was successfully committed.
STG_E_MEDIUMFULL	The commit failed due to lack of space on device.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

OLE does not support IStream objects being opened in transacted mode. Therefore, calling IStream::Commit in transacted mode has no effect.

However, for other implementations, IStream::Commit causes the changes made to this stream since it was opened or last committed to be permanently reflected in the persistent image of the stream. This is subject to any transaction of the parent storage object of the stream. That parent can still cancel changes by calling IStream::Revert at a later time.

In direct mode, IStream::Commit ensures that any memory buffers have been flushed out to the stream's underlying open IStorage instance. This is much like a "flush" call in traditional file systems. In compound files, IStream instances are only available inside IStorage instances. Thus, the open parent IStorage also needs to be flushed and committed.

See Also

IStorage::Commit


IStream::Revert

HRESULT IStream::Revert()

IStream::Revert discards all changes made to the stream since it was opened or last committed in transacted mode. In direct mode, IStream::Revert has no effect.

Return Values

Value	Meaning
S_OK	The stream was successfully reverted.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Comment

OLE does not support opening IStream objects in transacted mode. Accordingly, most applications have no need to call this function.


IStream::LockRegion

HRESULT IStream::LockRegion(libOffset, cb, dwLockType)
ULARGE_INTEGER libOffset
ULARGE_INTEGER cb
DWORD dwLockType

IStream::LockRegion locks a range of bytes in the stream. This method is not supported in the current release of OLE.

Parameters

libOffset

[in] Specifies the beginning of the region to lock.

cb

[in] Specifies the length of the region to be locked, in bytes.

dwLockType

[in] Specifies the kind of lock requested.

Return Values

Value	Meaning
S_OK	The range of bytes was successfully locked.
STG_E_INVALIDFUNCTION	The function is not supported in this release.
STG_E_LOCKVIOLATION	Requested lock is supported, but cannot be granted because of an existing lock.
Remarks

Range locking is not supported by compound files so calling IStream::LockRegion has no effect. It might or might not be supported by other implementations.

The range of bytes begins with the byte at the offset libOffset, and extends for cb bytes toward the end of the stream. The specified range of bytes can extend past the current end 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 requester 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 one of the following values:

typedef enum tagLOCKTYPE

{

    LOCK_WRITE      = 1,

    LOCK_EXCLUSIVE  = 2,

    LOCK_ONLYONCE   = 4

}   LOCKTYPE;

These values mean the following:

Values for dwLockType	Purpose
LOCK_WRITE	If the lock is granted, reading the specified region of the stream can be done by calling IStream::Read from any opening of this stream. Attempts to write to this region from any opening of this stream other than the one to which the lock was granted returns the error STG_E_ACCESSDENIED.
LOCK_EXCLUSIVE	Attempts to read or write this stream by other stream openings return the error STG_E_ACCESSDENIED.
LOCK_ONLYONCE	If the lock is granted, no other lock can be obtained on the bytes in the given region. Usually this lock type is an alias for some other lock type and other semantics can occur as a side effect. The underlying implementation can use the appropriate file system primitive to accomplish the lock.
For information on how a caller can determine whether locking is supported; see the discussion of IStream::Stat and its parameter block, STATSTG.

Any region locked with IStream::LockRegion must later be explicitly unlocked by calling IStream::UnlockRegion with exactly the same values for libOffset, cb, and dwLockType. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

All locks on a stream must be explicitly unlocked before the stream is released.

See Also

IStream::Stat, IStream::UnlockRegion


IStream::UnlockRegion

HRESULT IStream::UnlockRegion(libOffset, cb, dwLockType)
ULARGE_INTEGER libOffset
ULARGE_INTEGER cb
DWORD dwLockType

IStream::UnlockRegion unlocks a region of the stream previously locked with IStream::LockRegion. This method is not supported in the current release of OLE.

Parameter

libOffset

[in] Specifies the beginning of the region to unlock.

cb

[in] Specifies the length of the region to be locked, in bytes.

dwLockType

[in] Specifies the kind of lock being released.

Return Value

Value	Meaning
S_OK	The requested unlock was granted.
STG_E_LOCKVIOLATION	The requested unlock cannot be granted.
STG_E_INVALIDFUNCTION	The function is not supported in this release.
Remarks

Any locked region must be explicitly unlocked using IStream::UnlockRegion, with exactly the same libOffset, cb, and dwLockType parameters specified as in the original IStream::LockRegion call. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

See Also

IStream::LockRegion


IStream::Stat

HRESULT IStream::Stat(pstatstg, grfStatFlag)
STATSTG * pstatstg
DWORD grfStatFlag

IStream::Stat returns relevant statistics concerning this open stream.

Parameters

pstatstg

[out] Points to a STATSTG structure filled with the statistics of this open stream. If an error is returned, this parameter must be set to NULL.

grfStatFlag

[in] Controls the level of statistics returned about this open stream object; see the following comments.

Return Values

Value	Meaning
S_OK	The statistics were successfully returned.
STG_E_INSUFFICIENTMEMORY	The stream could not be accessed due to lack of memory.
STG_E_INVALIDFLAG	A bad flag was passed into grfStatFlag.
STG_E_INVALIDPOINTER	A bad pointer was passed in for pstatstg.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
Other errors.	Any ILockBytes or system errors.
Remarks

The values for grfStatFlag are taken from the enumeration STATFLAG, which is defined as follows:

typedef enum tagSTATFLAG

{

STATFLAG_DEFAULT = 0,

STATFLAG_NONAME  = 1

} STATFLAG;

See Also

IStorage::Stat, IEnumSTATSTG


IStream::Clone

HRESULT IStream::Clone(ppstm)
IStream ** ppstm

IStream::Clone returns a new IStream object that is a clone of this stream.

Parameters

ppstm

[out] Points to where the new stream should be returned. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The stream was successfully copied.
STG_E_INSUFFICIENT- MEMORY	The stream could not be copied due to lack of memory.
STG_E_INVALIDPOINTER	A bad pointer was passed in for ppstm.
STG_E_REVERTED	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_WRITEFAULT	Disk error during a write operation.
E_OUTOFMEMORY	The stream could not be copied due to lack of memory.
Other errors.	Any ILockBytes or system errors.
Remarks

The new IStream sees the same underlying stream data as does the original stream. Writes made through one instance are immediately visible through the other. Byte range locking is shared between the streams and a clone is not treated as "foreign" with respect to byte range locking. However, the cloned stream does have a seek pointer that is independent of the original. The initial setting of the seek pointer in the cloned stream is the same as the current setting of the seek pointer in the original at the time of the clone operation.


IRootStorage Interface

The IRootStorage interface is implemented by OLE and is used by containers to switch the underlying disk file that IStorage objects are saved to. The interface contains the following method:

DECLARE_INTERFACE_(IRootStorage, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IRootStorage methods ***

  HRESULT SwitchToFile (THIS_ LPOLESTR pszFile);

};


IRootStorage::SwitchToFile

HRESULT IRootStorage::SwitchToFile(pszFile)
LPOLESTR pszFile

IRootStorage::SwitchToFile makes a copy of the file underlying this IStorage in the file indicated by pszFile. Associates the IStorage object with this new file rather than with its current file, including uncommitted changes.

Parameters

pszFile

[in] Specifies the filename of the new base file.

Return Values

Value	Meaning
S_OK	The file was successfully copied.
STG_E_INVALIDPARAMETER	One or more invalid arguments.
STG_E_MEDIUMFULL	The storage medium is full.
STG_E_ACCESSDENIED	Insufficient permissions to access the storage.
STG_E_REVERTED.	The object has been invalidated by a revert operation above it in the transaction tree.
STG_E_INVALIDPOINTER	A bad pointer was passed in for pszFile.
STG_E_FILEALREADYEXISTS.	The file specified by pszFile already exists.
System errors.	As in ILockBytes, a file can return any system error, notably, STG_* error codes from 0x001 to 0x057.
Remarks

It is illegal to call IRootStorage::SwitchToFile if this IStorage or anything contained within it has been marshalled to another process. As a consequence, any embedded objects contained within the object owning this IStorage must be put in the hands-off storage mode before calling this function.

IRootStorage::SwitchToFile operates without consuming any additional memory or file handles.

See Also

IPersistStorage::HandsOffStorage


ILockBytes Interface

The ILockBytes interface contains the following methods:

DECLARE_INTERFACE_(ILockBytes, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** ILockBytes methods ***

  HRESULT ReadAt (THIS_ ULARGE_INTEGER ulOffset, void * pv, ULONG cb,

    ULONG * pcbRead);

  HRESULT WriteAt (THIS_ ULARGE_INTEGER ulOffset, VOID const **pv, ULONG cb,

    ULONG * pcbWritten);

  HRESULT Flush (THIS);

  HRESULT SetSize (THIS_ ULARGE_INTEGER cb);

  HRESULT LockRegion (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb,

    DWORD dwLockType);

  HRESULT UnlockRegion (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb,

    DWORD dwLockType);

  HRESULT Stat (THIS_ STATSTG * pstatstg, DWORD grfStatFlag);

};

The ILockBytes interface is implemented and used by OLE to manipulate the actual byte array underlying a root compound file. OLE provides an implementation of ILockBytes that can be used when the byte array is a real disk file, as is often the case. Some OLE containers are "database-like," meaning they store objects in places other than disk files. These containers can write their own ILockBytes implementation to direct data into their file system or data store.

ILockBytes differs from IStream in that it has no seek pointer. Offsets are provided as part of the method call, rather than by an explicit seek pointer. However, some of the ILockBytes methods behave the same as in their IStream counterpart, such as SetSize, LockRegion, and UnlockRegion.

Note  Be aware of the following if you are creating a lock-bytes object that is larger than the heap in your machine's memory. When a lock bytes object is created on an HGLOBAL, it calls GlobalRealloc internally whenever it needs to grow the HGLOBAL. Because GlobalRealloc always copies data from the source to the destination, growing a lock-bytes object from 20M to 25M, 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 45M of memory on the machine because of disk swapping.

The preferred solution is to implement an ILockBytes 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.

One alternative is to do a SetSize on the lock-bytes object to enlarge the memory allocation in advance. This is not, however, as efficient as the solution presented in the preceding paragraph.

Compound files use region locking to negotiate permissions when several copies of OLE on remote systems contend for the same storage object. When the compound file implementation of either IStorage or IStream is being marshalled to another process, the underlying ILockBytes interface pointer is marshalled to the same destination process. This marshalling is typically done by the default remoting mechanism. At the destination, a proxy ILockBytes instance is created whose function implementations call back to the original ILockBytes instance. To make remote access more efficient, ILockBytes implementors should consider implementing custom marshalling using OLE's marshalling support. For more information, see the IMarshal interface.


ILockBytes::ReadAt

HRESULT ILockBytes::ReadAt(ulOffset, pv, cb, pcbRead)
ULARGE_INTEGER ulOffset
void * pv
ULONG cb
ULONG * pcbRead

ILockBytes::ReadAt reads a specified number of bytes from the ILockBytes byte array starting at a specified position.

Parameters

ulOffset

[in] Specifies the offset in the byte array at which to begin reading.

pv

[in] Points to the buffer into which the data should be read.

cb

[in] Specifies the number of bytes to read.

pcbRead

[out] Specifies (after the call) the number of bytes actually read. The caller can set pcbRead to NULL, indicating this value is not required.

Return Values

Value	Meaning
S_OK	Some or all of the data requested was read.
STG_E_ACCESSDENIED	The data could not be read due to insufficient access to the byte array.
E_FAIL	The data could not be read for reasons other than insufficient access.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	The document file's ILockBytes on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

The number of bytes actually read is always returned in pcbRead, even if an error is returned.

The number of bytes actually read might be less than requested if the end of the byte array is reached during the read operation. This is the same end-of-file behavior as presently found in MS-DOS FAT file system files.

See Also

ILockBytes::WriteAt


ILockBytes::WriteAt

HRESULT ILockBytes::WriteAt(ulOffset, pv, cb, pcbWritten)
ULARGE_INTEGER ulOffset
VOID const * pv
ULONG cb
ULONG * pcbWritten

ILockBytes::WriteAt writes data to the byte array.

Parameters

ulOffset

[in] Specifies the offset in the byte array at which to begin writing the data.

pv

[in] Points to the buffer containing the data to be written.

cb

[in] Specifies the number of bytes to write.

pcbWritten

[out] Specifies (after the call) the number of bytes actually read. The caller can set pcbWritten to NULL, indicating this value is not of interest.

Return Values

Value	Meaning
S_OK	All of the data requested was written.
STG_E_ACCESSDENIED	The data could not be written due to insufficient access to the byte array.
E_FAIL	The data could not be written for reasons other than insufficient access.
STG_E_MEDIUMFULL	Returns STG_E_MEDIUMFULL for disk-full rather than a partly-successful write as MS-DOS does.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	The document file's ILockBytes on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

If the offset specified by ulOffset is past the end of the byte array, ILockBytes::WriteAt first increases the size of the byte array, filling it with bytes of an undefined value. This is not done if cb is zero, because writing a count of zero bytes has no effect.

The number of bytes actually written is returned in pcbWritten, even if an error is returned.

See Also

ILockBytes::ReadAt


ILockBytes::Flush

HRESULT ILockBytes::Flush()

ILockBytes::Flush ensures that any internal buffers maintained by the ILockBytes implementation are written out to the backing store.

Return Value

Value	Meaning
S_OK	The flush operation was successful.
STG_E_ACCESSDENIED	The data could not be written due to insufficient access to the backing store.
STG_E_MEDIUMFULL	The data could not be written due to insufficient space on the medium.
E_FAIL	The data could not be written for reasons other than insufficient access or no space left.
STG_E_TOOMANYFILESOPEN	Under certain circumstances, Flush does a dump-and-close to flush. This can lead to STG_E_TOOMANYFILESOPEN if there are no file handles available.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	OLE's ILockBytes implementation on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

ILockBytes::Flush is used by the OLE compound file implementation during a transacted commit operation to protect against loss of data.

See Also

IStorage::Commit


ILockBytes::SetSize

HRESULT ILockBytes::SetSize(cb)
ULARGE_INTEGER cb

ILockBytes::SetSize changes the size of the byte array.

Parameters

cb

[in] Specifies the new size of the byte array.

Return Value

Value	Meaning
S_OK	The size of the byte array was successfully changed.
STG_E_MEDIUMFULL	Data could not be written due to insufficient space on the storage medium.
STG_E_ACCESSDENIED	Insufficient permissions to byte array.
E_FAIL	The size of the byte array could not be changed due to miscellaneous errors.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	OLE's ILockBytes implementation on a file can return any system error. Most notably, STG_* error codes from 0x001 to 0x057.
Remarks

If the new size is larger than the current size of the byte array, ILockBytes::SetSize first extends the size of the byte array, filling it with bytes of an undefined value. If cb is smaller than the current size of the byte array, the array is truncated to the indicated size.

Note  Callers cannot rely on STG_E_MEDIUMFULL being returned at the appropriate time because the MS-DOS FAT does not support this correctly. However, some ILockBytes implementations do support it, so callers must be able to deal with this return code.


ILockBytes::LockRegion

HRESULT ILockBytes::LockRegion(libOffset, cb, dwLockType)
ULARGE_INTEGER libOffset
ULARGE_INTEGER cb
DWORD dwLockType

ILockBytes::LockRegion locks a range of bytes in the byte array.

Parameters

libOffset

[in] Specifies the beginning of the region to lock.

cb

[in] Specifies the number of bytes to lock.

dwLockType

[in] Specifies the type of lock access; see the following comments.

Return Values

Value	Meaning
S_OK	The requested lock was granted.
STG_E_LOCKVIOLATION	The requested lock is supported, but cannot be granted because of an existing lock.
STG_E_INVALIDFUNCTION	Invalid value for dwLockType.
STG_E_SHAREREQUIRED	The 16-bit file implementation can return STG_E_SHAREREQUIRED if it is unable to lock because no sharing support is loaded.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	The document file's ILockBytes on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

The libOffset parameter specifies the beginning of the lock range, extending cb bytes towards the end of the array. The range of bytes can extend past the current end of the array.

Region locking is optional for ILockBytes implementors. Whether region locking is supported depends on how the storage objects constructed on top of the byte array are used. If, at any one time, only one storage object is to be opened on the storage behind the byte array, region locking can be ignored. However, if the ILockBytes is built on top of a disk file, multiple simultaneous openings of a storage object are possible and region locking is needed to coordinate then.

The lock type is specified by dwLockType, using one of the following values from the LOCKTYPE enumeration, is defined as follows:

typedef enum tagLOCKTYPE{

LOCK_WRITE	= 1,

LOCK_EXCLUSIVE	= 2,

LOCK_ONLYONCE	= 4,

}LOCKTYPE;

A given ILockBytes instance might support one or all types. OLE's compound file implementation only supports LOCK_ONLYONCE. These values have the following meaning:

Types of Locks	Purpose
LOCK_WRITE	If the lock is granted, the specified region of the stream can be read by calling IStream::Read from any opening of this stream. Attempts to write to this region from any opening of this stream other than the one to which the lock was granted returns the error STG_E_ACCESSDENIED.
LOCK_EXCLUSIVE	Attempts to read or write this stream by other stream openings return the error STG_E_ACCESSDENIED.
LOCK_ONLYONCE	If the lock is granted, no other LOCK_ONLYONCE lock can be obtained on the bytes in the given region. Usually this lock type is an alias for some other lock type and other semantics might occur as a side effect.
For information on how a caller can determine whether locking is supported; see ILockBytes::Stat and its parameter block STATSTG.

See Also

IStream::LockRegion, ILockBytes::UnlockRegion


ILockBytes::UnlockRegion

HRESULT ILockBytes::UnlockRegion(libOffset, cb, dwLockType)
ULARGE_INTEGER libOffset
ULARGE_INTEGER cb
DWORD dwLockType

ILockBytes::UnlockRegion unlocks a region previously locked with ILockBytes::LockRegion.

Parameters

libOffset

[in] Specifies the beginning of the region to unlock.

cb

[in] Specifies the number of bytes to unlock.

dwLockType

[in] Specifies the type of lock access.

Return Values

Value	Meaning
S_OK	The requested unlock was granted.
STG_E_LOCKVIOLATION	The requested unlock cannot be granted.
STG_E_INVALIDFUNCTION	Invalid value for dwLockType.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	OLE's ILockBytes implementation on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

Any locked region must be explicitly unlocked using ILockBytes::UnlockRegion, using exactly the same libOffset, cb, and dwLockType parameters that were specified in the original ILockBytes::LockRegion call. Two adjacent regions cannot be locked separately and then unlocked with a single unlock call.

See Also

ILockBytes::LockRegion


ILockBytes::Stat

HRESULT ILockBytes::Stat(pstatstg, grfStatFlag)
STATSTG pstatstg
DWORD grfStatFlag

ILockBytes::Stat returns relevant statistics about this byte array.

Parameters

pstatstg

[out] Points to a STATSTG structure filled with statistics about this byte array; will be zero if an error occurs. If an error is returned, this parameter must be set to NULL. For more information, see the STATSTG structure at the beginning of this section.

grfStatFlag

[in] Specifies the level of statistics to return for this object. For more information, see the following comments.

Return Values

Value	Meaning
S_OK	Stat operation was successful.
STG_E_ACCESSDENIED	Statistics cannot be returned because access to the byte array cannot be granted.
E_OUTOFMEMORY	Memory not available for Stat operation.
E_FAIL	Stat operation did not succeed due to errors other than lack of access or available memory.
STG_E_INSUFFICIENTMEMORY	The name cannot be allocated.
STG_E_UNKNOWN	The file time can't be converted to FILETIME format.
STG_E_INVALIDHANDLE	An invalid floppy change has been made.
System errors.	The document file's ILockBytes on a file can return any system error, most notably, STG_* error codes from 0x001 to 0x057.
Remarks

The ILockBytes::Stat call is used by the compound file implementation to respond to an IStorage::Stat call invoked on a root storage object.

If no reasonably interpretable name for the ILockBytes exists, this function should return NULL in pstatstg->pwcsName.

The values for grfStatFlag are taken from the enumeration of STATFLAG, which is defined as follows:

typedef enum tagSTATFLAG

{

STATFLAG_DEFAULT = 0,

STATFLAG_NONAME  = 1

} STATFLAG;

See Also

IEnumSTATSTG Interface


IPersist Interface

The IPersist interface is the base interface from which the IPersistStorage, IPersistStream, and IPersistFile interfaces are derived. IPersist is implemented by object applications and used by container applications to get the CLSID associated with an object.

IPersist consists of the following methods:

DECLARE_INTERFACE_(IPersistStorage, IPersist)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IPersist methods ***

  HRESULT GetClassID (THIS_ CLSID pClsID);

};


IPersist::GetClassID

HRESULT IPersist::GetClassID(pClsID)
CLSID *pClsID

IPersist::GetClassID returns a document object's CLSID.

Parameter

pClsID

[out] Points to where to return the CLSID.

Return Values

Value	Meaning
S_OK	The CLSID was successfully returned.
E_FAIL	The CLSID could not be returned.
Remarks

The returned CLSID can be used to locate and dynamically load object-specific code into the caller's context.

When an object is being treated as an object of a different class (that is, an object that is typically edited class A is edited as class B), the CLSID returned from IPersistStorage::GetClassID is different that the CLSID of the current editor. The current editor may want to change the object's CLSID in the file so that the original editor (or a different one altogether) would no longer be able to edit the object. For example, it might be desirable to change the CLSID of an object from an old version of an application that is being treated as an object from a new version after the addition of a feature that is unintelligible to the old version. The object would change what it returns from IPersistStorage::GetClassID.

To change the persistent representation of an object's CLSID, an application must initiate a save and directly (if the object is a file-level link source) or indirectly (if the object is an embedding) cause the new CLSID to be written to the storage. The OnSave notification should be sent to all of the object's clients, informing them of the need to update their notion of the CLSID.

Example

The following example shows the GetClassID method for an object application that supports emulating objects of different types. In the example, the application checks to see whether it is currently performing a "Treat As" operation, in which case it would return the CLSID of the object it is emulating. A simple application would just return its own CLSID.

For more information on converting and emulating objects of different classes, see Component Object Interfaces and Functions.HRESULT IPersist_GetClassID(

        IPersistStorage *       lpThis,

        CLSID                   pClsID)

{

LPDOC lpDoc =

((struct CDocPersistStorageImpl *)lpThis)->lpDoc;

if (bTreatAs)  {

if (! IsEqualCLSID(&lpDoc->m_clsidTreatAs, &CLSID_NULL))

*pClsID = lpDoc->m_clsidTreatAs;

else

*pClsID = CLSID_APP;

}

else

*pClsID = CLSID_APP;

return NOERROR;

}


IPersistFile Interface

The IPersistFile interface is implemented by object applications and optionally by container applications.

IPersistFile permits applications to load and save documents stored in disk files (as opposed to objects stored in IStorage instances). Because the information needed to open a file varies from one application to another, the application is responsible for opening the file. Generally, IPersistFile is used to bind a linked object. In the container's case, this would be a link to an embedded object).

IPersistFile contains the following methods:

DECLARE_INTERFACE_(IPersistFile, IPersist)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IPersist methods ***

  HRESULT GetClassID (THIS_ CLSID pClsID);

    // *** IPersistFile methods ***

  HRESULT IsDirty (THIS);

  HRESULT Load (THIS_ LPCOLESTR pszFileName, DWORD dwMode);

  HRESULT Save (THIS_ LPCOLESTR pszFileName, BOOL fRemember);

  HRESULT SaveCompleted (THIS_ LPCOLESTR pszFileName);

  HRESULT GetCurFile (THIS_ LPOLESTR * ppszFileName);

};


IPersistFile::IsDirty

HRESULT IPersistFile::IsDirty()

IPersistFile::IsDirty checks a document object for changes since it was last saved.

Return Value

Value	Meaning
S_OK	The object has changed.
S_FALSE	The object has not changed.
Remarks

The dirty flag is conditionally cleared in IPersistFile::Save. A simple object with no contained objects simply checks its dirty flag. A container with one or more contained objects needs to maintain an internal dirty flag that reflects any one object being dirty. In practice, recursive polling isn't needed in the IsDirty method; rather, a container can keep its internal dirty flag up to date by the appropriate use of IAdviseSink::OnDataChange notifications.

Example

In the following example, IsDirty returns NOERROR if the object is dirty; otherwise, it returns S_FALSE.

HRESULT IPersistFile_IsDirty(IPersistFile * pThis)

{

LPOLEDOC lpOleDoc = ((struct IPersistFileImpl *)pThis)->lpOleDoc;

if (lpOleDoc->m_cfModified)

return NOERROR;

else

return S_FALSE;

}

See Also

IPersistFile::Save


IPersistFile::Load

HRESULT IPersistFile::Load(pszFileName, dwMode)
LPCOLESTR pszFileName
DWORD dwMode

IPersistFile::Load loads the document object contained in the given filename.

Parameters

pszFileName

[in] Points to the absolute path of the file to open.

dwMode

[in] Specifies the access mode with which the caller intends to use the file. The values for this parameter are described inStorage Access Mode Flags.

Return Values

IPersistFile::Load can return any of the error codes that begin with STG_E, in addition to the following codes.

Value	Meaning
S_OK	The document was successfully loaded.
E_FAIL	The document could not be loaded.
E_OUTOFMEMORY	There is not enough memory to load the document.
Remarks

The filename must be an absolute path. IPersistFile::Load is an initialization function only and it does not show the loaded document to the user.

The grfMode values express only the intent that the caller has in opening the file. The application can add more restrictive permissions as necessary. Zero is a valid value; the object should interpret the request as though the user opened the file using default values.

IPersistFile::Load is called by the File Moniker implementation of IMoniker::BindToObject.

Example

In the following example for an object application, IPersistFile::Load loads the object into memory with read-write permission. Because the object loads all information into memory at load time, it closes the file after loading.

HRESULT IPersistFile_Load (IPersistFile lpThis,

const char * pszFileName, unsigned long grfMode)

{

    LPDOC lpDoc = ((struct IPersistFileImpl *)lpThis)->lpDoc;

    HRESULT         hrErr;

    LPSTORAGE       lpSrcStg;

    unsigned long   fStatus;

hrErr = StgOpenStorage(pszFileName,

            NULL,

            STGM_READWRITE

            NULL,

            0,

            &lpSrcStg);

if ( (hrErr == STG_E_FILENOTFOUND) ||

 (hrErr == STG_E_FILEALREADYEXISTS) ||(hrErr != S_OK) )

return E_FAIL;

//Load the object

if(! LoadDocFromStg(lpDoc, lpSrcStg))

return E_FAIL;

//Register the object in the running object table

fStatus = SetDocFileName(lpDoc, pszFileName, lpSrcStg);

if (! fStatus)

return E_FAIL;

//Release the storage

(LPUNKNOWN)lpSrcStg->lpVtbl->Release(LPUNKNOWN(lpSrcStg);

return S_OK;

}

See Also

IMoniker::BindToObject


IPersistFile::Save

HRESULT IPersistFile::Save(pszFileName, fRemember)
LPCOLESTR pszFileName
BOOL fRemember

IPersistFile::Save saves a copy of the object to the indicated filename.

Parameters

pszFileName

[in] Points to the file in which the document should be saved.

fRemember

[in] Determines which disk file the document is to be logically associated with after the call is completed. Valid only if pszFileName is non-NULL.

Return Values

IPersistFile::Save can return any of the error codes beginning with STG_E in addition to the following codes.

Value	Meaning
S_OK	The file was successfully saved.
E_FAIL	The file could not be saved.
Remarks

The pszFileName parameter can be NULL, indicating that a simple "File Save" operation is requested to the now-current filename. A non-NULL value indicates either a "File Save As" or a "File Save Copy As" operation. In this case, the full path of the destination file is specified in the pszFileName parameter.

The fRemember parameter determines whether a "Save As" or a "Save Copy As" operation is requested by the user. If fRemember is TRUE, the file pointed to by pszFileName should become the current working copy of the document ("Save As"). FALSE indicates that after completing the IPersistFile::Save call, the working copy should be whatever it was before IPersistFile::Save was called ("Save Copy As").

If, upon exiting, the saved file is the current working copy, the internal dirty flag maintained by the object should be cleared and the container must call IPersistFile::SaveCompleted at some later time when it is done with the data in the file.

Whenever a file is saved with a new name, the object should send a rename notification (call IAdviseSink::OnRename) to its existing advisory connections.

Some clients of objects might find calling IPersistFile::Save useful for programmatically manipulating documents. IPersistFile::Save is not called by OLE.

Example

In the following example, Save checks fRemember and pszFileName to see which type of save operation is needed to save the object.

HRESULT IPersistFile_Save (IPersistFile lpThis,

LPCOLESTR pszFileName,BOOL fRemember)

{

LPDOC lpDoc =((struct CDocPersistFileImpl *)lpThis)->lpDoc;

LPSTORAGE lpDestStg = NULL;

HRESULT hrErr;

BOOL fStatus;

// Can only save file-based documents

if ( (lpszFileName==NULL ||

 (lpszFileName != NULL && fRemember)) &&

 ((lpDoc->m_docInitType != DOCTYPE_FROMFILE &&

   lpDoc->m_docInitType != DOCTYPE_NEW)) ) {

return E_INVALIDARG;

}

if (fRemember) {

if (pszFileName) {

fStatus = SetDocFileName(lpDoc,

                    (LPOLESTR)pszFileName, NULL);

if (! fStatus) goto error;

} else

// use current filename

pszFileName = lpDoc->m_szFileName;

} else if (! pszFileName)

return E_FAIL;

hrErr = StgCreateDocfile(lpszFileName,

  STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_CREATE,

  0, &lpDestStg);

if (hrErr != NOERROR)

return E_FAIL;

if(WriteClassStg(lpDestStg, &CLSID_APP) != NOERROR)

return E_FAIL;

fStatus = SaveDocSelToStg(lpDoc, NULL, uFormat, lpDestStg,

                FALSE, fRemember);

if (! fStatus)

return E_FAIL;

(LPUNKNOWN)lpDestStg->lpVtbl->Release((LPUNKNOWN)lpDestStg);

if (fRemember)

SetDocModified(lpDoc, FALSE, FALSE, FALSE);

SendAdvise ((LPDOC)lpDoc,  OLE_ONSAVE,  NULL, 0);

return S_OK;

}

See Also

IPersistFile::SaveCompleted


IPersistFile::SaveCompleted

HRESULT IPersistFile::SaveCompleted(pszFileName)
LPCOLESTR pszFileName

IPersistFile::SaveCompleted indicates the caller has saved the file with a call to IPersistFile::Save and is finished working with it.

Parameters

pszFileName

[in] Points to the filename in which the document was saved.

Return Value

Value	Meaning
S_OK	S_OK is returned in all cases.
Example

The following IPersistFile::SaveCompleted sample does nothing more than return NOERROR. This is because the object application never allows scribbling to its file after an object has been saved. Had the object been allowed to scribble to its storage, it would have needed to clear the no-scribble bit set in the call to IPersistFile::Save.

HRESULT IPersistFile_SaveCompleted (IPersistFile * pThis,

LPCOLESTR

pszFileName)

{

    return NOERROR;

}

See Also

IAdviseSink::OnSave, IPersistFile::Save


IPersistFile::GetCurFile

HRESULT IPersistFile::GetCurFile(ppszFileName)
LPOLESTR * ppszFileName

IPersistFile::GetCurFile returns either the absolute path of the document's currently associated file or the default filename prompt, if there is no currently-associated file.

Parameters

ppszFileName

[out] Points to where to return the current path or the default save prompt of the document, as appropriate. If an error is returned, this parameter must be set to NULL.

Return Values

Value	Meaning
S_OK	The return value is a path.
S_FALSE	The return value is the save prompt.
E_OUTOFMEMORY	Not enough memory to successfully return a value.
E_FAIL	Failure due to reasons other than insufficient memory.
Example

In the following example, GetCurFile ensures that memory for the pszFileName has been properly allocated using a registered instance of the IMalloc interface. GetCurFile then checks to see whether there is a valid filename (DOCTYPE_FROMFILE TRUE) passed in. GetCurFile releases the IMalloc instance just before returning.

HRESULT IPersistFile_GetCurFile (IPersistFile * pThis,

 LPOLESTR *

ppszFileName)

{

    LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl *)pThis)->lpOleDoc;

LPDOC lpDoc = (LPDOC)lpOleDoc;

    IMalloc pMalloc;

    LPOLESTR lpsz;

    HRESULT hrErr;

char szAnsiStr[256];

    *ppszFileName = NULL;

/*********************************************************************

    ** OLE2NOTE: Memory returned for the ppszFileName must be

    **    allocated appropriately using the currently registered IMalloc

    **    interface. This allows ownership of the memory to be

    **    passed to the caller, even if in another process.

    *********************************************************************/

    CoGetMalloc(MEMCTX_TASK, &pMalloc);

    if (! pMalloc) {

        return E_FAIL;

    }

    if (lpDoc->m_docInitType == DOCTYPE_FROMFILE) {

        /* valid filename associated; return filename */

        lpsz = (LPOLESTR)pMalloc->lpVtbl->Alloc(

                pMalloc,

                (OLESTRLEN lpDoc->m_szFileName)+1)*sizeof(OLECHAR)

);

        if (!lpsz){

hrErr = E_OUTOFMEMORY;

goto error;

{

        OLESTRCPY(lpsz, (LPOLESTR)lpDoc->m_szFileName);

        hrErr = S_OK;

    } else {

        /* no file associated; return default file save prompt */

        lpsz=(LPOLESTR)pMalloc->lpVtbl->Alloc(pMalloc,

ltrlen(DEFEXTENSION)+3)*sizeof(OLECHAR));

        wsprintf(szAnsiStr, lpsz, "*.%s", DEFEXTENSION);

A2W (szAnsiStr, lpsz, OLEUI_CCHPATHMAX);

        hrErr = S_FALSE;

    }

error:

OleStdRelease((LPUNKNOWN)pMalloc);

    *ppszFileName = lpsz;

    return hrErr;

}


IPersistStorage Interface

The IPersistStorage interface is implemented by object applications and/or object handlers. The interface's methods are used by OLE and container applications to manipulate an object's IStorage.

IPersistStorage consists of the following methods:

DECLARE_INTERFACE_(IPersistStorage, IPersist)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IPersist methods ***

  HRESULT GetClassID (THIS_ CLSID pClsID);

    // *** IPersistStorage methods ***

  HRESULT IsDirty (THIS);

  HRESULT InitNew (THIS_ IStorage * pStg);

  HRESULT Load (THIS_ IStorage * pStg);

  HRESULT Save (THIS_ IStorage * pStgSave, BOOL fSameAsLoad);

  HRESULT SaveCompleted (THIS_ IStorage * pStgNew);

  HRESULT HandsOffStorage (THIS);

};


IPersistStorage::IsDirty

HRESULT IPersistStorage::IsDirty()

IPersistStorage::IsDirty checks an object for changes since it was last saved.

Return Values

Value	Meaning
S_OK	The object has changed since the last save.
S_FALSE	The object has not changed since the last save.
E_OUTOFMEMORY	Memory constraints prohibit the successful determination of whether the object has changed.
E_FAIL	It is not possible to determine whether or not the object has changed due to errors other than lack of memory.
Remarks

Logically, the same sort of recursive calls to currently loaded objects must be made as in IPersistStorage::Save. Thus, an object is dirty if it or any of its contained objects are dirty. In practice, recursive polling is not needed in the IsDirty method. Rather, a container can keep its internal dirty flag up to date by the appropriate use of IAdviseSink::OnDataChange notifications.

The dirty flag must be conditionally cleared in the subsequent call to IPersistStorage::Save.

Example

In the following example, IsDirty returns NOERROR if the document or any contained objects have changed; otherwise, it returns S_FALSE.

HRESULT  IPersistStg_IsDirty(IPersistStorage * pThis)

{

    LPDOC lpDoc = ((struct IPersistStorageImpl *)pThis)->lpDoc;

    if (lpDoc->m_cfModified)

        return NOERROR;

    else

        return S_FALSE;

}

See Also

IAdviseSink::OnDataChange, IPersistStorage::Save


IPersistStorage::InitNew

HRESULT IPersistStorage::InitNew(pStg)
IStorage * pStg

IPersistStorage::InitNew initializes the specified IStorage object.

Parameters

pStg

[in] Points to the IStorage instance to initialize.

Return Values

Value	Meaning
S_OK	The new IStorage instance successfully returned.
CO_E_ALREADYINITIALIZED	This IStorage instance has already been initialized.
E_OUTOFMEMORY	Memory constraints prohibit the successful initialization of the IStorage instance.
E_FAIL	The IStorage instance could not be initialized due to errors other than lack of memory.
Remarks

An object that uses IPersistStorage must have access to a valid IStorage instance 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 access to an IStorage instance during this time by calling IPersistStorage::InitNew. Depending on the container's state, a temporary file might need to be created for this purpose.

If the object wants to retain the IStorage instance, it must call AddRef to increment its reference count.

Example

The following IPersistStorage::InitNew example calls WriteFmtUserTypeStg to save the object's data format to storage before initializing the document as one that contains an embedded object. Because an object should not open any storage or streams during an IPersistStorage::Save call (to preserve existing memory in case of low-memory saves), this InitNew example opens necessary streams ahead of time.

HRESULT IPersistStg_InitNew(IPersistStorage * pThis,

IStorage * pStg)

{

    LPDOC lpDoc = ((struct IPersistStorageImpl *)pThis)->lpDoc;

    LPAPP lpApp = (LPAPP)g_lpApp;

    LPOLESTR lpszUserType = (LPOLESTR)FULLUSERTYPENAME;

    HRESULT hrErr;

    WriteFmtUserTypeStg(pStg,lpApp->m_cfPrivate,lpszUserType);

    // set the document to a new embedded object.

    if (! InitDocNewEmbed(lpDoc)) {

        hrErr = E_FAIL;

        goto error;

    }

    /*   An object application CANNOT open or create any streams or

    **   storages when IPersistStorage::Save(fSameAsLoad==TRUE) is

    **   called. Thus, an embedded object should hold onto its storage

    **   and pre-open and hold open any streams it might need later

    **   to guarantee that it can save in low memory situations.

    */

    hrErr = pStg->lpVtbl->CreateStream(

            pStg,

            OLESTR"LineList",

            STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,

            0,

            0,

            &lpDoc->m_lpLLStm);

    if (hrErr != NOERROR) {

goto error;

    }

 	lpOleDoc->m_pStg = pStg;

    // OLE2NOTE: Hold onto IStorage* pointer, by calling AddRef on it

    pStg->lpVtbl->AddRef(pStg);

    return NOERROR;

error:

    return hrErr;

}

See Also

IUnknown::AddRef


IPersistStorage::Load

HRESULT IPersistStorage::Load(pStg)
IStorage pStg

IPersistStorage::Load loads an object into memory or in the running state, depending on whether the container or object handler calls the method.

Parameters

pStg

[in] Points to the IStorage instance from which to load or run the object.

Return Values

Value	Meaning
S_OK	The object was loaded successfully.
CO_E_ALREADYINITIALIZED	The object was already loaded.
E_OUTOFMEMORY	Memory constraints prohibit the object from loading.
E_FAIL	The object could not be loaded due to errors other than lack of memory.
IStorage errors	Can also return any of the error codes from IStorage methods.
Remarks

When IPersistStorage::Load is called by the container on the object handler, Load places the object in the loaded state. When IPersistStorage::Load is called by the object handler, Load places the object in the running state.

The IStorage instance passed in pStg contains the persistent representation of the object created when IPersistStorage::Save was called. The object should load whatever state information it needs. The object handler and/or object can hold onto the open IStorage instance while it is in the loaded or running state.

Example

In the following example, IPersistStorage::Load checks to see whether the object is being converted to another class of object. If it is, Load sets the dirty flag on the document (to force a save of the object to its new IStorage when the document is closed). To ensure that it can save in low-memory situations, Load opens its needed data streams before calling AddRef on the object's IStorage.

For more information on converting objects of one class type to another, see Component Object Interfaces and Functions.HRESULT IPersistStg_Load(IPersistStorage * pThis,

 IStorage * pStg)

{

    LPDOC lpDoc = ((struct IPersistStorageImpl *)pThis)->lpDoc;

LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;

LPDOC lpDoc = (LPDOC) lpServerDoc;

    HRESULT hrErr;

    if (LoadDocFromStg((LPDOC)lpServerDoc, pStg)) {

        ((LPDOC)lpDoc)->m_docInitType = DOCTYPE_EMBEDDED;

/* Check if the ConvertStg bit is on. If

** so, clear the ConvertStg bit and mark the

** document as dirty so as to force a save when the document

** is closed. The actual conversion of the bits should be

** performed when the data is loaded from the IStorage*.

** Any conversion of data formats would be done in

** ServerDoc_LoadFromStg function.

*/

if (GetConvertStg(pStg) == NOERROR) {

            SetConvertStg(pStg, FALSE);

/* set dirty flag to document *

Doc_SetModified = (pDoc, TRUE, FALSE, FALSE);

}

    } else {

        hrErr = E_FAIL;

        goto error;

    }

    /* Pre-open and hold open any streams needed to guarantee saves

       during low memory conditions.

    */

    if (lpDoc->m_pLLStm)

        OleStdRelease((IUnknown)pOleDoc->m_lpLLStm);

    hrErr = pStg->lpVtbl->OpenStream(

            pStg,

            OLESTR("LineList"),

            NULL,

            STGM_READWRITE | STGM_SHARE_EXCLUSIVE,

            0,

            &pDoc->m_lpLLStm);

    if (hrErr != NOERROR) {

        goto error;

    }

    if (lpDoc->m_lpNTStm)

        OleStdRelease((IUnknown)lpDoc->m_lpNTStm);

    hrErr = pStg->lpVtbl->OpenStream(

            pStg,

            OLESTR("NameTable"),

            NULL,

            STGM_READWRITE | STGM_SHARE_EXCLUSIVE,

            0,

            &lpDoc->m_lpNTStm);

    if (hrErr != NOERROR) {

goto error;

    }

    lpDoc->m_pStg = pStg;

    // Hold onto IStorage* pointer, by calling AddRef on it

    pStg->lpVtbl->AddRef(pStg);

    return NOERROR;

error:

    return hrErr;

}

See Also

IPersistStorage::Save


IPersistStorage::Save

HRESULT IPersistStorage::Save(pStgSave, fSameAsLoad)
IStorage *pStgSave
BOOL fSameAsLoad

IPersistStorage::Save saves an object to storage, including any nested objects.

Parameters

pStgSave

[in] Points to the IStorage instance in which the object is to be saved.

fSameAsLoad

[in] Indicates whether pStgSave is the same IStorage instance from which the object was loaded or just created.

Return Values

Value	Meaning
S_OK	The object was loaded successfully.
STG_E_MEDIUMFULL	The object could not be saved due to lack of disk space.
E_FAIL	The object could not be saved due to errors other than lack of memory.
Remarks

If fSameAsLoad is TRUE, the object writes incremental changes to pStgSave. If fSameAsLoad is FALSE, the caller is either cloning the object or responding to a Save As request and the object must copy all of its state to the new storage. Any state information from the object's old storage (the storage from which the object was loaded) must also be saved.

IPersistStorage::Save saves any currently loaded nested objects within the object by recursively calling IPersistStorage::Save, OleSave, or IStorage::CopyTo. Nonloaded nested objects do not need to have anything done to them (fSameAsLoad is TRUE) or they can be handled by copying their storage objects (fSameAsLoad is FALSE).

The object should not call IStorage::Commit on pStgSave. It is the responsibility of the caller to deal with the newly-saved changes. Often, the caller is using the transaction support, so it is appropriate to commit the changes with IPersistStorage::Commit. The object should not write its CLSID to pStgSave because this too is to be done by the caller.

After the save, it is the callee's responsibility to put the object in the no-scribble state. This temporarily revokes the object's right to write to its IStorage until IPersistStorage::SaveCompleted is called — a call that must be made after IPersistStorage::Save is called. When SaveCompleted is called, the object should, if necessary, clear the dirty flag reported by IPersistStorage::IsDirty. Because the storage is off limits between the time Save is completed and SaveCompleted is called, the caller of Save should call SaveCompleted as quickly as possible.

If pStgSave is not the current storage for the object (fSameAsLoad is FALSE) when the function exits, pStgSave can no longer be used.

Implementations of IPersistStorage::Save generally call WriteFmtUserTypeStg internally.

Example

The following example is a simple implementation of Save

STDMETHODIMP IPersistStg_Save(

IPersistStorage *       pThis,

IStorage                pStgSave,

BOOL                    fSameAsLoad

)

{

LPSERVERDOC lpServerDoc =

((struct CDocPersistStorageImpl *)pThis)->lpServerDoc;

LPDOC lpDoc = (LPDOC)lpServerDoc;

BOOL fStatus;

HRESULT hrErr;

fStatus = Doc_SaveSelToStg(

(LPDOC)lpServerDoc,

NULL,

lpDoc->m_cfSaveFormat,

pStgSave,

fSameAsLoad,

FALSE

);

if (!fStatus) {

App_ErrorMessage(g_lpApp, ErrMsgPSSaveFail);

hrErr = E_FAIL;

goto error;

}

lpServerDoc->m_fSaveWithSameAsLoad = fSameAsLoad;

lpServerDoc->m_fNoScribbleMode = TRUE;

return NOERROR;

error:

return hrErr;

}

See Also

OleSave, IStorage::CopyTo, IStorage::Commit


IPersistStorage::SaveCompleted

HRESULT IPersistStorage::SaveCompleted(pStgNew)
IStorage	* pStgNew

IPersistStorage::SaveCompleted terminates both the no-scribble and the hands-off mode, returning the object to normal storage mode.

Parameter

pStgNew

[in] Points to the IStorage instance in which the object was saved. It is NULL in no-scribble storage mode and is never NULL in hands-off storage mode.

Return Values

Value	Meaning
S_OK	Normal storage mode was re-entered successfully.
E_OUTOFMEMORY	The required streams cannot be opened. The container is left in the hands-off state.
E_UNEXPECTED	Neither IPersistStorage::Save nor IPersistStorage::HandsOffStorage was previously called.
E_INVALIDARG	IPersistStorage::HandsOffStorage was called previously and pStgNew is NULL.
E_FAIL	The object could not be saved due to errors other than lack of memory.
Remarks

In the hands-off storage state, pStgNew is never NULL. If the current mode is hands-off, the data in pStgNew depends on how the mode was entered:

•	If the hands-off state was entered from the normal state, pStgNew is the replacement for the one revoked by IPersistStorage::HandsOffStorage. The bits in pStgNew are the same as those that were in the revoked storage.

•	If the hands-off state was entered from the no-scribble state, the bits in pStgNew are the same as those last saved. If IPersistStorage::Save(pStgSave, FALSE) was used, the bits in pStgNew are different than the ones in the IStorage instance being held onto and that was revoked in IPersistStorage::HandsOffStorage.

In the no-scribble state, pStgNew might or might not be NULL. If it is NULL, the object is allowed to scribble to the storage it has. If it is non-NULL, the object should internally simulate receiving a call to HandsOffStorage, then proceed as in that case. Containers must be prepared for objects that refuse to do this (meaning, return an error, such as E_UNEXPECTED) and take responsibility for calling HandsOffStorage themselves.

As with IPersistStorage::Save and IPersistStorage::HandsOffStorage, SaveCompleted is a call that recurses to all currently loaded or running nested objects.

If SaveCompleted returns an error, the object should not return to the normal storage state. It should act as though IPersistStorage::SaveCompleted was never called in order to give the container the opportunity to try an alternate save strategy. IOleObject::Close can be called if necessary.

When IPersistStorage::SaveCompleted is called, often the object will clear the dirty flag reported by IPersistStorage::IsDirty. For more information on the various states an object can be in during a save operation and the effect it has on SaveCompleted, see Structured Storage Overview.

Example

In the following example, Save saves the dirty object(s) to storage and then puts the storage in the no-scribble state, where it remains until SaveCompleted is called.

HRESULT IPersistStg_SaveCompleted(

        IPersistStorage * pThis,

        IStorage               pStgNew)

{

    LPSERVERDOC lpServerDoc =

            ((struct CDocPersistStorageImpl *)pThis)->lpServerDoc;

LPOLEOC lpOleDoc + (LPOLEDOClpServerDoc;

LPDOC lpDoc = (LPDOC)lpServerDoc;

HRESULT hrErr;

/* OLE2NOTE: This sample application is a pure object application.

**    a container/object application would have to call

**    SaveCompleted for each of its contained compound document

**    objects. If a new storage was given, the container/object

**    would have to open the corresponding new substorage for each

**    compound document object and pass as an argument in the

**    SaveCompleted call.

*/

    /* OLE2NOTE: It is only legal to perform a Save or SaveAs operation

    **    on an embedded object. If the document is a file-based

    **    document, it cannot be changed to a IStorage-base object.

    **

    **      fSameAsLoad   pStgNew     Type of Save     Send OnSave

    **    ---------------------------------------------------------

    **         TRUE        NULL        SAVE             YES

    **         TRUE        ! NULL      SAVE *           YES

    **         FALSE       ! NULL      SAVE AS          YES

    **         FALSE       NULL        SAVE COPY AS     NO

    **

    **    * This is a strange case that is possible. It is inefficient

    **    for the caller; it would be better to pass pStgNew==NULL for

    **    the Save operation.

    */

    if ( ((lpServerDoc->m_fSaveWithSameAsLoad && pStgNew==NULL) || pStgNew)

            && (lpDoc->m_docInitType != DOCTYPE_EMBEDDED) ) {

        return E_INVALIDARG;

    }

    /* OLE2NOTE: Inform any linking clients that the document has been

    **    saved. In addition, any currently active pseudo objects

    **    should also inform their clients. Object should only broadcast

    **    an OnSave notification if a Save or SaveAs operation was

    **    performed. Object should NOT want to send the notification if

    **    a SaveCopyAs operation was performed.

    */

    if (pStgNew || lpServerDoc->m_fSaveWithSameAsLoad) {

        /* OLE2NOTE: If IPersistStorage::Save has been called, then

        **    object needs to clear the dirty bit and send OnSave

        **    notification. If HandsOffStorage is called directly

        **    without first calling Save, then object does NOT clear

        **    the dirty bit and send OnSave when SaveCompleted is

        **    called.

        */

        if (lpServerDoc->m_fNoScribbleMode) {

            Doc_SetModified(lpDoc, FALSE, FALSE, FALSE);

            ServerDoc_SendAdvise (

                    lpServerDoc,

                    OLE_ONSAVE,

                    NULL,	/* lpmkDoc -- not relevant here */

                    0);   	/* advf -- not relevant here */

        }

        lpServerDoc->m_fSaveWithSameAsLoad = FALSE;

    }

    lpServerDoc->m_fNoScribbleMode = FALSE;

    /* OLE2NOTE: An embedded object must guarantee that it can save

    **    even in low memory situations. It must be able to

    **    successfully save itself without consuming any additional

    **    memory. This means that an object app is NOT supposed to open

    **    or create any streams or storages when

    **    IPersistStorage::Save(fSameAsLoad==TRUE) is called. Thus an

    **    embedded object should hold onto its storage and pre-open and

    **    hold open any streams that it will need later when it is time

    **    to save. If this is a SaveAs situation, then we want to

    **    pre-open and hold open our streams to guarantee that a

    **    subsequent save will be successful in low memory. If we fail

    **    to open these streams, then we want to force ourselves to close

    **    to prevent editing changes from being made that can't be

    **    later saved.

    */

    if ( pStgNew && !lpServerDoc->m_fSaveWithSameAsLoad ) {

        // release previous streams

        if (lpOleDoc->m_lpLLStm) {

            OleStdRelease((IUnknown)lpOleDoc->m_lpLLStm);

            lpOleDoc->m_lpLLStm = NULL;

        }

        if (lpOleDoc->m_pStg) {

            OleStdRelease((IUnknown)lpOleDoc->m_pStg);

            lpOleDoc->m_pStg = NULL;

        }

        hrErr = pStgNew->lpVtbl->OpenStream(

                pStgNew,

                OLESTR("LineList"),

                NULL,

                STGM_READWRITE | STGM_SHARE_EXCLUSIVE,

                0,

                &lpOleDoc->m_lpLLStm);

        if (hrErr != NOERROR) {

            goto error;

        }

        lpOleDoc->m_pStg = pStgNew;

        // OLE2NOTE: to hold onto IStorage* pointer, we must AddRef it

        pStgNew->lpVtbl->AddRef(pStgNew);

    }

    return NOERROR;

error:

    return E_OUTOFMEMORY;

}

See Also

IPersistStorage::IsDirty, IPersistStorage::Save


IPersistStorage::HandsOffStorage

HRESULT IPersistStorage::HandsOffStorage()

IPersistStorage::HandsOffStorage causes the object to release its storage object and enter the hands-off storage state.

Return Value

Value	Meaning
S_OK	S_OK is returned in all cases.
Remarks

All object applications must implement this method in their IPersistStorage implementation, otherwise Save As scenarios will not work.

The hands-off storage state is terminated when IPersistStorage::SaveCompleted is called. SaveCompleted passes a pointer to the storage object taking the place of the one released in HandsOffStorage.

IPersistStorage::HandsOffStorage is called from either the no-scribble or the normal storage state. Between the time HandsOffStorage is called to initiate hands-off storage state and IPersistStorage::SaveCompleted is called to terminate it, callers cannot expect the object to respond in any predictable manner.

Example

In the following implementation, IPersistStorage::HandsOffStorage sets the object's storage state to hands-off, meaning the storage object is off limits to everyone until IPersistStorage::SaveCompleted is called.

HRESULT IPStg_HandsOffStorage(IPersistStorage * lpThis)

{

    LPDOC lpDoc =

            ((struct IPersistStorageImpl *)lpThis)->lpDoc;

lpDoc->m_dwStorageMode = STGMODE_HANDSOFF;

    // release previous streams

    if (lpDoc->m_lpLLStm) {

          OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm);

          lpDoc->m_lpLLStm = NULL;

    }

    if (lpDoc->m_pStg) {

          OleStdRelease((LPUNKNOWN)lpDoc->m_pStg);

          lpDoc->m_pStg = NULL;

    }

    return NOERROR;

}

See Also

IPersistStorage::SaveCompleted


IPersistStream Interface

The IPersistStream interface is implemented and used by OLE to support persistent objects that use a simple serial stream for their storage needs (monikers are an example of objects that use IPersistStream). Containers might be required to implement this interface in support of new moniker types.

Unlike the IStorage instances used in the implementation of IPersistStorage, the IStream instances used in IPersistStream are valid only during the call in which they are passed; the object cannot retain them after the call completes.

The IPersistStream interface contains the following methods:

DECLARE_INTERFACE_(IPersistStream, IPersist)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IPersist methods ***

  HRESULT GetClassID (THIS_ CLSID pClsID);

    // *** IPersistStream methods ***

  HRESULT IsDirty (THIS);

  HRESULT Load (THIS_ IStream * pStm);

  HRESULT Save (THIS_ IStream * pStm, BOOL fClearDirty);

  HRESULT GetSizeMax (THIS_ ULARGE_INTEGER * pcbSize);

};


IPersistStream::IsDirty

HRESULT IPersistStream::IsDirty()

IPersistStream::IsDirty checks an object for changes since it was last saved.

Return Values

Value	Meaning
S_OK	The object has changed since the last save.
S_FALSE	The object has not changed since the last save.
E_OUTOFMEMORY	Memory constraints prohibit the successful determination of whether the object has changed.
E_FAIL	It was not possible to determine whether or not the object has changed due to errors other than lack of memory.
Remarks

Calling IPersistStream::IsDirty determines whether any changes have occurred to an object that requires calling IPersistStream::Save to avoid losing information. The dirty flag is conditionally cleared in the implementation of IPersistStream::Save.

See Also

IPersistStream::Save


IPersistStream::Load

HRESULT IPersistStream::Load(pStm)
IStream * pStm

IPersistStream::Load initializes an object previously stored with IPersistStream::Save.

Parameters

pStm

[in] Points to the stream from which the object should load itself.

Return Values

Value	Meaning
S_OK	The object was successfully loaded.
E_FAIL	The object was not successfully loaded.
E_OUTOFMEMORY	Memory constraints prohibit the successful loading of the object.
Remarks

On entry, the stream is logically positioned just as it was in the call to IPersistStream::Save. The IPersistStream::Load method can both read and seek data from the stream but it cannot write to the stream.

Upon returning, the stream should be left in the same position as it was on exit from the call to IPersistStream::Save, which is just past the end of the data.

See Also

IPersistStream::Save


IPersistStream::Save

HRESULT IPersistStream::Save(pStm, fClearDirty)
IStream * pStm
BOOL fClearDirty

IPersistStream::Save saves the state of an object to the indicated stream.

Parameters

pStm

[in] Points to the stream into which the object should be stored.

fClearDirty

[in] Specifies whether or not the object's dirty flag should be cleared; TRUE clears the internal dirty flag after the save operation and FALSE leaves it alone.

Return Values

Value	Meaning
S_OK	Stream was successfully saved.
STG_E_MEDIUMFULL	IStream::Write returned STG_E_MEDIUMFULL.
STG_E_CANTSAVE	IStream::Write returned STG_E_CANTSAVE or if the object cannot currently be serialized.
Remarks

The object does not write its CLSID to the stream because this is done by the caller. This permits the caller, in appropriate situations, to store homogeneous collections of objects more efficiently.

On entry, the stream is positioned at the location where the object should write its data, at which point the object can immediately invoke IStream::Write calls. The object can read and seek about in the stream, but the object must not seek the stream before the position at which it was on function entry. On exiting from this function, the stream should be positioned immediately past all the persistent data of the object.

If the object contains other objects that cannot be serialized, the object itself cannot be serialized. IPersistStream::Save returns STG_E_CANTSAVE and the position of the seek pointer is undefined.

See Also

IPersistStream::Load, IStream::Write


IPersistStream::GetSizeMax

HRESULT IPersistStream::GetSizeMax(pcbSize)
ULARGE_INTEGER * pcbSize

IPersistStream::GetSizeMax determines an upper boundary for the size of the stream that would be needed to save this object with IPersistStream::Save.

Parameters

pcbSize

[out] Points to where to return the size boundary.

Return Value

Value	Meaning
S_OK	The size was successfully returned.
Remarks

The returned value can be used to set buffer sizes for immediate and subsequent calls to IPersistStream::Save. This value must be a conservative estimate of the size required because the caller of IPersistStream::Save can choose to provide a nongrowable buffer.

See Also

IPersistStream::Save


Storage Creation Functions

The functions described in the following sections deal with the creation of IStorage, IStream, and ILockBytes objects.

CreateILockBytesOnHGlobal (HGLOBAL hGlobal, BOOL fDeleteOnRelease,

    LPLOCKBYTES * pplkbyt);

CreateStreamOnHGlobal (HGLOBAL hGlobal, BOOL fDeleteOnRelease, LPSTREAM * ppstm);

StgCreateDocfile(const OLECHAR * pwcsName, DWORD grfMode, DWORD dwReserved,

    IStorage  ** ppstgopen);

StgCreateDocfileOnILockBytes(ILockBytes * plkbyt, DWORD grfMode, DWORD dwReserved,

    IStorage ** ppstgOpen);


CreateILockBytesOnHGlobal

HRESULT CreateILockBytesOnHGlobal(hGlobal, fDeleteOnRelease, pplkbyt)
HGLOBAL hGlobal
BOOL fDeleteOnRelease
LPLOCKBYTES * pplkbyt

CreateILockBytesOnHGlobal returns an OLE-provided instance of the ILockBytes interface whose data is stored in the specified memory handle allocated by GlobalAlloc. This interface will use standard marshalling across processes.

Parameters

hGlobal

Contains the memory handle allocated by GlobalAlloc. The handle must have been allocated as moveable and nondiscardable. If the handle is to be shared between processes, it must also have been 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.

fDeleteOnRelease

Indicates whether the underlying handle for this stream should be automatically freed when the stream is released.

pplkbyt

Points to where to put the newly created ILockBytes instance.

Return Values

Value	Meaning
S_OK	The ILockBytes instance was created successfully.
E_INVALIDARG	An invalid value was specified for hGlobal.
E_OUTOFMEMORY	Not enough memory to create an ILockBytes instance.
Remarks

The returned ILockBytes instance can be the basis for a compound file. A call to StgCreateDocfileOnILockBytes builds a compound file on top of the ILockBytes. GlobalReAlloc is called by the ILockBytes implementation to enlarge the memory block as needed.

The current contents of the memory block are undisturbed by the creation of the ILockBytes. The caller can use StgOpenStorageOnILockBytes to reopen a previously existing storage object already contained in the memory block.

Note  Freeing the handle that GetHGlobalFromILockBytes returns invalidates the ILockBytes instance. Therefore, pplkbyt->Release must be called before freeing the handle.

Using the hGlobal after passing the handle to CreateLockBytesOnHGlobal will not always work because GlobalRealloc can change the handle.

See Also

StgOpenStorageOnILockBytes


CreateStreamOnHGlobal

HRESULT CreateStreamOnHGlobal(hGlobal, fDeleteOnRelease, ppstm)
HGLOBAL hGlobal
BOOL fDeleteOnRelease
LPSTREAM * ppstm

CreateStreamOnHGlobal returns an OLE-provided IStream instance where data is stored inside a global memory handle. The interface returned will use standard marshalling across processes.

Parameters

hGlobal

Contains the memory handle allocated by GlobalAlloc. The handle must have been 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, CreateStreamOnHGlobal internally allocates such a shared new memory block.

fDeleteOnRelease

Specifies whether the handle to the memory is automatically freed when the stream is released.

ppstm

Points to where to return the stream. It cannot be NULL.

Return Values

Value	Meaning
S_OK	The stream was successfully created.
E_OUTOFMEMORY	The stream could not be created due to lack of memory.
E_INVALIDARG	Invalid value for one of the parameters.
Remarks

The returned stream 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 hGlobal. If hGlobal is NULL, CreateStreamOnHGlobal internally allocates a handle. The current contents of the memory block, therefore, are undisturbed by the creation of the stream. The user can call CreateStreamOnHGlobal to open a stream on an existing memory block.

The initial size of the stream is the size of the memory handle returned by GlobalSize. Because of allocation granularity rounding, this is not necessarily the same size as the handle originally allocated. If the logical size of the stream is important, the caller should follow the call to CreateStreamOnHGlobal with a call to IStream::SetSize.

See Also

CreateStreamOnHGlobal, GlobalSize, IStream::SetSize


StgCreateDocfile

HRESULT StgCreateDocfile(pwcsName, grfMode, reserved, ppstgOpen)
const OLECHAR * pwcsName
DWORD grfMode
DWORD reserved
IStorage** ppstgOpen

StgCreateDocfile creates a new root compound file in the file system.

Parameters

pwcsName

Points to the path of the compound file to create. 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

Specifies the access mode to use when opening the new compound file. For more information, see the following Remarks section.

reserved

Reserved for future use; must be zero.

ppstgOpen

If the operation is successful, it points to where the opened storage object is placed.

Return Values

Value	Meaning
S_OK	The compound file was successfully created.
STG_E_ACCESSDENIED	The calling process does not have sufficient access. Attempt to open file with conflicting permissions to a simultaneous open.
STG_E_FILEALREADYEXISTS	The compound file already exists and grfMode is set to STGM_FAILIFTHERE.
STG_S_CONVERTED	The compound file was successfully converted to Storage format.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDNAME	Bad name in pwcsName.
STG_E_INVALIDPOINTER	Bad pointer in pwcsName or ppstgOpen.
STG_E_INVALIDFLAG	Bad flag combination in grfMode.
STG_E_TOOMANYOPENFILES	Out of file handles.
File System Errors	Any file system error.
Remarks-

The name of the open compound file can be retrieved by calling IStorage::Stat.

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 grfMode indicate how to proceed:

Flag Values for grfMode	Meaning
STGM_CREATE	If the compound file exists, destroy it and replace it with the new name pwcsName.
STGM_CONVERT	If the compound file exists, replace it with the new file pwcsName, saving any data in the old file to a stream named CONTENTS in the new compound file. This stream contains the same data that was in the old file before the conversion.
STGM_DELETEONRELEASE	Indicates that the compound file is to be destroyed automatically when it is released. This capability is most useful with temporary files.
STGM_FAILIFTHERE	If the compound file exists, fail and return STG_E_FILEALREADYEXISTS.
If the compound file is opened in transacted mode (grfMode 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), grfMode 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 commit operation will fail (in transacted mode, write permissions are enforced at commit time).

If grfMode specifies STGM_TRANSACTED and no file yet exists with the name specified by pwcsName, 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.

The absence of STGM_TRANSACTED in grfMode indicates the file is to be created and opened in direct-access mode. In the current implementation of compound files, the following flags are necessary:

STGM_READWRITE | STGM_SHARE_EXCLUSIVE

Any existing file is destroyed before creating the new file.

StgCreateDocfile can be used to create a temporary compound file by passing a NULL value for pwcsName. 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 grfMode.

Note  By default, a Windows application can have 20 file handles open at one time. Each open IStorage in transacted mode uses three file handles. To increase the number of file handles the application has available, applications often call the Windows SetHandleCount API. However, SetHandleCount affects C run-time libraries (those statically linked to the application) that are hard coded to expect only 20 file handles. There is no check on overflow, so if the application already has a few compound files open and then uses a C run-time library (for example, fopen or sopen), the application trashes memory past the end of the file-handle array (low in its data segment) if the library gets a file handle above 20.

To solve this problem, applications should reserve some number of file handles on start up, making them available to the components that might be based on C run-time libraries. If your application does not use any dynamic link libraries that use the C run-time libraries, there is no need for any special file-handle reservation code.

See Also

StgCreateDocFileOnILockBytes


StgCreateDocfileOnILockBytes

HRESULT StgCreateDocfileOnILockBytes(plkbyt, grfMode, reserved, ppstgOpen)
ILockBytes * plkbyt
DWORD grfMode
DWORD reserved
IStorage ** ppstgOpen

StgCreateDocfileOnILockBytes creates and opens a new compound file on top of an ILockBytes instance provided by the caller.

Parameters

plkbyt

Points to the underlying byte array on which to create a compound file.

grfMode

Specifies the access mode to use when opening the new compound file. For more information, see the following Remarks section.

reserved

Reserved for future use; must be zero.

ppstgOpen

If the operation is successful, this parameter points to where the opened storage object is placed.

Return Values

Value	Meaning
S_OK	The compound file was successfully created.
STG_E_ACCESSDENIED	The calling process does not have sufficient access. Attempt to open ILockBytes with conflicting permissions to a simultaneous open.
STG_E_FILEALREADYEXISTS	The compound file already exists and grfMode is set to STGM_FAILIFTHERE.
STG_S_CONVERTED	The compound file was successfully converted. The original ILockBytes was successfully converted to IStorage format.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDPOINTER	A bad pointer was in plkbyt or ppstgOpen.
STG_E_INVALIDFLAG	A bad flag combination in grfMode.
STG_E_TOOMANYOPENFILES	Out of file handles.
File System Errors	Any file system error.
ILockBytes Errors	Any ILockBytes error.
Remarks

StgCreateDocfileOnILockBytes can be used to store a document in a relational database. The byte array indicated by plkbyt is used for the underlying storage in place of a disk file.

StgCreateDocfileOnILockBytes has almost exactly the same semantics as StgCreateDocfile. For more information, see the discussion of StgCreateDocfile.

The newly created compound file is opened according to the access modes in grfMode. For conversion purposes, the file is always considered to already exist. As a result, using the STGM_FAILIFTHERE value is not useful because it always causes an error to be returned. However, STGM_CREATE and STGM_CONVERT are both still useful.

The ability to build a compound file on top of an ILockBytes instance is provided to support having the data (underneath an IStorage and IStream tree structure) live in persistent space, space that does not ultimately reside in the file system. 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 OLE. However, it is recommended that OLE 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.

•	Outside tools can be written to access the embeddings and links within the document without special information about the document's file format. An example of such a tool is a copy utility that copies a whole a web of linked documents. It needs access to the contained links to determine the extent of the web.

•	The IStorage instance addresses the problem of how to commit the changes to the file. An application using the ILockBytes byte array must handle these issues itself.

•	Future file systems will probably implement IStorage and IStream as their native abstractions, rather than layer on top of a byte array as is done in compound files. Such a file system could be built so documents using the IStorage interface as their outer level containment structure would get an automatic efficiency gain by having the layering flattened when files are saved on the new file system.

See Also

StgCreateDocfile


Storage Query Functions

The functions included in this section test whether a file represents a valid IStorage object or if a pointer to an ILockBytes instance contains a IStorage object.

StgIsStorageFile(const OLECHAR Far * pwcsName);

StgIsStorageILockBytes(ILockBytes * plkbyt);


StgIsStorageFile

HRESULT StgIsStorageFile(pwcsName)
const OLECHAR * pwcsName

StgIsStorageFile determines whether a particular disk file contains an IStorage object.

Parameter

pwcsName

Points to the name of the disk file be examined. The pwcsName parameter is passed uninterpreted to the underlying file system.

Return Values

Value	Meaning
S_OK	The file contains an IStorage.
S_FALSE	The file does not contain an IStorage object.
STG_E_INVALIDFILENAME	A bad name was passed in.
STG_E_FILENOTFOUND	The pwcsName parameter could not be determined.
File System Errors	Any file system error.
Comment

At the beginning of the disk file underlying an IStorage object is a signature distinguishing an IStorage 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.

StgIsStorageILockBytes can be used in a similar manner, testing an ILockBytes byte array to see whether it contains an IStorage object.

See Also

StgIsStorageILockBytes


StgIsStorageILockBytes

HRESULT StgIsStorageILockBytes(plkbyt)
ILockBytes *plkbyt

StgIsStorageILockBytes determines whether or not a given ILockBytes byte array contains an IStorage object.

Parameters

plkbyt

Points to the ILockBytes byte array to be examined.

Return Value

Value	Meaning
S_OK	The plkbyt parameter contains an IStorage object.
S_FALSE	The plkbyt parameter does not contain an IStorage object.
Comment

At the beginning of the ILockBytes byte array underlying an IStorage object is a signature distinguishing an IStorage object from other file formats. StgIsStorageILockBytes is useful to applications whose documents use an ILockBytes byte array format that might or might not use storage objects.

StgIsStorageFile can be used in a similar manner, testing a disk file to see whether it contains a storage object.

See Also

StgIsStorageFile


Input/Output Storage Functions

The following functions support the retrieval of data from an IStorage, IStream, or ILockBytes object or the saving of data to objects of those types.

GetClassFile (LPCOLESTR szFilename, CLSID  * pclsid);

GetHGlobalFromILockBytes (LPLOCKBYTES plkbyt, HGLOBAL * phglobal);

GetHGlobalFromStream (LPSTREAM pstm, HGLOBAL * phglobal);

ReadClassStg(LPSTORAGE pStg, CLSID  *pclsid);

ReadClassStm(LPSTREAM pStm, CLSID  * pclsid);

ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT * pcf, LPOLESTR * lplpszUserType);

StgOpenStorage(const OLECHAR * pwcsName, ISTORAGE pstgPriority, DWORD grfMode,

    SNB snbExclude, DWORD reserved, IStorage  ** ppstgOpen);

StgOpenStorageOnILockBytes(ILockBytes plkbyt, IStorage pstgPriority,

    DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage  ** ppstgOpen);

StgSetTimes( OLECHAR const * lpszName, FILETIME const * pctime,

    FILETIME const * patime, FILETIME const * pmtime);

WriteClassStg(LPSTORAGE pStg, REFCLSID rclsid);

WriteClassStm(LPSTREAM pStm, REFCLSID rclsid);

WriteFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT CF, LPOLESTR lpszUserType);


GetClassFile

HRESULT GetClassFile(szFileName, pclsid)
LPCOLESTR szFileName
CLSID  * pclsid

GetClassFile returns the CLSID associated with the given filename.

Parameters

szFileName

Points to the filename from which the CLSID is to be retrieved.

pclsid

Points to where to return the CLSID.

Return Values

Value	Meaning
S_OK	The CLSID was successfully returned.
MK_E_CANTOPENFILE	Unable to open the specified filename.
MK_E_INVALIDEXTENSION	The specified extension in the registry is invalid.
File System Errors	Any file system error.
Remarks

GetClassFile locates the CLSID for the code that can work with the specified file. For example, when a link to a file-based document is activated, the file moniker uses the returned CLSID to locate the link source's application.

The following strategies are used to determine an appropriate CLSID:

1.	If the file is a storage object (call StgIsStorageFile to determine whether a file is a storage object), GetClassFile returns the CLSID written with IStorage::SetClass.

2.	If the file is not a storage object, GetClassFile attempts to match various bits in the file against a pattern. The registry contains a series of entries (possibly empty) that are logically of the form:

registry key = offset, cb, mask, value

The entries offset and cb denote, with a zero-origin offset and length, a particular byte range in the file (negative values for offset are interpreted from the end of the file). The next entry, mask, is a bitmask "AND-ed" with that set of bytes, and the result is compared with value. If mask is omitted, value is assumed to be all ones.

These entries are pattern matched in the file in the order in which they are found in the database. The first entry that matches, if any, is used as the class of the file. For example, the following entries require that the first four bytes be AB CD 12 34 and that the last four bytes be FE FE FE FE:

0 = 0, 4, FFFFFFFF, ABCD1234

2 = -4, 4,, FEFEFEFE

3.	If the preceding strategies fail, the file extension is consulted in the registry. If the value under the key ".EXT" is a valid CLSID, it is used.

4.	Otherwise, MK_E_INVALIDEXTENSION is returned.

See Also

WriteClassStg


GetHGlobalFromILockBytes

HRESULT GetHGlobalFromILockBytes(plkbyt, phglobal)
LPLOCKBYTES plkbyt
HGLOBAL * phglobal

GetHGlobalFromILockBytes returns a global memory handle to an ILockBytes instance created using CreateILockBytesOnHGlobal.

Parameters

plkbyt

Points to the ILockBytes instance for which the handle is being returned.

phglobal

Points to the current memory handle used by this ILockBytes instance.

Return Values

Value	Meaning
S_OK	The handle was returned successfully.
E_INVALIDARG	Invalid value specified for plkbyt. It can also indicate that the ILockBytes passed in is not one created by CreateILockBytesOnHGlobal.
Remarks

The handle returned might differ from the original handle due to intervening calls to GlobalRealloc. The contents of the returned memory can be written to a clean disk file, and opened as a storage object using StgOpenStorage.

GetHGlobalFromILockBytes only works in the process in which the memory ILockbytes was created.

See Also

StgOpenStorage


GetHGlobalFromStream

HRESULT GetHGlobalFromStream(pstm, phglobal)
LPSTREAM pstm
HGLOBAL * phglobal

GetHGlobalFromStream returns a global memory handle to a stream created using CreateStreamOnHGlobal.

Parameters

pstm

Points to the stream created by CreateStreamOnHGlobal.

phglobal

Points to the current memory handle used by this stream.

Return Value

Value	Meaning
S_OK	The handle was successfully returned.
E_INVALIDARG	Invalid value for pstm. It is in addition to the usual E_INVALIDARG cases, if the IStream passed in is not one created by CreateStreamOnHGlobal.
Remarks

The handle returned might differ from the original handle due to intervening GlobalRealloc calls.

GetHGlobalFromStream only works in the process in which the memory stream was created.

See Also

CreateStreamOnHGlobal, GlobalRealloc


ReadClassStg

HRESULT ReadClassStg(pStg, pclsid)
LPSTORAGE pStg
CLSID * pclsid

ReadClassStg returns the CLSID retrieved from a previous call to IStorage::Stat.

Parameters

pStg

Points to the IStorage instance containing the CLSID to be returned.

pclsid

Points to where to return the CLSID. May return CLSID_NULL.

Return Values

Value	Meaning
S_OK	The CLSID was returned successfully.
E_OUTOFMEMORY	The CLSID could not be returned due to lack of memory.
IStorage::Stat errors.	Can also return any of the error codes from IStorage::Stat.
See Also

OleLoad, WriteClassStg


ReadClassStm

HRESULT ReadClassStm(pStm, pclsid)
LPSTREAM pStm
CLSID  * pclsid

ReadClassStm returns an object's CLSID from the stream.

Parameters

pStm

Points to the stream from which the CLSID is to be read.

pclsid

Points to where to return the CLSID.

Return Values

Value	Meaning
S_OK	The CLSID was successfully returned.
STG_E_READFAULT	End of file was reached.
IStream::Read errors.	Can return any of the error codes from IStream::Read.
Remarks

The returned CLSID must have been previously written to the stream with WriteClassStm.

Most applications do not call ReadClassStm. OLE calls it before making a call to an application's IPersistStream::Load implementation.

See Also

WriteClassStm, ReadClassStg, WriteClassStg


ReadFmtUserTypeStg

HRESULT ReadFmtUserTypeStg(pstg, pcf, lplpszUserType)
LPSTORAGE pstg
CLIPFORMAT  * pcf
LPOLESTR * lplpszUserType

ReadFmtUserTypeStg returns the Clipboard format and user type saved with WriteFmtUserTypeStg.

Parameters

pstg

Points to the object's IStorage instance from which the information is to be read.

pcf

Points to where to return the Clipboard format. It can be NULL, indicating the format is of no interest to the caller.

lplpszUserType

Points to where to return the user type. It can be NULL, indicating the user type is of no interest to the caller.

Return Values

Value	Meaning
S_OK	The information was read successfully.
E_OUTOFMEMORY	Out of memory.
E_FAIL	WriteFmtUserTypeStg was never called on the object.
IStream::Read errors.	Can return any of the error codes from IStream::Read.
Remarks

WriteClassStg must have been previously called before calling ReadFmtUserTypeStg.

See Also

WriteFmtUserTypeStg


StgOpenStorage

HRESULT StgOpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstgOpen)
const OLECHAR * pwcsName
IStorage *pstgPriority
DWORD grfMode
SNB snbExclude
DWORD reserved
IStorage ** ppstgOpen

StgOpenStorage opens an existing IStorage object in the file system.

Parameter

pwcsName

Points to the path of the IStorage object to open. If ppStg is non-NULL, pwcsName is ignored. In this case, the name is inferred from the location of ppStg.

pstgPriority

Points to a previous opening of a root IStorage object (usually, one that was opened in priority mode) or NULL, which is most often the case.

If pstgPriority is non-NULL, the IStorage object is closed and reopened according to grfMode. After StgOpenStorage returns, the IStorage contained in pstgPriority on function entry is invalid, and can no longer be used. The caller should use ppstgOpen instead.

grfMode

Specifies the access mode to use to open the IStorage object. For more information, see the following Remarks section.

snbExclude

A non-NULL snbExclude points to a block of elements in this IStorage to be emptied as the IStorage is opened. This exclusion occurs independently of whether a snapshot copy happens on the open IStorage object. It can be NULL, indicating nothing is to be excluded.

reserved

Reserved for future use; must be zero.

ppstgOpen

If the operation is successful, ppstgOpen points to where the opened IStorage is placed.

Return Values

Value	Meaning
S_OK	The storage object was successfully opened.
STG_E_FILENOTFOUND	The file does not exist.
STG_E_ACCESSDENIED	Insufficient access to open file. Exclusions specified without read-write permissions or attempt to open file with conflicting permissions to a simultaneous open.
STG_E_FILEALREADYEXISTS	The file exists but is not a storage object.
STG_E_TOOMANYOPENFILES	Cannot open another file.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDNAME	Bad name in pwcsName or snbExclude.
STG_E_INVALIDPOINTER	Bad pointer in snbExclude, pwcsName, pstgPriority, or ppStgOpen.
STG_E_INVALIDFLAG	Bad flag combination in grfMode.
STG_E_INVALIDFUNCTION	STGM_DELETEONRELEASE specified in grfMode.
STG_E_TOOMANYOPENFILES	Out of file handles.
STG_E_OLDDLL	The DLL used to open this Storage is a version prior to the one used to create it.
STG_E_OLDFORMAT	The storage being opened was created by the Beta 1 IStorage provider. This format is no longer supported.
File System Errors	Any file system error.
Remarks

Opening a storage object in read and/or write mode without denying write permission to others (grfMode set to STGM_SHARE_DENY_WRITE) can be expensive because StgOpenStorage must make a snapshot copy of the entire storage object.

Applications often try to open storage objects with the following access permissions:

STGM_READWRITE | STGM_SHARE_EXCLUSIVE
// transacted vs. direct mode omitted for exposition

If the call is successful, a snapshot copy need never be made. If it fails, the application can either revert using STGM_READWRITE and have a snapshot copy made or use the following permissions:

STGM_READ | STGM_SHARE_DENY_WRITE
// transacted vs. direct mode omitted for exposition

In the former case, the application should prompt the user before making an expensive copy. In the latter case, if the call 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 use priority mode (grfMode set to STGM_PRIORITY).

The snbExclude parameter specifies a set of element names in the storage object to be emptied as the storage object is opened. Streams are set to a length of zero and 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 paradigm is used after first opening the storage object in priority mode, after which the now-excluded elements are read into memory. This earlier priority mode opening of the storage object should be passed through pstgPriority so the exclusion implied by priority mode is now removed. The caller is responsible for rewriting the contents of excluded items before committing. This technique is only useful for applications whose documents do not require constant access to their IStorage while they are active.

When a compound file is opened in transaction mode, there is a hard-coded limit of 20 concurrent users. If this limit is exceeded, StgOpenStorage returns the STG_E_LOCKVIOLATION error code.

See Also

StgOpenStorageOnILockBytes


StgOpenStorageOnILockBytes

HRESULT StgOpenStorageOnILockBytes (plkbyt, pstgPriority, grfMode, snbExclude, reserved, ppstgOpen)
ILockBytes plkbyt
IStorage pstgPriority
DWORD grfMode
SNB snbExclude
DWORD reserved
IStorage ** ppstgOpen

StgOpenStorageOnILockBytes opens an existing IStorage object that does not reside in a disk file, but instead lives in an ILockBytes instance provided by the caller.

Parameters

plkbyt

Points to the underlying byte array that contains the IStorage in which the compound file lives.

pstgPriority

Most often NULL. A non-NULL pstgPriority points to a previous opening of a root IStorage object, most often one that was opened in priority-mode. For more information, refer to the description of pstgPriority under StgOpenStorage.

grfMode

Specifies the access mode to use to open the IStorage object.

snbExclude

A non-NULL snbExclude points to a block of elements in this IStorage to be emptied as the IStorage is opened. This exclusion occurs independently of whether a snapshot copy happens on the open IStorage object. It can be NULL, indicating nothing is to be excluded.

reserved

Reserved for future use; must be zero.

ppstgOpen

If the operation is successful, the ppstgOpen parameter points to where the opened IStorage is placed.

Return Values

Value	Meaning
S_OK	IStorage object successfully opened.
STG_E_FILENOTFOUND	Byte array does not contain a storage object.
STG_E_ACCESSDENIED	Insufficient access. Exclusions specified without read-write permissions or attempt to open file with conflicting permissions to a simultaneous open.
STG_E_TOOMANYOPENFILES	Cannot open another file.
E_OUTOFMEMORY	Not enough memory to open IStorage object.
STG_E_INSUFFICIENTMEMORY	Out of memory.
STG_E_INVALIDNAME	Bad name in snbExclude.
STG_E_INVALIDPOINTER	Bad pointer in snbExclude, pLkbyt, pstgPriority, or ppStgOpen.
STG_E_INVALIDFLAG	Bad flag combination in grfMode.
STG_E_INVALIDFUNCTION	STGM_DELETEONRELEASE specified in grfMode.
STG_E_TOOMANYOPENFILES	Out of file handles.
STG_E_OLDDLL	The DLL being used to open this IStorage is a version prior to the one used to create it.
STG_E_OLDFORMAT	The IStorage being opened was created by the Beta 1 IStorage provider. This format is no longer supported.
File System Errors	Any file system error.
ILockBytes Errors	Any ILockBytes error.
Remarks

The IStorage object must have been previously created by calling StgCreateDocfileOnILockBytes. If plkbyt currently contains an IStorage, that object is opened according to the access modes in grfMode and returned through ppstgOpen.

The semantics of StgOpenStorageOnILockBytes are almost exactly the same as those of StgOpenStorage. For more information, see the discussion about StgOpenStorage.

See Also

StgOpenStorage


StgSetTimes

HRESULT StgSetTimes(lpszName, pctime, patime, pmtime)
OLECHAR const * lpszName
FILETIME const * pctime
FILETIME const * patime
FILETIME const * pmtime

StgSetTimes sets the creation, access, and modification times of the indicated file, if supported by the underlying file system.

Parameters

lpszName

Points to the name of the file to change.

pctime

Points to the new creation time.

patime

Points to the new access time.

pmtime

Points to the new modification time.

Return Values

Value	Meaning
S_OK	Time values successfully set.
STG_E_FILENOTFOUND	Element does not exist.
STG_E_INVALIDNAME	Bad name passed in lpszName, or a file system error.
STG_E_ACCESSDENIED	Insufficient permissions to access storage.
File System Errors	Any file system error.
Remarks

Each of the time-value parameters can be NULL, indicating no modification should occur.

It is possible that one or more of these time values are not supported by the underlying file system. IStorage::SetElementTimes sets the times that can be set and ignores the rest.


WriteClassStg

HRESULT WriteClassStg(pStg, rclsid)
LPSTORAGE pStg
REFCLSID rclsid

WriteClassStg writes the specified CLSID to the specified IStorage instance.

Parameters

pStg

Points to the object's IStorage instance.

rclsid

Points to the CLSID to be stored with the object.

Return Values

Value	Meaning
S_OK	CLSID returned successfully.
STG_E_MEDIUMFULL	CLSID could not be set due to lack of memory.
IStorage::SetClass errors.	Can return any of the error codes from IStorage::SetClass.
Remarks

Containers typically call WriteClassStg before calling IPersistStorage::Save.

See Also

OleSave, ReadClassStg


WriteClassStm

HRESULT WriteClassStm(pStm, rclsid)
LPSTREAM pStm
REFCLSID rclsid

WriteClassStm writes the indicated CLSID to the stream.

Parameters

pStm

Points to the stream into which the CLSID is to be written.

rclsid

Specifies the CLSID to write to the stream.

Return Values

Value	Meaning
S_OK	CLSID successfully written.
STG_E_MEDIUMFULL	No space left on device.
IStream::Write errors.	Can also return any of the error codes from IStream::Write.
Remarks

WriteClassStm writes the CLSID in a way that it can be retrieved by ReadClassStm.

Most applications do not call WriteClassStm. OLE calls it before making a call to an application's IPersistStream::Save implementation.

See Also

ReadClassStm, WriteClassStg, ReadClassStg


WriteFmtUserTypeStg

HRESULT WriteFmtUserTypeStg(pstg, cF, lpszUserType)
LPSTORAGE pstg
CLIPFORMAT cF
LPOLESTR lpszUserType

WriteFmtUserTypeStg writes the specified Clipboard format and current user type to the specified IStorage instance.

Parameters

pstg

Points to the IStorage instance where the information is to be written.

cF

Specifies the Clipboard format that describes the structure of the native area of the IStorage instance. The format tag includes the policy for the names of streams and substorages within this IStorage and rules for interpreting data within those streams.

lpszUserType

Points to the object's current user type. It cannot be NULL. This is the type returned by IOleObject::GetUserType. 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

Value	Meaning
S_OK	The information was written successfully.
STG_E_MEDIUMFULL	Information could not be written due to lack of space on the storage medium.
IStream::Write errors.	Can also return any of the error codes from IStream::Write.
Remarks

WriteClassStg must be called before calling WriteFmtUserTypeStg.

WriteFmtUserTypeStg must be called in an object's implementation of IPersistStorage::Save and by document-level objects that use structured storage for their persistent representation in their save sequence.

To read the information saved, applications call ReadFmtUserTypeStg.

See Also

IPersistStorage::Save, ReadFmtUserTypeStg


Compound Document Storage Functions
Compound-Document Storage Functions

The helper functions described in this section support the loading of storages and streams and the saving of data to those storages and streams.

OleLoad(LPSTORAGE pStg, REFIID riid, LPOLECLIENTSITE pClientSite,

            LPVOID * ppvObj);

OleLoadFromStream(LPSTREAM pStm, REFIID iidInterface, LPVOID * ppvObj);

OleSave(LPPERSISTSTORAGE pPS, LPSTORAGE pStg, BOOL fSameAsLoad);

OleSaveToStream(LPPERSISTSTREAM pPStm, LPSTREAM pStm );


OleLoad

HRESULT OleLoad (pStg, riid, pClientSite, ppvObj)
LPSTORAGE pStg
REFIID riid
LPOLECLIENTSITE pClientSite
LPVOID  * ppvObj

OleLoad loads an embedded or linked object into memory.

Parameters

pStg

Points to the IStorage instance from which to load the object.

riid

Identifies the interface to use when talking to the object.

pClientSite

Points to the object's client site.

ppvObj

Points to the newly loaded object.

Return Values

Value	Meaning
S_OK	Object loaded successfully.
E_OUTOFMEMORY	Object could not be loaded due to lack of memory.
E_NOINTERFACE	Object does not support the specified interface.
IPersistStorage::Load errors.
Remarks

OLE containers load objects into memory by calling the OleLoad function. When calling OleLoad, the application passes in a pointer to the open IStorage object in which the object's data is stored. This is usually a child storage object to the container's root IStorage 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. Its object application is not running.

Some applications load all the document's native data. Containers often defer loading the document's contained objects until required to do so. For example, until an object is scrolled into view and be drawn, it does not need to be loaded.

If the object is a linked object and the pClientSite parameter is non-NULL, OleLoad will attempt to automatically reconnect the linked object to its source if the source is already running. OleLoad performs the reconnection by querying the loaded object for IOleLink and calling IOleLink::BindIfRunning. If a container application calls OleLoad with pClientsite set to NULL, the container should call IOleLink::BindIfrunning itself to perform the reconnection

OleLoad performs the following steps as part of its functionality:

1.	Checks to see whether an auto-conversion of the object is necessary (see OleDoAutoConvert).

2.	Gets the CLSID from the open IStorage by calling IStorage::Stat.

3.	Calls CoCreateInstance to create an instance of the handler. If the handler code is not available, the default handler is used (see OleCreateDefaultHandler).

4.	Calls QueryInterface, asking for IPersistStorage. If successful, IPersistStorage::Load is invoked.

5.	Calls IOleObject::SetClientSite(pClientSite)to inform the object of its client site.

6.	Queries and returns the interface identified by riid.

See Also

ReadClassStg, IClassFactory::CreateInstance, IPersistStorage::Load


OleLoadFromStream

HRESULT OleLoadFromStream(pStm, iidInterface, ppvObj)
LPSTREAM pStm
REFIID iidInterface
LPVOID * ppvObj

OleLoadFromStream loads an object from the stream.

Parameter

pStm

Points to the stream from which the object is to be loaded.

iidInterface

Specifies the interface that the caller uses to talk to the object.

ppvObj

Points to where to return the object.

Return Values

Value	Meaning
S_OK	Object successfully loaded.
E_OUTOFMEMORY	Out of memory.
E_NOINTERFACE	Interface not supported.
ReadClassStm errors.	Can return any of the error codes from ReadClassStm.
CoCreateInstance errors.	Can return any of the error codes from CoCreateInstance.
IPersistStream::Load errors.	Can return any of the error codes from IPersistStream::Load.
Remarks

OleLoadFromStream 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.

If the CLSID for the stream is CLSID_NULL, ppvObj is set to NULL.

See Also

OleSaveToStream


OleSave

HRESULT OleSave (pPS, pStg, fSameAsLoad)
LPPERSISTSTORAGE pPS
LPSTORAGE pStg
BOOL fSameAsLoad

OleSave saves an object opened in transacted mode into the storage object pointed to by pStg.

Parameters

pPS

Points to the object to be saved.

pStg

Points to the destination storage object.

fSameAsLoad

Indicates whether or not pStg is the same storage object from which the object was loaded or created; TRUE indicates that the storage is the same and FALSE indicates a different storage object.

Return Values

Value	Meaning
S_OK	Object was successfully saved.
STG_E_MEDIUMFULL	Object could not be saved due to lack of disk space.
IPersistStorage::Save errors	Can return any of the error codes from IPersistStorage::Save.
Remarks

OleSave handles the common scenario in which an object uses the compound-file implementation for its storage and is open in transacted mode. More customized or sophisticated scenarios can be handled with the IPersistStorage and IStorage interfaces directly.

OleSave does the following:

1.	Calls IPersistStorage::GetClassID to get the CLSID.

2.	Writes the CLSID to the storage object.

3.	Calls IPersistStorage::Save to save the object.

4.	If there were no errors during the save operation, calls IPersistStorage::Commit to commit the changes.

Note  Static objects are saved into a stream called CONTENTS. Static metafile objects are saved in "placeable metafile format" and static DIB data is saved in "DIB file format." These formats are defined as the OLE standards for metafile and DIB. All data transferred using an IStream 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.

See Also

IStorage Interface, IPersistStorage Interface


OleSaveToStream

HRESULT OleSaveToStream(pPStm, pStm)
LPPERSISTSTREAM pPStm
LPSTREAM pStm

OleSaveToStream saves an object to the stream.

Parameters

pPStm

Points to the object to be saved to the stream, preceded by its serialized CLSID. It can be NULL, which has the effect of writing CLSID_NULL to the stream.

pStm

Points to the stream in which the object is to be saved.

Return Values

Value	Meaning
S_OK	Object successfully saved.
STG_E_MEDIUMFULL	No space left on device.
WriteClassStm errors	Can return any of the error codes from WriteClassStm.
IPersistStream::Save errors	Can return any of the error codes from IPerisistStream::Save.
Remarks

OleSaveToStream can be used to save an object that supports the IPersistStream interface. It does the following steps:

1.	Calls IPersistStream::GetClassID to get the object's CLSID.

2.	Writes the CLSID to the stream with WriteClassStm.

3.	Calls IPersistStream::Save with fClearDirty set to TRUE.

See Also

OleLoadFromStream


Drag and Drop Interfaces and Functions
Drag-and-Drop Interfaces and Functions

An OLE drag-and-drop operation allows for the transfer of data within a document, between documents, or between applications. Drag and drop provides 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.
The Windows File Manager is an example of an application that is both a drag source and drop target 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.


DROPEFFECT Enumeration

DoDragDrop and many of the drag-and-drop interface methods pass information about the effects that a drag source allows in a specific drag operation and the effect a potential drop will have on a target window. Valid drop effect values are the result of OR-ing together values contained in the DROPEFFECT enumeration:

typedef enum tagDROPEFFECT{

DROPEFFECT_NONE   = 0,	\\only lower three bits are significant

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.
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.


IDropSource Interface

The IDropSource interface is implemented by all applications containing data that can be dropped into another application. IDropSource contains the following methods:

33_INTERFACE_(IDropSource, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IDropSource methods ***

  HRESULT QueryContinueDrag (THIS_ BOOL fEscapePressed, DWORD grfKeyState);

  HRESULT GiveFeedback (THIS_ DWORD dwEffect);

};


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 has been canceled or a drop 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.

Creating the data-transfer object is dependent on the set of Clipboard formats supported and whether the drag source is a container or server. Drag sources can use the same type of object for data-transfer as the type that makes up the source of the selected data. When doing so, however, it is important to be aware of the areas that are treated differently.

The following table lists the areas in which the data-transfer object and data-source object differ:

Data-transfer object	Data-source object
Invisible.	Visible.
Stores a pointer to the data-source document.	Does not need to store a data-source document pointer.
Stores a pointer to a temporary moniker for the data selection.	Does not need to store a temporary moniker pointer.
Exposes only IDataObject and IDropSource.	Exposes IDataObject and other interfaces needed for linking and embedding, such as IOleObject. IDropSource not exposed.
SetData, Advise, Unadvise, and EnumAdvise methods in IDataObject are not supported.	If drag source is a server, all IDataObject methods are supported.  If drag source is a container, SetData, Advise, Unadvise, and EnumAdvise are not supported.
Embedded Object, Embed Source, and Link Source formats are offered.	Embedded Object, Embed Source, and Link Source formats are not offered.
For more information on creating a data-transfer object, see Data Transfer/Caching Interfaces and Functions."

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. Drop sources should use a variation on the standard northwest-pointing arrow, as shown in the following figure:

Default drag-and-drop pointers representing an illegal action, move, and copy.

For more information on the recommended user-interface considerations for a drag-and-drop operation, see User Interface Guidelines.


Drag Distance and Delay Values

Dragging occurs when the left mouse button is down and the drag pointer has been moved a specified minimum distance or a specified amount of time has passed since that distance was achieved. Specifying this behavior eliminates completion of an entire drag-move operation (copy, delete, and immediately paste the selection in the same place) when the user quickly clicks the mouse in place. The default minimum distance, or radius, for pointer movement is 2 pixels and the default delay before dragging begins is 200 milliseconds, as defined in the OLE 2 header files and shown below:

#define DD_DEFDRAGMINDIST 2 	//the minimum distance before
 	//dragging begins

#define DD_DEFDRAGDELAY 200 	//the delay before dragging begins

Overriding values for the minimum distance and the drag delay time can be stored in WIN.INI. These values can then be retrieved during application initialization using the Windows GetProfileInt function, as follows:

GetProfileInt("windows", "DragMinDist", DD_DEFDRAGMINDIST);

GetProfileInt("windows", "DragDelay", DD_DEFDRAGDELAY);

The drag delay value can be used when an application detects a left button down message to determine when to begin the drag operation. When the mouse enters the region with the left button down, in which dragging can be done for the first time, a timer can be set to the amount of time specified by DD_DEFDRAGDELAY. The drag operation can begin after the timer expires if the mouse is still within the region in which dragging is allowed.

The minimum distance setting is used when a mouse move is detected. If the mouse has entered the region in which dragging can be done, with the left button down,  "each movement can be tracked to determine whether the mouse position has changed by a sufficient number of pixels.


Generating Drag Feedback

IDropSource contains two methods, QueryContinueDrag, and GiveFeedback. QueryContinueDrag is called by DoDragDrop at the beginning of its loop to check whether the drag has been canceled, whether a drop is to occur, or whether the drag should continue.

The following table shows each state, listing the condition that determines the state and the designated return value.

User action	Effect on drag operation	Return value
Escape key is pressed	Cancel the drag	DRAGDROP_S_CANCEL
Left mouse button released	Perform a drop	DRAGDROP_S_DROP
Absence of the other two conditions	Continue the drag	S_OK
For every mouse move, DoDragDrop calls the drop target's IDropTarget::DragOver, which sets the drop effect. DoDragDrop passes the drop effect to IDropSource::GiveFeedback, which sets the pointer according to the feedback value. The drag source can either set its pointers manually or return DRAGDROP_S_USEDEFAULTCURSORS to ask OLE to use standard pointers defined for drag-and-drop operations. The standard pointers include the black brush to indicate scrolling, the plus sign (+) to indicate the copy effect, and a circle with a diagonal line across it to indicate an attempt to drop in an invalid region.


Ending the Drop Operation

When DoDragDrop returns, either the drop has occurred or the operation was canceled. The drag source uses the HRESULT returned from DoDragDrop to determine what to do with the data — delete it or leave it as is. If the data is to be deleted, as is the case with a move operation, the drag source must disable the sending of any data-change notifications until the delete operation has been completed.


IDropSource::QueryContinueDrag

HRESULT IDropSource::QueryContinueDrag(fEscapePressed, grfKeyState)
BOOL fEscapePressed
DWORD grfKeyState

IDropSource::QueryContinueDrag determines whether a drag operation should continue.

Parameter

fEscapePressed

[in] Specifies TRUE if the escape key has been pressed by the user since the previous call to IDropSource::QueryContinueDrag (or the call to DoDragDrop if IDropSource::QueryContinueDrag has not been called); otherwise, it is FALSE.

grfKeyState

[in] Identifies the present state of the modifier keys on the keyboard. This is a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.

Note that MK_ALT is the ALT key.

Return Value

Value	Meaning
S_OK	The drag operation should continue.
DRAGDROP_S_DROP	The drop operation should occur.
DRAGDROP_S_CANCEL	The drop operation should be canceled.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

To determine whether the drag operation should continue, IDropSource::QueryContinueDrag checks whether the mouse button that started the drag operation has been released, causing a drop operation to occur, and/or whether the escape key has been hit, causing a cancel. The former case can be handled by examining the flags in the grfKeyState parameter, while the latter can be done by examining the fEscapePressed flag.

Example

In the following example, IDropSource::QueryContinueDrag checks both the value of fEscapePressed and the key state to see whether the drag-and-drop operation should be canceled, a drop should occur, or whether the drag operation should continue.

HRESULT    IDropSource_QueryContinueDrag (

    LPDROPSOURCE            lpThis,

    BOOL                    fEscapePressed,

    DWORD                   grfKeyState)

{

    if (fEscapePressed)

        return DRAGDROP_S_CANCEL;

    else if (!(grfKeyState & MK_LBUTTON))

        return DRAGDROP_S_DROP;

    else

        return S_OK;

}

See Also

DoDragDrop


IDropSource::GiveFeedback

HRESULT IDropSource::GiveFeedback(dwEffect)
DWORD dwEffect

IDropSource::GiveFeedback enables a source application to provide feedback during a drag-and-drop operation.

Parameter

dwEffect

[in] Specifies the DROPEFFECT value returned by the most recent call to IDropTarget::DragEnter or IDropTarget::DragOver. For a list of values, see DROPEFFECT Enumeration, earlier in this section.

Return Value

Value	Meaning
S_OK	The function completed its task successfully.
DRAGDROP_S_USEDEFAULTCURSORS	OLE should use the default pointer (cursor) to provide feedback to the user.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IDropSource::GiveFeedback is called by DoDragDrop once for every time it calls IDropTarget::DragEnter or IDropTarget::DragOver. This action lets the drop source provide appropriate feedback in the source data. Unless a new target is immediately entered (in which case the call is omitted), IDropSource::GiveFeedback is called with dwEffect set to DROPEFFECT_NONE when IDropTarget::DragLeave is called.

There are two kinds of source feedback: appropriate changes to the mouse pointer, and changes in the display of the original dragged data. The different feedback provided by the mouse pointer in the Windows 3.1 File Manager, relative to a copy versus move operation, is a good example of both kinds of source feedback.

The dwEffect parameter can include   DROPEFFECT_SCROLL, indicating the source should put up the drag-scrolling variation of the appropriate pointer.

OLE defines a set of recommended mouse pointers that can be used for each of the different drop effects. For more information on these pointers, see User Interface Guidelines. For consistency across OLE applications, the drop source should use the recommended pointers.

IDropSource::GiveFeedback can return DRAGDROP_S_USEDEFAULTS, which causes DoDragDrop to put up the standard pointer appropriate for dwEffect. Other source feedback can be provided in addition to this standard feedback.

Example

Two example implementations are shown below. In the first example, IDropSource::GiveFeedback tells OLE to use its default drag-and-drop pointers by returning DRAGDROP_S_USEDEFAULTCURSORS (see Drag Source Responsibilities:

HRESULT IDropSource_GiveFeedback(IDrpSource * pThis, DWORD dwEffect)

{

return DRAGDROP_S_USEDEFAULTCURSORS;

}

The next example shows how to provide application-specific feedback. IDropSource_GiveFeedback checks first to determine whether the application is currently scrolling and if it is, the special scroll pointer is used. If the application is not scrolling, the appropriate pointer is loaded and set based on the value of dwEffect.

HRESULT IDropSource_GiveFeedback(IDropSource * pThis, DWORD dwEffect)

{

HCURSOR hcursor;

SCODE sc;

if (dwEffect & DROPEFFECT_SCROLL)  {

hcursor = LoadCursor(NULL, IDC_IBEAM);

} else  {

switch (dwEffect)

{

case DROPEFFECT_NONE:

hcursor = LoadCursor(NULL, IDC_ARROW);

break;

case DROPEFFECT_COPY:

hcursor = LoadCursor(vhInstApp, "PlusCursor");

break;

case DROPEFFECT_MOVE:

hcursor = LoadCursor(vhInstApp, "DragCursor");

break;

case DROPEFFECT_LINK:

hcursor = LoadCursor(vhInstApp, "PlusCursor");

break;

default:

hcursor = LoadCursor(NULL, IDC_ARROW);

break;

}

}

SetCursor(hcursor);

sc = S_OK;

return ResultFromScode(sc);

}

See Also

RegisterDragDrop, DoDragDrop, IDropTarget::DragLeave, IDropTarget::DragOver


IDropTarget Interface

The IDropTarget interface is implemented by all applications that can accept dropped data. Every application window that can be a drop target registers as such by calling RegisterDragDrop, passing an IDropTarget interface pointer as an argument.

IDropTarget contains the following methods:

DECLARE_INTERFACE_(IDropTarget, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IDropTarget methods ***

  HRESULT DragEnter (THIS_ IDataObject * pDataObj, DWORD grfKeyState, POINTL pt,

    DWORD * pdwEffect);

  HRESULT DragOver (THIS_ DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);

  HRESULT DragLeave (THIS);

  HRESULT Drop (THIS_ IDataObject, DWORD grfKeyState, POINTL pt,

    DWORD * pdwEffect);

};


Drop Target Responsibilities

The drop target is responsible for the following tasks:

•	Registering and revoking each drop-target window.

•	Determining the effect on the drag source at any given time.

•	Implementing drag scrolling.

•	Providing target feedback.

•	Integrating the data if a drop occurs or if the drag is canceled.


Registering as a Drop Target

Before they can accept dropped data, applications that have implemented the IDropTarget interface must register their class identifier (CLSID) and object handler in the registry as potential drop targets. Applications must call the OLE function RegisterDragDrop for every window that can accept dropped data.

Passed to RegisterDragDrop is a handle to the window and a pointer to the IDropTarget interface. RegisterDragDrop creates a stream to hold the IDropTarget interface pointer so the interface can be remoted across processes to support drag and drop from one application to another. Calling RegisterDragDrop also increments the reference count.

Typically, when the window is no longer available to accept drops, the drop target calls RevokeDragDrop to remove the registration. It is important to call RevokeDragDrop rather than relying on destroying the window to remove the registration. Neglecting to call RevokeDragDrop will result in a memory leak.

If the desktop is registered as a drop target along with windows belonging to applications running on the desktop, revoking the application windows might not be sufficient to block a potential drop. If the desktop is still a valid registered drop target when the user drags across the application window, the cursor might reflect that a drop can be accepted. This is misleading to the user, who might think the data is to be dropped onto the application window when it is really to be dropped onto the desktop. To guard against this effect, application windows should stay registered and return an appropriate cursor when a drop cannot be accepted.


Determining Drop Effect Values

The drop target determines the effect of a potential drop in every IDropTarget::DragEnter and IDropTarget::DragOver call. IDropTarget::DragEnter is called when the mouse pointer initially enters the drop-target window and is always called before IDropTarget::Drop or IDropTarget::DragLeave is called. IDropTarget::DragOver is called for every mouse move while the pointer is positioned over the drop-target window. DragLeave is called when the pointer leaves the drop-target window.

IDropTarget::DragEnter and IDropTarget::DragOver are passed parameters that identify the location of the mouse, the modifier key state, and a list of drop effects. DragEnter also receives a pointer to the data-transfer object's IDataObject interface so it can enumerate the available formats. The drop target uses the list of available formats, the mouse pointer position, and the modifier key state to determine the appropriate drop effect value to return.

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	None	DROPEFFECT_MOVE
SHIFT	None	DROPEFFECT_MOVE
Another factor in determining the drop effect value is whether the drag source and drop target are in the same window. Areas in which data can be dropped may be different if the drop is local.


Ending the Drag Operation

A particular drag operation is ended when either the user clicks outside the drop-target window or releases the mouse while inside the window. DoDragDrop calls IDropTarget::DragLeave in the first case, IDropTarget::Drop in the second case. The implementation of IDropTarget::DragLeave involves releasing the data-transfer object and, if appropriate, killing the drag scrolling timer.

The tasks involved in a drop operation are similar to the tasks involved in a Clipboard paste. Depending on the order and type of formats passed with the data-transfer object, the data is integrated statically, as an embedded object, or as a linked object.


IDropTarget::DragEnter

HRESULT IDropTarget::DragEnter( pDataObj, grfKeyState, pt, pdwEffect)
IDataObject *pDataObj
DWORD grfKeyState
POINTL pt
DWORD *pdwEffect

IDropTarget::DragEnter determines whether the target window can accept the dragged object and what effect the dragged object will have on the target window.

Parameters

pDataObj

[in] Points to an instance of the IDataObject interface, through which the dragged data is accessible.

grfKeyState

[in] Specifies a combination of the MK_CONTROL, MK_SHIFT, MK_ALT, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON flags that identifies the present state of the keyboard modifier keys.

pt

[in] Points to a structure containing the current mouse/cursor coordinates in screen coordinates.

pdwEffect

[in, out] Points to where to return the effect of this drag operation to the caller. The value for pdwEffect is from the DROPEFFECT enumeration. For a list of values, see DROPEFFECT Enumeration.

Return Value

Value	Meaning
S_OK	The function completed its task successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

During calls to IDropTarget::DragEnter and IDropTarget::DragOver, the target should provide appropriate target feedback to the user. During a drop operation, as the mouse passes over the unobscured portion of the window associated with this drop target, IDropTarget::DragEnter, IDropTarget::DragOver, and IDropTarget::DragLeave are called as the mouse first enters, moves around within, then leaves each drop target.

The first time the mouse enters the screen region for a given target, IDropTarget::DragEnter is called. As long as the drag operation continues and the target remains the same, IDropTarget::DragOver is called. When the target changes or the drag operation is canceled, IDropTarget::DragLeave is called. When the drop finally happens, IDropTarget::Drop is called. IDropTarget::Drop is never called without first calling IDropTarget::DragEnter on the given drop target.

IDropTarget::DragEnter determines whether the dragged object is acceptable by examining the Clipboard formats available for the object, the values pointed to by the pdwEffect parameter, and the state of the modifier keys, as indicated in the grfKeyState parameter. If the dragged object can be dropped, IDropTarget::DragEnter determines the corresponding effect on the drop source.

The drop target indicates its decision through the pdwEffect parameter. The pdwEffect parameter is an OR-ing together of values contained in the DROPEFFECT enumeration. Typically, DROPEFFECT_COPY is passed in pdwEffect, although DROPEFFECT_LINK can be passed to create a link to the source data. Whether the source allows the dragged data to be permanently moved from the source application is indicated by the presence or absence of DROPEFFECT_MOVE.

The value returned in pdwEffect is passed through the DoDragDrop function to IDropSource::GiveFeedback so the source can change the pointer appropriately and provide feedback to the user.

To provide appropriate target feedback, the drop target can pull data from pDataObj. (For example, a drop operation into a spreadsheet that knows the size of the table being dropped can highlight an appropriate set of cells.)

Example

The following implementation of IDropTarget::DragEnter first checks for a valid IDataObject pointer parameter and then sets several state variables that relate to drag scrolling. Two OLE functions, OleQueryCreateFromData and OleQueryLinkFromData, are called to determine if the data-transfer object offers data in an acceptable format for embedding or linking.

The state of the modifier keys influences the drop effect. The absence of a modifier indicates the drop will result in a move operation, the CTRL key indicates a copy operation, and the CTRL and SHIFT key combination indicates a link operation.

The contents of pdwEffect is set to DROPEFFECT_NONE under the following conditions:

•	There are no acceptable formats available from the data-transfer object.

•	The data selection being dragged is a selection from the drop target's current document.

•	The modifier keys imply that the drop effect is to be either a move or a copy operation, but OleQueryCreateFromData returned an error, indicating that no appropriate formats were available from the data-transfer object.

•	The modifier keys imply that the drop effect is to be a link operation, but OleQueryLinkFromData indicated that the formats required to create a linked object were not available.

DoDragScroll, an application-specific function is called to determine whether the pointer has been in the scrollable region for the specified amount of time and, if so, sets dwDropEffect to DROPEFFECT_SCROLL. If the resulting drop effect is a move, copy, or link operation, the value of dwScrollEffect is OR-ed into the value of pdwEffect before IDropTarget_DragEnter returns.

HRESULT IDropTarget_DragEnter (

    IDropTarget *           lpThis,

    IDateObject *           pDataObj,

    DWORD                   grfKeyState,

    POINTL                  pointl,

    DWORD                   pdwEffect)

{

LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;

LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl *)lpThis)->lpOleDoc;

LPLINELIST lpLL = (LPLINELIST)&((LPDOC)lpOleDoc)->m_LineList;

BOOL fDragScroll;

lpDoc->m_fDragLeave              = FALSE;

lpDoc->m_dwTimeEnterScrollArea   = 0;

lpDoc->m_lastdwScrollDir         = SCROLLDIR_NULL;

lpDoc->m_dwScrollInset = (INT)GetProfileInt ("windows",

"DragScrollInset",

DD_DEFSCROLLINSE

T);

lpDoc->m_dwScrollDelay = (INT)GetProfileInt ("windows",

"DragScrollDelay",

DD_DEFSCROLLDELA

Y);

HRESULT hRes = OleQueryCreateFromData(pDataObj);

if ((hRes == S_OK) || (hRes == OLE_S_STATIC))

lpDoc->m_fCanDropCopy = TRUE;

else

lpDoc->m_fCanDropCopy = FALSE;

if ( (OleQueryLinkFromData(pDataObj)) == NOERROR)

lpDoc->m_fCanDropLink = TRUE;

else

lpDoc->m_fCanDropLink = FALSE;

if (! lpDoc->m_fCanDropCopy && ! lpDoc->m_fCanDropLink)  {

*lpdwDropEffect = DROPEFFECT_NONE;

return NOERROR;

}

if (lpDoc->m_fLocalDrag) {

if (SelectionLocal (pointl,lpDoc))  {

*lpdwDropEffect = DROPEFFECT_NONE;

return NOERROR;

}

}

if (grfKeyState & MK_CONTROL) {

if (grfKeyState & MK_SHIFT)

*pdwEffect = DROPEFFECT_LINK;

else

*pdwEffect = DROPEFFECT_COPY;

} else

*pdwEffect = DROPEFFECT_MOVE;

if ( (*pdwEffect == DROPEFFECT_COPY 	||

   *pdwEffect == DROPEFFECT_MOVE) 	&&

  ! lpDoc->m_fCanDropCopy )  {

*pdwEffect = DROPEFFECT_NONE;

return NOERROR;

}

if ( *pdwEffect == DROPEFFECT_LINK && ! lpDoc->m_fCanDropLink )  {

*pdwEffect = DROPEFFECT_NONE;

return NOERROR;

}

if (DoDragScroll (lpDoc, pointl))

dwScrollEffect = DROPEFFECT_SCROLL;

*pdwEffect |= dwScrollEffect;

return NOERROR;

}

See Also

DoDragDrop, IDropSource::GiveFeedback


IDropTarget::DragOver

HRESULT IDropTarget::DragOver(grfKeyState, pt, pdwEffect)
DWORD grfKeyState
POINTL pt
DWORD *pdwEffect

IDropTarget::DragOver provides feedback to the user and to DoDragDrop about the state of the drag operation within a drop target application.

Parameters

grfKeyState

[in] A combination of the MK_CONTROL, MK_SHIFT, MK_ALT, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON flags that identifies the present state of the keyboard modifier keys.

pt

[in] Structure containing the current mouse/cursor coordinates in screen coordinates.

pdwEffect

[in, out] Points to where to return the effect of this drag operation to the caller. The return value is a combination of DROPEFFECT values. For a list of values, see DROPEFFECT Enumeration.

Return Value

Value	Meaning
S_OK	State information returned successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

After the pointer initially enters the target window, IDropTarget::DragOver is called each time the pointer (mouse) moves until the drag operation is canceled or a drop occurs. For efficiency reasons, no Clipboard data object is passed. The Clipboard data object passed in the most recent call to IDropTarget::DragEnter is used.

IDropTarget::DragOver determines whether the dragged object is acceptable by examining the Clipboard formats available for the object, the values pointed to by the pdwEffect parameter, and the state of the modifier keys, as indicated in the grfKeyState parameter. If the dragged object can be dropped, IDropTarget::DragOver determines the corresponding effect on the drag source application.

The target application indicates its decision through the pdwEffect parameter.

To provide appropriate target feedback, the drop target can pull data from pDataObj (as was passed in the most recent call to IDropTarget::DragEnter).

Example

In the following implementation, IDropTarget::DragOver first checks whether it is time to begin scrolling before calling QueryDrop to determine whether a drop could happen at the current mouse position. IDropTarget_DragOver changes its feedback based on the return value from QueryDrop.

HRESULT IDropTarget_DragOver (

    IDropTarget *           lpThis,

    DWORD                   grfKeyState,

    POINTL                  pt,

    DWORD                 pdwEffect)

{

LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;

    LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl *)lpThis)->lpDoc;

LPLINELIST lpLL = (LPLINELIST)&((LPDOC)lpOleDoc)->m_LineList;

    BOOL    fDragScroll;

    fDragScroll = DoDragScroll ( lpDoc, pt );

    if (QueryDrop(lpDoc,grfKeyState,pt,fDragScroll,pdwEffect))

SetDragOverLineFromPointl(lpLL, pointl);

else

        RestoreDragFeedback(lpLL);

    return NOERROR;

}

See Also

IDropTarget::DragEnter, IDropSource::GiveFeedback, DoDragDrop


IDropTarget::DragLeave

HRESULT IDropTarget::DragLeave()

IDropTarget::DragLeave causes the drop target to remove its feedback. This method is called when the mouse leaves the area of a given target while a drag is in progress or when the drag operation is canceled.

Return Value

Value	Meaning
S_OK	Drop target feedback was removed.
E_OUTOFMEMORY	Out of memory.
Remarks

The data-transfer object passed to the most recent call to IDropTarget::DragEnter is implied.

The drop target should remove any target feedback it currently has showing. If the drop target is currently holding on to the data-transfer object passed to IDropTarget::DragEnter, it should release it at this time.

Example

The following example of IDropTarget::DragLeave sets a flag to indicate that a drag-and-drop operation has terminated. It then restores the window to its previous state if the last known drop effect was a move, copy, or link.

HRESULT IDropTarget_DragLeave ( IDropTarget * lpThis)

{

LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl *)lpThis)->lpOleDoc;

LPLINELIST lpLL = (LPLINELIST)&((LPDOC)lpOleoc)->m_LineList;

HDC hdc;

lpDoc->m_fDragLeave = TRUE;

if (lpDoc->dwLastEffect != DROPEFFECT_NONE) {

hdc = GetDC(lpDoc->hwnd);

HighlightRect(lpOleDoc->hwnd, hdc, &lpOleDoc->rcDrag);

ReleaseDC(lpOleDoc->hwnd, hdc);

}

return NOERROR;

}

See Also

IDropTarget::DragEnter


IDropTarget::Drop

HRESULT IDropTarget::Drop(pDataObj, grfKeyState, pt, pdwEffect)
IDataObject *pDataObj
DWORD grfKeyState
POINTL pt
DWORD *pdwEffect

IDropTarget::Drop drops the source data, indicated by pDataObj, on this target application.

Parameter

pDataObj

[in] Points to an instance of the IDataObject interface, through which the data is accessible.

grfKeyState

[in] A combination of the MK_CONTROL, MK_SHIFT, MK_ALT, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON flags that identifies the present state of the keyboard modifier keys.

pt

[in] Structure containing the current mouse/cursor coordinates in screen coordinates.

pdwEffect

[in, out] Points to where to return the effect of this drag operation to the caller. The return value is a combination of DROPEFFECT values. For a list of these values, see DROPEFFECT Enumeration.

Return Value

Value	Meaning
S_OK	The function completed its task successfully.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The data being dropped is accessible through pDataObj. The formats available through the IDataObject interface should be used in conjunction with the state of the modifier keys to determine the semantics of what should happen on the drop. Appropriate information is passed back through pdwEffect. The caller can only use pDataObj for the duration of this call. By the time IDropTarget::Drop returns, the drop operation should have obtained everything it needs from pDataObj.

Example

The following implementation of IDropTarget::Drop determines the effect of the drop by examining grfKeyState and other variables internal to the data-transfer object, lpDoc. If the drop results in a move, copy, or link operation, the same code used to paste and paste link a compound-document object (PasteFromData) is called to complete the drop operation.

HRESULT    IDropTarget_Drop (

    IDropTarget *           lpThis,

    IDataObject *           pDataObj,

    DWORD                   grfKeyState,

    POINTL                  pt,

    DWORD                 pdwEffect)

{

    LPOLEDOC  lpOleDoc = ((struct CDocDropTargetImpl *)lpThis)->lpOleDoc;

    lpDoc->m_fDragLeave = TRUE;

    lpDoc->m_fLocalDrop = TRUE;

if (fDragScroll)

dwScrollEffect = DROPEFFECT_SCROLL;

if ( (! lpOleDoc->m_fCanDropCopy && ! lpDoc->m_fCanDropLink) ||

 ((lpOleDoc->m_fLocalDrag) && (DataInLocalSelection(lpDoc)))  {

*pdwEffect = DROPEFFECT_NONE;

}

else if (grfKeyState & MK_CONTROL)  {

if (grfKeyState & MK_SHIFT)  {

if (lpOleDoc->m_fCanDropLink)

*pdwEffect = DROPEFFECT_LINK;

else

*pdwEffect = DROPEFFECT_NONE;

}

else if (lpOleDoc->m_fCanDropCopy)

*pdwEffect = DROPEFFECT_COPY;

else

*pdwEffect = DROPEFFECT_NONE;

} else if (lpOleDoc->m_fCanDropCopy)

*pdwEffect = DROPEFFECT_MOVE;

*pdwEffect |= dwScrollEffect;

if (*pdwEffect != DROPEFFECT_NONE)  {

BOOL fLink     = (*pdwEffect == DROPEFFECT_LINK);

BOOL fStatus;

fStatus = PasteFromData (lpDoc, pDataObj,

lpOleDoc->m_fLocalDrag, fLink);

// If the drop was unsuccessful, restore the original focus

// line.

if (! fStatus)

ResetFocus( lpLL, (WORD)iFocusLine );

// Do not enable redraw for a local drag/drop move until the

// source is done deleting the moved object.

if (! (lpOleDoc->m_fLocalDrag

&& (*pdwEffect & DROPEFFECT_MOVE) != 0 ))

DisableRedraw ( (LPOLEDOC)lpOleDoc );

}

return NOERROR;

}

See Also

IDropTarget::DragEnter, IDropSource::GiveFeedback, DoDragDrop


Drag and Drop API Functions
Drag-and-Drop Functions

The following functions are used in drag-and-drop operations:

RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget);

RevokeDragDrop(HWND hwnd);

DoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects,
    LPDWORD pdwEffect);


RegisterDragDrop

HRESULT RegisterDragDrop(hwnd, pDropTarget)
HWND hwnd
LPDROPTARGET pDropTarget

RegisterDragDrop registers an application window as capable of accepting dropped objects. This function only registers the window specified by the hwnd parameter.

Parameter

hwnd

Specifies the handle to the window that is the target for the drop.

pDropTarget

Points to the IDropTarget implementation through which information about the hwnd is communicated while the drag-and-drop operation is in progress.

Return Values

Value	Meaning
S_OK	The application was registered successfully.
DRAGDROP_E_INVALIDHWND	Invalid hwnd.
DRAGDROP_E_ALREADYREGISTERED	This window has already been registered as a drop target.
E_OUTOFMEMORY	Out of memory.
Remarks

To accept drop operations, an application registers the applicable windows as potential drop targets. RegisterDragDrop must be called for each window capable of accepting dropped objects.

The pDropTarget parameter points to an instance of the IDropTarget interface, through which communication is made with the target application during a drag-and-drop operation. During a drag-and-drop operation, as the mouse passes over unobscured portions of the target window(s), the passed IDropTarget::DragOver is called. When a drop operation actually occurs in a given window, IDropTarget::Drop is called.

See Also

RevokeDragDrop


RevokeDragDrop

HRESULT RevokeDragDrop(hwnd)
HWND hwnd

RevokeDragDrop revokes an application window previously registered for drag-and-drop operations.

Parameter

hwnd

Specifies the handle to the window that is the target for the drop.

Return Values

Value	Meaning
S_OK	Window was revoked successfully.
DRAGDROP_E_INVALIDHWND	Invalid hwnd.
DRAGDROP_E_NOTREGISTERED	An attempt was made to revoke a drop target that has not been registered.
Remarks

To revoke a window's ability to accept dropped objects, an application calls the RevokeDragDrop function on the window previously registered with the RegisterDragDrop function.

See Also

RegisterDragDrop


DoDragDrop

HRESULT DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect)
LPDATAOBJECT pDataObj
LPDROPSOURCE pDropSource
DWORD dwOKEffects
LPDWORD pdwEffect

DoDragDrop initiates a drag-and-drop operation and facilitates communication between the drag source and the drop target.

Parameter

pDataObj

Points to an instance of the IDataObject interface, which provides the data being dragged.

pDropSource

Points to an instance of the IDropSource interface, which is used to communicate with the source during the drag operation.

dwOKEffects

Determines the effects the drag source allows in the drag operation. Most significant is whether it permits a move. The dwOKEffects and pdwEffect parameters obtain values from the DROPEFFECT enumeration. For a list of values, see DROPEFFECT Enumeration.

pdwEffect

Points to a value that indicates how the drag operation affected the source data. The pdwEffect parameter is set only if the operation is not canceled.

Return Value

Value	Meaning
S_OK	The drop operation was initiated successfully.
DRAGDROP_S_DROP	The drop operation was successful.
DRAGDROP_S_CANCEL	The drop operation was canceled.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

When a user starts to drag data, the drag source calls DoDragDrop, passing it pointers to the data-transfer object's IDataObject and IDropSource interfaces and a place holder for a value indicating the effect of a potential drop. When the mouse pointer passes over a window that is a registered drop target, DoDragDrop calls IDropTarget::DragEnter.

The modifier key state, location of the mouse, and the IDataObject interface pointer are passed as parameters to IDropTarget::DragEnter. This information is used by IDropTarget::DragEnter to return a drop effect — a value that represents what the effect of a drop would be on the drag source. From the perspective of the drag source, the result is that the data is either removed or unchanged. For a list of valid drop effects, see DROPEFFECT Enumeration.

For each iteration of its loop, DoDragDrop calls IDropSource::QueryContinueDrag to check whether to continue or cancel the drag. The return value from IDropSource::QueryContinueDrag determines which IDropTarget method is called next.

IDropSource::QueryContinueDrag  return value	Resulting interface method call(s)
DRAGDROP_S_CANCEL	IDropTarget::DragLeave
DRAGDROP_S_DROP	IDropTarget::Drop
S_OK	IDropTarget::DragOver IDropSource::GiveFeedback
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. DoDragDrop continues to loop until such time as the user either cancels the drag or drops the data. Depending on the user action, IDropSource::GiveFeedback returns DRAGDROP_S_CANCEL for a canceled drag and DRAGDROP_S_DROP for a successful drop.

The values for dwOKEffects are based on an OR-ing of the values in the DROPEFFECT enumeration. The pdwEffect parameter determines the effect the drag-and-drop operation had on the source data. Typically, DROPEFFECT_COPY is passed in dwOKEffects, although DROPEFFECT_LINK can be passed to create a link to the source data. The presence or absence of DROPEFFECT_MOVE indicates whether the source allows the dragged data to be permanently moved from the source application.

See Also

IDropSource::GiveFeedback


In-Place Activation Interfaces and Functions

This section describes the interaction required between a container and an object to create an in-place edit session. In-place editing lets users interact with embedded objects within the context of their compound documents. Unlike OLE 1, in which the object application had to create a separate, top-level window in which to edit an embedded object, OLE 2 can integrate the editing window into the container's document. The user is often unaware of the interaction between the active object and its container that allows this seamless integration.

In-place editing is sometimes referred to as visual editing but more generically as in-place activation. In-place activation includes primary verbs other than "edit." Not all objects support editing as their primary verb; a video clip, for example, may play in place.

This section is divided into two parts: a discussion of the architecture and issues involved in implementing in-place activation, and a reference for the in-place activation interfaces and APIs.


Window Hierarchy

Five types of windows are involved in OLE in-place activation:

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.

The following drawing is an example of a simple container without split-pane views that implements its document, pane, and parent windows in the same window.

In-place activation window hierarchy for a simple container

The next drawing is an example of a more complex container. This word-processing program is an MDI application with separate frame, document, pane, parent, and in-place windows.

More complex cases involve MDI applications, split-pane and structured views, and nested objects (where the parent is not the same as the pane). In these cases, the object creates its in-place window as a child of the parent window, and uses the other windows to place the composite menu bar and optional toolbars and/or frame adornments.

In-place activation window hierarchy for a more complex application

While an object is active in place, the user can switch between windows, leaving the object and its containing document dormant. If the user switches to another top-level window (i.e., to a different application), the border, title, and colors of the in-place container's frame window are shown as inactive. The menu bar and any toolbars or other editing controls remain visible but inactive. Switching back causes the in-place user interface to become active again, and the focus returns to the object's window. If the in-place container is an MDI application and the user switches to a different document window within the same application, the in-place document window is inactive, the composite menu bar is replaced by the menu bar of the document being activated, and any of the object's frame-level tools and adornments are hidden. When the user switches back, the composite menu is restored, focus is returned to the object's window, the document window is active, and any hidden frame-level tools and adornments are displayed again.


Interface Hierarchy

Containers and objects implement and use six interfaces to support in-place activation. The following table summarizes these interfaces:

Interface	Description
IOleWindow	This is the base interface for all the other in-place interfaces, and so it is implemented and used by containers and objects. Provides window handle and supports context-sensitive help.
IOleInPlaceObject	Implemented by objects; used by immediate container to activate and deactivate the object.
IOleInPlaceActiveObject	Implemented by objects; used by top-level container to manipulate the object while it is active. Provides direct channel of communication between active object and frame and document windows.
IOleInPlaceUIWindow	Implemented by containers; used by objects to manipulate the container's document window.
IOleInPlaceFrame	Implemented by containers; used by objects to control the container's frame window.
IOleInPlaceSite	Implemented by containers; used by objects to interact with the in-place client site.
The interfaces are related hierarchically. The next figure shows the three levels in this hierarchy and the interfaces implemented at each level. Calls that install user-interface menus 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 go through the containment hierarchy, and each level performs the correct actions.

Relationship of object, container, and frame interfaces

An in-place active object communicates with its immediate container using IOleInPlaceSite. To communicate with objects higher in the containment hierarchy, the object uses IOleInPlaceFrame and IOleInPlaceUIWindow. Pointers to these interfaces are obtained by calling IOleInPlaceSite::GetWindowContext. If the container is itself an in-place object, it returns pointers to its immediate container's IOleInPlaceFrame and to IOleInPlaceUIWindow interfaces rather than its own.


Activating the Object In-Place
Activating the Object In Place

In-place activation is initiated when the embedded object's container calls IOleObject::DoVerb, typically after the user either double-clicks the object or selects a verb from the Edit menu. The following steps constitute the basic approach to in-place activation:

1.	Protocol negotiation

2.	In-place window creation

3.	Composite menu creation

4.	Installation of frame adornments

5.	Setting the active object and composite menu installation

It is important to separate the activation into two distinct phases: in-place activation and UI activation. During the in-place activation, the object does the protocol negotiation and in-place window creation but does not install its user interface. Only when the object is becoming UI-active does it create and install the composite menu, negotiate for border space in the container's windows, and display its tools and adornments.

For inside-out objects, the distinction between in-place active and UI-active is particularly important. Typically, when a container first activates an inside-out object, it calls IOleObject::DoVerb with the OLEIVERB_INPLACEACTIVATE verb. At this time, the inside-out object becomes in-place active but not UI-active. It is mandatory that an object not install its UI in response to OLEIVERB_INPLACEACTIVATE. Also, when a UI-active object is UI-deactivated (see (IOleInPlaceObject::UIDeactivate), it reverts to the in-place active state. It must repeat each of the UI-activation steps each time it becomes UI-active. This applies to both inside-out and outside-in objects. For more information, see the "Object States" section.


Protocol Negotiation

Protocol negotiation is the communication required between container and object to determine if the object can be activated in place. Both the object and the container must have implementations of the appropriate interfaces to allow in-place activation to succeed. The object initiates the negotiation with a call to IOleClientSite::QueryInterface, asking for a pointer to IOleInPlaceSite. The container will return a valid pointer if it supports in-place activation.

Using the IOleInPlaceSite pointer, the object next calls IOleInPlaceSite::CanInPlaceActivate to ask the container whether or not this embedded object can be activated in place. The container returns S_OK if the object can be activated; an error code if it cannot. A container that normally supports in-place activation may refuse to activate a particular object, for example, if the view scale is other than 100%. If the container does refuse, the object opens a new window for editing, as in OLE 1. If the container indicates support by returning S_OK, the object sends a notification that in-place activation is beginning by calling IOleInPlaceSite::OnInPlaceActivate.


In-Place Window Creation

Before the in-place window can be created, the object must collect information about the container and its physical layout. The object calls IOleInPlaceSite::GetWindowContext to return the position of the object in the container, pointers to IOleInPlaceFrame and IOleInPlaceUIWindow, handles to the accelerator table and frame window, and indicates whether the container is an SDI or MDI application. With this information, the object can install its windows into the window hierarchy of the container application. A typical strategy for creating object applications is to make one window provide the hatched border, and make another window the object's editing window. The object application uses the SetParent function to install the hatch window as a child of the container's parent window. The object's editing window is a child of the hatch window. The object passes a handle to its editing window when it calls IOleInPlaceFrame::SetMenu; and it passes a handle to its top-level in-place window (typically the hatch window) when the container calls IOleInPlaceSite::GetWindowContext.

Windows created by the object application are installed in the container's window hierarchy during the in-place session, and removed from the hierarchy when the session ends. If the object application is an in-process server (i.e. DLL object application), the object's windows belong to the container's process. If the object application is a local server (i.e. EXE object application), the windows belong to the object application's process and messages are dispatched from the object application's message queue. For more information on how messages are dispatched during an in-place session, see IOleInPlaceFrame::TranslateAccelerator.


Composite Menu Creation

The composite menu is made up of groups from both the object and container applications. The object application supplies the Edit, Object, and Help menu groups. The container application optionally supplies the File, Container, and Window groups. This menu temporarily replaces the container's main menu while the object is UI-active in place. All objects should install the shared menu only when they are becoming UI-active, and not when they are merely becoming in-place active. It is mandatory that objects not install the shared menu in response to the OLEIBVERB_INPLACEACTIVATE verb. In addition, when a UI-active object is UI-deactivated, it must remove and destroy the shared menu. The object must rebuild the shared menu each time it becomes UI-active.

One notable limitation on composite menu content cannot be avoided. Owner-draw menu items must not be included. In no case are they guaranteed to work and, in the case of embeddings activated in place, they are known to fail.

These menu groups from the composite menu are combined in the following order:

File	Edit	Container	Object	Window	Help
As an example, consider the following container and object application menu groups that are combined to create an in-place composite menu:

Container menu groups:

Menu Group:		File		Container		Window
Contents:		File		empty		Window

Object application menu groups:

Menu Group:		Edit		Object			Help
Contents:		Edit		DataSeries	Chart	Format		Help

Composite menu bar:

File	Edit	DataSeries	Chart	Format	Window	Help
container menu		object application menu			container menu	object application menu
To consider a more complex example, refer to the composite menu comprising the following container and object application menu groups:

Container menu groups:

Menu Group:		File		Container		Window
Contents:		File		View		Window

Object application menu groups:

Menu Group:		Edit		Object			Help
Contents:		Edit		Gallery	Format		Help

Composite menu bar:

File	Edit	View	Gallery	Format	Window	Help
container menu	object       applica-      tion menu	container menu	object application menus		container menu	object    application    menu
The individual menu groups are referenced, not copied, in the composite menu. That means the container or object can check, gray, or draw menu items using the HMENU for the particular menu. Also, the container can use the same menu for multiple-object menus, provided that the lifetime of the menu exceeds that of any object menu that refers to it.

When the user selects a command from the composite menu, the command and associated menu messages are sent to the application that owns the particular menu group selected. WM_INITMENU messages always go to the container. OLE ensures that menu commands and messages are dispatched correctly.

The following interaction occurs between object and container during composite menu creation:

1.	The object creates a menu resource by calling the CreateMenu function.

2.	The object calls IOleInPlaceFrame::InsertMenus to have the container insert its menu groups into the composite menu. The container inserts its menus using references rather than copies. The menus can be created or reused from the main menu. The container returns the number of items in each of its three menu groups in the OLEMENUGROUPWIDTHS members 0, 2, and 4.

3.	If the container fails the IOleInPlaceFrame::InsertMenus call, the object destroys the menu resource. In this case, the object would end the in-place activation, and open its own window for editing the object.

4.	If the IOleInPlaceFrame::InsertMenus call succeeds, the object calls the InsertMenu function to insert its menu groups in the composite menu. The object then calls OleCreateMenuDescriptor so that OLE can build a menu descriptor to be used for dispatching messages and commands to the appropriate container or object window.


Installation of Frame Adornments

Frame adornments are auxiliary windows, such as toolbars, palettes, status lines, and formula bars, that the active in-place object can supply to the container. It is up to the container to provide space for any adornments the object is to display. If the container does not or can not provide space, the object can either do without the adornment, display it as a pop-up window, or refuse to activate in place. In general, containers should remove or disable their adornments during in-place activation because their commands may not be available.

The object uses the IOleInPlaceUIWindow interface to negotiate for border space in the container's document window, or the IOleInPlaceFrame interface to negotiate for space in the container's frame window. The object makes the following calls to negotiate for container space:

1.	IOleInPlaceUIWindow::GetBorder to retrieve the outer rectangle within which the adornments can be displayed. This rectangle is typically the content area of the window.

2.	IOleInPlaceUIWindow::RequestBorderSpace to determine whether the container can accommodate the object's request for a specified amount of space at the borders of the window. The container can grant the request or refuse.

3.	IOleInPlaceUIWindow::SetBorderSpace to allocate the border space.

The following drawing shows the relationship between the various windows in the container and interface methods that support border-space negotiation. The windows are shown hierarchically as they would appear in the container. The relevant interface methods appear in the appropriate windows.

Active object negotiating space for control bars and frame adornments

Negotiation can be re-invoked at any time. Applications that support the repositioning of toolbars, for example, may renegotiate. To ask for more space, the object can call IOleInPlaceUIWindow::SetBorderSpace. If this request fails, the object can begin another full negotiation sequence for the needed space.

While an object is active in place, the container must be notified about the object adornments that are being displayed. Containers should follow these guidelines:

•	The object should not be moved when it is displaying or hiding adornments. The user may have double-clicked the object, and moving it might remove it from under the mouse cursor.

•	If the container scrolls, causing the active in-place object to move, the container must call IOleInPlaceObject::SetObjectRects to make sure that the object's in-place window gets the correct move and/or size messages. Because the container can move or resize the object's in-place window, the object must take notice of these move and size messages to maintain any necessary relationships, such as aligning a ruler on the document border with the object.

•	If the container's window(s) change size, the container must call IOleInPlaceActiveObject::ResizeBorder to notify the object that it must renegotiate any border space allocations. Since floating palettes are simply pop-up windows owned by the in-place window, they are not affected and the container need not be informed of their existence. The object is responsible for hiding and showing floating palette windows on activation and deactivation. Only windows attached as children of the frame and document windows are affected by a change in the container window size.

Objects can display controls, such as row and column headings for tables, around their borders while active. These attachments exist only during the in-place activation session and are not part of the object. Gluing attachments to objects should not affect the layout of the surrounding container objects. If necessary, the attachments can overlap the surrounding contents.

Note  It is possible to have an MDI client window intersect with an object application-supplied toolbar. If the object application's frame adornment window is not created using WS_CLIPSIBLINGS, then when the MDI client window is moved to its appropriate space during a SetWindowPos or DeferWindowPos operation, bits from the object application's toolbar will be incorrectly copied for the intersection of the MDI client window.


Setting the Active Object and Installing the Composite Menu

The last few steps involve passing interface pointers from the object to the container and installing the composite menu. The object calls IOleInPlaceUIWindow::SetActiveObject to give the container's frame window a pointer to its IOleInPlaceActiveObject interface and its name. If the container has a separate document window and supports IOleInPlaceUIWindow, it also calls IOleInPlaceUIWindow::SetActiveObject.

To install the composite menu on the container's frame window, the object calls IOleInPlaceFrame::SetMenu. It is important to call the SetActiveObject methods before calling IOleInPlaceFrame::SetMenu; this is the only way that the container can ask OLE to provide F1 accelerator support, if desired.

In IOleInPlaceFrame::SetMenu, an SDI container application calls the function SetMenu while MDI containers send a WM_MDISETMENU message, using the handle to the composite menu. Containers next call OleSetMenuDescriptor to install the OLE dispatching code.

At this point, the object can call the SetFocus function to set the keyboard focus to the object's in-place editing window.


Deactivating the In-Place Object

Deactivation can be initiated by either the container or the active object. There are two stages to deactivation: UI deactivation and full deactivation. UI deactivation is a ready state that allows for quick reactivation should the user activate the object again or choose to undo changes made while the object was active in place. UI deactivation occurs when the user clicks outside the in-place window but in the client area of the container. The in-place object removes the composite menu bar and all adornments before removing the hatched shading. The new selection state determines the container's user interface. Full deactivation returns the object to its running state; the object can no longer undo changes.

When the object initiates deactivation, the following interaction occurs:

1.	The object calls IOleInPlaceSite::OnUIDeactivate to notify the container. This must be called before any other deactivation steps are taken, to allow the container to restore its menus and tools.

2.	The container calls IOleInPlaceFrame::SetMenu with NULL parameters to notify the container that it should remove the composite menu and restore the usual menu in its frame window. The menu is removed to minimize window repaints.

3.	The object calls OleDestroyMenuDescriptor to free the handle to the menu descriptor.

4.	The object removes its menus from the composite menu and calls IOleInPlaceFrame::RemoveMenus to ask the container to do the same. IOleInPlaceSite::OnUIDeactivate must be called before IOleInPlaceFrame::RemoveMenus is called.

5.	The container removes its menus by calling the function RemoveMenus.

6.	The object calls the function DestroyMenu.

When deactivating, the object should hold onto its undo state until the container calls IOleInPlaceObject::InPlaceDeactivate. Also, the object should not save changes to its storage; it should only write to storage when explicitly asked to do so.

When the deactivation is initiated by the container, first the container calls IOleInPlaceObject::UIDeactivate to ask the object to remove its user interface and then calls IOleInPlaceObject::InPlaceDeactivate to fully deactivate the object but leave it running. The object remains running until the container calls IOleObject::Close.

The object destroys or hides its adornments and its in-place window and calls IOleInPlaceSite::OnUIDeactivate to ask the container to reinstall its user interface. When the container returns, the object makes the following calls in the order listed:

1.	OleDestroyMenuDescriptor to free the menu descriptor handle.

2.	The function RemoveMenu to remove its menu groups.

3.	IOleInPlaceFrame::RemoveMenus to ask the container to remove its menus.

4.	DestroyMenu to free the composite menu.


Undoing Changes

If a container supports an Undo command, it should preserve its undo state when it activates an object in place. Note that activating an object in place is not considered an undoable action (just as selecting text is not undoable). When the user performs some undoable action during an in-place session, the object application should call IOleInPlaceSite::DiscardUndoState to notify the container that it can discard its undo state.

If the object application supports an Undo command, and if the end user's first action during an in-place session is to select the Undo command, then the object application should call IOleInPlaceSite::DeactivateAndUndo. The container ends the in-place session, and if the container also supports an Undo command, it reverts to its own saved undo state. For example, if the user inserted some text in a document, then activated an object in place, and then selected the object's Undo command, the container would deactivate the object and remove the inserted text.

When an in-place active object deactivates, it calls IOleInPlaceSite::OnUIDeactivate, which, among other things, tells the container whether the object has a saved undo state. If so, it is possible to activate the object in place and cause it to revert to its saved undo state. This can only be done if the object has a saved undo state and if the container supports an Undo command. Then, if the user selects Undo so that the container's next undoable action is to undo the last undoable action during the in-place session, the container can call IOleInPlaceObject::ReactivateAndUndo. This activates the object in place and tells the object to revert to its saved undo state.

After the in-place session, the container can tell the object to discard its saved undo state by passing OLEIVERB_DISCARDUNDOSTATE as the verb to IOleObject::DoVerb. The object's undo state is also discarded if the container calls IOleInPlaceObject::InPlaceDeactivate. The container can end the in-place session and still save the object's undo state, if it calls IOleInPlaceObject::UIDeactivate rather than IOleInPlaceObject::InPlaceDeactivate.


Object States

In-place active objects may have three or four possible states depending on the activation model in use. Most objects use the outside-in model, which requires the user to double-click or select a verb from the Edit menu to activate the object. The other model, called inside-out, activates an object with a single click.

Outside-in objects have three states: running, UI active and visible, and UI active and invisible. UI active implies that the object's user interface has been integrated into the container. It may or may not be visible. The figure below shows the state diagram for an outside-in object and the methods that cause the object to change from one state to another.

In-place state transition diagram for outside-in objects

Whereas outside-in objects hide and destroy their windows when their user interface is deactivated, inside-out objects do not. Inside-out objects have an additional state between the time the user interface is deactivated and the active in-place object is deactivated. In this state, the object's window is visible in place, but its adornments, hatch border, and tools are not visible. The next figure shows the state diagram for an inside-out object:

In-place state transition diagram for inside-out objects

Containers can treat all objects as outside-in, or they can support inside-out for some objects. Two miscellaneous status bits exist to support the inside-out style of activation. All inside-out objects must store OLEMISC_INSIDEOUT in the registry and return OLEMISC_INSIDEOUT from IOleObject::GetMiscStatus. OLEMISC_ACTIVATEWHENVISIBLE is typically registered and returned only by lightweight inside-out objects.

Containers that ignore the inside-out style need never look at these status bits. Containers that do support this style use the status bits to determine how to deal with inside-out. Containers can treat objects registered as OLEMISC_INSIDEOUT as either inside-out or outside-in, depending on whether or not the OLEMISC_ACTIVEWHENVISIBLE bit is set. If an object is not registered as OLEMISC_INSIDEOUT, however, it must be treated as outside-in.

Objects that support the inside-out style must add two predefined OLE verbs to their implementation of IOleObject::DoVerb. The OLEIVERB_UIACTIVATE verb indicates that the object should enter the UI active state; the OLEIVERB_INPLACEACTIVATE verb indicates that the object should be in-place active but not in-place visible. If the object cannot satisfy these requests, it should return an error from IOleObject::DoVerb.

Whenever an inside-out object becomes visible, the object must be activated in place (using OLEIVERB_INPLACEACTIVATE) so it can be single-clicked while in the container. All other objects can be double-clicked. Notice that the same object can behave differently in different containers but it must behave the same way in the same container all the time.

Z-Ordering and In-Place Active Objects

Many containers support overlapping embeddings. For overlapping embeddings to be useable, the container must be able to implement Z-ordering. That is, for any pair of overlapping embeddings, the container must control which embedding appears on top of the other embedding. This is parallel to the Z-ordering support in Windows. In Windows, there is a single Z-order for every set of sibling windows; the Z-order controls which sibling windows appear on top of which.

For inactive objects (that is, objects in the loaded or running states, which do not have a window associated with them), implementing Z-ordering is straightforward. The container does all rendering for the in-place objects, so it can control overlapping and clipping. The container can either render embeddings from back to front or it can set up clipping regions so the object's visualizations reflect the proper Z-order.

For objects in the in-place active or UI-active states, things are more complicated. The object has a window created in these states, and typically this window appears on top of any rendering the container (which provides a parent window) happens to do. For the container to properly manage the relative Z-order of its embeddings, those embeddings must behave in a predictable way when creating and managing their windows.

In particular, objects should follow these rules when creating and managing their window in the in-place active and UI-active states:

•	Inside-out objects should create their window at the top of the Z-order when in-place activated. Outside-in objects should do the same when UI activated. This is the default behavior of CreateWindow, so it should not mean extra work for the object.

•	Once the Z-order of the window has been created, objects should not manipulate it. In particular, inside-out objects should not attempt to bring their window to the front when they are UI activated. The container will do this on behalf of the object if it is appropriate. The decision about where the object's window exists in the Z-order is up to the container; otherwise, chaos would reign.

•	Containers will manipulate the inside-out object's window's Z-order dynamically. Objects should be written to expect this.

It is also important to note that all objects must create their windows using the WS_CLIPSIBLINGS style. This is true both for inside-out objects and outside-in objects. Objects that neglect this important step may break in any container that supports overlapping objects.

For more information about Z-ordering, see the Win32 SDK.


Providing Context-Sensitive Help

OLE 2 requires that containers and object applications capable of in-place editing provide at least minimal support for entering (Shift+F1) or exiting (ESC) context-sensitive help mode. During an in-place session, both the container and the object applications must be in the same help mode state at the same time. Even if your container or object does not provide context-sensitive help, the other application in an in-place session may. The ContextSensitiveHelp methods of the various in-place interfaces provide both applications with the means for coordinating this help mode state.

OLE 2 also requires that containers capable of in-place editing provide at least minimal support for using the F1 accelerator to get help for a selected menu item. The container can implement its own message filter to trap F1, in which case the filter must call the ContextSensitiveHelp methods of both the container's IOleInPlaceFrame interface and the object's IOleInPlaceActiveObject interface. As an alternative, the container can cause the OLE DLL to provide a message filter by passing non-NULL IOleInPlaceFrame and IOleInPlaceActiveObject pointers when it calls the OleSetMenuDescriptors function to set up the in-place shared menu.

See the OLE 2 technical note, CSHELP.DOC, for a description of the way ContextSensitiveHelp is implemented and used in the different interfaces.

Many applications use an accelerator key (usually SHIFT+F1) to allow the user to get help on a visible control or item. On receipt of this key, the application enters a modal state wherein the next mouse click sets the help topic. The active in-place object receives the accelerator and determines whether to enter help mode. The object calls IOleInPlaceSite::ContextSensitiveHelp to enter and exit help mode.

When the user clicks on something owned by the object, the object calls IOleInPlaceSite::ContextSensitiveHelp(TRUE) to enter help mode. When the user clicks on something owned by the container, the container calls IOleInPlaceObject::ContextSensitiveHelp(FALSE) to ask the object to exit help mode, allowing the container to provide the help.

There are two ways that a user can invoke context-sensitive help:

•	For help on any item, press SHIFT+F1 and click on the item.

•	For help on a menu item, press the F1 key when the menu item is selected.

When an object is active in-place, it shares the container's main menu and frame-level real estate. The keyboard focus is with the in-place window. When the user selects SHIFT+F1, either the frame or the in-place object can receive the keystrokes. When the frame receives the keystrokes, it calls IOleInPlaceUIWindow::ContextSensitiveHelp(TRUE) on each of its containing documents.

The following pseudo code for the various implementations of ContextSensitiveHelp shows how the state is propagated from the top-level container to all of the in-place objects so that they can take the appropriate action when they receive a mouse click or WM_COMMAND message.

// Frame window's private ContextSensitiveHelp. This is not the

// implementation of IOleInPlaceFrame:ContextSensitiveHelp that gets

// used when the F1 key is pressed during menu processing.

{

if (m_fCSHMode != fEnterMode) {

m_fCSHMode = fEnterMode;            // remember the state

if this application is an MDI application

call IOleInPlaceUIWindow::ContextSensitiveHelp(fEnterMode)

on all of the documents;

if this is an SDI application & an object is UI active,

call IOleInPlaceObject::ContextSensitiveHelp(fEnterMode)

}

}

// only if MDI application

IOleUIWindow::ContextSensitiveHelp(BOOL fEnterMode)	// Document window

{

if (m_fCSHMode != fEnterMode) {

m_fCSHMode = fEnterMode;

if (I have an object active in place)

call IOleInPlaceObject::ContextSensitiveHelp(fEnterMode);

}

}

IOleInPlaceObject::ContextSensitiveHelp(BOOL fEnterMode)

{

if (m_fCSHMode != fEnterMode) {

m_fCSHMode = fEnterMode;

if (I have an object active in place)

call IOleInPlaceObject::ContextSensitiveHelp(fEnterMode);

}

}

The following pseudo code shows how the state propagates in the other direction from the active in-place object (the one with keyboard focus) to all of the in-place objects in the container tree of which the frame window is the root. If the in-place object receives the SHIFT+F1 keystroke, it must recognize that it has entered help mode and it must call IOleInPlaceSite::ContextSensitveHelp (TRUE).

IOleInPlaceSite::ContextSensitiveHelp (BOOL fEnterMode)

{	if (m_fCSHMode != fEnterMode) {

m_fCSHMode = fEnterMode;

if (I have an in-place parent)

IOleInPlaceSite::ContextextSensitiveHelp(fEnterMode);

else {

if (my window is a doc window)	//Only if MDI

call my frame's private CSH method;

//not IOleInPlaceFrame::ContextSensitiveHelp(fEnterMode)

}

}

}

As shown in the preceding example, each in-place site calls, in turn, its in-place parent site recursively. The top-level container site is associated with the document only if it is an MDI application. In that case, it should call IOleInPlaceFrame::ContextSensitiveHelp, which in turn calls IOleInPlaceUIWindow::ContextSensitiveHelp for each of its documents. Otherwise, it is associated with the frame, and the propagation stops there.

Supporting context-sensitive help is optional. However, all applications that can activate an object in place must follow these simple guidelines:

•	Implement code for ContextSensitiveHelp in each of the relevant interfaces, as described in the preceding pseudo code examples.

•	While in context-sensitive help mode, if the container receives a mouse click, it can either ignore it or provide help after telling the other objects to exit help mode. The following pseudo code shows how to tell other objects to exit help mode:

{

m_fCSHMode = FALSE;

// call Down....

if (I have any objects in-place active in me)

IOleInPlaceObject::ContextSensitiveHelp(FALSE);

// call Up.....

call my IOleInPlaceSite::ContextSensitiveHelp(FALSE);

{

•	While in context-sensitive help mode, if the object receives a WM_COMMAND message or a mouse click, it should tell all the other objects to exit help mode. Then, if it can, the object should provide help for the command without executing it. The following pseudo code illustrates this process:

WM_COMMAND:

if (m_fMenuMode || m_fCSHMode)  {

if (m_fCSHMode) {

m_fMenuMode  = FALSE;

call parents ContextSensitiveHelp(FALSE), if I have any

call children's ContextSensitiveHelp(FALSE), if I have any	}

if (m_fMenuMode) {

m_fMenuMode = FALSE;

call IOleInPlaceFrame::ContextSensitiveHelp(FALSE);

//instead call IOleInPlaceActiveObject::ContextSensitiveHelp

//(FALSE) if this is the top-level container

}

call WinHelp if you provide help.

return;

}

do the normal thing.

Mouse click:

if (m_CSHMode) {

 if (I provide context-sensitive help) {

m_fMenuMode  = FALSE;

call parent's ContextSensitiveHelp(FALSE), if  I have any

call children's ContextSensitiveHelp(FALSE), if  I have any

} else {

ignore the click.

}

return;

}


Help for Menu Items

When an application enters menu-processing mode, the GetMessage and Dispatch calls process some messages and throw away others. This poses a problem when a menu item is selected and the user presses the F1 key. The F1 key message may be one of the discarded messages. To provide help for menu items, applications typically install their own message filters so that they can intercept the F1 key.

Recall that the object invokes IOleInPlaceFrame::SetMenu to have the container install the composite menu on the menu bar. The container in turn calls OleSetMenuDescriptor, at which time OLE subclasses the application's frame window so that it can properly dispatch menu commands. Usually the application determines whether or not to provide help on a menu item. However, if the application is a container for in-place active objects, it must do one of the following:

•	Add a keyboard message filter for intercepting the F1 key.

•	Ask the default handler to add a message filter by calling OleSetMenuDescriptor with valid, non-NULL values in the IOleInPlaceFrame and IOleInPlaceActiveObject pointer parameters.

The diagram shown below displays the message hierarchy for context-sensitive help. Box 1 must be present when an object is being activated in place and menu processing is in progress, whether or not the container supports F1-based context-sensitive help. Box 2 exists as long as menu processing continues. Box 3 exists only if the application is an in-place container with an in-place active object.

Message hierarchy for context-sensitive help.


Writing a Message Filter

The following code provides an example of how to write an application-specific message filter.

MessageFilterProc(int nCode, WPARAM wParam, LPARAM lParam)

{

LPMSG   lpMsg = (LPMSG) lParam;

// If it is not the F1 key then let the message (without

// modification) go to the next proc in the message filter chain.

if (lpMsg  &&  lpMsg->message == WM_KEYDOWN  &&

lpMsg->wParam == VK_F1) {

HMENU   hmenuPopUp;

// Container app cannot know what the current menu selection is

// if an object is getting in-place edited in it, because the

// menu bar is shared and the menu messages are intercepted and

// dispatched to the appropriate process by OLE's frame window

// sub-classing (see Box 3).  So, when the container sends the

// OM_POST_WM_COMMAND:

//

//	if (an object is getting in-place edited)

//	it will go to Box 3, and OLE posts WM_COMMAND to the

//	right process.

//	else

//	it will go to Box 4, which is app's own frame wnd

//	proc, and its OM_POST_WM_COMMAND code can post the

//	WM_COMMAND for the current selection (that the app

//	maintains).

//

// With this scheme the same message filter proc can be used

// whether the object is getting activated in place or not.

// NOTE 1: If the current selection is a popup menu,

// WM_COMMAND cannot be generated, so the code for

// OM_POST_WM_COMMAND returns the handle of the popup menu;

// otherwise, it returns NULL

// NOTE 2: Do  uOmPostWmCommand = RegisterWindowMessage

// ("OM_POST_WM_COMMND") at startup.

if (hmenuPopup = (HMENU) SendMessage(hwndFrame,

uOmPostWmCommand, 0, 0L)) {

// Now the applications have 4 options:

// 1. Give help for the popup menu and cancel the menu mode

//    as well as the CSH mode (WORD does this).

// 2. Change the cursor to question mark cursor

//	  (== SHIFT+F1), and do not disturb the menu state, and

//    enter the CSH mode.

// 3. Remove the CSH mode and cancel the menu mode.

// 4. Leave the menu state as it is and ignore the F1 key

//    (ie. do not pass the F1 key down the  msg filter

//    chain).

//

// OLE recommends option 4; if the container app wants OLE

// to install the message filter, OLE will. In this

// sample code, OLE is going for option 4.

return TRUE; 	// let the system know that we have handled

// the message

}

else {

// call the frame and active in-place object's

// ContextSensitiveHelp() methods.

lpFrame->ContextSensitiveHelp(TRUE);

// IOleInPlaceFrame.

lpInPlaceActiveObj->ContextSensitiveHelp(TRUE);

// IOleInPlaceActiveObject

// when either of these 2 objects receive the WM_COMMAND

// they will call the other one's ContextSensitiveHelp()

// method. Note that the tree walk does not happen if the

// CSH mode has been entered because of F1 on selected

// menu.

}

// Change message value to be WM_CANCELMODE and then call the

// next proc in the message filter chain. When windows

// USER's menu processing code sees this message it will bring

// down menu state and come out of its menu processing loop.

lpMsg->message = WM_CANCELMODE;

lpMsg->wParam   =  NULL;

lpMsg->lParam     =  NULL;

    	}

return CallNextHookEx (hMsgHook, nCode, wParam, lParam);

}

As shown in the following example, the code for IOleInPlaceFrame::ContextSensitiveHelp and IOleInPlaceActiveObject::ContextSensitiveHelp is as simple as remembering the flag passed in:

IOleInPlaceFrame::ContextSensitiveHelp(BOOL fEnterMode)

{

m_fMenuMode = fEnterMode;

}

IOleInPlaceActiveObject::ContextSensitiveHelp(BOOL fEnterMode)

{

m_fMenuMode = fEnterMode;

}

The following pseudo code shows what the application should do when it receives either a WM_COMMAND or mouse click.

WM_COMMAND:

if (m_fMenuMode || m_fCSHMode)  {

if (m_fCSHMode) {

m_fMenuMode  = FALSE;

call parents ContextSensitiveHelp(FALSE), if I have any

call children's ContextSensitiveHelp(FALSE), if I have any

if (m_fMenuMode) {

m_fMenuMode = FALSE;

call IOleInPlaceFrame::ContextSensitiveHelp(FALSE);

//instead call IOleInPlaceActiveObject::ContextSensitiveHelp

//(FALSE) if this is the top-level container

}

call WinHelp if you provide help.

return;

}

do the normal thing.

Mouse click:

if (m_CSHMode) {

 if (I provide context-sensitive help) {

m_fMenuMode  = FALSE;

call parent's ContextSensitiveHelp(FALSE), if  I have any

call children's ContextSensitiveHelp(FALSE), if  I have any

} else {

ignore the click.

}

return;

}


Dispatching Messages

During an in-place session, the object must always have the first opportunity to handle its accelerators. If the object determines that a particular message is not one of its accelerators, it must allow the container to handle the message if it is one of the container's accelerators. If the message is still unhandled, it is passed through the normal sequence of TranslateMessage and DispatchMessage. OLE will call IOleInPlaceFrame::TranslateAccelerator and return the result that the container returns. The container may call the TranslateAccelerator and/or TranslateMDISysAccel functions, or do its own processing. Note that the system has no state associated with the message, such as key state or extra message information, because the message has been transferred from the object to the container. Containers should therefore choose accelerators that can be encoded into the accelerator tables used when an object is active in place.

The way in which the message processing is implemented depends on whether the object application is a DLL or an EXE. If the object application is a DLL, the container's message queue receives all keyboard messages during an in-place session. Consequently, the container's message loop must call IOleInPlaceActiveObject::TranslateAccelerator before doing any other message processing. This allows the object to handle its accelerators. Only if this method does not return S_OK does the container proceed with its normal message processing.

If the object application is an EXE, the application's message queue receives all keyboard messages during an in-place session. The application's message loop first calls the TranslateAccelerator function to process its own accelerators. If TranslateAccelerator returns FALSE (indicating that it did not handle the message), the application should call IsAccelerator to determine whether the message does correspond to an accelerator in the object's accelerator table. This is necessary because it is possible for TranslateAccelerator to return FALSE even though the message is one of the object's accelerators. If this is the case, passing the message on to the container could result in inconsistent behavior if the message corresponds to one of the container's accelerators (i.e., the end user is expecting the object's accelerator and gets the container's instead). If both TranslateAccelerator and IsAccelerator return FALSE, the object application must call the OleTranslateAccelerator function to give the container a chance to process its accelerators; but if IsAccelerator returns TRUE, the object application should not call OleTranslateAccelerator. OleTranslateAccelerator determines whether the message corresponds to one of the container's accelerators, and if so, it calls the container's IOleInPlaceFrame::TranslateAccelerator method to translate the message. If OleTranslateAccelerator returns S_FALSE indicating that the container did not handle the message, the application's message loop should proceed with its usual message processing.

A container application should maintain a flag that it can check from within the message loop to determine whether there is currently an in-place active object. Similarly, an object application EXE should have a flag that tells whether it is in-place active. If the container typically processes messages from the queue based on message type, it should suspend this special processing while an object is active in place.

Re-entrancy problems can occur when calling SendMessage between the container and object application's process boundaries. To guard against possible problems, containers can call InSendMessage, a function that specifies whether the current window procedure is processing a message that was sent by SendMessage from another thread. The following interface methods are dispatched through SendMessage; these methods cannot contain calls to the Yield, Peek, or GetMessage functions, display a dialog box, or call any other interface method or function other than another method in the list.

IOleWindow::GetWindow

IOleInPlaceActiveObject::OnFrameWindowActivate
IOleInPlaceActiveObject::OnDocWindowActivate
IOleInPlaceActiveObject::ResizeBorder

IOleInPlaceUIWindow::GetBorder
IOleInPlaceUIWindow::RequestBorderSpace
IOleInPlaceUIWindow::SetBorderSpace

IOleInPlaceFrame::SetMenu
IOleInPlaceFrame::SetStatusText

IOleInPlaceObject::SetObjectRects


Miscellaneous Issues

This section covers a variety of issues that come up with the implementation of in-place activation.

Avoiding Excessive Repaints

Creating the Hatch Border

Uole_sing Status Lines

Displaying Dialog Boxes

Defining Keyboard Accelerators


Avoiding Excessive Repaints

Excessive repainting can occur in the following scenarios; the goal is to reach the final state with minimal repainting:

•	when the user is switching between two MDI documents, both of which have UI active objects.

•	when the user is switching from one MDI document that has a UI active object to another document that does not have an active object.

•	when the user is double-clicking an in-place object while there is another UI active object in the same document.

•	when nested in-place objects are activated.


Creating the Hatch Border

The active object should be surrounded by a black diagonal hatch border that indicates the active state and suggests the area of focus. The hatch border consists of right ascending diagonal lines with transparent pixels between the lines. The default size for the hatch border is defined to be four pixels:

#define INPLACE_DEFBORDERWIDTH 	4

This default can be overridden by adding an "OleInPlaceBorderWidth" entry to the WIN.INI file. Call GetProfileInt to retrieve the value as follows:

GetProfileInt("windows", "OleInplaceBorderWidth",
INPLACE_DEFBORDERWIDTH);

For more information on the user-interface recommendations for shading an object, see User Interface Guidelines.


Using Status Lines

Status lines give the user information about the application's status. For example, the user can see the effect of a command before choosing it in the status line. During in-place activation, the container owns the status line. This means that if the object wants to display status when the user is working with one of the object menus, an explicit call to IOleInPlaceFrame::SetStatusText must be made. Otherwise, the status line will always display container text. To avoid problems, the object should process the WM_MENUSELECT message and call IOleInPlaceFrame::SetStatusText — even if the object does not usually provide status information. If this is the case, the object can pass NULL to IOleInPlaceFrame::SetStatusText.


Displaying Dialog Boxes

It is possible for one application to display a modal dialog box at the same time that the other application is displaying a modeless dialog box during an in-place activation session. To get the right enable/disable behavior, the application that displays the modal dialog box passes FALSE to either IOleInPlaceFrame::EnableModeless, if it is a container, or IOleInPlaceActiveObject::EnableModeless, if it is an object application. When the dialog box is complete, another call is made with the parameter set to TRUE.

Since objects can be nested within other objects, it is possible for several levels of embedded objects to be active. However, only one object can use the container's main menu.

When an object is in-place active, its container should only respond to commands that affect the document as a whole, such as zooming, scrolling, or printing. It should not respond to commands that apply to or affect the selection because the selection is under the object's control.

Instead, to make sure that the window of the inside-out window is hidden on UI Deactivation, they call IOleObject::DoVerb(OLEIVERB_HIDE) after they call IOleInPlaceObject::UIDeactivate. The IOleObject::DoVerb(OLEIVERB_HIDE) call has no effect on outside-in objects, because they would have hidden their windows as part of UI deactivation.

More than one object can be in-place active with its window visible. In these cases, the container calls IOleInPlaceObject::SetObjectRects on all the objects when the document window size changes in addition to calling IOleInPlaceActiveObject::ResizeBorder.

An active in-place object is modal with respect to the document in which it is activated. The user cannot edit other objects or data in the document other than through the container commands available on the composite menu.


Defining Keyboard Accelerators

Keyboard accelerators generally provide faster, more direct access to commands than do menus. The following guidelines should be used when integrating accelerator keys with in-place active objects:

•	The container should not have alpha-numeric characters (with or without SHIFT) as accelerators.

•	The container should disable any accelerator keys (CTRL+, ALT+) that affect the selection. Accelerator keys that affect the whole document or, in rare cases, affect the object (but which won't disable the object), may remain active.

•	The arrow, INSERT, DELETE, and TAB keys should be disabled in the container since these directly affect the selection.

•	The PAGE UP and PAGE DOWN keys should remain enabled since these affect up and down movement within a page of the document.

•	The container can enable the HOME and END keys if they are used to scroll the document (to the top or bottom). These keys should be disabled if they relocate the selection (or insertion point) to the beginning or end of a structure (such as a line in a text container).

Here is the recommended way for containers to define their accelerator tables:

nCode, wID, VIRTKEY, [,NOINVERT] [,SHIFT] [,CONTROL] [,ALT]

Setting up an accelerator table in this way permits the table to work properly with object applications that do their own accelerator keystroke translations, and prevents keystrokes from being lost or sent to the wrong object.


Pseudo Code for In-Place Objects

This section supplies pseudo code for each of the interface methods that object applications will implement to support in-place activation:

*******************************************************************

IOleObject::DoVerb (LONG lVerb, ...) for activating in-place object

*******************************************************************

{

SCODE sc = S_OK	/* initialize status code

switch (lVerb) {

// Process unknown verbs first. For unknown positive numbered

// verbs, return OLEOBJ_S_INVALIDVERB and perform primary verb.

// For unknown negative numbered verbs, return E_NOTIMPL.

default:

if (lVerb < 0)

return ResultFromScode (E_NOTIMPL)

else

sc = OLEOBJ_S_INVALIDVERB

case 0:

case OLEIVERB_SHOW:

if already in open mode (visible),

show object in window

else

if (DoInPlaceActivate(lVerb) == error)

do normal activation.

break;

case 1:

case OLEIVERB_OPEN:

if another object is in-place active

deactivate it

show object window

break;

case OLEIVERB_HIDE:

if object in-place active

UIDeactivate

if object is INSIDE-OUT model

DoInPlaceHide();

else

Hide object window

break;

#ifdef INSIDEOUT	// only INSIDE-OUT model objects support this verb

case OLEIVERB_INPLACEACTIVATE:

#endif

case OLEIVERB_UIACTIVATE:

if already in open mode, cannot activate in-place

return ResultFromScode(OLE_E_NOT_INPLACEACTIVE

else

return DoInPlaceActivate(lVerb);

// Must not do normal activation if DoInPlaceActivate fails

break;

case OLEIVERB_DISCARDUNDOSTATE:

if already in-place active

return ResultFromScode(OLE_E_NOT_INPLACEACTIVE);

discard undo state if you have any;

break;

}

*******************************************************************

DoInPlaceActivate(LONG lVerb)

*******************************************************************

// In-place activation can only be done if ClientSite is non-NULL.

if client site pointer is NULL

return NOERROR;

if object is in open mode, do not in-place activate {

return NOERROR;

QueryInterface for IOleInPlaceSite pointer

if interface not available

return HRESULT from QueryInterface

Call IOleInPlaceSite::CanInPlaceActivate to see if container

supports in-place activation

If no support

return HRESULT from CanInPlaceActivate

Call IOleInPlaceSite::OnInPlaceActivate

return with HRESULT if HRESULT != NOERROR

m_fInPlaceActive = TRUE

if (! AllocInPlaceData()) {

Call IOleInPlaceSite::OnInPlaceDeactivate

return ResultFromScode(E_OUTOFMEMORY)

}

// set flag marking object visible as visible

m_fInPlaceVisible = TRUE

Call IOleInPlaceSite::GetWindow to get parent window

if no parent window or other error

return HRESULT from GetWindow

Call IOleInPlaceSite::GetWindowContext();

if error

return HRESULT from GetWindowContext

Hide object window

// Reparent in-place window to the hatch border window.

// Set in-place site's window as the parent of the hatch window.

// Important to parent and position the in-place windows BEFORE

// calling IOleInPlaceFrame::SetMenu and SetBorderSpace.

ShowHatchedWindow

SetParent(hWndHatched, hWndParent);

SetParent(hWndDoc, lhWndHatched);

save the current PosRect and ClipRect

// if verb is OLEIVERB_INPLACEACTIVATE do NOT want to show UI

// Clip the hatch window to the size of pos rect so, that

// hatch and object adornments  will not be visible.

if object is INSIDE-OUT model and lVerb == OLEIVERB_INPLACEACTIVATE 	ResizeInPlaceWindow()

return NOERROR;

if (! m_fUIActive) {

m_fUIActive = TRUE;

IOleInPlaceSite::OnUIActivate

SetFocus(hWndDoc);

// Show the object adornments and hatch border

ResizeInPlaceWindow()

IOleInPlaceFrame::SetActiveObject(pIOleInPlaceActiveObject...)

if Doc

Doc->SetActiveObject(pIOleInPlaceActiveObject...)

// Install menu and frame-level tools on in-place frame.

AddFrameLevelUI(lpServerDoc);

}

return NOERROR;

}

*******************************************************************

IOleInPlaceActiveObject::OnDocWindowActivate

*******************************************************************

{

if (activating )

AddFrameLevelUI

else

//do not call SetBorderSpace(NULL);

remove frame level tools (toolbars etc);

}

#ifdef INSIDEOUT

// An outside-in object does not have its window visible when in UI

// deactivated state and would not receive the mouse-click/activation.

// The inside-out object, after it receives mouse activation, must

// notify its inplace site that it is going to display its UI and then

// display the frame/document level tools, adornments, and hatch border.

// This gives its container a chance to UI deactivate, if there is

// another object that is UI active in it.

// This UI activation must be done on WM_LBUTTONUP.

// Add the following code to your WNDPROC.

{

static BOOL	fUIActivateOnLButtonUP = FALSE;

WM_MOUSEACTIVATE:

if (m_fInPlaceActive && !m_fUIActive)

m_fUIActivateOnLButtonUp = TRUE;

WM_LBUTTONUP:

if (m_fInPlaceActive && !m_fUIActive &&
m_fUIActivateOnLButtonUP) {

m_fUIActivateOnLButtonUp = FALSE;

DoInPlaceActivate(OLEIVERB_UIACTIVATE);

}

.... Do other stuff;

}

#endif

*******************************************************************

AssembleMenus()

*******************************************************************

{

HMENU	m_hmenuShared = NULL;

m_hmenuShared = CreateMenu();

if (frame->InsertMenus(hmenuShared, &menugroupwidths) == NOERROR)

{

Insert my menus...

update menugroupwidths structure.

} else {

DestroyMenu(hmenuShared);

m_hmenuShared = NULL;

}

// It is critical to create the "hOleMenu" and call

// IOleInPlaceFrame::SetMenu. This is the only way that the top-level

// container can let OLE subclass the frame window. The subclassing is

// necessary to deal with Alt-Tab, Alt-Esc, or user pulling down menus

// using menu-mnemonics when the in-place object has the focus.

m_hOleMenu= OleCreateMenuDescriptor(m_hmenuShared,&menugroupwidths);

}

*******************************************************************

AddFrameLevelUI()

*******************************************************************

{

// Build the shared menu

AssembleMenus

frame->SetMenu(m_hOleMenu, m_hmenuShared);

negotiate for space and show frame level tools

}

*******************************************************************

IOleObject::Close for deactivating in-place object

*******************************************************************

{

if (m_fInPlaceActive)

object->InPlaceDeactivate();

... Do other stuff

}

*******************************************************************

IOleInPlaceObject::InPlaceDeactivate

*******************************************************************

{

if (! m_fInPlaceActive)

return NOERROR;

m_fInPlaceActive = FALSE;

DeactivateUI()

DoInPlaceHide()

discard Undo state

inplacesite->OnInPlaceDeactivate()

}

*******************************************************************

DoInPlaceHide()

*******************************************************************

{

if (! m_fInPlaceVisible)

return NOERROR;

m_fInPlaceVisible = FALSE;

#ifdef CNTR_SVR

InPlaceDeactivateContainedObjects();

#endif

re-parent back to object app's window

frame->Release

if (doc) doc->Release

}

*******************************************************************

IOleInPlaceObject::UIDeactivate

*******************************************************************

{

DeactivateUI();

if object is OUTSIDE-IN model - hides its wnd at UIDeactivate time

DoInPlaceHide();

}

*******************************************************************

DeactivateUI()

*******************************************************************

{

if (! (m_fUIActive || m_lpUIActiveObject))

return NOERROR;

if (m_fUIActive) {

m_fUIActive = FALSE;

remove border shading and adornments

if (doc)   doc->SetActiveObject(NULL, NULL)

frame->SetActiveObject(NULL, NULL)

DisassembleMenus

hide frame level tools

}

#ifdef CNTR_SVR

else

UIDeactivateContainedObject();

#endif

inplacesite->OnUIDeactivate

}

*******************************************************************

DisassembleMenus()

*******************************************************************

{

OleDestroyMenuDescriptor(m_hOleMenu);

if (m_hmenuShared) {

Remove my menus

frame->RemoveMenus

destroy the composite menu;

}

}

#ifdef (CNTR_SVR || CNTR_ONLY)

// The next 2 routines get used in the in-place container's pseudo code.

*******************************************************************

UIDeactivateContainedObject()

*******************************************************************

{

if (m_lpUIActiveObject) {

m_fDeactivating = TRUE;

m_lpUIActiveObject->UIDeactivate();

#ifndef INSIDEOUT

m_lpUIActiveObject->DoVerb(OLEIVERB_HIDE)

#endif

m_lpUIActiveObject = NULL;

m_fDeactivating = FALSE;

}

}

*******************************************************************

InPlaceDeactivateContainedObjects()

*******************************************************************

{

m_fDeactivating = TRUE;

while (lpInPlaceObject = GetNextInPlaceObject())

lpInPlaceObject->InPalceDeactivate();

m_fDeactivating = FALSE;

}

#endif


Pseudo Code for In-Place Containers

This section supplies pseudo code for each of the interface methods that container applications will implement to support in-place activation. The following variables are declared with the document object:

BOOL	m_fAddMyUI;

BOOL	m_fUIActive;

BOOL	m_fMyToolsOnFrame;	// Needed only if you have frame-level tools

BOOL	m_fMyToolsOnDoc;	// Needed only if you have document-level tools

BOOL	m_fDeactivating;

HWND	m_hwnd;

LPOLEINPLACEFRAME	m_lpTopFrame;

LPOLEINPLACEUIWINDOW	m_lpTopDoc;

RECT 	nullRect = {0, 0, 0, 0};

// For combination container/object applications

HMENU	m_hSharedMenu;	// NULL if it is the top level container

HOLEMENU	m_hOleMenu; 	// NULL if it is the top level container

// For INSIDE-OUT objects

LPOLEINPLACEOBJECT	m_lpUIActiveObject;

// For a container-only application or the top-level container,

// m_lpTopFrame and m_lpTopDoc are pointers to the container's frame

// and document-level object pointers.

*******************************************************************

IOleInPlaceSite::OnInPlaceActivate()

*******************************************************************

{

QueryInterface() for IOleInPlaceObject pointer and remember it

}

*******************************************************************

IOleInPlaceSite::OnInPlaceDeactivate()

*******************************************************************

{

Release() the IOleInPlaceObject pointer that you remembered.

}

*******************************************************************

IOleInPlaceSite::OnUIActivate()

*******************************************************************

m_fUIActive = TRUE;

m_fAddMyUI = FALSE;

// INSIDE-OUT container must UIDeactivate the previous UIActive

// object when a new object is going UIActive. Since INSIDE-OUT

// windows have their own windows visible, it is possible that a

// click directly in another server window will cause it to UIActivate.

// OnUIActivate is the container's notification that this has occurred.

if InsideOutContainer {

if PreviousActiveObj != CurrentObj {

UIDeactivate PreviousActiveObj

// Make sure new UIActive window is on top of all others

SetWindowPos(hWndInPlaceActiveObj, HWND_TOPMOST, ....)

return NOERROR;

}

}

#ifdef   HAVEFRAMETOOLS

RemoveFrameLevelTools

#endif

#ifdef   HAVEDOCTOOLS

RemoveDocLevelTools

#endif

return NOERROR;

}

*******************************************************************

IOleInPlaceSite::OnUIDeactivate(BOOL fUndoAble)

*******************************************************************

{

m_fUIActive = FALSE;

m_fIpChangesUndoable = fUndoable;

m_hWndUIActiveObj = NULL;

// Look ahead to see if there is a DBCLK message sitting in the

// queue. If there is, it could result in in-place activating

// another object. Want to avoid multiple repainting.

if (! PeekMessage(&msg, pDoc->m_hWndDoc,

WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK,

PM_NOREMOVE | PM_NOYIELD)) {

// Generate QueryNewPalette only if you own the top level

// frame (ie. you are a top-level inplace container).

QueryNewPalette

#ifdef  (USE_DOCTOOLS)

AddDocLevelTools

#endif

#ifdef   (USE_FRAMETOOLS)

AddFrameLevelUI

#endif

} else

m_fAddMyUI = TRUE;

// Re-take focus

SetFocus

Redraw object and remove in-place active hatch

#if defined( UNDOSUPPORTED )

// Outside-in container that supports UNDO calls IOleObject::DoVerb

// (OLEIVERB_HIDE) to make in-place object go invisible.

if OUTSIDE-IN container or OUTSIDE-IN object {

if changes are undoable

IOleObject::DoVerb(OLEIVERB_HIDE,NULL,FALSE,FALSE)

else

IOleInPlaceObject->InPlaceDeactivate();

m_fIpVisible = FALSE;

m_hWndIpObject = NULL;

}

#else

// Inside-out container that does NOT support UNDO calls

// IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE) to tell the object

// to discard its undo state. It calls

// IOleInPlaceObject::InPlaceDeactivate to have the object

// take down its window.

if INSIDE-OUT container {

if INSIDE-OUT object and changes are undoable

IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE,....);

if OUTSIDE-IN object

IOleInPlaceObject::InPlaceDeactivate

// Outside-in container that does NOT support UNDO immediately

// tells the UIDeactivated object (UI removed) to

// IOleInPlaceObject::InPlaceDeactivate.

if OUTSIDE-IN container

IOleInPlaceObject::InPlaceDeactivate

#endif // ! UNDOSUPPORTED

return NOERROR;

}

*******************************************************************

IOleInPlaceFrame::SetMenu(hmenuShared, hOleMenu, hwndActiveObj)

*******************************************************************

// Top-level container's SetMenu method.

{

// Either put up the shared menu or container's normal menu

if (hOleMenu && hMenuShared)

hMenu = hMenuShared;

else

hMenu = myApp->m_hMenuApp;

// SDI apps put menu on frame by calling SetMenu. MDI apps would

// send WM_MDISETMENU message instead.

#ifdef SDI_APPLICATION

SetMenu (hwndFrame, hMenu);

#else

SendMessage(hwndFrame, WM_MDISETMENU, ...);

#endif

hrErr = OleSetMenuDescriptor (hOleMenu, hwndFrame,

hwndActiveObject, NULL, NULL);

return hrErr;

}

 #ifdef  HAVEFRAMETOOLS

*******************************************************************

MyDocClass::RemoveFrameLevelTools()

*******************************************************************

{

if (m_fMyToolsOnFrame)

m_fMyToolsOnFrame = FALSE;

Hide frame-level tools 	// Do not call SetMenu(NULL) or

// SetBorderSpace(NULL)

}

}

#endif

#ifdef  HAVEDOCTOOLS

*******************************************************************

MyDocClass::RemoveDocLevelTools()

*******************************************************************

{

if (lpMyDoc->m_lpTopDoc && m_fMyToolsOnDoc)  {

m_fToolsOnDoc = FALSE;

Hide my doc-level tools;   	// Don't call SetBorderSpace(NULL).

}

}

#endif

*******************************************************************

MyDocClass::AddFrameLevelUI()

*******************************************************************

//Adding a composite menu bar to the top-level container's frame window

{

#ifdef  CNTR_SVR

// Frame will not be your frame if you are an intermediate container

// in a nested chain.

lpTopFrame->SetMenu(m_hSharedMenu, m_hOleMenu, m_hwnd);

#else

lpTopFrame->SetMenu(NULL, NULL, NULL);

#endif

AddFrameLevelTools();

}

*******************************************************************

MyDocClass::AddFrameLevelTools()

*******************************************************************

//Adding toolbars and/or frame adornments to the frame-level window

{

#ifdef   HAVEFRAMETOOLS

if (!m_fMyToolsOnFrame) {

Negotiate with m_lpTopFrame for border space.

Place frame tools and make them visible.

 	m_fMyToolsOnFrame = TRUE;

}

#else

#ifdef  CNTR_SVR

if (I am not the top level container)

m_lpTopFrame->SetBorderSpace(NULL);

// Note: container must pass a NULL pointer

else

#endif

m_lpTopFrame->SetBorderSpace(&nullRect);

#endif

}

#ifdef  (CNTR_SVR  ||  MDI)

*******************************************************************

MyDocClass::AddDocLevelTools()

*******************************************************************

//Adding toolbars and/or adornments to the document-level window

{

// Document will not be your document if you are an intermediate

// container in a nested chain.

if (!m_lpTopDoc)

return;

#ifdef  HAVEDOCTOOLS

if (!m_fMyToolsOnDoc) {

Negotiate with m_lpTopDoc for border space.

Place doc tools  and make them visible.

m_fMyToolsOnDoc = TRUE;.

}

#else

#ifdef   CNTR_SVR

if (I am not the top level container)

m_lpTopDoc->SetBorderSpace(NULL);

// Note: container must pass NULL pointer

else

#endif

m_lpTopDoc->SetBorderSpace(&nullRect);

#endif

}

#endif

#ifdef  MDI

*******************************************************************

WM_MDIACTIVATE Message handler(BOOL fActivate)

*******************************************************************

// Top level container document's WM_MDIACTIVATE handler routine

{

if (lpInPlaceActiveObject in this document) {

if  (fActivate)

lpFrame->SetActiveObject(lpInPlaceActiveObject, NULL);

else

lpFrame->SetActiveObject(NULL, NULL);

lpInPlaceActiveObejct->OnDocWindowActivate(fActivate);

}

else if (fActivate)

lpMyDoc->AddFrameLevelUI();

}

#endif

*******************************************************************

IOleInPlaceFrame::SetBorderSpace(lpBorderWidths)

*******************************************************************

// Top-level container's frame's method

{

// Note: check for NULL pointer (not for rect with NULLs) is made

if (lpBorderWidths == NULL) {

lpActiveDoc = GetActiveDocument();

lpActiveDoc->AddFrameLevelTools();

}

 else {

validate widths

remember the widths, and size your window

}

}

#ifdef   MDI

*******************************************************************

IOleInPlaceUIWindow::SetBorderSpace(lpBorderWidths)

*******************************************************************

// Top-level container document's method

{

// Note: check for NULL pointer (not for rect with NULLs) is made

if (lpBorderWidths == NULL)

this->AddDocLevelTools();

else {

validate widths

remember the widths, and size your window

}

}

#endif


In-Place Data Structures

This section describes the data structures that support in-place activation.

OLEINPLACEFRAMEINFO

OLEMENUGROUPWIDTHS

BORDERWIDTHS


OLEINPLACEFRAMEINFO

The OLEINPLACEFRAMEINFO structure provides data needed by OLE to dispatch keystroke accelerators to a container (frame) while an object is active in place. The structure is defined as follows:

typedef struct tagOIFI	//OleInPlaceFrameInfo

{

UINT    cb;	//Size of OleInPlaceFrameInfo structure

//provided by the object application

BOOL	fMDIApp;	//TRUE if an MDI app; otherwise, FALSE

HWND	hwndFrame;	//Handle to the container's frame window

HACCEL	haccel;	//Handle to container's accelerator table

int	cAccelEntries;	//Number of entries in container's

//accelerator table

} OLEINPLACEFRAMEINFO, * LPOLEINPLACEFRAMEINFO;

Object applications must fill in the cb field with the correct size of the OLEINPLACEFRAMEINFO structure. If the container receives an OLEINPLACEFRAMEINFO structure with an uninitialized cb field, it should assume that the structure is defined for OLE 2. If the cb field is not the size that the container expects, it is assumed to be in the uninitialized state.

The hAccel member is a handle to the container-supplied accelerator table. The number of entries in the table is in cAccelEntries.

To have the container fill in the fields to the OLEINPLACEFRAMEINFO structure, the object application calls the container's IOleInPlaceSite::GetWindowContext method.


OLEMENUGROUPWIDTHS

The OLEMENUGROUPWIDTHS structure keeps track of the number of menus in each menu group. The structure is defined as follows:

typedef struct tagOleMenuGroupWidths {

LONG	width[6];

} OLEMENUGROUPWIDTHS,  * LPOLEMENUGROUPWIDTHS;

The container supplies values for elements 0, 2, and 4, and the object supplies values for elements 1, 3 and 5. To create the in-place composite menu, the object application first creates an empty menu and then passes the handle of the menu to the container in the call to the IOleInPlaceFrame::InsertMenus method. The container adds its menu groups to the composite menu and fills in the fields to the OLEMENUGROUPS structure, which is then passed back to the object application. The object application then adds its menu groups to the shared menu before creating a shared menu descriptor with OleCreateMenuDescriptor.

To activate the shared menu, the object application calls IOleInPlaceFrame::SetMenu, which in turn should call OleSetMenuDescriptor, so that OLE can install the dispatching code.

For more information on creating the composite menu, see Composite Menu Creation.

typedef HANDLE HOLEMENU;

HOLEMENU is a handle to a data structure that OLE uses to dispatch menu messages and commands to containers and objects as appropriate.


BORDERWIDTHS

The BORDERWIDTHS structure contains four integer widths for determining border space on the frame- and document-level windows for displaying toolbars and optional frame adornments.

typedef RECT    BORDERWIDTHS;

typedef LPRECT  LPBORDERWIDTHS;

typedef LPCRECT LPCBORDERWIDTHS;

Each member of the structure is the width in pixels being requested or set for the top, bottom, left, and right sides of the window, as shown in the following figure:

Negotiating space for frame adornments

To negotiate space, the object fills in the BORDERWIDTHS structure and passes it to the IOleInPlaceUIWindow::RequestBorderSpace and IOleInPlaceUIWindow::SetBorderSpace methods on the frame or document window, depending on which window the space is being requested for.


IOleWindow Interface

The IOleWindow interface is implemented and used by both container and object applications. The IOleWindow interface contains methods that allow an application to obtain the handle to the various windows participating in in-place activation and also to enter and exit context-sensitive help mode.

All other in-place activation interfaces are derived from the IOleWindow interface.

DECLARE_INTERFACE_(IOleWindow, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

};


IOleWindow::GetWindow

HRESULT IOleWindow::GetWindow(phwnd)
HWND * phwnd

IOleWindow::GetWindow returns the window handle to one of the various windows participating in the in-place activation (frame, document, parent, or in-place object window).

Parameter

phwnd

[out] Points to where to return the window handle.

Return Value

Value	Meaning
S_OK	The window handle was successfully returned.
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	There is no window handle currently attached to this object.
Example

The following IOleWindow::GetWindow example returns the handle to the window that currently contains the focus of the active object.

HRESULT InPlaceWindow_GetWindow(

        LPOLEINPLACEOBJECT  lpThis,

        HWND *              phwnd)

{

    LPSERVERDOC lpServerDoc =

            ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

    *phwnd = ((LPSERVERDOC) lpServerDoc)->m_hWndDoc;

    return S_OK;

}


IOleWindow::ContextSensitiveHelp

HRESULT IOleWindow::ContextSensitiveHelp(fEnterMode)
BOOL fEnterMode

IOleWindow::ContextSensitiveHelp determines whether context-sensitive help mode should be entered during an in-place activation session.

Parameter

fEnterMode

[in] Specifies TRUE if help mode should be entered; FALSE if it should be exited.

Return Value

Value	Meaning
S_OK	The help mode was entered or exited successfully, depending on the value passed in fEnterMode.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	The argument is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Applications can invoke context-sensitive help if the user:

•	presses SHIFT+F1, then clicks a topic

•	presses F1 when a menu item is selected.

When Shift+F1 is completed, either the frame or the active object can receive the keystrokes. If the container's frame receives the keystroke, it calls its containing document's IOleWindow::ContextSensitiveHelp with fEnterMode 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 keystroke, it calls the container's IOleInPlaceSite::ContextSensitiveHelp 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 (ContextSensitiveHelp(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 can intercept the F1 key or ask the OLE library to add a message filter by calling the OleSetMenuDescriptor function and passing valid, non-NULL values for the lpFrame and lpActiveObj parameters.

For more detailed information on implementing context-sensitive help, see Providing Context-Sensitive Help.

See Also

OleSetMenuDescriptor


IOleInPlaceObject Interface

The IOleInPlaceObject interface is implemented by object applications and is used by containers to activate and deactivate an in-place object. A pointer to the IOleInPlaceObject interface can be obtained by calling QueryInterface on the IOleObject interface.

The IOleInPlaceObject interface contains the following methods:

DECLARE_INTERFACE_(IOleInPlaceObject, IOleWindow)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

    // *** IOleInPlaceObject methods ***

  HRESULT InPlaceDeactivate (THIS);

  HRESULT UIDeactivate (THIS);

  HRESULT SetObjectRects (THIS_ LPCRECT lprcPosRect,

    LPCRECT lprcClipRect);

  HRESULT ReactivateAndUndo (THIS);

};


IOleInPlaceObject::InPlaceDeactivate

HRESULT IOleInPlaceObject::InPlaceDeactivate()

IOleInPlaceObject::InPlaceDeactivate deactivates an active in-place object and discards the object's undo state.

Return Value

Value	Meaning
S_OK	The object was successfully deactivated.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Before deactivating, the object application should give the container a chance to put its user interface back on the frame window.

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.

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. If the user interface has not already been destroyed when the container calls IOleObject::Close, the user interface 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 in-place active. If so, it should call InPlaceDeactivate.

Example

In the following IOleInPlaceObject::InPlaceDeactivate example, the object application checks to see whether the object is still active, in which case the object and its UI are hidden (but available for quick reactivation should the user again double-click the object). If the object is being closed, the in-place UI will be destroyed as will the in-place window hierarchy.

HRESULT InPlaceObj_InPlaceDeactivate(LPOLEINPLACEOBJECT lpThis)

{

    LPSERVERDOC lpServerDoc =

            ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

    HRESULT hrErr;

    hrErr = ServerDoc_DoInPlaceDeactivate(lpServerDoc);

    return hrErr;

}

HRESULT ServerDoc_DoInPlaceDeactivate(LPSERVERDOC lpServerDoc)

{

    LPINPLACEDATA  lpIPData = lpServerDoc->m_lpIPData;

    // object still active?

    if (!lpServerDoc->m_fInPlaceActive)

        return S_OK;

    lpServerDoc->m_fInPlaceActive = FALSE;

    SvrDoc_IPObj_UIDeactivate(

            (LPOLEINPLACEOBJECT)&lpServerDoc->m_OleInPlaceObject);

    /* OLE2NOTE: An inside-out style in-place object will

    **    NOT hide its window in IPObj::UIDeactive (an outside-in

    **    style object will hide its window in UIDeactivate).

    **    Thus, an inside-out object must explicitly hide

    **    its window in InPlaceDeactivate. It is ALSO important for an

    **    outside-in style object to call ServerDoc_DoInPlaceHide here

    **    BEFORE freeing the InPlaceData structure. It is common

    **    for in-place containers to call IOleInPlaceObject::

    **    InPlaceDeactivate in their IOleInPlaceSite::OnUIDeactiate

    **    implementation.

    */

    ServerDoc_DoInPlaceHide(lpServerDoc);

    lpIPData->lpSite->lpVtbl->OnInPlaceDeactivate(lpIPData->lpSite);

    OleStdRelease((LPUNKNOWN)lpIPData->lpSite);

    lpIPData->lpSite = NULL;

    ServerDoc_FreeInPlaceData(lpServerDoc);

    return NOERROR;

}

HRESULT ServerDoc_DoInPlaceHide(LPSERVERDOC lpServerDoc)

{

    LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;

LPDOC	        lpDoc = (LPDOC)lpServerDoc;

    LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;

HWND	hWndApp = OleApp_GetWindow(g_lpApp);

if (! lpServerDoc->m_fInPlaceVisible)

return NOERROR;

    // Set the parent back to object app's window

OleDoc_HideWindow((LPOLEDOC)lpServerDoc, FALSE /* fShutdown */);

    /* We need to ensure that our window is set to normal 100% zoom.

    **    If the window is next shown in open mode, it should start out

    **    at normal zoom factor. Our window might have been set to a

    **    different zoom factor while it was in-place active.

    */

    Doc_SetCurrentZoomCommand(lpDoc,IDM_V_ZOOM_100);

lpServerDoc->m_fInPlaceVisible = FALSE;

    lpServerDoc->m_hWndParent = hWndApp;

    SetParent(

        lpDoc->m_hWndDoc,

        lpServerDoc->m_hWndParent

    );

    // make sure App busy/blocked dialogs are parented to our

    // own hWndApp

    OleStdMsgFilter_SetParentWindow(lpOleApp->m_lpMsgFilter, hWndApp);

// Hide the in-place hatch border window.

ShowWindow(lpServerDoc->m_hWndHatch, SW_HIDE);

    ServerDoc_DisassembleMenus(lpServerDoc);

/* We no longer need the IOleInPlaceFrame* or the doc's

** IOleInPlaceWindow* interface pointers.

*/

    if (lpIPData->lpDoc) {

OleStdRelease((LPUNKNOWN)lpIPData->lpDoc);

        lpIPData->lpDoc = NULL;

    }

    if (lpIPData->lpFrame) {

        OleStdRelease((LPUNKNOWN)lpIPData->lpFrame);

        lpIPData->lpFrame = NULL;

    }

    ((LPSERVERAPP)g_lpApp)->m_lpIPData = NULL;

return NOERROR;

}

See Also

IOleInPlaceSite::OnInPlaceDeactivate, IOleObject::Close


IOleInPlaceObject::UIDeactivate

HRESULT IOleInPlaceObject::UIDeactivate()

IOleInPlaceObject::UIDeactivate deactivates and removes the user interface that supports in-place activation.

Return Value

Value	Meaning
S_OK	The in-place UI was deactivated and removed.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Resources such as menus and windows can be either cleaned up or kept around in a hidden state until IOleInPlaceObject::InPlaceDeactivate or IOleObject::Close is called to completely deactivate the object. The object application must call IOleInPlaceSite::OnUIDeactivate before doing anything with the composite menus so that the container is first detached from the frame window. On deactivating the in-place object's user interface, the object is left in a ready state for quick reactivation. 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.

If the container has called IOleInPlaceObject::UIDeactivate, it should later call the IOleInPlaceObject::InPlaceDeactivate method 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 at these points.

Example

In the following IOleInPlaceObject::UIDeactivate example for an inside-out object, the object application first notifies the document and frame windows that the object's UI is being deactivated. The object then removes its frame adornments and in-place tools and menus. Although the object's menus and tools are removed from the screen, the object does not call the container's IOleInPlaceUIWindow::SetBorderSpace or IOleInPlaceFrame::SetMenu if there is another object that is going to be activated in place.

For more information on handling UI deactivation, see Deactivating the In-Place Object.

HRESULT InPlaceObj_UIDeactivate(IOleInPlaceObject lpThis)

{

    LPSERVERDOC	lpServerDoc =

struct CDocOleObjectImpl *)lpThis)-

>lpServerDoc;

    LPSERVERAPP	lpServerApp = (LPSERVERAPP)g_lpApp;

LPCONTAINERDOC	lpContainerDoc = (LPCONTAINERDOC)lpServerDoc;

    LPINPLACEDATA	lpIPData = lpServerDoc->m_lpIPData;

    LPOBJLIST	lpObj = (LPOBJLIST)&((LPCONTAINERDOC)lpServerDoc)->

                     m_ObjList;

HWND	hWndApp = OleApp_GetWindow(g_lpApp);

if (!lpServerDoc->m_fUIActive)

        return NOERROR;

    lpServerDoc->m_fUIActive = FALSE;

// Clip the hatch window to the size of pos rect so that the object

// adornments and hatch border is not visible.

    ServerDoc_ResizeInPlaceWindow(lpServerDoc,

            (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect),

            (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect));

    // Notify document and frame that active object is going to be

    // UI deactivated

    if (lpIPData->lpDoc)

        lpIPData->lpDoc->lpVtbl->SetActiveObject(lpIPData->lpDoc, NULL, NULL);

    if (lpIPData->lpFrame) {

        lpIPData->lpFrame->lpVtbl->SetActiveObject(

            lpIPData->lpFrame,

            NULL,

            NULL);

    }

/* OLE2NOTE: The object must hide its frame tools here but NOT call

**    IOleInPlaceFrame::SetBorderSpace(NULL) or SetMenu(NULL).

**    The object must also hide its tools BEFORE calling

**    IOleInPlaceSite::OnUIDeactivate. The container then puts

**    its menus and tools back when OnUIDeactivate is called.

*/

    ServerDoc_RemoveFrameLevelTools(lpServerDoc);

    lpIPData->lpSite->lpVtbl->OnUIDeactivate(lpIPData->lpSite, FALSE);

/* Reset to use our normal application's accelerator table */

    g_lpApp->m_hAccelApp = lpServerApp->m_hAccelBaseApp;

    g_lpApp->m_hAccel = lpServerApp->m_hAccelBaseApp;

    g_lpApp->m_hWndAccelTarget = hWndApp;

#if !defined(SVR_INSIDEOUT)

    /* OLE2NOTE: An outside-in style in-place object would hide its

    **    window here. An inside-out style object leaves its window

    **    visible when it is UIDeactivated (it would only hide its

    **    window when InPlaceDeactivated). This application is an

    **    inside-out style object. It is recommended for most objects

    **    to support inside-out behavior, if possible.

    */

ServerDoc_DoInPlaceHide(lpServerDoc);

#endif

    return NOERROR;

}

void ServerDoc_RemoveFrameLevelTools(LPSERVERDOC lpServerDoc)

{

LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpServerDoc;

FrameTools_Enable(lpContainerDoc->m_lpFrameTools, FALSE);

}

HRESULT ServerDoc_DoInPlaceHide(LPSERVERDOC lpServerDoc)

{

    LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;

LPCONTAINERDOC	lpContainerDoc = (LPCONTAINERDOC)lpServerDoc;

    LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;

HWND	hWndApp = ContainerApp_GetWindow(g_lpApp);

if (! lpServerDoc->m_fInPlaceVisible)

return NOERROR;

    // Set the parent back to object app's window

OleDoc_HideWindow((LPOLEDOC)lpServerDoc, FALSE /* fShutdown */);

    /* We need to ensure that our window is set to normal 100% zoom.

    **    If the window is next shown in open mode, it should start out

    **    at normal zoom factor. Our window might have been set to a

    **    different zoom factor while it was in-place active.

    */

    ContainerDoc_SetCurrentZoomCommand(lpContainerDoc,IDM_V_ZOOM_100);

lpServerDoc->m_fInPlaceVisible = FALSE;

    lpServerDoc->m_hWndParent = hWndApp;

    SetParent(

        lpContainerDoc->m_hWndDoc,

        lpServerDoc->m_hWndParent);

    // make sure App busy/blocked dialogs are parented to our own hWndApp

    OleStdMsgFilter_SetParentWindow(lpOleApp->m_lpMsgFilter, hWndApp);

// Hide the in-place hatch border window.

ShowWindow(lpServerDoc->m_hWndHatch, SW_HIDE);

    ServerDoc_DisassembleMenus(lpServerDoc);

/* we no longer need the IOleInPlaceFrame* or the doc's

**    IOleInPlaceWindow* interface pointers.

*/

    if (lpIPData->lpDoc) {

OleStdRelease((LPUNKNOWN)lpIPData->lpDoc);

        lpIPData->lpDoc = NULL;

    }

    if (lpIPData->lpFrame) {

        OleStdRelease((LPUNKNOWN)lpIPData->lpFrame);

        lpIPData->lpFrame = NULL;

    }

    ((LPSERVERAPP)g_lpApp)->m_lpIPData = NULL;

return NOERROR;

}

See Also

IOleInPlaceObject::InPlaceDeactivate, IOleInPlaceSite::OnUIDeactivate, IOleInPlaceObject::ReactivateAndUndo, IOleObject::Close


IOleInPlaceObject::SetObjectRects

HRESULT IOleInPlaceObject::SetObjectRects(lprcPosRect, lprcClipRect)
LPCRECT lprcPosRect
LPCRECT lprcClipRect

IOleInPlaceObject::SetObjectRects indicates how much of the in-place object is visible.

Parameters

lprcPosRect

[in] Points to the rectangle containing the position of the in-place object in the client coordinates of its parent window.

lprcClipRect

[in] Points 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 Value

Value	Meaning
S_OK	Operation was successful.
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The container should call IOleInPlaceObject::SetObjectRects whenever the window position of the in-place object and/or visible part of the in-place object changes. 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 either scaling or zooming to the object. The object must then decide whether it should continue the in-place editing in the scale/zoom mode or deactivate.

It is possible for lprcClipRect to change without the 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, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within SetObjectRects. For more information, see Defining Keyboard Accelerators.

Example

The following example shows the object's IOleInPlaceObject::SetObjectRects method, in which the object resizes its in-place window. Because the object must size itself to the intersection of lprcPosRect and lprcClipRect, it saves the new values passed in. If the object is not UI active, the object clips itself to just the size of its content before hiding its hatch and frame adornments. The object then calls ServerDoc_ResizeInPlaceWindow to redraw the window. ResizeInPlaceWindow then calculates the space needed to display the object's frame adornments and then draws the object within the new lprcClipRect space.

HRESULT InPlaceObj_SetObjectRects(

        IOleInPlaceObject   lpThis,

        LPCRECT             lprcPosRect,

        LPCRECT             lprcClipRect)

{

    LPSERVERDOC  lpServerDoc =

                  ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

    LPINPLACEDATA lpIPData = lpServerDoc->m_lpIPData;

    LPOBJLIST lpObj = ContainerDoc_GetObjList((LPCONTAINERDOC)lpServerDoc);

// save the current PosRect and ClipRect

lpIPData->rcPosRect = *lprcPosRect;

lpIPData->rcClipRect = *lprcClipRect;

if (! lpServerDoc->m_fUIActive)	// object's hatch and adornaments

                                     // must not be drawn

lprcClipRect = lprcPosRect;

ServerDoc_ResizeInPlaceWindow(

lpServerDoc, (LPRECT)lprcPosRect, (LPRECT)lprcClipRect);

    return NOERROR;

}

/* ServerDoc_ResizeInPlaceWindow

** -----------------------------

**    Resize the in-place ServerDoc windows according to the

**    PosRect and ClipRect allowed by the in-place container.

**

**    OLE2NOTE: the PosRect rectangle that the in-place container tells

**    us is always the rectangle required for the object to display

**    itself. It does NOT include the space we require for object frame

**    adornments.

*/

void ServerDoc_ResizeInPlaceWindow(

LPSERVERDOC	lpServerDoc,

        LPCRECT             lprcPosRect,

        LPCRECT             lprcClipRect)

{

    LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpServerDoc;

    LPOBJLIST   lpObj = (LPOBJLIST)&lpServerDoc->m_ObjList;

SCALEFACTOR	 scale;

RECT	 rcDoc;

    POINT        ptOffset;

/* OLE2NOTE: Calculate the space needed for our object frame

**    adornments. Our in-place container tells us the size our

**    object should take in window client coordinates

**    (lprcPosRect). The rectangle corresponds to the size our

**    LineList ListBox should be. Our document window must the

**correct amount larger to accommodate our row/column headings.

**    Then move all windows into position.

*/

ServerDoc_CalcInPlaceWindowPos(

lpServerDoc,

(LPRECT)lprcPosRect,

(LPRECT)&rcDoc,

(LPSCALEFACTOR)&scale);

    /* OLE2NOTE: The object needs to honor the lprcClipRect specified by

    **    its in-place container. Object must NOT draw outside of the

    **    ClipRect.In order to achieve this, the object will size

    **    the hatch window to be exactly the size that should be visible

    **    (rcVisRect). The rcVisRect is defined as the intersection of

    **    the full size of the in-place object window and the

    **    lprcClipRect. The ClipRect could, in fact, clip the HatchRect

    **    on the right/bottom and/or on the top/left. If it is clipped

    **    on the right/bottom, it is sufficient to simply resize the

    **    hatch window. If the HatchRect is clipped on the top/left,

    **    the object must "move" the ServerDoc window (child of

    **    HatchWindow) by the delta that was clipped. The window origin

    **    of the ServerDoc window will then have negative coordinates

    **    relative to its parent HatchWindow.

    */

    SetHatchWindowSize(

            lpServerDoc->m_hWndHatch,

            (LPRECT)&rcDoc,

            (LPRECT)lprcClipRect,

            (LPPOINT)&ptOffset);

    // shift Document window to account for the hatch frame being drawn

OffsetRect((LPRECT)&rcDoc, ptOffset.x, ptOffset.y);

    // move/size/set scale factor of ServerDoc window.

ServerDoc_SetScaleFactor(

lpServerDoc, (LPSCALEFACTOR)&scale, (LPRECT)&rcDoc);

}

See Also

IOleInPlaceSite::OnPosRectChange


IOleInPlaceObject::ReactivateAndUndo

HRESULT IOleInPlaceObject::ReactivateAndUndo()

IOleInPlaceObject::ReactivateAndUndo reactivates a previously deactivated object, undoing the last state of the object.

Return Value

Value	Meaning
S_OK	The object was successfully reactivated.
E_NOTUNDOABLE	Called when the Undo state is not available.
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
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.

For more information on undoing changes, see Undoing Changes.


IOleInPlaceActiveObject Interface

The IOleInPlaceActiveObject interface is implemented by object applications to provide a direct channel of communication between the in-place object and the frame and document windows. The container uses the interface methods to manipulate an object while it is active in place.

The IOleInPlaceActiveObject interface contains the following methods:

DECLARE_INTERFACE_(IOleInPlaceActiveObject, IOleWindow)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

    // *** IOleInPlaceActiveObject methods ***

  HRESULT TranslateAccelerator (THIS_ LPMSG lpmsg);

  HRESULT OnFrameWindowActivate (THIS_ BOOL fActivate);

  HRESULT OnDocWindowActivate (THIS_ BOOL fActivate);

  HRESULT ResizeBorder (THIS_ LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow);

  HRESULT EnableModeless (THIS_ BOOL fEnable);

};


IOleInPlaceActiveObject::TranslateAccelerator

HRESULT IOleInPlaceActiveObject::TranslateAccelerator(lpmsg)
LPMSG lpmsg

IOleInPlaceActiveObject::TranslateAccelerator translates messages from the active object's message queue.

Parameter

lpmsg

[in] Points to the message that might need to be translated.

Return Value

Value	Meaning
S_OK	The message was translated successfully.
S_FALSE	The message was not translated.
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceActiveObject::TranslateAccelerator is called by the container's message loop when an embedded object is active in place. While active in place, an active object always has the first chance at translating the messages. Therefore, IOleInPlaceActiveObject::TranslateAccelerator should be called before any other translation. The container should apply its own translation only if this function returns S_FALSE.

The IOleInPlaceActiveObject::TranslateAccelerator function should only be invoked for an object created by a DLL object application. An object created by an EXE object application gets keystrokes from its own message pump, so the container does not get those messages.

If the container calls IOleInPlaceActiveObject::TranslateAccelerator for an object that is not created by a DLL object application, the default object handler returns S_FALSE.

An object application can call the Windows TranslateAccelerator function for its implementation of IOleInPlaceActiveObject::TranslateAccelerator.

Example

The following example shows a simple implementation of IOleInPlaceActiveObject::TranslateAccelerator for an EXE object application. Because the application is an EXE, all incoming accelerator messages are received first by the object. Therefore, there is no need to provide an implementation. (Had the object been implemented as a DLL, the messages would have been first received by the container which would have called OleTranslateAccelerator to send the message to the object.)

For more information on translating accelerator messages, see Defining Keyboard Accelerators.

HRESULT InPlaceActiveObj_TranslateAccelerator(

        IOleInPlaceActiveObject *   lpThis,

        LPMSG                       lpmsg)

{

    // This will never be called because this object application is

    // implemented as an EXE

    return NOERROR;

}

See Also

OleTranslateAccelerator


IOleInPlaceActiveObject::OnFrameWindowActivate

HRESULT IOleInPlaceActiveObject::OnFrameWindowActivate(fActivate)
BOOL fActivate

IOleInPlaceActiveObject::OnFrameWindowActivate notifies the object when the container's top-level frame window gains or loses activation.

Parameter

fActivate

[in] Indicates the state of the container's top-level frame window. It is TRUE if the window is activating; FALSE if it is deactivating.

Return Value

Value	Meaning
S_OK	The method completed successfully.
Remarks

IOleInPlaceActiveObject::OnFrameWindowActivate is called 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, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within OnFrameWindowActivate.

Example

In the following IOleInPlaceActiveObject::OnFrameWindowActivate example, the object application sends a PostMessage to itself to update its toolbar (because it cannot call any OLE functions to do this work while OnFrameWindowActivate is executing).

HRESULT InPlaceActiveObj_OnFrameWindowActivate(

        IOloeInPlaceActiveObject    lpThis,

        BOOL                        fActivate)

{

    LPSERVERDOC lpServerDoc = (LPSERVERDOC)

            ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

    //Get the window handle of the document.

    HWND hWndDoc = ServerDoc_GetWindow(lpServerDoc);

/* OLE2NOTE: This is a notification of the container application's

**    WM_ACTIVATEAPP status. Some applications might find this

**    important. We need to update the enable/disable status of our

    **    toolbar buttons.

*/

    // OLE2NOTE: We cannot call ServerDoc_UpdateFrameToolButtons

    //           until this call completes because that would

    //           generate some OLE calls (and eventually

    //           WM_ACTIVATEAPP), which would form a loop. Therefore, we

    //           delay the frame tool initialization until

    //	 WM_ACTIVATEAPP is finished by posting a message

    //	 to ourselves.

    /* Update enable/disable state of buttons in toolbar */

    if (fActivate)

        PostMessage(hWndDoc, WM_U_INITFRAMETOOLS, 0, 0L);

    return NOERROR;

}

HWND ServerDoc_GetWindow(LPSERVERDOC lpServerDoc)

{

    if(! lpServerDoc) return NULL;

    return lpServerDoc->m_hWndDoc;

}


IOleInPlaceActiveObject::OnDocWindowActivate

HRESULT IOleInPlaceActiveObject::OnDocWindowActivate(fActivate)
BOOL fActivate

IOleInPlaceActiveObject::OnDocWindowActivate notifies the active in-place object when the container's document window gains or loses activation.

Parameter

fActivate

[in] Indicates the state of the MDI child document window. It is TRUE if the window is activating; FALSE if it is deactivating.

Return Value

Value	Meaning
S_OK	The method completed successfully.
Remarks

IOleInPlaceActiveObject::OnDocWindowActivate is called when the MDI child document window is activated or deactivated and the object is the current active object for the document.

If activating, the object should install frame-level tools (including the shared composite menu and/or optional toolbars and frame adornments), and take focus. When deactivating, the object should remove the frame-level tools, but not call IOleInPlaceUIWindow::SetBorderSpace (NULL).

Note  While executing IOleInPlaceActiveObject::OnDocWindowActivate, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within OnDocWindowActivate. For more information, see Dispatching Messages.

Example

In the following example for IOleInPlaceActiveObject::OnDocWindowActivate, the object installs its frame-level tools and UI if it is active in place. If the object is not active in place, it simply hides the tools from the user.

For more information on implementing IOleInPlaceActiveObject::OnDocWindowActivate, see Pseudo Code for In-Place Objects.

HRESULT InPlaceActiveObj_OnDocWindowActivate(

        LPOLEINPLACEACTIVEOBJECT    lpThis,

        BOOL                        fActivate)

{

    LPSERVERDOC lpServerDoc =

              ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

    LPINPLACEDATA lpIPData = lpServerDoc->m_lpIPData;

    if (fActivate) {

        ServerDoc_AddFrameLevelUI(lpServerDoc);	//add tools

    }

    else {

        ServerDoc_RemoveFrameLevelTools(lpServerDoc); //remove tools

    }

    return NOERROR;

}


IOleInPlaceActiveObject::ResizeBorder

HRESULT IOleInPlaceActiveObject::ResizeBorder(prcBorder, pUIWindow, fFrameWindow)
LPCRECT prcBorder
IOleInPlaceUIWindow *pUIWindow
BOOL fFrameWindow

IOleInPlaceActiveObject::ResizeBorder is called to alert the object that it needs to resize its border space.

Parameters

prcBorder

[in] Points to a RECT structure containing the new outer rectangle within which the object can request border space for its tools.

pUIWindow

[in] Points to the frame or document window object whose border has changed.

fFrameWindow

[in] Specifies TRUE if the frame window object is calling ResizeBorder; otherwise, it is FALSE.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The IOleInPlaceActiveObject::ResizeBorder function 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 unaware of which window has changed (the frame- or document-level window), IOleInPlaceActiveObject::ResizeBorder needs to be passed the pointer to the window's IOleInPlaceUIWindow interface.

In most cases, the resize just requires the object to grow, shrink, or scale the frame adornments. However, for more complicated adornments, the object might need to renegotiate the border space with calls to IOleInPlaceUIWindow::RequestBorderSpace and IOleInPlaceUIWindow::SetBorderSpace. For more information on negotiating border space, see Installation of Frame Adornments.

Note  While executing IOleInPlaceActiveObject::ResizeBorder, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within ResizeBorder. For more information, see Dispatching Messages.

Example

In the following example, the top-level container calls ResizeBorder to tell the object that it needs to renegotiate its border space on the newly resized frame window. In its implementation of ResizeBorder, the object application calls FrameTools_NegotiateForSpaceAndShow to negotiate frame space for displaying the object's toolbars and/or frame adornments. Should the container not make any space available to the object, the object displays its tools as pop-ups.

HRESULT InPlaceActiveObj_ResizeBorder(

        IOleInPlaceActiveObject *   lpThis,

        LPCRECT                     lprectBorder,

        IOleInPlaceUIWindow *       pUIWindow,

        BOOL                        fFrameWindow)

{

    LPSERVERDOC lpServerDoc =

               ((struct CDocOleObjectImpl *)lpThis)->lpServerDoc;

LPDOC lpDoc = (LPDOC)lpServerDoc;

if (fFrameWindow) {

FrameTools_NegotiateForSpaceAndShow(

lpDoc->m_lpFrameTools,

(LPRECT)lprectBorder,

IOleInPlaceUIWindow pUIWindow);

}

    return NOERROR;

}

void FrameTools_NegotiateForSpaceAndShow(

        LPFRAMETOOLS            lpft,

        LPRECT                  lprcFrameRect,

        IOleInPlaceUIWindow     lpTopIPFrame)

{

    BORDERWIDTHS    borderwidths;

    RECT            rectBorder;

    HRESULT         hrErr;

    if (lprcFrameRect)

        rectBorder = *lprcFrameRect;

    else {

        /* OLE2NOTE: By calling GetBorder, the object can find out the

        **    size of the frame window. It can use this information to

        **    make decisions about how to orient/organize it tools

        **    (for example, if window is taller than wide put tools

        **    vertically at the left edge).

        */

        hrErr = lpTopIPFrame->lpVtbl->GetBorder(

                lpTopIPFrame,

                (LPRECT)&rectBorder);

    }

    /* Try SetBorderSpace() with the space you need. If it fails,

    ** you can negotiate for space and then do the SetBorderSpace().

    */

    FrameTools_GetRequiredBorderSpace(lpft,(LPBORDERWIDTHS)&borderwidths);

    hrErr = lpTopIPFrame->lpVtbl->SetBorderSpace(

            lpTopIPFrame,

            (LPCBORDERWIDTHS)&borderwidths);

    if (hrErr != NOERROR) {

        // Frame did not give the tools space that we want. So negotiate

        hrErr = lpTopIPFrame->lpVtbl->RequestBorderSpace(

                lpTopIPFrame,

                (LPCBORDERWIDTHS)&borderwidths);

        if (hrErr == NOERROR) {

            hrErr = lpTopIPFrame->lpVtbl->SetBorderSpace(

                    lpTopIPFrame,

                    (LPCBORDERWIDTHS)&borderwidths);

        }

    }

    if (hrErr == NOERROR) {

        FrameTools_Move(lpft, (LPRECT)&rectBorder); // we got what we wanted

    } else {

        /* We did not get tool space, so display them as pop up.

        /* OLE2NOTE: Because we are popping up our tools, we MUST inform

        **    the top-level frame window that we need NO tool space

        **    BUT that it should NOT put its own tools up. If we were

        **    to pass NULL instead of (0,0,0,0), the container

        **    would have the option to leave its own tools up.

        */

        hrErr = lpTopIPFrame->lpVtbl->SetBorderSpace(

                lpTopIPFrame,

                (LPCBORDERWIDTHS)&g_rectNull);

        FrameTools_PopupTools(lpft);

    }

}

See Also

IOleInPlaceUIWindow::GetBorder


IOleInPlaceActiveObject::EnableModeless

HRESULT IOleInPlaceActiveObject::EnableModeless(fEnable)
BOOL fEnable

IOleInPlaceActiveObject::EnableModeless is called to enable or disable modeless dialog boxes when the container creates or destroys a modal dialog box.

Parameter

fEnable

[in] Specifies TRUE to enable modeless dialog box windows; FALSE to disable them.

Return Value

Value	Meaning
S_OK	The method completed successfully.
Remarks

IOleInPlaceActiveObject::EnableModeless is called by the top-level container to enable and disable modeless dialogs that the object displays. To display a modal dialog box, the container first calls the IOleInPlaceActiveObject::EnableModeless method, specifying FALSE to disable the object's modeless dialog windows. After completion, the container calls IOleInPlaceActiveObject::EnableModeless (TRUE) to re-enable the object's modeless dialog boxes.

For more information on enabling and disabling the display of modal dialog boxes, see Displaying Dialog Boxes.

See Also

IOleInPlaceFrame::EnableModeless


IOleInPlaceUIWindow Interface

The IOleInPlaceUIWindow interface is implemented by container applications and used by object applications to negotiate border space on the document or frame window. The document window might not exist in all applications. If this is the case, NULL is returned for IOleInPlaceUIWindow by IOleInPlaceSite::GetWindowContext.

The IOleInPlaceUIWindow interface contains the following methods:

DECLARE_INTERFACE_(IOleInPlaceUIWindow, IOleWindow)

{

   // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

    // *** IOleInPlaceUIWindow methods ***

  HRESULT GetBorder (THIS_ LPRECT lprectBorder);

  HRESULT RequestBorderSpace (THIS_ LPCBORDERWIDTHS pborderwidths);

  HRESULT SetBorderSpace (THIS_ LPCBORDERWIDTHS pborderwidths);

  HRESULT SetActiveObject (THIS_ IOleInPlaceActiveObject *pActiveObject,

    LPCOLESTR pszObjName);

};


IOleInPlaceUIWindow::GetBorder

HRESULT IOleInPlaceUIWindow::GetBorder(lprectBorder)
LPRECT lprectBorder

IOleInPlaceUIWindow::GetBorder returns a RECT structure in which the object can put toolbars and similar controls while an object is active in place.

Parameter

lprectBorder

[out] Points to a RECT structure where the outer rectangle is to be returned. The RECT structure is relative to the window being represented by the interface.

Return Value

Value	Meaning
S_OK	The rectangle was successfully returned.
E_NOTOOLSPACE	The object cannot install toolbars in this window object
E_INVALIDARG	One or more invalid arguments.
E_OUTOFMEMORY	Out of memory.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within GetBorder. For more information, see Dispatching Messages.

Example

In the following example, the container's IOleInPlaceUIWindow::GetBorder implementation returns a RECT structure to the in-place object. Within the space of this returned RECT, the object can then negotiate space for the display of its toolbars and/or frame adornments.

HRESULT InPlaceUIWindow_GetBorder(

IOleInPlaceFrame *lpThis,

LPRECT lprectBorder)

{

CntrApp_GetFrameRect(g_lpApp, lprectBorder);

    return NOERROR;

}

void CntrApp_GetFrameRect(LPCONTAINERAPP lpContainerApp,

                             LPRECT lprcFrameRect)

{

    GetClientRect(lpContainerApp->m_hWndApp, lprcFrameRect);

    lprcFrameRect->bottom -= STATUS_HEIGHT;

}

See Also

IOleInPlaceUIWindow::RequestBorderSpace, IOleInPlaceUIWindow::SetBorderSpace


IOleInPlaceUIWindow::RequestBorderSpace

HRESULT IOleInPlaceUIWindow::RequestBorderSpace(pborderwidths)
LPCBORDERWIDTHS pborderwidths

IOleInPlaceUIWindow::RequestBorderSpace determines whether tools can be installed around the object's window frame while the object is active in place.

Parameter

pborderwidths

[in] Points to a BORDERWIDTHS structure containing the requested widths (in pixels) needed on each side of the window for the tools.

Return Value

Value	Meaning
S_OK	The requested space could be allocated to the object.
E_NOTOOLSPACE	The object cannot install toolbars in this window object, or there is insufficient space to install the toolbars.
E_INVALIDARG	One or more invalid arguments (for example, an invalid pointer or parameter).
E_UNEXPECTED	An unexpected error occurred.
Remarks

The 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

For more information on negotiating border space, see the description of the BORDERWIDTHS structure.

Note  While executing IOleInPlaceUIWindow::RequestBorderSpace, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within RequestBorderSpace. For more information, see Dispatching Messages.

Example

If the amount of space the active object uses for its toolbars is irrelevant to the container, it can simply return NOERROR as shown in the following IOleInPlaceUIWindow::RequestBorderSpace example. It is recommended that containers not unduly restrict the display of tools by the active in-place object. For more information on the user interface considerations related to in-place activation, see User Interface Guidelines.HRESULT InPlaceUIWindow_RequestBorderSpace(

    IOleInPlaceFrame *  lpThis,

    LPCBORDERWIDTHS     pborderwidths)

{

    /* Container allows the object to have as much border space as it

    ** wants.

    */

    return NOERROR;

}

See Also

IOleInPlaceUIWindow::GetBorder, IOleInPlaceUIWindow::SetBorderSpace


IOleInPlaceUIWindow::SetBorderSpace

HRESULT IOleInPlaceUIWindow::SetBorderSpace(pborderwidths)
LPCBORDERWIDTHS pborderwidths

IOleInPlaceUIWindow::SetBorderSpace allocates space for the border requested in the call to the IOleInPlaceUIWindow::RequestBorderSpace method.

Parameter

pborderwidths

[in] Points 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 Value

Value	Meaning
S_OK	The requested space has been allocated to the object.
OLE_E_INVALIDRECT	The rectangle does not lie within that returned by IOleInPlaceUIWindow::GetBorder.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The object must call the IOleInPlaceUIWindow::SetBorderSpace method. It can do any of the following:

1.	Use its own toolbars, requesting border space of a specific size; or,

2.	Use no toolbars, but insist that the container remove its toolbars by passing a valid BORDERWIDTHS structure containing nothing but zeros in the pborderwidths parameter; or,

3.	Use no toolbars but allow the in-place container to leave its toolbars up by passing NULL for the pborderwidths parameter.

The BORDERWIDTHS structure used in this call generally would have been passed in a previous call to IOleInPlaceUIWindow::RequestBorderSpace, which must have returned S_OK.

If an object needs to renegotiate space on the border, it can just 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, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within SetBorderSpace. For more information, see Dispatching Messages.

Example

In the following IOleInPlaceUIWindow::SetBorderSpace example, the container checks to see whether an active object requires any tool space. If not (pborderwidths is NULL), the container displays its own tools on the frame window. Otherwise, the container gives the active object the space needed for its tools.

HRESULT InPlaceUIWin_SetBorderSpace(

    IOleInPlaceFrame *  lpThis,

    LPCBORDERWIDTHS     pborderwidths)

{

    LPCONTAINERAPP lpContainerApp =

            ((struct COleInPlaceFrameImpl *)lpThis)->lpContainerApp;

if (pborderwidths == NULL) {

/* OLE2NOTE: IOleInPlaceSite::SetBorderSpace(NULL) is called

**    when the in-place active object does NOT want any tool

**    space. In this situation, the in-place container should

**    put up its tools.

*/

LPCONTAINERDOC lpContainerDoc;

lpContainerDoc

=(LPCONTAINERDOC)ContainerApp_GetActiveDoc(lpContainerApp);

ContainerDoc_AddFrameLevelTools(lpContainerDoc);

} else {

ContainerApp_SetBorderSpace(

(LPCONTAINERAPP) lpContainerApp,

(LPBORDERWIDTHS)pborderwidths);

}

    return NOERROR;

}

See Also

IOleInPlaceUIWindow::GetBorder, IOleInPlaceUIWindow::RequestBorderSpace


IOleInPlaceUIWindow::SetActiveObject

HRESULT IOleInPlaceUIWindow::SetActiveObject (pActiveObject, lpszObjName)
IOleInPlaceActiveObject *pActiveObject
LPCOLESTR pszObjName

IOleInPlaceUIWindow::SetActiveObject is called by the object to provide each of the frame and document windows a direct channel of communication with the active in-place object.

Parameters

pActiveObject

[in] Points to the active in-place object's IOleInPlaceActiveObject interface.

pszObjName

[in] Points to a string containing a name that describes the object an embedding container may use in composing its window title. It can be NULL if the object does not require the container to change its window titles. Normally, containers ignore this parameter and always their own name in the title bar.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

An object calls IOleInPlaceUIWindow::SetActiveObject 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 the processing of IOleInPlaceFrame::SetMenu method and to pass to the OleSetMenuDescriptor function.

The Microsoft Windows User Interface Design Guide recommends that an in-place container should ignore the pszObjName parameter passed in this method. The guide says that "The title bar is not affected by in-place activations. It always displays the top-level container's name." Generally then, an object should pass NULL for the pszObjName parameter. If the object is working in conjunction with a container that does display the name of the in-place active object in its title bar, the object should compose a string in the following form:

<application name> - <object short-type name>

Example

In the following IOleInPlaceUIWindow::SetActiveObject example, the container first checks to see whether there is an object already active in place. If there is, the container releases the current active object before it sets a pointer to the new in-place active object.

HRESULT InPlaceUIWindow_SetActiveObject(

    IOleInPlaceFrame *           lpThis,

    IOleInPlaceActiveObject *    lpActiveObject,

    LPCOLESTR                     pszObjName)

{

    LPCONTAINERAPP lpContainerApp =

            ((struct COleInPlaceFrameImpl *)lpThis)->lpContainerApp;

    if (lpContainerApp->m_lpIPActiveObj)

        lpContainerApp->m_lpIPActiveObj->lpVtbl->

        Release(lpContainerApp->m_lpIPActiveObj);

    if (lpContainerApp->m_lpIPActiveObj = lpActiveObject)

        lpContainerApp->m_lpIPActiveObj->lpVtbl->

        AddRef(lpContainerApp->m_lpIPActiveObj);

    return NOERROR;

}

See Also

IOleInPlaceFrame::SetMenu, OleSetMenuDescriptor


IOleInPlaceFrame Interface

The IOleInPlaceFrame interface is implemented by container applications and used by object applications to control the display and placement of the composite menu, keystroke accelerator translation, context-sensitive help mode, and modeless dialog boxes.

The IOleInPlaceFrame interface contains the following methods:

DECLARE_INTERFACE_(IOleInPlaceFrame, IOleInPlaceUIWindow)

{

   // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, void ** ppvObject);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

    // *** IOleInPlaceUIWindow methods ***

  HRESULT GetBorder (THIS_ LPRECT lprectBorder);

  HRESULT RequestBorderSpace (THIS_ LPCBORDERWIDTHS pborderwidths);

  HRESULT SetBorderSpace (THIS_ LPCBORDERWIDTHSlpborderwidths);

  HRESULT SetActiveObject (THIS_ IOleInPlaceActiveObject * pActiveObject,

    LPCOLESTR lpszObjName);

    // *** IOleInPlaceFrame methods ***

  HRESULT InsertMenus (THIS_ HMENU hmenuShared,

    LPOLEMENUGROUPWIDTHS lpMenuWidths);

  HRESULT SetMenu (THIS_ HMENU hmenuShared, HOLEMENU holemenu,

    HWND hwndActiveObject);

  HRESULT RemoveMenus (THIS_ HMENU hmenuShared);

  HRESULT SetStatusText (THIS_ LPCOLESTR pszStatusText);

  HRESULT EnableModeless (THIS_ BOOL fEnable);

  HRESULT TranslateAccelerator (THIS_ LPMSG lpmsg, WORD wID);

};


IOleInPlaceFrame::InsertMenus

HRESULT IOleInPlaceFrame::InsertMenus(hmenuShared, lpMenuWidths)
HMENU hmenuShared
LPOLEMENUGROUPWIDTHS lpMenuWidths

IOleInPlaceFrame::InsertMenus is called by the object application to allow the container to insert its menu groups in the composite menu that is to be used during the in-place session.

Parameters

hmenuShared

[in] Specifies a handle to an empty menu.

lpMenuWidths

[in, out] Points to an OLEMENUGROUPWIDTHS array of six LONG values. The container fills in elements 0, 2, and 4 to reflect the number of menus elements it provided in the File, View, and Window menu groups.

Return Value

Value	Meaning
S_OK	The menu groups were inserted successfully.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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 several times to build several composite menus. The container should reuse the same menu handles for the drop-downs and use the same handles as on the usual menu bar.

For more information on installing a composite menu for the in-place object, see Composite Menu Creation.

Example

In the following IOleInPlaceFrame::InsertMenus example, the container calls the Windows AppendMenu function to add its menus to the in-place menu passed in by the object through hMenu. After adding its menus, the container sets the group counts for these menus in OLEMENUGROUPWIDTHS.

HRESULT InPlaceFrame_InsertMenus(

    IOleInPlaceFrame *      lpThis,

    HMENU                   hMenu,

    LPOLEMENUGROUPWIDTHS    lpMenuWidths)

{

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;

BOOL	fNoError = TRUE;

    fNoError &= AppendMenu(hMenu, MF_POPUP, (UINT)lpContainerApp->m_hMenuFile,

"&File");

    fNoError &= AppendMenu(hMenu, MF_POPUP, (UINT)lpContainerApp->m_hMenuView,

"O&utline");

fNoError &= AppendMenu(hMenu, MF_POPUP,(UINT)lpContainerApp->m_hMenuDebug,

"DbgI&Cntr");

    lpMenuWidths->width[0] = 1;

    lpMenuWidths->width[2] = 1;

    lpMenuWidths->width[4] = 1;

return (fNoError ? NOERROR : ResultFromScode(E_FAIL));

}


IOleInPlaceFrame::SetMenu

HRESULT IOleInPlaceFrame::SetMenu(hmenuShared, holemenu, hwndActiveObject)
HMENU hmenuShared
HOLEMENU holemenu
HWND hwndActiveObject

IOleInPlaceFrame::SetMenu installs the composite menu into the window frame containing the object being activated in place.

Parameters

hmenuShared

[in] Specifies a handle to the composite menu constructed by calls to IOleInPlaceFrame::InsertMenus and the Windows InsertMenu function.

holemenu

[in] Specifies the handle to the menu descriptor returned by the OleCreateMenuDescriptor function.

hwndActiveObject

[in] Specifies a handle to a window owned by the object and to which menu messages, commands, and accelerators are to be sent.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

The object calls IOleInPlaceFrame::SetMenu to ask the container to install the composite menu. An SDI container 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(NULL) to remove the shared menu. This is done to help minimize window repaints. The container should also call OleSetMenuDescriptor (NULL) to unhook the dispatching code. Finally, the object application calls OleDestroyMenuDescriptor to free the data structure.

For more information on installing a composite menu for the in-place object, see Setting the Active Object and Installing the Composite Menu.

Note  While executing IOleInPlaceFrame::SetMenu, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within SetMenu. For more information, see Dispatching Messages.

Example

In the following IOleInPlaceFrame::SetMenu example, the container either puts up the shared in-place menu (passed in through hMenuShared) or its usual menu if the object specifies NULL for hMenu (usually in response to a IOleInPlaceSite::OnUIDeactivate call). The menu is set in place by calling the Windows SetMenu function. For more information on setting the menu, see the pseudo code examples for the container and the object.

HRESULT InPlaceFrame_SetMenu(

    IOleInPlaceFrame *  lpThis,

    HMENU               hMenuShared,

    HOLEMENU            hOleMenu,

    HWND                hwndActiveObject)

{

    LPOUTLINEAPP lpContainerApp = (LPOUTLINEAPP)g_lpApp;

    HMENU   hMenu;

HRESULT hrErr;

    if (hOleMenu && hMenuShared)

        hMenu = hMenuShared;

else

hMenu = lpContainerApp->m_hMenuApp;

/* OLE2NOTE: SDI apps put menu on frame by calling Windows SetMenu.

** MDI apps would send WM_MDISETMENU message instead.

*/

    SetMenu (lpContainerApp->m_hWndApp, hMenu);

    hrErr = OleSetMenuDescriptor (hOleMenu, lpContainerApp->m_hWndApp,

                    hwndActiveObject, NULL, NULL);

return hrErr;

}

See Also

IOleInPlaceFrame::InsertMenus, OleSetMenuDescriptor, OleDestroyMenuDescriptor


IOleInPlaceFrame::RemoveMenus

HRESULT IOleInPlaceFrame::RemoveMenus(hmenuShared)
HMENU hmenuShared

IOleInPlaceFrame::RemoveMenus is called by the object application to give the container a chance to remove its menu elements from the in-place composite menu.

Parameter

hmenuShared

[in] Specifies a handle to the in-place composite menu that was constructed by calls to IOleInPlaceFrame::InsertMenus and the Windows InsertMenu function.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
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.

For more information on removing the shared menu, see Deactivating the In-Place Object.

Example

In the following IOleInPlaceFrame::RemoveMenus example, the object asks the container to remove its menu items from the shared in-place menu as part of the normal tear down of an in-place session.

HRESULT InPlaceFrame_RemoveMenus(

    IOleInPlaceFrame *  lpThis,

    HMENU               hMenu)

{

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;

BOOL fNoError = TRUE;

    /* Remove container group menus */

while (GetMenuItemCount(hMenu))

fNoError &= RemoveMenu(hMenu, 0, MF_BYPOSITION);

    return (fNoError ? NOERROR : ResultFromScode(E_FAIL));

}

See Also

IOleInPlaceFrame::SetMenu


IOleInPlaceFrame::SetStatusText

HRESULT IOleInPlaceFrame::SetStatusText(pszStatusText)
LPCOLESTR pszStatusText

IOleInPlaceFrame::SetStatusText sets and displays status text about the in-place object in the container's frame window status line.

Parameter

pszStatusText

[in] Points to a null-terminated character string containing the message to display.

Return Value

Value	Meaning
S_OK	The text was displayed.
S_TRUNCATED	Some text was displayed but the message was too long.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	The text could not be displayed.
Remarks

The object calls IOleInPlaceFrame::SetStatusText to ask the frame to display object text in the frame's status line, if it has one. Because the status line is owned by the container's frame window, calling IOleInPlaceFrame::SetStatusText is the only way an object can display status information. If the container refuses the object's request, the object application can negotiate for border space to display its own status window.

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 selected 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.

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).

For more information on displaying status lines during an in-place session, see Using Status Lines.

Note  While executing IOleInPlaceFrame::SetStatusText, an application cannot call the Windows Yield, Peek, or GetMessage functions or display a dialog box. There are further restrictions on which OLE interface methods and functions can be called from within SetStatusText. For more information, see Dispatching Messages.

Example

In the following IOleInPlaceFrame::SetStatusText example, the container displays pszStatusText for the object in the frame's status line. Because pszStatusText is only guaranteed to be valid during the duration of this call, the container must hold a copy of pszStatusText if it will need to do anything with it later.

HRESULT InPlaceFrame_SetStatusText(

    IOleInPlaceFrame *     lpThis,

    LPCOLESTR              pszStatusText)

{

    LPCONTAINERAPP   lpContainerApp = (LPCONTAINERAPP)g_lpApp;

    //Save a copy of the string for future use

    if(lpContainerApp->m_szMessageHold)

         delete lpcontainerApp->m_szMessageHold;

    lpContainerApp->m_szMessageHold =

new OLECHAR[strlen(lpszStatusText) +1];

    strcpy(lpcontainerApp->szMessageHold, pszStatusText);

    ContainerApp_SetStatusText(lpContainerApp, (LPCOLESTR)pszStatusText);

    return ResultFromScode(S_OK);

}

void ContainerApp_SetStatusText(LPCONTAINERAPP lpContainerApp,

                                LPOLESTR pszMessage)

{

    SetStatusText(lpContainerApp->m_hWndStatusBar, pszMessage);

}


IOleInPlaceFrame::EnableModeless

HRESULT IOleInPlaceFrame::EnableModeless(fEnable)
BOOL fEnable

IOleInPlaceFrame::EnableModeless enables or disables a frame's modeless dialog boxes.

Parameter

fEnable

[in] Specifies whether the modeless dialog boxes are to be enabled (TRUE) or disabled (FALSE).

Return Value

Value	Meaning
S_OK	The dialog box was either enabled or disabled successfully, depending on the value for fEnable.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceFrame::EnableModeless is called by the active in-place object to manage enabling and disabling modeless dialog boxes that the container might be displaying. To display a modal dialog box, the object first calls the IOleInPlaceFrame::EnableModeless method, specifying FALSE to disable the container's modeless dialog box windows. After completion, the object calls IOleInPlaceFrame::EnableModeless (TRUE) to re-enable them.

For more information on displaying modal dialog boxes, see Displaying Dialog Boxes.

See Also

IOleInPlaceActiveObject::EnableModeless


IOleInPlaceFrame::TranslateAccelerator

HRESULT IOleInPlaceFrame::TranslateAccelerator(lpmsg, wID)
LPMSG lpmsg
WORD wID

IOleInPlaceFrame::TranslateAccelerator translates accelerator keystrokes intended for the container's frame while an object is active in place.

Parameters

lpmsg

[in] Points to an MSG structure containing the keystroke message.

wID

[in] Contains the command identifier value corresponding to the keystroke in the container-provided accelerator table. Containers should use this value instead of translating again.

Return Value

Value	Meaning
S_OK	The keystroke was used.
S_FALSE	The keystroke was not used.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceFrame::TranslateAccelerator is called indirectly by OleTranslateAccelerator when a keystroke accelerator intended for the container (frame) is received. 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.

When an object is implemented by a DLL object application, the object receives messages from the container's message queue. In this case, the container should always call IOleInPlaceActiveObject::TranslateAccelerator before doing its own accelerator translation, giving the in-place object the first chance at translating the message. Conversely, an executable object application should call OleTranslateAccelerator after calling TranslateAccelerator, calling TranslateMessage and DisplatchMessage only if both translation functions fail.

Note  So that they will work properly with object applications that do their own accelerator keystroke translations, accelerator tables for containers 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.

Example

In the following IOleInPlaceFrame::TranslateAccelerator example, the container calls the Windows TranslateAccelerator function to translate the accelerator message sent to it by the in-place object (by using an earlier call to OleTranlateAccelerator).

HRESULT InPlaceFrame_TranslateAccelerator(

    LPOLEINPLACEFRAME   lpThis,

    LPMSG               lpmsg,

    WORD                wID)

{

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;

    SCODE sc;

    if (TranslateAccelerator (lpContainerApp->m_hWndApp,

                              lpContainerApp->m_hAccelIPCntr, lpmsg))

        sc = S_OK;

#if defined(MDI_VERSION)

    else if (TranslateMDISysAccel(lpContainerApp->m_hWndMDIClient, lpmsg))

        sc = S_OK;

#endif

    else

sc = S_FALSE;

        return ResultFromScode(S_FALSE);

}

See Also

OleTranslateAccelerator


IOleInPlaceSite Interface

The IOleInPlaceSite interface is implemented by container applications and is used by object applications to interact with the object's in-place client site.

The IOleInPlaceSite interface contains the following methods:

DECLARE_INTERFACE_(IOleInPlaceSite, IOleWindow)

{

   // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IOleWindow methods ***

  HRESULT GetWindow (THIS_ HWND * phwnd);

  HRESULT ContextSensitiveHelp (THIS_ BOOL fEnterMode);

    // *** IOleInPlaceSite methods ***

  HRESULT CanInPlaceActivate (THIS);

  HRESULT OnInPlaceActivate (THIS);

  HRESULT OnUIActivate (THIS);

  HRESULT GetWindowContext (THIS_ IOleInPlaceFrame ** ppFrame,

    IOleInPlaceUIWindow ** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect,

    LPOLEINPLACEFRAMEINFO lpFrameInfo);

  HRESULT Scroll (THIS_ SIZE scrollExtent);

  HRESULT OnUIDeactivate (THIS_ BOOL fUndoable);

  HRESULT OnInPlaceDeactivate (THIS);

  HRESULT DiscardUndoState (THIS);

  HRESULT DeactivateAndUndo (THIS);

  HRESULT OnPosRectChange (THIS_ LPCRECT lprcPosRect);

};

The IOleInPlaceSite interface pointer is obtained by calling QueryInterface on the object's IOleClientSite interface.


IOleInPlaceSite::CanInPlaceActivate

HRESULT IOleInPlaceSite::CanInPlaceActivate()

IOleInPlaceSite::CanInPlaceActivate is called by the object to determine whether the container can activate the object in place.

Return Value

Value	Meaning
S_OK	The container allows in-place activation for this object.
S_FALSE	The container does not allow in-place activation for this object.
E_INVALIDARG	One or more invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceSite::CanInPlaceActivate is called by the client site's immediate child object when this object must to activate in place. This function allows the container application to accept or refuse the activation request.

Only objects being displayed as DVASPECT_CONTENT can be activated in place.

Example

In the following example for IOleInPlaceSite::CanInPlaceActivate, the container returns S_FALSE if the object requesting to be activated is in any format other than DVASPECT_CONTENT; otherwise, the container returns NOERROR.

HRESULT InPlaceSite_CanInPlaceActivate(IOleInPlaceSite lpThis)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    /* OLE2NOTE: The container canNOT allow in-place activation if it

    **    is currently displaying the object as an ICON (DVASPECT_ICON).

    **    It can ONLY do in-place activation if it is displaying the

    **    DVASPECT_CONTENT of the OLE object.

    */

    if (lpContainerObj->m_dwDrawAspect == DVASPECT_CONTENT)

        return NOERROR;

    else

        return ResultFromScode(S_FALSE);

}


IOleInPlaceSite::OnInPlaceActivate

HRESULT IOleInPlaceSite::OnInPlaceActivate()

IOleInPlaceSite::OnInPlaceActivate notifies the container that one of its objects is being activated in place.

Return Value

Value	Meaning
S_OK	The container allows the in-place activation.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceSite::OnInPlaceActivate is called by the client site's immediate child object when it is activated in-place for the first time. The container should note that the object is becoming active.

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's being left in an unstable state should a linking client update 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.

Example

In the following IOleInPlaceSite::OnInPlaceActivate example for an outside-in container, only one object can be in-place active at one time. If another object is in-place activated, the container calls ContainerDoc_ShutDownLastInPlaceObjectIfNotNeeded to close the previously activated object application if it is not needed.

Because the container supports links to embedded objects, it guards the in-place object from being prematurely closed should a link client need to do a silent update against the object while it is UI deactivated and in a hidden, unlocked state. To lock the object in a running state (thus guaranteeing that it is to be saved later), the container calls OleLockRunning on the active object.

HRESULT InPlaceSite_OnInPlaceActivate(IOleInPlaceSite lpThis)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    LPCONTAINERDOC lpContainerDoc = lpContainerObj->m_lpDoc;

SCODE sc = S_OK;

    if (lpContainerDoc->m_lpLastIpActiveObj) {

        ContainerDoc_ShutDownLastInPlaceObjectIfNotNeeded(

             lpContainerDoc, lpContainerObj);

    }

        lpContainerDoc->m_lpLastIpActiveObj = lpContainerObj;

/* OLE2NOTE: To avoid RPC problems, it is important to cache the

**    IOleInPlaceObject* pointer and NOT call QueryInterface

**    each time it is needed.

*/

    lpContainerObj->m_lpOleIPObj = (IOleInpaceObject)

           ContainerObj_GetOleObject(lpContainerObj,&IID_IOleInPlaceObject);

if (! lpContainerObj->m_lpOleIPObj) {

return ResultFromScode(E_FAIL);	// ERROR: refuse to in-place activate

    }

    lpContainerObj->m_fIpActive        = TRUE;

    lpContainerObj->m_fIpVisible       = TRUE;

    // Lock the object running to prevent problems if a link client does

    // a silent update when our in-place object is UI deactivated and

    // running in a hidden state.

    if (! lpContainerObj->m_fIpObjRunning) {

        lpContainerObj->m_fIpObjRunning = TRUE;

        OleLockRunning((LPUNKNOWN)lpContainerObj->m_lpOleIPObj, TRUE, 0);

    }

lpContainerObj->m_lpDoc->m_cIPActiveObjects++;

    return NOERROR;

}

void ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded(

        LPCONTAINERDOC          lpContainerDoc,

        LPCONTAINEROBJ         lpNextActiveObj)

{

    LPCONTAINEROBJ lpLastIpActiveObj =

                  lpContainerDoc->m_lpLastIpActiveObj;

    BOOL fEnableServerShutDown = TRUE;

    LPMONIKER lpmkLinkSrc;

    LPMONIKER lpmkLastActiveObj;

    LPMONIKER lpmkCommonPrefix;

    LPOLELINK lpOleLink;

    HRESULT hrErr;

    if (lpLastIpActiveObj != lpNextActiveObj) {

        if (lpLastIpActiveObj) {

            /* OLE2NOTE: If the object which is about to be activated is

            **    actually a link to the last activated object,

            **    we do NOT want to shut down the last activated

            **    object because it is about to be used. When activating

            **    a linked object, the source of the link gets .

            **    activated

            */

            lpOleLink = (LPOLELINK)ContainerObj_GetOleObject(

                    lpNextActiveObj,

                    &IID_IOleLink);

            if (lpOleLink) {

                lpOleLink->lpVtbl->GetSourceMoniker(

                        lpOleLink,

                        (LPMONIKER *)&lpmkLinkSrc);

                if (lpmkLinkSrc) {

                    lpmkLastActiveObj = ContainerObj_GetFullMoniker(

                            lpLastIpActiveObj,

                            GETMONIKER_ONLYIFTHERE);

                    if (lpmkLastActiveObj) {

                        hrErr = lpmkLinkSrc->lpVtbl->CommonPrefixWith(

                                lpmkLinkSrc,

                                lpmkLastActiveObj,

                                &lpmkCommonPrefix);

                        if (GetScode(hrErr) == MK_S_HIM ||

                                GetScode(hrErr) == MK_S_US) {

                            /* The link source IS to the object

                            **    contained in the last activated

                            **    object of the document. Disable the

                            **    attempt to shut down the last

                            **    running in-place object application.

                            */

                            fEnableServerShutDown = FALSE;

                        }

                        if (lpmkCommonPrefix)

                            OleStdRelease((LPUNKNOWN)lpmkCommonPrefix);

                        OleStdRelease((LPUNKNOWN)lpmkLastActiveObj);

                    }

                    OleStdRelease((LPUNKNOWN)lpmkLinkSrc);

                }

                OleStdRelease((LPUNKNOWN)lpOleLink);

            }

            /* If it is OK to shut down the previous in-place object

            **    and one is still running, shut it down. Shutting

            **    down the object application is done by forcing the OLE

            **    object to close. This forces the object to transition

            **    from running to loaded. If the object is actually

            **    only loaded, this is an NOP.

            */

            if (fEnableServerShutDown &&

                    lpLastIpActiveObj->m_fIpServerRunning) {

                ContainerObj_CloseOleObject(

                        lpLastIpActiveObj, OLECLOSE_SAVEIFDIRTY);

                // we can now forget this last in-place active object.

                lpContainerDoc->m_lpLastIpActiveObj = NULL;

            }

        }

    }

}


IOleInPlaceSite::OnUIActivate

HRESULT IOleInPlaceSite::OnUIActivate()

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 that of the in-place composite menu.

Return Value

Value	Meaning
S_OK	The container allows the in-place activation.
E_UNEXPECTED	An unexpected error occurred.
Remarks

IOleInPlaceSite::OnUIActivate is called by the in-place object just prior to activating its user interface. 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.

Example

The following IOleInPlaceSite::OnUIActivate code example is for an inside-out container. In OnUIActivate, the container is notified that another in-place object has become UI active. As an inside-out style container, the container must deactivate the current object's UI when a new object becomes UI active. (Because inside-out objects have their own windows visible, it is possible that a click directly in another object application window can cause it to become UI active. Therefore, the container must deactivate the other object's UI because at most only one object can have its UI active at a time.)

HRESULT InPlaceSite_OnUIActivate (IOleInPlaceSite lpThis)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    LPCONTAINERDOC lpContainerDoc = lpContainerObj->m_lpDoc;

    LPCONTAINEROBJ lpLastUIActiveObj = lpContainerDoc->m_lpLastUIActiveObj;

    HWND hWndNewUIActiveObj;

    lpContainerObj->m_fUIActive        = TRUE;

    lpContainerDoc->m_fAddMyUI          = FALSE;

    lpContainerDoc->m_lpLastUIActiveObj = lpContainerObj;

    lpContainerObj->m_lpOleIPObj->lpVtbl->GetWindow(

            lpContainerObj->m_lpOleIPObj,

            (HWND *)&hWndNewUIActiveObj);

    //UI deactivate the object

    if (lpLastUIActiveObj && (lpLastUIActiveObj!=lpContainerObj)) {

       ContainerObj_UIDeactivate(lpLastUIActiveObj);

       // Make sure new UIActive window is on top of all others

       SetWindowPos(

          hWndNewUIActiveObj,

          HWND_TOPMOST,

          0,0,0,0,

          SWP_NOMOVE | SWP_NOSIZE);

       return NOERROR;

    }

    lpContainerDoc->m_hwndUIActiveObj = hWndNewUIActiveObj;

ContainerDoc_RemoveFrameLevelTools(lpContainerDoc);

    return NOERROR;

}

See Also

IOleInPlaceObject::UIDeactivate


IOleInPlaceSite::GetWindowContext

HRESULT IOleInPlaceSite::GetWindowContext(ppFrame, ppDoc, lprcPosRect, lprcClipRect,
  lpFrameInfo)
IOleInPlaceFrame **ppFrame
IOleInPlaceUIWindow **ppDoc
LPRECT lprcPosRect
LPRECT lprcClipRect
LPOLEINPLACEFRAMEINFO lpFrameInfo

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.

Parameters

ppFrame

[out] Points to where the pointer to the frame interface is to be returned. If an error is returned, this parameter must be set to NULL.

ppDoc

[out] Points to where the pointer to the document window interface is to be returned. NULL is returned through ppDoc if the document window is the same as the frame window. In this case, the object can only use ppFrame or border negotiation. If an error is returned, this parameter must be set to NULL.

lprcPosRect

[out] Points 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] Points 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] Points to an OLEINPLACEFRAMEINFO structure that the container is to fill in with appropriate data. If an error is returned, this parameter must be set to NULL.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	One or more invalid arguments (for example, an invalid pointer or parameter).
E_UNEXPECTED	An unexpected error occurred.
Remarks

The OLEINPLACEFRAMEINFO structure provides data needed by OLE to dispatch keystroke accelerators to a container frame while an object is active in place. For more information on this structure, see In-Place Data Structures. For more information on dispatching keystrokes, see Dispatching Messages.

When an object is activated, it calls the GetWindowContext method from its container. The container returns the handle to its in-place accelerator table through the OLEINPLACEFRAMEINFO structure. Before calling GetWindowContext, the object must fill in the lpFrameInfo->cb member.

Example

The following example shows the container's IOleInPlaceSite::GetWindowContext method. The example is written for an SDI container application. When the object calls GetWindowContext, it passes in pointers to its PosRect and ClipRect, which the container uses to calculate the object's position in the window hierarchy. The container returns a pointer to its implementation of the OLEINPLACEFRAMEINFO structure and NULL for ppDOC, indicating that its document window is the same as its frame window.

HRESULT InPlaceSite_GetWindowContext(

    IOleInPlaceSite *            lpThis,

    IOleInPlaceFrame *          ppFrame,

    IOleInplaceUIWindow *       ppDoc,

    LPRECT                      lprcPosRect,

    LPRECT                      lprcClipRect,

    LPOLEINPLACEFRAMEINFO       lpFrameInfo)

{

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    //Get pointer to OLEINPLACEFRAME structure

   *lplpFrame = (LPOLEINPLACEFRAME)&lpContainerApp->m_OleInPlaceFrame;

    // must return AddRef'ed ptr

    (*lplpFrame)->lpVtbl->AddRef(*ppFrame);

    *ppDoc  = NULL;  // An MDI app would have to provide *lplpDoc

    // Get the extent of the object in client coordinates(after scaling)

    ContainerObj_GetOleObjectRectInPixels(lpContainerObj, lprcPosRect);

    //Get the ClipRect in client coordinates

    ContainerDoc_GetClipRect(lpContainerObj->m_lpDoc, lprcClipRect);

    lpFrameInfo->hwndFrame      = lpContainerApp->m_hWndApp;

    lpFrameInfo->haccel         = lpContainerApp->m_hAccelIPCntr;

    lpFrameInfo->cAccelEntries  = NUM_INPLACE_ACCELS;

    return NOERROR;

}


IOleInPlaceSite::Scroll

HRESULT IOleInPlaceSite::Scroll(scrollExtent)
SIZE scrollExtent

IOleInPlaceSite::Scroll is called by an in-place object to tell the container to scroll the object by the number of pixels specified by scrollExtent.

Parameter

scrollExtent

[in] Contains the number of pixels by which to scroll in the X and Y directions. Positive X values mean to move X pixels to the left (scroll to the right).

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	An invalid argument.
E_UNEXPECTED	An unexpected error occurred.
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 Also

IOleInPlaceObject::SetObjectRects


IOleInPlaceSite::OnUIDeactivate

HRESULT IOleInPlaceSite::OnUIDeactivate(fUndoable)
BOOL fUndoable

On deactivation, IOleInPlaceSite::OnUIDeactivate notifies the container that it should reinstall its user interface and take focus.

Parameter

fUndoable

[in] Specifies whether the object can undo changes. It is TRUE if the object can undo, FALSE if it cannot.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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.

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.

Example

The following IOleInPlaceSite::OnUIDeactivate example was written to support an outside-in container that supports Undo state. When called, the container first checks to see whether there is a DBLCLK message waiting in its message queue. If there is, the container calls ContainerDoc_AddFrameLevelUI to put its normal tools and menu back. Otherwise, the container sets the fAddMyUI flag to TRUE to indicate that it needs to restore its tools later. (If another object is being activated, the container clears fAddMyUI in its implementation of IOleInPlaceSite::OnUIActivate.)

Before handling any undo state, the container takes focus of the frame window and forces a redraw of the object to remove the in-place hatched border. If the object has an undo state that needs to be saved, the container calls IOleObject::DoVerb(OLEIVERB_HIDE) to hide the running object from the user. Otherwise, the container calls IOleInPlaceObject::InPlaceDeactivate to release the object.

HRESULT InPlaceSite_OnUIDeactivate(

    IOleInPlaceSite    lpThis,

    BOOL                fUndoable)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    LPCONTAINERDOC lpcontainerDoc = (LPCONTAINERDOC)lpContainerObj->m_lpDoc;

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP) g_lpApp;

    LPOBJLIST lpObj;

    int nIndex;

MSG msg;

    HRESULT hrErr;

    lpContainerObj->m_fUIActive = FALSE;

    lpContainerObj->m_fIpChangesUndoable = fUndoable;

    lpContainerObj->m_lpDoc->m_hwndUIActiveObj = NULL;

if (lpContainerObj == lpContainerObj->m_lpDoc->m_lpLastUIActiveObj) {

        lpContainerObj->m_lpDoc->m_lpLastUIActiveObj = NULL;

/* See if there is a DBLCLK in our message queue.

        **    NOTE: MDI containers and nested-level container/servers

**    may NOT do this menu optimization. The nested-level

**    containers MUST ALWAYS directly put back their menus.

**    MDI containers MUST put back their menus if the MDI child

 	**    window is maximized. This is due to the fact that the MDI

 	**    child window system menu has been added to the menu bar.

*/

if (! PeekMessage(&msg, lpContainerDoc->m_hWndDoc,

WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK,

PM_NOREMOVE | PM_NOYIELD)) {

ContainerDoc_AddFrameLevelUI(lpContainerObj->m_lpDoc);

} else {

lpContainerObj->m_lpDoc->m_fAddMyUI = TRUE;

}

/* OLE2NOTE: The in-place object application window previously had

** the focus. Because this window has just been removed, we should

** take focus.

*/

SetFocus(ContainerDoc_GetWindow(

                 (LPCONTAINERDOC)lpContainerObj->m_lpDoc));

// force the object to redraw to remove in-place active hatch

lpObj = ContainerDoc_GetObject(lpContainerDoc);

ForceObjectRedraw(lpLL, nIndex, TRUE);

}

    /* OLE2NOTE: An outside-in style container that supports UNDO

    **    calls IOleObject::DoVerb(OLEIVERB_HIDE) to make the in-place

    **    object go invisible. When it wants the in-place active object

    **    to discard its undo state, it calls

    **    IOleInPlaceObject::InPlaceDeactivate. There is no need for an

    **    outside-in style container to call

    **    IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE). If either

    **    the container or the object do not support UNDO, the

    **    container can immediately call InPlaceDeactivate

    **    instead of calling DoVerb(HIDE).

    */

    if (lpContainerObj->m_fIpChangesUndoable) {

        ContainerObj_DoVerb(lpContainerbj,OLEIVERB_HIDE,NULL,FALSE,FALSE);

        return NOERROR;

    } else {

            return ResultFromScode(lpContainerObj->m_lpOleIPObj->

                    lpVtbl->InPlaceDeactivate(lpContainerObj->m_lpOleIPObj));

      }

}

void ContainerDoc_AddFrameLevelUI(LPCONTAINERDOC lpContainerDoc)

{

    LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpContainerDoc;

    LPOLEINPLACEFRAME lpTopIPFrame = ContainerDoc_GetTopInPlaceFrame(

            lpContainerDoc);

    HMENU           hSharedMenu;            // combined obj/cntr menu

    HOLEMENU        hOleMenu;               // returned by OleCreateMenuDesc.

    ContainerDoc_GetSharedMenuHandles(

            lpContainerDoc,

            &hSharedMenu,

            &hOleMenu);

    lpTopIPFrame->lpVtbl->SetMenu(

            lpTopIPFrame,

            hSharedMenu,

            hOleMenu,

            lpContainerDoc->m_hWndDoc);

    /* OLE2NOTE: Even if our application does NOT use FrameTools, we

** must still call IOleInPlaceFrame::SetBorderSpace.

    */

    ContainerDoc_AddFrameLevelTools(lpContainerDoc);

}

void ContainerDoc_AddFrameLevelTools(LPCONTAINERDOC lpContainerDoc)

{

    LPOLEINPLACEFRAME lpTopIPFrame = ContainerDoc_GetTopInPlaceFrame(

            lpContainerDoc);

    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;

    LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpContainerDoc;

    FrameTools_Enable(lpContainerDoc->m_lpFrameTools, TRUE);

    FrameTools_NegotiateForSpaceAndShow(

            lpContainerDoc->m_lpFrameTools,

            NULL,

            lpTopIPFrame);

}

void FrameTools_Enable(LPFRAMETOOLS lpft, BOOL fEnable)

{

    lpft->m_fToolsDisabled = !fEnable;

    if (lpft->m_fToolsDisabled) {

        ShowWindow(lpft->m_hWndPopupPalette, SW_HIDE);

        ShowWindow(lpft->m_ButtonBar.m_hWnd, SW_HIDE);

        ShowWindow(lpft->m_FormulaBar.m_hWnd, SW_HIDE);

    }

}

void FrameTools_NegotiateForSpaceAndShow(

        LPFRAMETOOLS            lpft,

        LPRECT                  lprcFrameRect,

        LPOLEINPLACEFRAME       lpTopIPFrame)

{

    BORDERWIDTHS    borderwidths;

    RECT            rectBorder;

    HRESULT         hrErr;

    if (lprcFrameRect)

        rectBorder = *lprcFrameRect;

    else {

        /* Object can call GetBorder to find out the

        **    size of the frame window. it can use this information to

        **    make decisions about how to orient/organize it tools (eg.

        **    if window is taller than wide put tools vertically at

        **    left edge).

        */

        hrErr = lpTopIPFrame->lpVtbl->GetBorder(

                lpTopIPFrame,

                (LPRECT)&rectBorder);

    }

    /* Try SetBorderSpace() with the space that you need. If it fails,

    ** negotiate for space and then call SetBorderSpace().

    */

    FrameTools_GetRequiredBorderSpace(lpft,(LPBORDERWIDTHS)&borderwidths);

    hrErr = lpTopIPFrame->lpVtbl->SetBorderSpace(

            lpTopIPFrame,

            (LPCBORDERWIDTHS)&borderwidths);

    if (hrErr == NOERROR) {

        FrameTools_Move(lpft, (LPRECT)&rectBorder);   // got what we wanted

    } else {

        /* We did not get tool space, so POP them up.

        /* OLE2NOTE: Because we are popping up our tools, we MUST inform

        **    the top in-place frame window that we need NO tool space

        **    BUT that it should NOT put its own tools up. If we were

        **    to pass NULL instead of (0,0,0,0), the container

        **    would have the option to leave its own tools up.

        */

        hrErr = lpTopIPFrame->lpVtbl->SetBorderSpace(

                lpTopIPFrame,

                (LPCBORDERWIDTHS)&g_rectNull);

        FrameTools_PopupTools(lpft);

    }

}

See Also

IOleInPlaceObject::ReactivateAndUndo


IOleInPlaceSite::OnInPlaceDeactivate

HRESULT IOleInPlaceSite::OnInPlaceDeactivate()

IOleInPlaceSite::OnInPlaceDeactivate notifies the container that the object is no longer active in place.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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.

If the container is holding pointers to the IOleInPlaceObject and IOleInPlaceActiveObject interface implementation, it should release them after the IOleInPlaceSite::OnInPlaceDeactivate call.

Example

In the following IOleInPlaceSite::OnInPlaceDeactivate example, the container clears the flags that it internally maintained on the object while it was active in place before releasing its pointer to IOleInPlaceObject.

HRESULT InPlaceSite_OnInPlaceDeactivate(IOleInPlaceSite lpThis)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    lpContainerObj->m_fIpActive            = FALSE;

    lpContainerObj->m_fIpVisible           = FALSE;

    lpContainerObj->m_fIpChangesUndoable   = FALSE;

lpContainerObj->m_hWndlpObject	= NULL;

OleStdRelease((LPUNKNOWN) lpContainerObj->m_lpOleIPObj);

lpContainerObj->m_lpOleIPObj = NULL;

lpContainerObj->m_lpDoc->mcIPActiveObjects--;

    return NOERROR;

}

See Also

IOleInPlaceObject::InPlaceDeactivate


IOleInPlaceSite::DiscardUndoState

HRESULT IOleInPlaceSite::DiscardUndoState()

IOleInPlaceSite::DiscardUndoState is called by the active object while performing some action that would discard the undo state of the object. IOleInPlaceSite::DiscardUndoState tells the container to discard its undo state.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_UNEXPECTED	An unexpected error occurred.
Remarks

If an object is activated in place and the object's object application maintains only one level of undo stack, 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. The in-place object calls IOleInPlaceSite::DiscardUndoState to notify the container to discard the object's last saved undo state.


IOleInPlaceSite::DeactivateAndUndo

HRESULT IOleInPlaceSite::DeactivateAndUndo()

IOleInPlaceSite::DeactivateAndUndo is called by the active object when the user invokes undo in a state just after activating the object.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_UNEXPECTED	An unexpected error occurred.
Remarks

Upon completion of this call, the container should call IOleInPlaceObject::UIDeactivate to remove the user interface for the object, activate itself, and undo.


IOleInPlaceSite::OnPosRectChange

HRESULT IOleInPlaceSite::OnPosRectChange(lprcPosRect)
LPCRECT lprcPosRect

IOleInPlaceSite::OnPosRectChange is called by an in-place object when the object's extents have changed.

Parameter

lprcPosRect

[in] Points to the rectangle containing the position of the in-place object in the client coordinates of its parent window.

Return Value

Value	Meaning
S_OK	The method completed successfully.
E_INVALIDARG	An invalid arguments.
E_UNEXPECTED	An unexpected error occurred.
Remarks

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.

Example

The following IOleInPlaceSite::OnPosRectChange example is for an inside-out container. When an in-place object's size changes, the object calls OnPosRectChange to notify the container of its new extents. When called, the container calls IOleObject::GetExtent to get the new extents for the object before updating the object's position in the in-place frame. The container then calls ContainerDoc_UpdateInPlaceObjectRects to update the extents of all objects below this object in the container.

HRESULT InPlaceSite_OnPosRectChange(

    IOleInPlaceSite *   lpThis,

    LPCRECT             lprcPosRect)

{

    LPCONTAINEROBJ lpContainerObj =

            ((struct COleInPlaceSiteImpl *)lpThis)->lpContainerObj;

    LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpContainerObj->m_lpDoc;

LPSCALEFACTOR lpscale = ContainerDoc_GetScaleFactor(lpContainerDoc);

LPOBJ lpObj = (LPOBJ)lpContainerObj;

    LPOBJLIST lpObj;

    int nIndex;

RECT rcClipRect;

RECT rcNewPosRect;

SIZEL sizelPix;

SIZEL sizelHim;

int nIPObjHeight = lprcPosRect->bottom - lprcPosRect->top;

int nIPObjWidth = lprcPosRect->right - lprcPosRect->left;

    HRESULT hrErr;

/* Get the new extents of the in-place object.

    ** The call to IOleObject::GetExtent will be delegated to the

    ** running object to retrieve the actual current extents in the

    ** object application. Do NOT call IViewObject2::GetExtent

    ** as that would retrieve the extents from the last cached picture.

    ** At this point, the cache has not been updated.

    hrErr = lpContainerObj->m_lpOleObj->lpVtbl->GetExtent(

lpContainerObj->m_lpOleObj,

lpContainerObj->m_dwDrawAspect,

(LPSIZEL)&sizelHim);

ContainerObj_UpdateExtent(lpContainerObj, &sizelHim);

ContainerObj_GetOleObjectRectInPixels(

lpContainerObj, (LPRECT)&rcNewPosRect);

ContainerDoc_GetClipRect(lpContainerObj->m_lpDoc, (LPRECT)&rcClipRect);

    lpContainerObj->m_lpOleIPObj->lpVtbl->SetObjectRects(

            lpContainerObj->m_lpOleIPObj,

            (LPRECT)&rcNewPosRect,

            (LPRECT)&rcClipRect);

    lpObj = ContainerDoc_GetObjList(lpContainerDoc);

    nIndex = ObjList_GetObjIndex(lpObj, (LPOBJ)lpContainerObj);

if (g_InsideOut)

       // Need to call SetObjectRects for all of the in-place active

       // objects that are below this object in the document

       ContainerDoc_UpdateInPlaceObjectRects(lpContainerObj->

               m_lpDoc, nIndex);

    return NOERROR;

}

/* ContainerDoc_UpdateInPlaceObjectRects

** -------------------------------------

**    Update the PosRect and ClipRect of the currently in-place active

**    object. If there is no object active in place, do nothing.

**

**    OLE2NOTE: This function is called when an action occurs

**    that changes either the position of the object in the document

**    (for example, changing document margins changes PosRect) or the

**    clipRect changes (for example, resizing the document window

**    changes the ClipRect).

*/

void ContainerDoc_UpdateInPlaceObjectRects(LPCONTAINERDOC lpContainerDoc, int nIndex)

{

LPOBJLIST lpObj = &((LPCONTAINERDOC)lpContainerDoc)->m_ObjList;

int i;

LPOBJ lpObj;

    RECT rcClipRect;

    if (g_fInsideOutContainer) {

        if (lpContainerDoc->m_cIPActiveObjects) {

            /* OLE2NOTE: As an INSIDE-OUT CONTAINER, we must update the

            **    PosRect/ClipRect for all in-place active objects

            **    starting from the object "nIndex".

            */

            ContainerDoc_GetClipRect(lpContainerDoc, (LPRECT)&rcClipRect);

            for (i = nIndex; i < lpObj->m_nNumObjs; i++) {

                lpObj=ObjList_GetObj(lpObj, i);

                if (lpObj && (Obj_GetObjType(lpObj)==CONTAINEROBJTYPE)) {

                    LPCONTAINEROBJ lpContainerObj = (LPCONTAINEROBJ)lpObj;

                    ContainerObj_UpdateInPlaceObjectRects(

                            lpContainerObj, &rcClipRect);

                }

            }

        }

    }

    else {

        /* OLE2NOTE: (OUTSIDE-IN CONTAINER) If there is a currently

        **    UIActive object, we must inform it that the

        **    PosRect/ClipRect has now changed.

        */

        LPCONTAINEROBJ lpLastUIActiveObj =

                lpContainerDoc->m_lpLastUIActiveObj;

        if (lpLastUIActiveObj && lpLastUIActiveObj->m_fUIActive) {

            ContainerDoc_GetClipRect(lpContainerDoc, (LPRECT)&rcClipRect);

            ContainerObj_UpdateInPlaceObjectRects(

                    lpLastUIActiveObj, &rcClipRect);

        }

    }

}

See Also

IOleInPlaceObject::SetObjectRects


In-Place Activation Functions

OLE provides the following functions, which can be used to support in-place activation:

OleCreateMenuDescriptor(HMENU hmenuCombined, LPOLEMENUGROUPWIDTHS
  lpMenuWidths);

OleDestroyMenuDescriptor(HOLEMENU holemenu);

OleSetMenuDescriptor(HOLEMENU holemenu, HWND hwndFrame,
  HWND hwndActiveObject, LPOLEINPLACEFRAME lpFrame,
  LPOLEINPLACEACTIVEOBJECT lpActiveObject);

OleTranslateAccelerator(LPOLEINPLACEFRAME lpFrame,
  LPOLEINPLACEFRAMEINFO lpFrameInfo, LPMSG lpmsg);


OleCreateMenuDescriptor

HOLEMENU OleCreateMenuDescriptor(hmenuCombined, lpMenuWidths)
HMENU hmenuCombined
LPOLEMENUGROUPWIDTHS lpMenuWidths

OleCreateMenuDescriptor creates and returns a descriptor for OLE to use when dispatching menu messages and commands.

Parameters

hmenuCombined

Specifies a handle to the combined menu created by the object.

lpMenuWidths

Points 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

OleCreateMenuDescriptor 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.


OleDestroyMenuDescriptor

void OleDestroyMenuDescriptor(holemenu)
HOLEMENU holemenu

OleDestroyMenuDescriptor is called by the container to free the shared menu descriptor allocated by the OleCreateMenuDescriptor function.

Parameter

holemenu

Specifies a handle to the shared menu descriptor that was returned by OleCreateMenuDescriptor.

Return Value

None. (This function cannot indicate failure.)

See Also

OleCreateMenuDescriptor


OleSetMenuDescriptor

HRESULT OleSetMenuDescriptor(holemenu, hwndFrame, hwndActiveObject, lpFrame,
    lpActiveObj)
HOLEMENU holemenu
HWND hwndFrame
HWND hwndActiveObject
LPOLEINPLACEFRAME lpFrame
LPOLEINPLACEACTIVEOBJECT lpActiveObj

OleSetMenuDescriptor installs or removes dispatching code from the container's frame window.

Parameters

holemenu

Specifies the handle to the composite menu descriptor returned by OleCreateMenuDescriptor. If NULL, the dispatching code is unhooked.

hwndFrame

Specifies the handle to the container's frame window where the in-place composite menu is to be installed.

hwndActiveObject

Specifies the handle to the object's in-place activation window. OLE dispatches menu messages and commands to this window.

lpFrame

Points to the container's frame window.

lpActiveObj

Points to the active in-place object.

Return Value

Value	Meaning
S_OK	The menu was installed correctly.
E_INVALIDARG	One or more arguments are invalid.
E_UNEXPECTED	An unexpected error occurred.
E_FAIL	A Windows function call failed, indicating a resource allocation failure or improper arguments.
Remarks

OleSetMenuDescriptor should be called by the container to install the dispatching code on hwndFrame when the object calls IOleInPlaceFrame::SetMenu, or to remove the dispatching code by passing NULL as the value for holemenu to OleSetMenuDescriptor.

OLE installs the context-sensitive help F1 message filter for the application if both the lpFrame and lpActiveObj parameters are non-NULL. Otherwise, the application must supply its own message filter.

See Also

OleCreateMenuDescriptor, IOleInPlaceFrame::SetMenu


OleTranslateAccelerator

HRESULT OleTranslateAccelerator(lpFrame, lpFrameInfo, lpmsg)
LPOLEINPLACEFRAME lpFrame
LPOLEINPLACEFRAMEINFO lpFrameInfo
LPMSG lpmsg

OleTranslateAccelerator is called by the object application, allowing the container to translate its accelerators pointed to by lpFrameInfo.

Parameters

lpFrame

Points to the IOleInPlaceFrame interface where the keystroke might be sent.

lpFrameInfo

Points to an OLEINPLACEFRAMEINFO structure containing the accelerator table obtained from the container.

lpmsg

Points to an MSG structure containing the keystroke.

Return Value

Value	Meaning
S_OK	The keystroke was processed.
S_FALSE	The object should continue processing this message.
E_INVALIDARG	One of the arguments is invalid.
E_UNEXPECTED	An unexpected error occurred.
Remarks

While an object is UI active, it always has first chance at translating any messages received. If the object does not want to translate the message, it calls OleTranslateAccelerator to give the 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.

OleTranslateAccelerator is intended to be called only by local server object applications and not by in-process server objects. In the case of a local server object application, keyboard input goes directly to the object application's message pump. If the object does not translate the key, OleTranslateAccelerator gives the container application to do it. In the case of an in-process server object, the keyboard input goes directly to the container's message pump.

Note  So that they will work properly with object applications that do their own accelerator keystroke translations, accelerator tables for containers 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.

Objects can call IsAccelerator to see whether the accelerator keystroke belongs to it or the container.

See Also

IsAccelerator, IOleInPlaceFrame::TranslateAccelerator


IsAccelerator

BOOL IsAccelerator(hAccel, cAccelEntries, lpMsg, lpwCmd)
HACCEL hAccel
INT cAccelEntries
LPMSG lpMsg
WORD  * lpwCmd

IsAccelerator can be called to determine whether the keystroke maps to an accelerator in the given accelerator table.

Parameters

hAccel

Specifies the handle to the accelerator table.

cAccelEntries

Specifies the number of entries in the accelerator table.

lpMsg

Points to the keystroke message that is to be translated.

lpwCmd

Points to where to return the corresponding command ID if there is an accelerator for the keystroke. It may be NULL.

Return Value

Value	Meaning
TRUE	The message is for the object application.
FALSE	The message is not for the object and should be forwarded on to the container.
Remarks

While active in-place, the object always has first chance to translate the keystrokes into accelerators. If the keystroke corresponds to one of its accelerators, then the object must not call OleTranslateAccelerator — even if its call to the Windows TranslateAccelerator function fails. Failure to process keystrokes in this manner can lead to inconsistant behavior.

If the keystroke is not one of object's accelerators then the object must call OleTranslateAccelerator to let the container try its accelerator translation.

The object application can call IsAccelerator to determine if the accelerator message belongs to it. Some object applications do accelerator translation on their own and do not call TranslateAccelerator. Those applications will not call IsAccelerator, because they already have the information.

For more information on processing accelerator messages, see Pseudo Code for In-Place Objects.

See Also

OleTranslateAccelerator


Compatibility with OLE 1
Compatibility with OLE 1 and 16:32-Bit Interoperability

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."

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.

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- and 32-bit implementations means that 16-bit and 32-bit implementations can call each other.


Compatibility with OLE 1

The following figure illustrates how OLE 1 and OLE 2 applications communicate. OLE 1 applications make calls to other OLE 1 applications and to OLE 2 applications using DDE.

Communication between OLE 1 and OLE 2 applications.

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.

This section discusses 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

This section describes some of the idiosyncrasies of working with OLE 1 clients.

A successful call to IOleClientSite::GetContainer returns a pointer to the container's IOleContainer interface. If the container does not support IOleContainer, 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.

IOleClientSite::ShowObject, 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 effect OLE functionality.

When an OLE 1 client contains an OLE 2 object and the object is activated or OleUpdate 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 link source:

•	is represented by a file moniker or a generic composite moniker consisting of a file moniker and one item moniker.

•	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, to allow the OLE 1 version of the application access to its data, saves the data to an OLE 1 file. 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 converted to an OLE 2 object using OleConvertOLESTREAMToIStorage, 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 OleCopyFromLink to convert it to an embedded object will fail if the data transfer object provided by the link source does not support IPersistStorage. Creating an embedded object always requires native data, and IPersistStorage provides access to native data.


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 IPersistStorage::InitNew or IPersistStorage::Load 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 OleLoad 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 IDataObject::GetData or IDataObject::GetDataHere, 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. IPersistStorage::IsDirty is defined to return S_OK if the object has changed since its last save to persistent storage; S_FALSE if it has not changed. When an OLE 2 container with an OLE 1 embedded object calls IPersistStorage::IsDirty, the compatibility code always returns S_OK when the server is running, because there is no way to determine if 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 either 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 IOleItemContainer::EnumObjects always returns OLE_E_NOTSUPPORTED because it is not possible for an OLE 1 server to enumerate its objects.

IOleObject::Close 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 IOleObject::Close 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 CoIsOle1Class to determine at Clipboard copy time if a data selection being copied is an OLE 1 object. If CoIsOle1Class 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 any more 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 IOleObject::Update, 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();


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 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 either 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 IPersistStorage::Load 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 ReadFmtUserTypeStg(pStg, pcf, lplpszUserType) and examining the contents of pcfFormat. This is where the OLE 1 class name would appear.

In IPersistStorage::Save, 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 once 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 registration database under CLSID\{...}\Conversion\Readable\Main. 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, CLSIDFromProgId or CLSIDFromString 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 CLSIDFromProgId.

Refer to Registering Object Applications for detailed information on the required registration database entries for upgraded applications.


Functions to Support Compatibility

The functions described in this section enable applications to determine whether an object class is from OLE 1, and to support conversion between OLE 1 and OLE 2 storage formats.


CoIsOle1Class

BOOL CoIsOle1Class(rclsid)
REFCLSID rclsid

CoIsOle1Class determines whether a given CLSID represents an OLE 1 object.

Parameters

rclsid

Reference to the CLSID to check.

Return Values

Value	Meaning
S_TRUE	CLSID refers to an OLE 1 object.
S_FALSE	CLSID does not refer to an OLE 1 object.
Remarks

CoIsOle1Class is useful for preventing linking to embedded OLE 1 objects within a container. Once a container has determined that copied data represents an embedded object, CoIsOle1Class can be called to determine whether the embedded object is an OLE 1 object. If CoIsOle1Class returns S_TRUE, the container does not offer Link Source.


OleConvertIStorageToOLESTREAM

HRESULT OleConvertIStorageToOLESTREAM(pstg, lpolestm)
LPSTORAGE pstg
LPOLESTREAM lpolestm

OleConvertIStorageToOLESTREAM converts the storage of an OLE object from OLE 2 structured storage to OLE 1 storage.

Parameters

pstg

Points to the object's OLE 2 IStorage instance to be converted to an OLE 1 OLESTREAM storage.

lpolestm

Points to the object's OLESTREAM storage.

Return Values

Value	Meaning
CONVERT10_E_STG_NO_STD_STREAM	Object cannot be converted because its storage is missing a stream.
E_INVALIDARG	Invalid value for pstg or lpolestm.
Comment

OleConvertIStorageToOLESTREAM is useful for converting an OLE 2 document to the OLE 1 format. The OLESTREAM code implemented for OLE 1 must be available.

On entry, the stream pointed to by lpolestm should be created and positioned just as it would be for an OLE 1 OleSaveToStream call. On exit, the stream contains the persistent representation of the object using OLE 1 storage.

Paintbrush objects are dealt with differently from other objects because their native data is in DIB format. When Paintbrush objects are converted using OleConvertIStorageToOLESTREAM, no presentation data is added to the OLESTREAM.

See Also

OleConvertOLESTREAMToIStorage


OleConvertIStorageToOLESTREAMEx

HRESULT OleConvertIStorageToOLESTREAMEx (pstg, cfFormat, lWidth, lHeight, dwSize, pmedium, polestm)
LPSTORAGE pstg
CLIPFORMAT cfFormat
LONG lWidth
LONG lHeight
DWORD dwSize
LPSTGMEDIUM pmedium
LPOLESTREAM polestm

OleConvertIStorageToOLESTREAMEx converts the storage of an embedded object from OLE 2 structured storage to OLE 1 storage. This function differs from OleConvertIStorageToOLESTREAM in that the presentation data to be written to the OLE 1 storage is passed in.

Parameters

pstg

Points to the object's OLE 2 IStorage instance to be converted to an OLE 1 OLESTREAM storage.

cfFormat

The format of the presentation data. May be NULL, in which case the lWidth, lHeight, dwSize, and pmedium parameters are ignored.

lWidth

The width of the object presentation data in HIMETRIC units.

lHeight

The height of the object presentation data in HIMETRIC units.

dwSize

The size of the data to be converted, in bytes.

pmedium

Points to the STGMEDIUM structure for the serialized data to be converted.

polestm

Points to the object's OLESTREAM storage.

Return Values

Value	Meaning
S_OK	The conversion was completed successfully.
DV_E_STGMEDIUM	pmedium->hGlobal is NULL.
E_INVALIDARG	dwSize is NULL or pstg or lpolestm is invalid.
DV_E_TYMED	pmedium->tymed is not TYMED_HGLOBAL or TYMED_ISTREAM.
Remarks

Because OleConvertIStorageToOLESTREAMEx can specify which presentation data to convert, it can be used by applications that do not use the OLE default caching resources but do use OLE's conversion resources.

The value of pmedium->tymed may only be TYMED_HGLOBAL or TYMED_ISTREAM. The medium will not be released by OleConvertIStorageToOLESTREAMEx.

See Also

OleConvertOLESTREAMToIStorageEx, OleConvertIStorageToOLESTREAM.


OleConvertOLESTREAMToIStorage

HRESULT OleConvertOLESTREAMToIStorage(lpolestm, pstg, ptd )
LPOLESTREAM lpolestm
LPSTORAGE pstg
const DVTARGETDEVICE * ptd

OleConvertOLESTREAMToIStorage converts the storage of an embedded object from OLE 1 OLESTREAM storage to OLE 2 structured storage.

lpolestm

Points to the OLE 1 OLESTREAM storage for the object.

pstg

Points to the OLE 2 IStorage instance of the object.

ptd

Points to a target device.

Return Values

Value	Meaning
CONVERT10_S_NO_PRESENTATION	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.
E_INVALIDARG	Invalid value for lpolestm.
Remarks

OleConvertOLESTREAMToIStorage can be used to convert an application document that supported OLE 1 to one that supports OLE 2. The lpolestm pointer contains the current OLE 1 persistent storage of the embedded object.

On entry, lpolestm should be created and positioned just as it would be for an OleLoadFromStream function call. On exit, lpolestm is positioned just as it would be on exit from OleLoadFromStream, and pstg now contains the uncommitted persistent representation of the object using the OLE 2 storage model.

For OLE 1 objects that use native data for their presentation, OleConvertOLESTREAMToIStorage returns CONVERT10_S_NO_PRESENTATION. On receiving this return value, callers should call IOleObject::Update to get the presentation data so that it can be written to storage.

The following steps illustrate the conversion process using C:

1.	Create a root IStorage object by calling StgCreateDocfile(..., &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.

Example

To get native data from an OLE 1 stream that has been converted to OLE 2 storage, use the following code, where kszOle10Native is set to "\1Ole10Native":

HGLOBAL GhGetNativeData(LPSTORAGE pstg)

{

LPSTREAM    lpstmNative = NULL;

HGLOBAL     gh = NULL;

HRESULT     hrErr;

/* call IStorage::OpenStream on Ole10Native stream */

hrErr = pstg->lpVtbl->OpenStream(pstg, kszOle10Native, NULL,

STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &lpstmNative);

// this is an OLE 1 object: its native data is in the stream

if (SUCCEEDED(hrErr) && lpstmNative)  {

DWORD       dwSize = 0;

LPOLESTR    lpb = NULL;

/* call IStream::Read on native stream */

hrErr = lpstmNative->lpVtbl->Read

(lpstmNative, &dwSize, sizeof(DWORD), NULL);

if (FAILED(hrErr) || !dwSize)

return gh;

gh = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, dwSize);

if (!gh || !(lpb = GlobalLock(gh)))  {

if (gh && lpb)

GlobalUnlock(gh);

if (gh && (!lpb || FAILED(hrErr)))  {

GlobalFree(gh);

gh = NULL;

}

}

else hrErr = lpstmNative->lpVtbl->Read	(lpstmNative, lpb,

dwSize, NULL);

}

return gh;

}


OleConvertOLESTREAMToIStorageEx

HRESULT OleConvertOLESTREAMToIStorageEx(polestm, pstg, pcfFormat, lWidth, lHeight, dwSize, pmedium)
LPOLESTREAM polestm
LPSTORAGE pstg
CLIPFORMAT  * pcfFormat
LONG * plWidth
LONG * plHeight
DWORD * pdwSize
LPSTGMEDIUM pmedium

OleConvertOLESTREAMToIStorageEx converts the storage of an embedded object from OLE 1 OLESTREAM storage to OLE 2 structured storage. This function differs from OleConvertOLESTREAMToIStorage in that the presentation data that is read from OLESTREAM is passed out and the newly created IStorage does not contain a presentation stream.

Parameters

polestm

Points to the OLE 1 OLESTREAM storage for the object.

pstg

Points to the OLE 2 IStorage instance of the object.

pcfFormat

Points to where to return the format of the presentation data. May be NULL, indicating the absence of presentation data.

plWidth

Points to where to return the width value (in HIMETRIC) of the presentation data.

plHeight

Points to where to return the height value (in HIMETRIC) of the presentation data.

pdwSize

Points to where to return the size in bytes of the converted data.

pmedium

Points to where to return the STGMEDIUM structure for the converted serialized data.

Return Values

Value	Meaning
S_OK	The conversion was completed successfully.
DV_E_TYMED|	The value of pmedium->tymed is not TYMED_ISTREAM or TYMED_NULL.
Remarks

Since OleConvertOLESTREAMToIStorageEx 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 TYMED_NULL, then 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.

See Also

OleConvertIStorageToOLESTREAMEx, OeConvertIStorageToOLESTREAM


16:32 Bit Interoperability

The 32-bit implementation of OLE 2 is compatible with OLE 1 applications in the manner described above. 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 marshalling and converting parameters between the two models.


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 CreateFileMoniker returns a pointer to a moniker. When a 16-bit implementation calls CreateFileMoniker, 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. Thus, the 16-bit application sees the same pointer it passed in. This works when passing from 16 to 32 to 16, or from 32 to16 to 32. For more information about thunking, see the Win32 SDK.


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 to 16 (inprocess server)

•16 to 32 (inprocess server, except the IDispatch interface)

•16 to 16 (local server)

•16 to 32 (local server)

•32 to 32 (inprocess server)

•32 to 32 (local server)

•32 to 16 (local server)

32- to16-bit (inprocess server) interoperability using the standard interfaces is not supported.


Interoperability Using Custom Interfaces
Interoperability Using Custom Interfaces (either MIDL or Manually Written Marshalling)

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 inprocess server should still function correctly. The following combinations of interoperability using custom interfaces are supported:

•16 to 16 (inprocess server)

•32 to 32 (inprocess server)

•32 to 32 (local server using MIDL-generated stubs)

The following combinations of interoperability using custom interfaces are not supported:

•16 to 16 (local server)

•16 to 32 (all combinations)

•32 to 16 (all combinations)

Programmers who have written applications that provide custom interfaces must rewrite the custom interface marshalling code to conform to the RPC-based communication layer. However, information about writing a 16-bit custom-marshalling library is not available at this time. For more information about RPC, see the RPC Programmer's Guide and Reference.


Concurrency Management

Concurrency management facilitates handling a variety of demands placed on OLE applications in any typical session. OLE applications must be able to deal correctly with user input while processing one or more calls from OLE or the operating system. OLE calls, when made between processes, fall into three categories:

•	Synchronous calls

•	Asynchronous notifications

•	Input-synchronized calls

Most of the communication that takes place within OLE is synchronous. When making synchronous calls, the caller waits for the reply before continuing and can receive incoming messages while waiting. OLE enters into a modal loop to wait for the reply, receiving and dispatching other messages in a controlled manner. When OLE is running on Windows platforms, OLE makes synchronous calls by using PostMessage. When OLE is running on Macintosh platforms, a high-level event is posted in place of the PostMessage call.

When sending asynchronous notifications, the caller does not wait for the reply. OLE uses PostMessage or high-level events to send asynchronous notifications, depending on the platform. OLE defines five asynchronous methods:

•	IAdviseSink::OnDataChange

•	IAdviseSink::OnViewChange

•	IAdviseSink::OnRename

•	IAdviseSink::OnSave

•	IAdviseSink::OnClose

While OLE 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.

When making input-synchronized calls, the callee 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 OLE through the Windows SendMessage function. A modal loop is not entered. While processing an input-synchronized call, the callee 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

The following diagram is a high-level view of the communication that occurs between a caller, OLE, and a callee on a Windows platform when each of these types of calls are sent.

Communication between caller, callee, and OLE.

To minimize problems that can arise from asynchronous message processing, the majority of OLE 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, OLE enters a modal wait loop that handles the required replies and dispatches incoming messages to applications capable of processing them.

OLE 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 OLE operation. Subsequent calls that relate to the initial OLE call are assigned the same logical thread ID as the initial call.


Application Shutdown with WM_QUERYENDSESSION and WM_ENDSESSION

When the user exits Windows, such as by choosing File Exit Windows from the Program Manager, each open application receives a WM_QUERYENDSESSION message followed by a WM_ENDSESSION message if the exit is not canceled. These messages are invoked with SendMessage, which unfortunately restricts the initiation of all outgoing LRPC calls. This situation is problematic for those container applications that have open embedded objects when they receive a shutdown request. LRPC is needed to close those objects.

Container or container/object applications with open documents typically display a message box on receipt of the WM_QUERYENDSESSION message that asks the user if he or she wants to save changes. 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 the user if he or she 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, a signal 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.

Object applications should return TRUE for WM_QUERYENDSESSION without prompting the user.

On receipt of a WM_ENDSESSION message, all OLE applications should execute the normal close sequence for each of the application's documents and/or objects. During this time, any errors that occur from the close sequence(s) should be ignored. 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.


IMessageFilter Interface

Applications can selectively handle incoming or outgoing messages while waiting for responses from synchronous calls by implementing the IMessageFilter interface. The ability to filter messages is often useful when a dialog box is being displayed or a lengthy operation is in progress. Performance can be improved when applications handle some messages and defer others.

IMessageFilter contains the following methods:

DECLARE_INTERFACE_(IMessageFilter, IUnknown)

{

    // *** IUnknown methods ***

  HRESULT QueryInterface (THIS_ REFIID riid, LPVOID * ppvObj);

  ULONG AddRef (THIS);

  ULONG Release (THIS);

    // *** IMessageFilter methods ***

  DWORD HandleInComingCall (THIS_ DWORD dwCallType,

    HTASK threadIDCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo);

  DWORD RetryRejectedCall (THIS_

    HTASK threadIDCallee, DWORD dwTickCount, DWORD dwRejectType);

  DWORD MessagePending (THIS_

    HTASK threadIDCallee, DWORD dwTickCount, DWORD dwPendingType);

};

For applications that do not implement IMessageFilter, OLE provides default behaviors for each of the conditions that would be handled by an IMessageFilter method. For incoming calls, OLE dispatches all calls, regardless of their logical thread ID. For incoming Windows messages or Macintosh events, the default behavior is as if IMessageFilter::MessagePending had returned PENDINGMSG_WAITDEFPROCESS. This means that task-switching and window-activation messages are dispatched, as well as WM_TIMER messages. Other input messages are discarded and OLE continues to wait for the reply.


IMessageFilter::HandleInComingCall

DWORD IMessageFilter::HandleInComingCall(dwCallType, threadIDCaller, dwTickCount,
lpInterfaceInfo)
DWORD dwCallType
HTASK threadIDCaller
DWORD dwTickCount
LPINTERFACEINFO lpInterfaceInfo

IMessageFilter::HandleInComingCall is called when an incoming OLE message is received. IMessageFilter::HandleInComingCall provides the application with a single entry point for all incoming calls.

Parameters

dwCallType

[in] Indicates the kind of incoming call that has been received; valid values are from the enumeration CALLTYPE:

typedef enum tagCALLTYPE

{

CALLTYPE_TOPLEVEL 	= 1,

CALLTYPE_NESTED	= 2,

CALLTYPE_ASYNC 	= 3,

CALLTYPE_TOPLEVEL_CALLPENDING	= 4,

CALLTYPE_ASYNC_CALLPENDING	= 5

} CALLTYPE;

The following table describes the CALLTYPE values:

Value	Meaning
CALLTYPE_TOPLEVEL	A call has arrived with a new logical thread ID. The application is not currently waiting for a reply from an outgoing call. Calls of this type should always be handled.
CALLTYPE_NESTED	A call has arrived with the same logical thread ID as that of an outgoing call for which the application is currently waiting for a reply. Calls of this type should always be handled.
CALLTYPE_ASYNC	An asynchronous call has arrived; calls of this type cannot be rejected. OLE always delivers them.
CALLTYPE_TOPLEVEL_CALLPENDING	A call has arrived with a new logical thread ID. The application is currently waiting for a reply from an outgoing call. Calls of this type may be handled or rejected.
CALLTYPE_ASYNC_CALLPENDING	An asynchronous call has arrived and the application is currently waiting for a reply from an outgoing call. Calls of this type cannot be rejected.
threadIDCaller

[in] Specifies the handle of the task that is calling this task.

dwTickCount

[in] Specifies the 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

Points to an INTERFACEINFO structure that contains information about the incoming call. Can also be NULL. The INTERFACEINFO structure is defined as follows:

typedef struct tagINTERFACEINFO

{

    LPUNKNOWN	pUnk;      // the pointer to the object

    IID	iid;       // the interface id

    WORD	wMethod;   // the interface method

} INTERFACEINFO,  * LPINTERFACEINFO;

Return Values

Value	Meaning
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.
SERVERCALL_RETRYLATER	The application cannot handle the call at this time. For example, an application might return this value when it is in a user-controlled modal state.
Remarks

Depending on the application's current state, the call can be either accepted and processed or rejected (permanently or temporarily). The return value SERVERCALL_ISHANDLED indicates that the application might be able to process the call. Whether processing is successful might depend on the interface for which the call is destined. OLE rejects or processes the call, returning RPC_E_CALL_REJECTED if the call could not be processed.

Input-synchronized calls 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. IViewObject::Freeze should be used for that purpose.

IMessageFilter::HandleInComingCall can also be used to set up the application's state so that the call can be processed in the future.

Example

OLE provides a standard implementation of IMessageFilter in its user interface library. The OLESTDMESSAGEFILTER object, shown below, tracks the state of the application and stores, in addition to a pointer to the IMessageFilter interface VTBL, pointers to several callback functions. The function pointed to by m_lpfnHandleInComingCallback is called in the implementation of IMessageFilter::HandleInComingCall to override the current call status. Applications can instantiate the OLESTDMESSAGEFILTER object to access the standard IMessageFilter implementation.

typedef struct tagOLESTDMESSAGEFILTER {

IMessageFilterVtbl *    m_lpVtbl;

UINT                    m_cRef;

HWND                    m_hWndParent;

DWORD                   m_dwInComingCallStatus;

HANDLEINCOMINGCALLBACKPROC m_lpfnHandleInComingCallback;

BOOL                    m_fEnableBusyDialog;

BOOL                    m_fEnableNotRespondingDialog;

MSGPENDINGPROC          m_lpfnMessagePendingCallback;

LPFNOLEUIHOOK           m_lpfnBusyDialogHookCallback;

LPOLESTR                   m_lpszAppName;

HWND                    m_hWndBusyDialog;

BOOL                    m_bUnblocking;

 }OLESTDMESSAGEFILTER, * LPOLESTDMESSAGEFILTER;

The following method is the sample implementation of IMessageFilter::HandleInComingCall. OleStdMsgFilter_HandleInComingCall accepts or rejects the incoming call depending on the value of dwCallType. If the call is a top-level call (pending or otherwise) or an unrecognized type, the current incoming call status is returned. SERVERCALL_ISHANDLED is returned when dwCallType is CALLTYPE_NESTED, CALLTYPE_ASYNC, or CALLTYPE_ASYNC_PENDING because these calls must be handled. The current call status, stored in m_dwInComingCallStatus, is set initially to SERVER_ISHANDLED and changed when necessary.

DWORD OleStdMsgFilter_HandleInComingCall (

        LPMESSAGEFILTER     lpThis,

        DWORD               dwCallType,

        HTASK               threadIDCaller,

        DWORD               dwTickCount,

        DWORD               dwReserved

)

{

   LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;

// Call HandleInComingCallbackProc if it exists.

if (lpStdMsgFilter->m_lpfnHandleInComingCallback && !IsBadCodePtr

((FARPROC)lpStdMsgFilter->m_lpfnHandleInComingCallback)){

return lpStdMsgFilter->m_lpfnHandleInComingCallback

(dwCallType, threadIDCaller, dwTickCount,

(LPINTERFACEINFO)dwReserved);

}

switch (dwCallType) {

case CALLTYPE_TOPLEVEL:

return lpStdMsgFilter->m_dwInComingCallStatus;

case CALLTYPE_TOPLEVEL_CALLPENDING:

return lpStdMsgFilter->m_dwInComingCallStatus;

case CALLTYPE_NESTED:

return SERVERCALL_ISHANDLED;

case CALLTYPE_ASYNC:

return SERVERCALL_ISHANDLED;    // value doesn't matter

case CALLTYPE_ASYNC_CALLPENDING:

return SERVERCALL_ISHANDLED;    // value does not matter

default:

return lpStdMsgFilter->m_dwInComingCallStatus;

}

}


IMessageFilter::RetryRejectedCall

DWORD IMessageFilter::RetryRejectedCall(threadIDCallee, dwTickCount, dwRejectType)
HTASK threadIDCallee
DWORD dwTickCount
DWORD dwRejectType

IMessageFilter::RetryRejectedCall is called by OLE immediately after it receives SERVERCALL_RETRYLATER or SERVERCALL_REJECTED from IMessageFilter::HandleInComingCall. IMessageFilter::RetryRejectedCall gives the application a chance to display a dialog box so the user can either retry or cancel the call, or switch to the task identified by threadIDCallee.

Parameters

threadIDCallee

[in] Specifies the handle of the server task that rejected the call.

dwTickCount

[in] Specifies the 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

Value	Meaning
-1	The call should be canceled. OLE then returns RPC_E_CALL_REJECTED from the original method call.
Value >= 0 and <100	The call is to be retried immediately.
Value >= 100	OLE will wait for this many milliseconds and then retry the call.
Remarks

If an application task rejects a call, the application is probably in a state where it cannot handle such calls, perhaps temporarily. IMessageFilter::RetryRejectedCall is called when this condition occurs.

Applications should silently retry calls that have returned SERVERCALL_RETRYLATER and show a dialog box only after a reasonable amount of time has passed, such as five seconds. The callee may momentarily be in a state where calls can be handled. This 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 in a nonintrusive way.

If, after a dialog box is displayed, the user chooses to cancel, the call will appear to fail with RPC_E_CALL_REJECTED.

Example

The following example of IMessageFilter::RetryRejectCall puts up the standard OLE dialog box to alert the user that the called application is too busy to process the call. Only when the callee has responded with SERVERCALL_RETRYLATER is the dialog box displayed using the user-interface library function OleUIBusy. If the dialog box cannot be displayed, -1 is returned. This indicates that the call should be canceled. If the value of threadIDCallee is invalid, the call is tried again after a predefined amount of time. For this application, the call is retried after 5000 milliseconds.

DWORD OleStdMsgFilter_RetryRejectedCall (

        LPMESSAGEFILTER     lpThis,

        HTASK               threadIDCallee,

        DWORD               dwTickCount,

        DWORD               dwRejectType

)

{

LPOLESTDMESSAGEFILTER lpStdMsgFilter =

(LPOLESTDMESSAGEFILTER)lpThis;

DWORD dwRet = 0;

UINT uRet;

if (dwRejectType == SERVERCALL_RETRYLATER &&

lpStdMsgFilter->m_fEnableBusyDialog) {

OLEUIBUSY bz;

bz.cbStruct = sizeof(OLEUIBUSY);

bz.dwFlags = 0L;

bz.hWndOwner = lpStdMsgFilter->m_hWndParent;

bz.lpszCaption = lpStdMsgFilter->m_lpszAppName;

bz.lpfnHook = lpStdMsgFilter->m_lpfnBusyDialogHookCallback;

bz.lCustData = 0;

bz.hInstance = NULL;

bz.lpszTemplate = NULL;

bz.hResource = 0;

bz.hTask = threadIDCallee;

bz.lphWndDialog = NULL; // We don't need the hDlg for this call

uRet = OleUIBusy(&bz);

switch (uRet) {

case OLEUI_BZ_RETRYSELECTED:

dwRet = 0;                  // Retry immediately

break;

case OLEUI_CANCEL:

dwRet = OLESTDCANCELRETRY;

break;

case OLEUI_BZERR_HTASKINVALID:

dwRet = OLESTDRETRYDELAY;

break;

}

} else {

dwRet = OLESTDCANCELRETRY;

}

return dwRet;

}


IMessageFilter::MessagePending

DWORD IMessageFilter::MessagePending(threadIDCallee, dwTickCount, dwPendingType)
HTASK threadIDCallee
DWORD dwTickCount
DWORD dwPendingType

IMessageFilter::MessagePending is called if a Windows message or Macintosh event appears in the queue while OLE is waiting to reply to a remote call.

Parameters

threadIDCallee

[in] Specifies the task handle of the called application that has not yet responded.

dwTickCount

[in] Specifies the elapsed time since the call was made. It is calculated from the Windows GetTickCount function.

dwPendingType

[in] Indicates the 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 PENDINGYPE_NESTED means the outgoing call was nested within a call from another application):

typedef enum tagPENDINGTYPE {

PENDINGTYPE_TOPLEVEL	= 1,

PENDINGTYPE_NESTED	= 2,

} PENDINGTYPE;

Return Values

Value	Meaning
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. OLE fails the original call and return 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.
PENDINGMSG_WAITDEFPROCESS	Invoke OLE default message handling, dispatching task-switching and window-activation messages, as well as WM_TIMER messages. Other input messages are discarded and OLE continues to wait for the reply.
Remarks

IMessageFilter::MessagePending is called by OLE after an application has made an OLE method call and, for example, while waiting for the reply, the user selects a menu command or double-clicks an object. Before OLE makes the IMessageFilter::MessagePending call, it calculates the elapsed time since the original OLE method call was made to pass in dwTickCount. In the meantime, OLE does not remove the message from the queue.

Windows messages events that appear in the caller's queue should remain in the queue until sufficient time has passed. A two or three second delay is recommended to ensure that the messages are probably not the result of typing ahead but are, instead, an attempt to get attention. If the amount of time has passed and the call has not been completed, the messages should be flushed from the queue, and a dialog box displayed offering the user the choice to retry (keep waiting) or switch to the task identified by threadIDCallee.

The above rule ensures that:

•	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 dialogs.

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, continue waiting, or cancel the operation.

When there is no response to the original OLE call, the application can cancel the call and restore the OLE object to a consistent state by calling IStorage::Revert on its storage. The object can be released when the container shuts 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.

Caution  Returning PENDING_WAITNOPROCESS can cause the message queue to fill.

Example

The following example of IMessageFilter::MessagePending displays the standard dialog box provided in the OLE user-interface library if the tick count for this call exceeds the retry delay (in this case, 5000) and if the user has issued a significant event such as a mouse click or keyboard event. A simple mouse move does not trigger this dialog box. When one call is in the dialog box message loop, there is a possibility that another call will be initiated and that the dialog box procedure will be re-entered. The m_bUnblocking variable is used to prevent the display of two dialog boxes simultaneously.

All mouse and keyboard input messages are removed from the queue because these messages were generated by the user while the application was waiting for a response to its OLE call.

If the dialog box cannot be displayed because the application is blocked, the message must be rejected and PENDINGMSG_WAITDEFPROCESS is returned.

If there is a callback function set up for the message filter, the function pointed to by m_lpfnMessagePendingCallback is called by using the current message. However, if there is no callback function, the default behavior takes over and OLE is told to apply default message handling automatically.

DWORD IMessageFilter_MessagePending (

        LPMESSAGEFILTER     lpThis,

        HTASK               threadIDCallee,

        DWORD               dwTickCount,

        DWORD               dwPendingType

)

{

LPOLESTDMESSAGEFILTER lpStdMsgFilter =

(LPOLESTDMESSAGEFILTER)lpThis;

    DWORD                 dwReturn = PENDINGMSG_WAITDEFPROCESS;

MSG                   msg;

BOOL                  fIsSignificantMsg = IS_SIGNIFICANT_MSG(&msg);

UINT                  uRet;

if (dwTickCount > (DWORD)OLESTDRETRYDELAY

&& (! lpStdMsgFilter->m_bUnblocking) && fIsSignificantMsg)

{

if (lpStdMsgFilter->m_fEnableNotRespondingDialog)  {

OLEUIBUSY bz;

lpStdMsgFilter->m_bUnblocking = TRUE;

while (PeekMessage(&msg, NULL, WM_MOUSEFIRST,

WM_MOUSELAST,

PM_REMOVE | PM_NOYIELD));

while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,

PM_REMOVE | PM_NOYIELD));

// Set up "not responding" OLEUIBUSY dialog */

bz.cbStruct = sizeof(OLEUIBUSY);

bz.dwFlags = BZ_NOTRESPONDINGDIALOG;

bz.hWndOwner = lpStdMsgFilter->m_hWndParent;

bz.lpszCaption = lpStdMsgFilter->m_lpszAppName;

bz.lpfnHook = lpStdMsgFilter->m_lpfnBusyDialogHookCallback;

bz.lCustData = 0;

bz.hInstance = NULL;

bz.lpszTemplate = NULL;

bz.hResource = 0;

bz.hTask = threadIDCallee;

bz.lphWndDialog =

(HWND  *)&(lpStdMsgFilter->m_hWndBusyDialog);

uRet = OleUIBusy(&bz);

lpStdMsgFilter->m_bUnblocking = FALSE;

return PENDINGMSG_WAITNOPROCESS;

}

}

if (lpStdMsgFilter->m_bUnblocking)

return PENDINGMSG_WAITDEFPROCESS;

if (lpStdMsgFilter->m_lpfnMessagePendingCallback && !IsBadCodePtr

  ((FARPROC)lpStdMsgFilter->m_lpfnMessagePendingCallback)){

MSG msg;

if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD)) {

if (lpStdMsgFilter->m_lpfnMessagePendingCallback(&msg)) {

// Message was processed; can remove it from queue.

PeekMessage(&msg, NULL, msg.message, msg.message,

PM_REMOVE | PM_NOYIELD);

dwReturn = PENDINGMSG_WAITNOPROCESS;

} else {

// Message not processed; let OLE take default action.

dwReturn = PENDINGMSG_WAITNOPROCESS;

}

}

}

return dwReturn;

}


CoRegisterMessageFilter

HRESULT CoRegisterMessageFilter(lpMessageFilter, lplpMessageFilter)
LPMESSAGEFILTER lpMessageFilter
LPMESSAGEFILTER * lplpMessageFilter

CoRegisterMessageFilter registers, with OLE, the instance of an EXE application's IMessageFilter interface to be used for handling concurrency issues. DLL object applications cannot register a message filter.

Parameters

lpMessageFilter

Points to an IMessageFilter interface supplied by the application. Can be NULL, indicating that the current IMessageFilter registration should be revoked.

lplpMessageFilter

Returns a pointer, if this parameter is non-NULL, to the previously registered IMessageFilter instance. If NULL, indicates no previous IMessageFilter instance was registered.

Return Values

Value	Meaning
S_OK	IMessageFilter instance registered or revoked successfully.
S_FALSE	An error registering or revoking IMessageFilter instance.


Object Handlers

In OLE, an object handler is nothing more than a piece of code that implements the interfaces expected by a container when an object is in its loaded state. OLE includes a default object handler, OLE32.DLL.

This section discusses the following types of object handlers:

Types of object handlers	Description
Default object handler	OLE32.DLL is the default object handler provided with the OLE libraries. It is used to cache presentation data and to render objects using data from the OLE default data cache. In most cases, the default handler/cache provides the level of functionality needed for container applications.
Custom handler	A custom handler is typically used with the OLE default object handler and an EXE object application to do special rendering. A custom handler can be written to supplement or replace some or all of the functionality offered by the OLE default object handler. Custom handlers are stored in the registry in place of the OLE32.DLL default object handler using the keyword InprocHandler32.
DLL object application	An object application can be written as a DLL to reside directly in the container's process space. DLL object applications replace not only the functionality of an EXE object application, but that of the default object handler and/or custom object handler. DLL object applications are stored in the registry using the keyword InprocServer32, and are referred to as in-process servers.


OLE Default Object Handler

Note  Object handlers provide services on behalf of OLE object applications while an object is in its loaded state, thus avoiding the process time required to start the object applications. The OLE default object handler is always used unless a custom handler or DLL object application is written and registered for an object class. The default object handler serves also as a DLL object application for objects without registered CLSIDs and for static metafile objects and DIB objects.

The next figure shows a representation of the OLE default object handler and its aggregation of the default data cache. The illustration shows only the interface implementations exposed externally; internal implementations are not shown. For example, the data cache has internal implementations of IDataObject and IPersistStorage that are made available to the default handler through its aggregation with the cache.

The default handler is the intermediary between the container application and an object application. If the default handler can't handle a request from the container or the object must be put into its running state, the handler passes the call on to the object application for processing. Through the exposed IOleClientSite interface, the object application can obtain the services offered by the container application.

OLE default object handler, OLE32.DLL.

The OLE default handler and data cache are often thought of as one object; in reality, they are two separate objects; the cache is aggregated with the handler. The default handler (through IViewObject2) is responsible for drawing the requested object using the data maintained in the data cache. Drawing is always done locally, never by remoting the call to the object application.

The object handler transparently converts the cached data for drawing purposes as the object is moved from one platform to another platform (for example, from Windows to the Macintosh). OLE, through its implementation of the IOleCache2 interface, can convert between the presentation formats it has information about: metafiles, bitmaps and DIB on Windows and PICTs on the Macintosh.

The default handler is also responsible for changing the object from the loaded state to the running state and for keeping the data cache up to date against changes made to the running object. As an object enters its running state, the handler sets up appropriate advisory connections on the running object by passing an IAdviseSink of itself to the running object. When the object changes, OnDataChange is invoked on the handler's sink, at which point the handler updates the cache. After updating the cache, the handler sends OnDataChange and/or OnViewChange notifications to the advisory sink in the container.

Through the default handler, the following interface implementations are exposed for use by the container while an object is in its loaded state:

Default Handler's Interface Implementations	Purpose
IDataObject	Uses the cached presentation data to enumerate and retrieve data from the registry and to be notified when an object's data has changed.
IPersistStorage	Saves and loads presentation/data caches.
IViewObject2	Enables the container to render an object, using one of three standard data formats obtained from the object's cached data. These formats include metafile, bitmap, and DIB.
IOleObject	Enumerates the list of verbs (using entries from the registry) that the container makes available to the user for a given object. IOleObject::DoVerb puts the object into its running state.
IOleCache2	Caches, uncaches, and selectively updates an object's presentation data.


Interfaces Implemented by the Default Handler

To clarify how the default handler works with an object in its loaded and running states, the following sections describe the interfaces implemented by the default handler.


IOleObject Interface

The default handler's implementation of the IOleObject interface is minimal in that only the DoVerb and Update methods put the object in the running state; the remainder of the IOleObject methods either return an HRESULT (SCODE) or save the information for future use.

The following table lists the IOleObject methods along with a description of how the default handler's implementation affects an object when it is in its loaded and running states.

IOleObject Method	Object in Loaded State	Object in Running State
Advise	Remembers and forwards to the object application at the appropriate time.	Remembers the Advise and delegates to the running object.
EnumAdvise	Enumerates the remembered advises.	Enumerates the remembered advises.
Unadvise	Delegates the call to the advise holder object in order to remove the advise from the list.	Delegates the call to the advise holder object in order to remove the advise from the list and tells the object to do an unadvise.
Close	Returns NOERROR. Containers can safely call Close, but it is meaningless while an object is in its loaded state.	Delegates the call to the running object.
DoVerb	Puts the object in its running state and delegates the call to the running object.	Delegates the call to the object.
EnumVerbs	Searches the verb entries in the registry to create the needed enumeration.	Delegates the call to the running object; if the object returns OLE_S_USEREG, the object searches the registration database.
GetClientSite	Returns the last IOleClientSite (as remembered by SetClientSite) and returns OLE_E_ERROR.	Delegates the call to the running object.
GetClipboardData	Cannot be called unless the object is running; returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
GetExtent	If available, returns the size of the requested aspect from the cache. Otherwise, returns NOERROR if aspect not found.	Delegates the call to the running object. A successful call returns to the container while an error forces object to get the extents from the cache.
GetMiscStatus	Searches the MiscStatus entries in the registry for the specified CLSID and returns NOERROR.	Delegates the call to the running object. If the object returns OLE_S_USEREG, searches the registry.
GetMoniker	Calls IOleClientSite::Get-Moniker if SetClientSite has been called with a valid IOleClientSite pointer. Otherwise, returns E_FAIL.	Calls IOleClientSite::Get-Moniker if SetClientSite has been called with a valid IOleClientSite pointer. Otherwise, returns E_FAIL.
GetUserClassID	Returns the CLSID remembered from creation time and returns E_FAIL.
GetUserType	Retrieves a string from the CLSID's AuxUserType entry in the registry and returns NOERROR.	Delegates the call to the running object; if the object returns OLE_S_USEREG, searches the registry.
InitFromData	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
IsUpToDate	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
SetClientSite	Saves the IOleClientSite pointer for future use and returns NOERROR.	Remembers and delegates the call to the running object.
SetColorScheme	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
SetExtent	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
SetHostNames	Saves the string for future use and returns NOERROR.	Saves the string and delegates the call to the running object.
SetMoniker	Returns NOERROR (object can obtain later from object's client site).	Delegates the call to the running object.
Update	Runs and delegates the call to the object; if the object was not previously running, it stops the object after the update. Updates the OnSave caches.	Delegates the call to the running object. Updates the OnSave caches.


IDataObject Interface

The default handler's IDataObject implementation depends on what is in the cache. In the loaded state, the handler delegates GetData, GetDataHere, and QueryGetData method calls to the cache's IDataObject implementation.

The default handler's IDataObject implementation is as follows:

IDataObject Methods	Object in Loaded State	Object in Running State
DAdvise	Remembers and forwards to the object application at the appropriate time.	Remembers the Advise and delegates the call to the running object.
EnumDAdvise	Enumerates the remembered advises.	Enumerates the remembered advises.
DUnadvise	Delegates the call to the advise holder object in order to remove the advise from the list.	Delegates the call to the advise holder object in order to remove the advise from the list and tells the object to do an unadvise.
EnumFormatEtc	Creates an enumerator from the object's DataFormats and GetSet entries in the registry.	Delegates the call to the running object; if the object returns OLE_S_USEREG, searches the registry.
GetCanonicalFormatEtc	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.
GetData	Calls the cache. If the data is not cached, the handler returns OLE_E_NOTRUNNING.	Calls the cache. If the data is not cached, the handler delegates the call to the running object and returns the data from the object. (A subsequent call to IOleCache::Cache is needed to cache the data.)
GetDataHere	Calls the cache. If the data is not cached, the handler returns OLE_E_NOTRUNNING.	Calls the cache. If the data is not cached, delegates the call to the running object and returns the data from the object.
QueryGetData	Calls the cache. If the data is not cached, the handler returns OLE_E_NOTRUNNING.	Calls the cache. If the data is not cached, delegates the call to the running object and returns the data from the object.
SetData	Returns OLE_E_NOTRUNNING.	Delegates the call to the running object.


IPersistStorage Interface

In the loaded state, the default handler (after performing some action) delegates all IPersistStorage method calls to the cache. The default handler never tries to run the object as the result of an IPersistStorage call; the handler works exclusively with the cached data and never with the object's native data.

The default handler's implementation of IPersistStorage works as follows when an object is in both the loaded and running states:

IPersistStorage Methods	Object in Loaded State	Object in Running State
GetClassID	Returns what is in the object's IStorage.	Delegates the call to the running object and then to the cache.
IsDirty	Delegates the call to the cache.	Delegates the call to the running object; if the object is dirty, it returns immediately. If the object is not dirty, it asks the cache if it is dirty, returning whatever from the cache.
InitNew	Saves the IStorage and delegates the call to the cache.	Delegates the call to the running object and then to the cache.
Load	Increments the object's IStorage reference count and delegate the call to the cache.	Delegates the call to the running object and then to the cache.
Save	Saves the OLE private data stream and then delegates the call to the cache, which will write the presentation data stream.	Delegates the call to the running object and then to the cache.
SaveCompleted	Clears the internal state saved with the Save method and delegates the call to the cache.	Delegates the call to the running object and then to the cache.
HandsOffStorage	Releases the object's IStorage and delegates the call to the cache.	Delegates the call to the running object and then to the cache.


Interfaces Implemented by the Cache

This section describes the interfaces implemented by the OLE default data cache. The cache implements the IPersistStorage, IDataObject, IViewObject2, and IOleCache2 interfaces, as described in the following sections.


IPersistStorage Interface

The following table describes the cache's IPersistStorage implementation and the effect of calling its methods on an object in the loaded state:

IPersistStorage Methods	Object in Loaded State
GetClassID	Returns the CLSID passed to OleCreateDefaultHandler or CreateDataCache. GetClassId is not usually called because the default handler already recognizes the CLSID.
IsDirty	Returns NOERROR if the cache is dirty; otherwise, it returns S_FALSE.
InitNew	Saves the new IStorage reference count and returns NOERROR.
Load	Saves the IStorage; reference count. Does demand loading of the presentation streams available in the cache as required by the cache's IDataObject implementation. Regardless of the running state of the object application, an error code may be returned.
Save	Saves all changes to presentation data made since the call to Load along with information about what was cached. Regardless of the running state of the object application, an error code may be returned.
SaveCompleted	Releases the IStorage pointer and returns NOERROR.
HandsOffStorage	Releases the IStorage pointer that the cache had previously remembered.


IDataObject Interface

The following table lists the cache's IDataObject implementation and the effect of calling its methods on an object in the loaded state. The cache does not make any IDataObject method calls on the object in its running state; in the running state, the cache delegates all calls to the default handler.

IDataObject Methods	Object in Loaded State
DAdvise	Returns OLE_E_ADVISENOTSUPPORTED.
EnumDAdvise	Returns OLE_E_ADVISENOTSUPPORTED.
DUnadvise	Returns OLE_E_NOCONNECTION.
EnumFormatEtc	Returns FORMATETC enumeration; otherwise it returns OLE_E_BLANK.
GetCanonicalFormatEtc	Returns E_NOTIMPL.
GetData	Returns the data if the requested presentation was cached before this call, otherwise, returns OLE_E_BLANK. GetData supports TYMED_HGLOBAL, TYMED_MFPICT, and TYMED_GDI.
GetDataHere	Returns the data if the requested presentation was cached before this call, otherwise, returns OLE_E_BLANK. GetDataHere supports TYMED_ISTREAM and TYMED_HGLOBAL.
QueryGetData	If the requested FORMATETC was cached, then it returns NOERROR; otherwise, the cache returns S_FALSE or OLE_E_BLANK.
SetData


IViewObject2 Interface

The cache's IViewObject2 implementation is used to render objects using data from the cache. The cache tries to perform the requested action based on a presentation in the cache. The cache does not try to run the object application. If you plan to implement the IViewObject2 interface in a handler, you must implement the entire interface method set. However, in your implementation you can delegate method calls as needed to the cache's IViewObject.

The cache's IViewObject2 interface implementation is as follows when an object is in the loaded state (IViewObject2 is an extension of IViewObject; as such, it inherits all of the IViewObject methods):

IViewObject2 Methods	Effect on Object in Loaded State
Draw	Attempts to draw the object using a presentation from the cache; otherwise, it returns OLE_E_BLANK.
GetColorSet	Tries to determine the color set from the metafile or DIB in the cache. Returns OLE_E_BLANK if there is no presentation; otherwise, it returns NOERROR or S_FALSE, depending on the success of the method.
Freeze	Adds the aspect to an internal list that affects the behavior of Draw and returns NOERROR if successful. It returns OLE_E_BLANK if not. If this is a repeat request, it returns VIEW_S_ALREADY_FROZEN.
UnFreeze	Removes an entry from the internal list of frozen aspects and returns OLE_E_NOCONNECTION if the aspect was not frozen, otherwise, it returns NOERROR.
SetAdvise	Saves the IAdviseSink so that the cache itself will call its OnViewChange when the object application notifies the cache (through the cache's IAdviseSink). It returns NOERROR.
GetAdvise	Returns the last IAdviseSink and the last aspect from which the advise happened (from SetAdvise) and returns NOERROR.
GetExtent	Returns the view extents of an object using data from the cache.


IOleCache2 Interface

The cache only works with an object in its loaded state; when an object is in the running state, the default handler delegates all calls to the cache.

The following table summarizes the calling of the IOleCache2 methods, and the effect that call has on an object in its loaded state. (IOleCache2 is an extension of IOleCache and, as such, it inherits all of the IOleCache methods.)

IOleCache2 Methods	Effect on Object in the Loaded State
Cache	Tries to create a cache for the requested FORMATETC. Returns the cache connection ID and OLE_S_SAMECACHE if the FORMATETC is already cached. If the FORMATETC is already cached but a new set of Advise flags are passed in to Cache, then Cache overwrites the old Advise flags with the new ones before returning CACHE_S_SAMECACHE. (Note that these flags are advisory only. The cache may be updated more frequently, depending on the state of the running object.) In the running state, Cache checks to see if the object supports the specified FORMATETC. Even if the FORMATETC is not supported, the cache creates the cache node and returns CACHE_S_FORMATNOTSUPPORTED. There may be cases in which you get a cache connection ID of zero but no error. This implies that the FORMATETC is implicitly cached by the object as part of its native data and need not be explicitly cached (thus reducing storage space for the object).
UnCache	Invalidates the cache connection ID returned by Cache.
EnumCache	Returns an enumerator that enumerates all implicit and explicit cache nodes. For more information, see "Strategies for Caching Data with Custom Handlers."
InitCache	Using the IDataObject as the source, InitCache updates each of the associated cache nodes. NOERROR is returned if one or more nodes are updated; otherwise, CACHE_S_NOTUPDATED is returned.
SetData	Using the IDataObject as its source, SetData determines whether there are any associated cache nodes that need to be updated.
UpdateCache	Selectively updates the various cache nodes according to the control flag(s) passed in to the method. Semantically, InitCache is similar to UpdateCache with flags.


Strategies for Caching Data

The default cache supports two types of data caching: implicit and explicit. Implicit caching refers to when an object directly supports the data formats needed to render itself in the container, thus saving the overhead of storing these data formats in the object's IStorage. Explicit caching means that the data formats required to render the object are physically cached and saved with the object.

To better understand the use of implicit caching, consider the case of a custom handler used to render objects. In this case the handler can render the object directly from the object's native data in IStorage. When the container asks the handler to cache the data format for an object, the handler first checks to see if the object supports the specified data format. If it does, the handler returns a cache connection ID of zero and returns CACHE_S_SAMECACHE. A connection ID of zero indicates that the object directly supports the data format and that an explicit cache of the data format is not required. This behavior is always true for objects created by DLL object applications.

Only two cases require an explicit cache of the data:

•	The container asks to cache an iconic representation of the object (DVASPECT_ICON), or

•	The container specifies ADVFCACHE_FORCEBUILTIN cache the data by force.

In these cases, the handler delegates the call to the default cache to create a cache node for the data format explicitly. The handler must remember the connection ID returned by IOleCache::Cache and support enumerating the connection ID in IOleCache::EnumCache.

Typically, handlers use an array of STATDATAs for enumeration:

typedef struct tagSTATDATA

{

FORMATETC formatetc;

DWORD advf;

IAdviseSink * pAdvSink;

DWORD dwconnection;

} STATDATA

The number of entries in the array will be equal to the number of FORMATETCs that the object supports. The array is initialized with the supported FORMATETCs and zero for connection IDs. An explicit cache against one of the supported FORMATETCs results in a nonzero cache connection ID being stored in the corresponding STATDATA entry. At load time, the explicit caches must be enumerated and the connection IDs stored in the appropriate location in the STATDATA array.

Of note about handlers that render objects directly by using the object's native data: even if the handler is told explicitly to cache the presentation data (through ADVFCACHE_FORCEBUILTIN), the handler will not use the cached representation for drawing; it continues to draw using the native data from the object's IStorage.


Custom Object Handlers

A custom handler is simply a class-specific handler, something other than the default handler provided with OLE. Custom handlers typically implement one or more interfaces in a different manner than does the default handler. Most often the custom handler uses the features provided by the default handler, either through aggregation or delegation. The handler can delegate against an entire interface implementation or on an individual method basis. If the custom handler delegates based on methods, it must implement all of the methods in that interface. In almost all cases, a custom handler delegates caching to the OLE default cache.

Currently, a custom handler must aggregate the default handler, because this is the only means by which the object can be changed into and out of the running state. The next figure shows a common integration point for a custom object handler, between the container application and the OLE default object handler, OLE32.DLL. As shown, the custom handler acts like a filter between the container application and the default handler and object application. (The drawing shown is representative of how the sample object handler (shipped with the OLE SDK) is designed to work.

Relationship between a custom handler and the default handler.

More complex handlers can be created that not only replace the functionality offered by the default handler, but that of the object application also. In these cases, the handler is acting as a DLL object application. For more information on DLL object applications, see "Considerations for DLL Object Applications," later in this section.

In general, writing a custom handler to totally replace the functionality offered by the default object handler is unlikely to be to your advantage. Before you set out to write an object handler, determine your needs. As a first step, look at the support offered by the OLE default object handler and ask yourself what is needed beyond it.

Here are some of the reasons you might consider writing a custom object handler:

•	Custom handlers can be written to handle data formats beyond the metafile, bitmap, and DIB formats supported by the default object handler.

•	Custom handlers can be used for special resizing while an object is in its loaded state. You can design the handler to trap the incoming IOleObject::SetExtent calls and use the object's native data to render the object instead of starting the object application.

•	Custom handlers reside in the container application's process space and are much faster than object applications.

•	Custom handlers provide very fast services for display and print operations; however, because they typically do not have a user interface, they are generally not suitable for tasks requiring an object to be in its running state (for example, editing). Because they often reduce or eliminate the need to run an EXE object application; this makes certain tasks in container applications much faster.

•	Custom handlers can support any class of object, as can the default object handler. An object handler can be designed to work with just a specific class of object(s), as is discussed later in this section.

•	Custom handlers can support unique interface implementations; implementing them in a handler avoids the RPC overhead associated with providing the IMarshal interface needed for marshalling the methods to and from an object application.

•	Custom handlers lend themselves more readily to licensing for redistribution than do EXE applications.


Rendering Handlers

Rendering handlers are the most common type of custom object handler. Rendering handlers provide better drawing functionality than that offered by the default handler and they save disk storage space. Because the object can draw directly from its native data, no data cache is needed.


Rendering Objects from Native Data

Rendering an object from its native data requires the object application to first write the data to storage. The handler then draws the object from the data in storage. This requires that the handler and EXE object application coordinate the use of object's IStorage. Elements within an IStorage must be opened in STG_SHARE_EXCLUSIVE mode. Hence the storage cannot be opened by the object application and the handler at the same time.

The following list outlines the coordination of storage between the object application and the rendering handler:

•	The handler sets up an ADVF_NODATA advise with the object application.

•	The object application writes its data into the data streams and releases the streams before sending an OnDataChange to the handler. Note that the object must not commit the changes to IStorage.

•	When the handler receives the OnDataChange notification, it generates an OnViewChange notification and sets a flag to indicate it needs to get the object's new extents in a subsequent call to IViewObject2::GetExtent.

•	When the container calls IViewObject::Draw or IDataObject::GetData, the handler opens the stream, reads the data, and releases the stream immediately.


Interfaces Implemented by Rendering Handlers

A rendering handler must implement the IPersistStorage, IOleCache2, IViewObject2, and IDataObject interfaces. For a description of these interfaces and their role when implemented in a handler, see Interfaces Implemented by the Default Handler.

In addition, a rendering handler must implement the IAdviseSink interface as follows:

•	The IAdviseSink must have a different object identity from the rendering handler, meaning that it must be implemented as a separate object.

•	The handler and sink objects cannot share a common QueryInterface method.

•	When QueryInterface is called on the sink, the sink must not return pointers to the handler object's interface implementations.

•	The sink object typically supports only IUnknown and IAdviseSink. In the IAdviseSink implementation, only the OnDataChange method need be implemented; the remaining methods can be empty. In the OnDataChange method, the sink sets the flag indicating a need to get the new extents on the object and send the OnViewChange notification for the appropriate aspects to the container.


Writing a Custom Object Handler

Writing an object handler is fairly straightforward; the level and complexity of implementation is directly related to the functionality required. To write a custom object handler, do the following:

1.	Implement the IClassFactory interface and the DllGetClassObject and DllCanUnloadNow functions.

2.	Determine the additional OLE interfaces preferred for the object handler, then implement them into the handler's code.

3.	Aggregate the OLE default handler with the custom object handler.

4.	Register the object handler in the registry, using the key InprocHandler32.

In addition to these steps, all those common to any OLE application apply, including program initialization and termination, interface negotiation, object loading, and so forth.


Required Interface Implementations

An object handler must implement the IClassFactory interface and DllGetClassObject. IClassFactory enables the object handler to create objects of a specific class while the exported DllGetClassObject function returns a pointer to the handler's IClassFactory interface implementation. In addition to these, you can implement any of the other OLE interfaces, depending on the level of functionality you want. Once implemented, an interface's methods are used just as they would be when interacting with the OLE default object handler or an object application. For example, you might include an implementation of the IOleObject interface, in which DoVerb executes certain verbs on behalf of the loaded object (for example, play) while delegating all other supported verbs to the object application. Or you could decide to implement a custom IViewObject2 and IOleCache2 to support data formats other than those supported by the default handler (metafile, bitmap, and DIB formats).

Note  A custom object handler can instantiate any class of objects, just as the default object handler does. On the other hand, an object handler can be designed to work specifically with a certain class of object(s). This can be done by testing for a certain CLSID of an object and rejecting all others in the IClassFactory::CreateInstance implementation.

The following code example shows the implementation of the DllGetClassObject function and the IClassFactory::CreateInstance method, as they were written for the sample object handler that is shipped with the OLE toolkit. The implementation of DllGetClassObject is quite simple: when the container application calls one of the OLE object-creation functions, OLE first searches in the registry for the keyword InprocHandler and loads the associated object handler into memory. When the object handler is loaded , OLE then calls DllGetClassObject to return an instance of the IClassFactory interface. OLE can then use the IClassFactory pointer to call IClassFactory::CreateInstance to instantiate the object for the container application.

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppv)

//---------------------------------------------------------------------

// Exported DLL initialization is run in context of running application

// --------------------------------------------------------------------

{

HRESULT hRes;

CClassFactory *pClassFactory = new CClassFactory(rclsid);

if (pClassFactory != NULL) {

hRes = pClassFactory->QueryInterface(riid, ppv);

pClassFactory->Release(); //OK to release ptr since

 //container now responsible for

 //releasing the instantiated

 //class factory object

}

return hRes;

} //DLLGetClassObject()

CClassFactory::CClassFactory(REFCLSID rclsid)

{

m_dwRefs = 1;

m_ClsId = rclsid;

} 	// CClassFactory()

CClassFactory::~CClassFactory()

{

}	// ~CClassFactory()

STDMETHODIMP CClassFactory::CreateInstance(IUnknown * pUnkOuter,

  REFIID riid,

  LPLPVOID ppunkObject)

{

HRESULT hRes;

CHandlerObject* pObject;

*ppunkObject = NULL;	//NULL-out return parameters

if (pUnkOuter != NULL)

{

//Custom handler doesn't support being an aggregate object (although

//it aggregates the default handler)

hRes = ResultFromScode(LPSS_E_NOAGGREGATION);

goto errReturn;

}

pObject = new CHandlerObject(NULL);	//create memory for object

//Create object

if ((hRes = pObject->CreateObject(m_ClsId,riid,ppunkObject)) != NOERROR)

{

delete pObject;

}

errReturn:

return hRes;

}


Aggregating the OLE Default Object Handler

In writing a custom object handler, you must make the default object handler an aggregate object of your custom handler. By aggregating the default handler, you expose the features offered by the OLE default object handler, including the ability to cache the presentation data for an object and to launch the object application when an object must be put into its running state. In this way, you write only those interface implementations needed to support your class of object(s), while delegating other interface implementations to the default handler.

The custom handler processes all calls within its capabilities, delegating requests it does not support to the default object handler (such as cache updating). It can delegate either on a method-by-method basis or it can delegate on a complete interface implementation.

The default handler launches the object application when an object must be put in its running state. In the running state, calls from the object application to the container are first passed to the custom handler, which might process them, depending on in its interface implementations. Depending on the call, the container is then notified of changes made to an object.

To aggregate the default handler, the custom handler calls OleCreateDefaultHandler, passing in its CLSID (as registered in the registry) and a pointer to the its IUnknown interface. The CLSID is used by the default handler to locate certain entries in the registry for that object, the IUnknown is the controlling Unknown that ensures that the custom handler always gets first chance at any call. After aggregating the default handler, the custom handler can call QueryInterface to get pointers to the default handler's interface implementations so it can later delegate calls to them.

The following example illustrates how to create a custom handler that aggregates the default handler. CreateObject calls OleCreateDefaultHandler to instantiate a default handler. If the call is successful, the default handler's QueryInterface method is called to retrieve pointers to each of the interfaces to which the custom handler may delegate future calls.

HRESULT CHandlerObject::CreateObject(REFCLSID rclsid, REFIID riid,

LPVOID * ppv)

//----------------------------------------------------------------------

// CreateObject:

//----------------------------------------------------------------------

{

HRESULT hRes = ResultFromScode(E_FAIL);

ULONG dwRefs;

m_ClsId = rclsid;

//Create instance of default handler as an aggregate object

hRes = OleCreateDefaultHandler(m_ClsId, &m_Unknown, IID_IUnknown,

 (LPVOID *) &m_pDefHandler);

if (hRes != NOERROR)

{

return hRes;

}

//Cache pointers to interfaces of default handler; note that these

//are non-Addref'd pointers.

hRes = m_pDefHandler->QueryInterface(IID_IOleObject,

(LPVOID *)&m_pOleObject);

if (hRes == NOERROR)

{

//OK to  release because OleCreateDefaultHandler keeps object

//alive

dwRefs = m_Unknown->Release();

}

hRes = m_pDefHandler->QueryInterface(IID_IDataObject,

(LPVOID *)&m_pDataObject);

if (hRes == NOERROR)

{

dwRefs = m_Unknown->Release();

}

hRes = m_pDefHandler->QueryInterface(IID_IViewObject,

(LPVOID *)&m_pViewObject);

if (hRes == NOERROR)

{

dwRefs = m_Unknown->Release();

}

hRes = m_pDefHandler->QueryInterface(IID_IOleCache,

(LPVOID *)&m_pOleCache);

if (hRes == NOERROR)

{

dwRefs = m_Unknown->Release();

}

hRes = m_pDefHandler->QueryInterface(IID_IPersistStorage,

(LPVOID *) &m_pPersistStorage);

if (hRes == NOERROR)

{

dwRefs = m_pPersistStorage->Release();

}

//Get interface for whoever is creating the object

return m_Unknown.QueryInterface(riid, ppv);

}


Registering Object Handlers

Object handlers must be registered in the registry. The registration process is the same as for registering object applications, except that the keyword InprocHandler is used as the registration key.

The registry contains, by default, the following entry set belonging to the OLE default handler, OLE32.DLL.

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-c000-000000000046}\InprocHandler32 = OLE32.DLL

To register a custom handler, set this entry to the CLSID the handler is associated with and the name of the DLL, as shown in the following entry (the entry shown is for the OLE sample object handler):

HKEY_CLASSES_ROOT\CLSID\{00000402-0000-0000-c000-000000000046}\InprocHandler = DBGHNDLR.DLL

Whenever a container application calls one of the OLE object-creation functions, OLE uses the CLSID specified for the object and the InprocHandler key to search the registry. If an object handler is found for the CLSID, it is loaded and initialized. Because handlers are usually designed to work with a particular object application, the registry entry should be included with the entries for the related object application. When installing a custom handler with an object application, the preceding registration entry can be placed in the .REG file used to install the object application.

For more information on registering object handlers, see "Registering Object Applications."


Loading Object Handlers

Object handlers are loaded when the container application calls one of the following OLE object-creation functions or IClassFactory::CreateInstance:

OleCreate	OleCreateLink
OleCreateLinkToFile	OleCreateFromData
OleCreateLinkFromData	OleCreateFromFile
OleLoad	CoCreateInstance
CoGetClassObject

Once the object handler has been loaded, the container can call QueryInterface for any interface pointer, just as it usually does. If the custom object handler has implemented that interface, it returns a pointer to it; otherwise, it passes QueryInterface on to the default handler for processing.


Unloading Object Handlers

Because object handlers are DLLs, they remain in the process space of the container that instantiated them until the container frees them, usually at application shutdown. To optimize the freeing of an object handler from memory, the object handler has the option to implement and export the DllCanUnloadNow function. Containers call DllCanUnloadNow to find out whether there are any existing instances of an object class remaining for which the object handler is responsible. If there are no objects loaded, DllCanUnloadNow returns TRUE, indicating that the handler can be safely freed from memory. To take advantage of this optimization, the container must call CoFreeUnusedLibraries.

Whether or not the object handler implemented DllCanUnloadNow, the object handler is forcibly unloaded when the container application calls OleUninitialize to uninitialize the OLE libraries. DllCanUnloadNow is optional and can be used to better manage memory resources.


DLL Object Applications

The next figure shows how an object application can be implemented as a DLL object application. An object handler implemented in this manner always replaces the functionality offered by the OLE default handler and/or custom object handler. Obviously, process time is much faster, given that there is no remoting overhead or startup time. It's much easier to expose a new interface in a DLL object application because remoting is not required, hence there is no need to write custom marshalling code (see the IMarshal interface). The downside of a DLL object application is that it has full responsibility for an object, in any and all of its states.

Object application implemented as a DLL.

While OLE provides a default data cache, DLL object applications must expose their own implementations of the IOleCache2 interface, delegating method calls as needed to the default cache. Because a DLL object application replaces the default handler (which exposes the default cache), DLL object applications must call the CreateDataCache function to get an instance of the default cache.

Consider the following factors before writing a DLL object application.

•	DLL object applications are responsible for handling an object in any and all of its states. This implies that a DLL object application has a user interface for editing objects.

•	DLL object applications cannot register a message filter for handling concurrency issues (by using the IMessageFilter interface); only EXE object applications can register message filters.

•	DLL object applications are never "single use," they are always "multiple-use."

•	Because you cannot run a DLL by itself to create or load files, a DLL object application cannot be the source of a link to such files. Such a source must be run as a stand-alone executable to create the initial linked object.

•	DLL object applications are registered in the registration database using the keyword InprocServer32. Other than using the required keyword, register DLL object applications just as you would register a custom object handler (see Registering Object Handlers.

•	The DllCanUnloadNow function can be implemented by the DLL object application and called by the container application to see whether there are any existing objects in memory for which the DLL object application is responsible. If there are no objects, the DLL object application can be safely freed.

•	DLL object applications can be written to support any of the OLE interfaces, including those that handle in-place activation and drag-and-drop operations. Support of in-place activation implies there is a user interface.

•	Currently, DLL object applications are not compatible with OLE 1 applications because there is no way for the OLE 1 application to load the OLE 2 DLL. One workaround is to create a small stub EXE that handles the loading and unloading of the OLE 2 DLL.

•	DLL object applications (16-bit only) written for both 16- and 32-bit platforms must be compiled against both platforms because OLE does not automatically support interoperability across these platforms. 32-bit applications cannot run on 16-bit platforms.

•	DLL object applications have no message loop for processing Windows messages. In the case of in-place activation, where the active object is sharing a menu, the object handler must rely on the container application calling IOleInPlaceActiveObject::TranslateAccelerator before calling the Win32 API TranslateAccelerator. The DLL object handler can then call TranslateAccelerator with the appropriate table and HWND. If the DLL object application has an open edit window, there is no way to get accelerators translated properly. One solution to this problem is to set a message hook when the open window has the focus.

•	Because DLL object applications do not have a message processing loop, they cannot perform background processing during idle message processing. However, the object handler can implement a timer loop during which it processes any background tasks to work around this problem.


Required Interfaces for DLL Object Applications

A DLL object application must implement all the interfaces required of a custom handler as well as the IExternalConnection, IRunnableObject, and IOleObject interfaces. In addition, a DLL object application is free to implement any of the other OLE interfaces. Generally, a DLL object application implements at least IDataObject and IPersistStorage. DLL object applications typically don't implement the IAdviseSink interface.

The implementation of IDataObject, IPersistStorage, and IOleObject should be similar to that for an EXE object application, with one exception:

The handler's IPersistStorage::Save and IOleObject::Update methods need to call IOleCache2::UpdateCache to update all cache nodes created with ADVFCACHE_ONSAVE.

The IOleCache2 implementation should be similar to that implemented for a rendering handler; For more information, see Rendering Handlers.

The following sections describe the implementation aspects behind the IRunnableObject and IExternalConnection interfaces. For information on the other interfaces required of a custom handler, see Interfaces Implemented by the Default Handler.


IRunnableObject Interface and DLL Object Applications

IRunnableObject is also implemented by DLL object applications to change the object in and out of the running state. IRunnableObject is required only when the DLL server supports linking to embedded objects.

IRunnableObject Interface Methods	Object in Running State
GetRunningClass	Returns the object's CLSID.
Run	If the object is running, it returns NOERROR. Otherwise, the object sets the run flag to TRUE, registers itself in the Running Object Table,and puts a lock on its embedding container by calling IOleContainer::LockContainer.
IsRunning	Returns the run flag previously set in Run.
LockRunning	Calls CoLockObjectExternal to create or release a strong external connection to the object.
SetContainedObject	Returns NOERROR; can be ignored.


IExternalConnection Interface and Silent Updates to Linked Objects

The IExternalConnection interface must be implemented to support silent updates of linked objects when the DLL server supports linking to embedded objects.

IExternalConnection Interface Methods	Object in Running State
AddConnection	Creates an external connection to the running object.
ReleaseConnection	Releases an external connection previously created by AddConnection and, if this is the last connection to the object, closes the object.


OLE Property Sets

Property sets store information in such a way that any conforming program can later manipulate the information. Examples of property sets are the character formatting properties in a word processor or rendering attributes of an element in a drawing program. OLE does not provide any code or interfaces for manipulating property sets, it only specifies a standard data format structure.


Structure of Property Sets

Property sets are made up of tagged sections of values, each section uniquely identified by a name, a CLSID, and and 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 names the property. The type indicator describes the representation of the data in the value. For example, if a specific property in a property set was to hold an animal's scientific name, that name would be stored as a zero-terminated string. Stored along with the name would be a type indicator to indicate the value is a zero-terminated string. The representation of properties is shown below:

Property Identifier	Type Indicator	Value Represented
PID_ANIMALNAME	VT_LPSTR	Zero-terminated string
PID_LEGCOUNT	VT_I2	WORD
Any application that recognizes the property set format can look at the property with an ID of PID_ANIMALNAME, determine it is a zero-terminated string, and read and write the value. Of course, the given application might not have any information about the property. The standard structure defined by OLE for property sets is generic as it relates to the semantics of the properties being represented; it says nothing about what they mean.


Examples of Using Property Sets

To illustrate the diverse functionality of property sets, this section presents two samples. The first uses the OLE Documentary Summary Information property set to show how property sets can be stored within files to allow common access to the information represented. The second example shows how property sets can be transferred between applications or OLE objects as an effective means of communication.


Storing Document Summary Information

The Document Summary Information property set is one of the simplest and most commonly used property sets. 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.

In Windows 3.1, each application has a different way of storing this information within its documents. To examine the summary information for a given document, the user has to run the application that created the document, open it, and invoke the application's Summary Information dialog box. For example, the summary information dialog boxes that Microsoft Word displays for its documents are shown in the following drawings:

Sample Summary Information dialog boxes.

Unfortunately, no application but Microsoft Word can display the summary information for the document. Eventually, the OLE Document Summary Information Property Set is likely to help transcend this limitation. Any application that understands the data format for property sets will be able to access the summary information contained in any document created by an OLE application that also understands the format.

For more information about the Document Summary Information property set and implementing it in your applications, see Guidelines for Implementing the Document Summary Information Property Set.


Transferring Data Contained in Property Sets

Storing information about the contents of a document is useful, and a standard that would allow all files to share the same information with all applications would be even better. However, this is only one example of how property sets can be used.

Another use for them is in the transfer of data between OLE objects or applications. To better understand the process of transferring data between two OLE objects, consider the following example. A trader on Wall Street relies on large amounts of data to make important financial decisions regarding trades of securities. This market data must be delivered to the trader's workstation in real time (or at least very soon after it was generated).

Assume that, at any given moment during an open day on Wall Street, the trader is interested in the opening, high, low, current, and last closing price of an instrument (a specific stock), and the volume of trading on that stock so far that day.

As noted earlier, a property in a property set has three attributes: the property identifier, a value type indicator, and a value. In this example, the following properties can be defined:

Property Identifier	Type Indicator	Value Represented
PID_SYMBOL	VT_LPSTR	Zero-terminated string
PID_OPEN	VT_CY	Currency value
PID_CLOSE	VT_CY	Currency value
PID_HIGH	VT_CY	Currency value
PID_LOW	VT_CY	Currency value
PID_LAST	VT_CY	Currency value
PID_VOLUME	VT_I4	32-bit unsigned integer
Without worrying about how the data is transferred between the data server and the trader's application, think about the data transferred and its format. Every property set must have a FMTID associated with it.

A single data element representing the stock of XYZ during the day might look like this:

PID_SYMBOL 	contains "XYZ"

PID_OPEN	contains 42-3/4

PID_CLOSE	contains 42-3/4

PID_HIGH	contains 49-1/8

PID_LOW	contains 40-7/8

PID_LAST	contains 47-3/8

PID_VOLUME	contains 123,032

During a trading day on Wall Street only a few of the properties of a single instrument change. The opening price doesn't change all day, and the low price may go unchanged for several hours. Using property sets as the data transfer format allows applications to transfer only the data that has changed. For example, at some point during the day the Data Server may update the Traders Application by transferring the following StockQuote property set:

PID_SYMBOL	contains "XYZ"

PID_HIGH	contains 49-1/8

PID_LAST	contains 49-1/8

PID_VOLUME	contains 23,321

This example demonstrates how property sets can be used as a data transfer format, allowing sparse data representation. Transferring only the changed data enhances the overall performance of the trader's workstation.


Notes on Using Property Sets

OLE property sets were designed to store data that is suited to representation as a collection of fine-grained values. Furthermore, it is assumed that the entire stream containing the property set would be read and/or written as a whole. Data sets that are too large for this to be feasible should be broken into separate streams and/or storages. The OLE property set data format was not meant to provide a substitute for a database of millions of tiny objects.


OLE Property Set Specification

All data elements within a property set are stored in Intel representation (byte order).

OLE defines a standard, serialized data format for property sets, which gives them the following characteristics:

•	Property sets may be stored in a single IStream instance or in an IStorage instance containing multiple streams.

•	Property sets can be transmitted using the IDataObject interface and/or the Clipboard. Because property sets are self-identifying, they are ideal for transferring data between applications.

•	Property sets are open-ended. Additional type identifiers can be published allowing new data types to be represented. For more information, see Defining New Type Indicators.

•	Property sets allow for extensibility. A property set may be defined as an extension of an existing property set, allowing readers to skip over unknown extensions, and preserve them, while extracting information from the properties that they do know.

•	Property sets allow for a dictionary of human-readable names to be included to further describe the contents. For more information on this optional dictionary, see Property Set Dictionaries.

The figure below shows the overall structure of a property set:

Serialized format for property sets.

The following sections describe the individual components that make up the property set data format shown in the previous figure.


Property Set Header

The property set stream can be divided into three major parts. At the beginning of the first part is a header. It consists of a byte-order indicator, a format version, the originating operating system version, the CLSID, and a count of sections within the property set (there must be at least one). Each offset is the distance in bytes from the start of the whole stream to where the section begins.

The following pseudo-structures illustrate the header:

typedef struct tagPROPERTYSET

{

// Header

WORD	wByteOrder ;	// Always 0xFFFE

WORD	wFormat ;	// Always 0

WORD	dwOSVer ;	// System version

CLSID	clsID ;	// Application CLSID

DWORD	cSections ; 	// Number of sections (must be at least 1)

// Format ID/Offset pairs

FORMATIDOFFSET 	rgFIDO[cSections] ;

// Sections

PROPERTYSECTION 	rgSections[cSections] ;

} PROPERTYSET, FAR * LPPROPERTYSET ;


Byte-Order Indicator

The byte-order indicator is a WORD and should always hold the value 0xFFFE. This is the same as the Unicode byte-order indicator. When written in Intel byte order, this appears in the file or stream as 0xFe (0). The byte order indicator lets generic tools determine whether the file or stream is written in Intel byte order.


Format Version

The Format Version is a WORD used to indicate the format version of this stream. It should always be zero. The format-version indicator should be checked when reading the property set. If it is not zero, then the stream was written to a different specification and cannot be read by code developed according to the OLE 2 specification.


Originating OS Version

This DWORD should hold the kind of operating system in the high word and the version number of the operating system in the low 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 of GetVersion. 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


CLSID

The CLSID is that of a class that can display and/or provide programmatic access to the property values. If there is no such class, this value should be CLSID_NULL.


Count of Sections

This DWORD indicates how many sections exist in the stream. A property set can consist of any number of sections. Readers must be able to handle the case where there are zero sections.


Format Identifier/Offset Pairs

The second part of the property set stream contains Format Identifier (FMTID)/Offset Pairs. Each of these pairs both names and points to a section within a property set. The FMTID uniquely identifies how to interpret the contents of a 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

{

GUID  	FormatID ;

DWORD	dwOffset ;

} FORMATIDOFFSET, FAR * LPFORMATIDOFFSET;


Format Identifiers

To allow for extension of property sets over time, while still permitting older clients to access the properties that they do understand, the values are stored in a sequence of sections, each tagged with a unique FMTID. Each property set FMTID indicates the meaning of the properties in the section, and succeeding sections are assumed to be defined as extensions of the preceding one so that property identifiers do not conflict.

For example, the FMTID for the OLE Document Summary Information property set is:

F29F85E0-4FF9-1068-AB91-08002B27B3D9

To define a FMTID for the Document 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(FormatID_SummaryInformation, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9);

Anywhere in your code you need to use the FMTID for the Document Summary Information property set, you can access it through the FormatID_SummaryInformation variable.


Allocating Format Identifiers

FMTIDs are created and represented in the same way as OLE CLSIDs and interface IDs. To create a unique FMTID, use the UUIDGEN.EXE program included in the OLE SDK.


Sections

This is the third part of the property set stream, as shown in the next figure. Property sets can contain many sections to allow for the extension of property sets over time. To allow older clients to access the properties that they do understand, property values are stored in a sequence of sections. The oldest definition would be found in the first section, with newer or extended definitions following. Because each section is tagged with a unique FMTID and succeeding sections are defined as extensions to the preceding ones, property identifiers do not conflict.

Each section contains:

•	A byte count for the section (which is inclusive of the byte count itself)

•	A count of the property values in the section

•	An array of 32-bit Property ID/Offset pairs

•	An array of property Type Indicators/Value pairs

The two arrays must be in corresponding order. Offsets are the distance from the start of the section to the start of the property (type, value) pair. This allows sections to be copied as an array of bytes without any translation of internal structure.

The following pseudo-structure illustrates the format of a section:

typedef struct tagPROPERTYSECTION

{

DWORD	cbSection ;	// Size of Section

DWORD	cProperties ;	// Count of Properties in section

// Property ID/Offset Pairs

PROPERTYIDOFFSET	rgPropIDOffset[cProperties] ;

// Type/Value Pairs

PROPERTY  	rgTypeValue[cProperties] ;

} PROPERTYSECTION, FAR *LPPROPERTYSECTION ;


Size of Section

This DWORD indicates the size of the section. Because the section size is the first four bytes, you can copy sections as an array of bytes.

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 eight bytes:

08 00 00 00 00 00 00 00


Count of Properties

This DWORD 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 ID/Offset Pairs

Following the Count of Properties is an array of Property ID/Offset Pairs. Property IDs are 32-bit values that uniquely identify a property within a section. The Offsets indicate the distance from the start of the section to the start of the property Type/Value Pair. If the offsets are relative to the section, sections can be copied as an array of bytes.

Property IDs 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 IDs.

Property ID/Offset Pairs can be described by the following structure:

typedef struct tagPROPERTYIDOFFSET

{

DWORD	PropertyID;

DWORD	dwOffset;

} PROPERTYIDOFFSET, FAR * LPPROPERTYIDOFFSET;


Type Indicators

After the table of Property ID/Offset Pairs comes the actual properties. Each property is stored as a DWORD type followed by the data value.

typedef struct tagPROPERTY

{

DWORD	dwType;

 	BYTE	rgbValue[ /*size of value*/ ];

} PROPERTY, FAR * LPPROPERTY;

Type indicators and their associated values are defined in the OLE header files that are shipped with the Win32 SDK.

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 value must align with its natural alignment rather than with a 32-bit alignment. In practice, this is only significant for types VT_I2 and VT_BOOL (which have two-byte natural alignment). All other types have four-byte natural 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.

A property value of type identifier VT_LPSTR | VT_VECTOR would be:

•	A DWORD element count (DWORD cch), followed by

•	A sequence of strings (char rgch[]), each of which may be followed by null padding to round to a 32-bit boundary.

The following table lists the standard OLE-defined property type indicators and their meaning:

Type Indicator	Code	Value Representation
VT_EMPTY	0	None. A property set 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_I2	2	Two bytes representing a 2-byte signed int value. This value will be zero-padded to a 32-bit boundary.
VT_I4	3	Four bytes representing a 4-byte signed int value.
VT_R4	4	Four bytes representing a 32-bit IEEE floating point value.
VT_R8	5	Eight bytes representing a 64-bit IEEE floating point value.
VT_CY	6	Eight-byte two's complement integer (scaled by 10,000). This type is commonly used for currency amounts.
VT_DATE	7	Time format used by many applications, it is a 64-bit floating point number representing seconds since January 1, 1900. This is stored in the same representation as VT_R8.
VT_BSTR	8	Counted, zero-terminated binary string; represented as a DWORD byte count (including the terminating null character) followed by the bytes of data.
VT_BOOL	11	Two bytes representing a Boolean (WORD) value containing 0 (FALSE) or -1 (TRUE). This type must be zero-padded to a 32-bit boundary.
VT_VARIANT	12	Four-byte indicator followed by the corresponding value. This is only used in conjunction with VT_VECTOR.
VT_I8	20	Eight bytes representing a signed integer.
VT_LPSTR	30	Same as VT_BSTR; this is the representation of most strings.
VT_LPWSTR	31	A counted and zero-terminated Unicode string; a DWORD character count (where the count includes the terminating null character) followed by that many Unicode (16-bit) characters. Note that the count is not a byte count, but a WORD count.
VT_FILETIME	64	64-bit FILETIME structure, as defined by Win32: typedef struct_FILETIME{   DWORD dwLowDateTime;   DWORD dwHighDateTime; }FILETIME;
VT_BLOB	65	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 would have a count of zero, followed by zero bytes. This is similar to VT_BSTR but does not guarantee a null byte at the end of the data.
VT_STREAM	66	A VT_LPSTR (DWORD count of bytes followed by a zero-terminated string that many bytes long) that names the stream containing the data. The real value for this property is stored in an IStream, which is a sibling to the CONTENTS stream. This type is only valid for property sets stored in the CONTENTS stream of an IStorage.
VT_STORAGE	67	A VT_LPSTR (DWORD count of bytes followed by a zero-terminated string that many bytes long) that names the storage containing the data. The real value for this property is stored in an IStorage, which is a sibling to the CONTENTS stream that contains the property set. This type is only valid for property sets stored in the CONTENTS stream of an IStorage.
VT_STREAMED_OBJECT	68	Same as VT_STREAM, but indicates that the IStream named in this property contains a serialized object, which is a CLSID followed by initialization data for the class. The named IStream is a sibling to the CONTENTS stream that contains the property set. This type is only valid for property sets stored in the CONTENTS stream of an IStorage.
VT_STORED_OBJECT	69	Same as VT_STORAGE, but indicates that the IStorage named in this property contains an object. This type is only valid for property sets stored in the CONTENTS stream of an IStorage.
VT_BLOB_OBJECT	70	An array of bytes containing a serialized object in the same representation as would appear in a VT_STREAMED_OBJECT (VT_LPSTR). The only significant difference between this type and VT_STREAMED_OBJECT is that VT_BLOB_OBJECT does not have the system-level storage overhead as VT_STREAMED_OBJECT. VT_BLOB_OBJECT is more suitable for scenarios involving numerous small objects.
VT_CF	71	An array of bytes containing a Clipboard format identifier followed by the data in that format. That is, following the VT_CF identifier is the data in the format of a VT_BLOB. This is a DWORD count of bytes followed by that many bytes of data in the following format: a LONG followed by an appropriate Clipboard identifier and a property whose value is plain text should use VT_LPSTR, not VT_CF to represent the text. Notice also that an application should choose a single Clipboard format for a property's value when using VT_CF. For more information on Clipboard format identifiers, see Clipboard Format Identifiers.
VT_CLSID	72	A CLSID, which is a DWORD, two WORDs, and eight bytes.
VT_VECTOR	0x1000	If the type indicator is one of the previous values in addition to this bit being set, then the value is a DWORD count of elements, followed by that many repetitions of the value. When VT_VECTOR is combined with VT_VARIANT (VT_VARIANT must be combined with VT_VECTOR) the value contains a DWORD element count, a DWORD type indicator, the first value, a DWORD type indicator, the second value, and so on. Examples: VT_LPSTR | VT_VECTOR has a DWORD element count, a DWORD byte count, the first string data, a DWORD byte count, the second string data, and so on.  VT_I2 | VT_VECTOR has a DWORD element count followed by a sequence of two-byte integers, with no padding between them.


Defining New Type Indicators

There is no provision in the OLE specification for adding new type indicators to the list above. Property sets are designed to be partially self-describing so that code that does not recognize everything about the property set can at least tell the types of values contained in it. If new VT tags could be defined, it would be impossible for code to skip over values that are not understood. However, new types can be defined by using the VT_VARIANT type combined with the VT_VECTOR flag. For example, assume you want to store the following packed structure in a property set:

typedef struct tagPACKED

{

DWORD	dwValue1 ;	// 32 bit value

WORD	wFlag ;	// 16 bits of flags

WORD	wValue2 ;	// 16 bit value

} PACKED ;

This 64-bit structure could be stored using VT_VARIANT | VT_VECTOR as follows:

DWORD	// dwTypeIndicator = VT_VARIANT | VT_VECTOR ;

DWORD	// dwElementCount = 3 ;

DWORD // dwTypeIndicator = VT_I4 ;

DWORD	// dwValue1 ;

DWORD // dwTypeIndicator = VT_I2 ;

WORD	// wFlag ;

DWORD // dwTypeIndicator = VT_I2 ;

WORD	// wValue2 ;


Extending an Existing Property Set

A property set can be defined as an extension to an existing property set. To do this, allocate a new FMTID using the GUUID.EXE utility. Then define property identifiers for your extension that do not conflict with the identifiers in the set you are extending.

Extensions defined in this manner allow clients of the property set to skip over extensions they do not know, but still access properties in the original definition. The serialized data format for property sets stores both the base and extension FMTIDs in the property set.


Reserved Property IDs

As a designer of property sets you can use any Property ID for your properties except 0 and 1, which are reserved for use by applications as follows:


Property ID Zero

To enable users of property sets to attach meaning to properties beyond those provided by the type indicator, property ID zero is reserved for an optional dictionary of human-readable names for the property set.

The value will be an array of Property ID/String pairs, where the first ID is zero and its corresponding string is the name of the property set. The remaining entries are the IDs and corresponding names of the properties. The dictionary is stored as a VT_BLOB | VT_VECTOR value. Each BLOB in the vector contains the prerequisite DWORD count of bytes followed by a DWORD indicating the Property ID followed by the name (a zero-terminated string).

Property ID zero must be in the following format:

•	A DWORD count of the number of following pairs

•	An unsorted sequence of pairs of the form:

DWORD propertyID, LPSTR propertyName

Not all the names of the properties in the set need appear in the dictionary. The dictionary in property ID zero can 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 extensions or special purpose sets may include dictionaries for use by browsers.


Property Set Dictionaries

Property sets may contain an optional dictionary of human-readable names that describe the properties in the set. The property set dictionary is stored in property ID zero.

The dictionary is stored as a list of Property ID/String pairs. This can be illustrated using the following pseudo-structure definition for a dictionary entry (it's a pseudo-structure because the sz[] member is variable in size):

typedef struct tagENTRY

{

DWORD	dwPropID ;	// Property ID

DWORD	cb ;	// Count of characters in the string

char	sz[cb];	// Zero-terminated string.

} ENTRY ;

typedef struct tagDICTIONARY

{

DWORD	cbEntries ;	// Count of entries in the list

ENTRY	rgEntry[ cbEntries ] ;

} DICTIONARY ;

Note the following about property set dictionaries:

•	Property ID Zero does not have a type indicator. The DWORD that indicates the count of entries sits in the type indicator position.

•	If the dictionary exists, the first entry in this list must contain a property ID (dwPropID) of zero, and a string that names the property set. In other words, the name of the property set is stored using property ID zero and is always the first element in the list.

•	The count of characters in the string (cb) includes the zero character that terminates the string.

•	The dictionary is entirely optional. However, if it does exist, it must contain at least one entry (the name of the property set). As already pointed out, not all the names of properties in the set need appear in the dictionary. The dictionary may omit entries for properties assumed to be universally known by clients that manipulate the property set.


Example Dictionary

The stock market data transfer example (see Transferring Data Contained in Property Sets) might include a human-readable name of "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	43 01 00 00	; DWORD size of section

0004	02 00 00 00	; DWORD number of properties in section

; Start of PropID/Offset pairs

0008	00 00 00 00	; DWORD Property ID (0 == dictionary)

000C	18 00 00 00	; DWORD offset to property ID

0010	03 00 00 00	; DWORD Property ID (i.e. PID_SYMBOL)

0014	00 00 00 00	; DWORD offset to property ID

; Start of Property (Type/Value Pair, which is really the dictionary

; because it has Property ID 0)

0018	08 00 00 00	; DWORD Number of entries in dictionary

; (NOTE: No type indicator!!)

001C	00 00 00 00	; DWORD dwPropID = 0

0020	00 00 00 00	; DWORD cb = strlen("Stock Quote")+1 == 12

0024	"Stock Quote\0"	; char sz[12]

0030	03 00 00 00	; DWORD dwPropID = 3 (PID_SYMBOL)

0034	00 00 00 00	; DWORD cb = strlen("Ticker Symbol")+1 == 14

0038	"Ticker Symbol\0"	; char sz[14]

... ; dictionary would continue, but may not contain entries for

  ; every possible entry. Also entries do not need to be in order

  ; (except dwPropID == 0 must be first).

; Start of Property (Type/Value Pair)

013B	1E 00 00 00	; DWORD type indicator (VT_LPSTR == 1E)

013F	05 00 00 00	; DWORD count of bytes

0143	"MSFT\0"	; char sz[5]


Property ID One

Property ID One is reserved as an indicator of which code page (Windows) or Script (Macintosh) any strings in the property set originated with. All values must be stored with the same code page. If an application cannot understand this indicator, it should not modify the property. 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 property and either write its values out to that code page or rewrite all other string values in the property set to the new code page and modify the code page's property ID value. Possible values are given in the Window 32 API (see the GetACP function) and Inside Macintosh Volume VI, §14-111. If the code page indicator is not present, the prevailing code page on the reader's machine must be assumed. Property ID one begins with a VT_I2 type indicator.


Clipboard Format Identifiers

There are five kinds of Clipboard FMTIDs that can occur in VT_CF values: Windows Clipboard Format Values, Macintosh Format Values, Format Identifiers, Clipboard Format Names, and No Format Name. The following table illustrates the representation of each of these five types:

First Four Bytes	Following Value Size	Meaning
-1L	4 bytes (DWORD)	Windows built-in Clipboard Format Value (CF_TEXT).
-2L	4 bytes (DWORD)	Macintosh Format Value (4-byte tag).
-3L	16 bytes (Format ID)	FMTID.
Length of String	Variable	Clipboard format name that has been registered by RegisterClipboardFormat (or some Macintosh equivalent, if any).
0L	Zero	No format name.
Therefore, the format of a VT_CF value is:

DWORD cb ;	// count of bytes that follow (4 + cbTag +

// cbData)

DWORD cftag ;	// contains one of the five cftag values

// (0,-1,-2,-3, or positive)

BYTE rgcftag[ cbTag ] ;	// cbTag bytes representing the FMTID

BYTE rgData[ cbData ] ; 	// clipboard data in the specified format

For example, if a VT_CF property contained a 4235 byte bitmap that was stored in the Windows CF_DIB (0x08) Clipboard format, the count of bytes would be 4243 (4235 + 4 + 4) or 0x1093. The following stream of bytes would be stored:

93 10 00 00 FF FF FF FF 08 00 00 00 ?? ?? ?? ?? ?? ?? ...

|--- cb ---|-- cftag --|- rgcftag -|--- rgData (4235 bytes )...


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.

Note  If you are storing a property set that is internal to your application, you might not want to follow the guidelines described below. If you want to expose your property set to other applications, you need to follow the 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. Prepend the name of your IStorage or IStream with "\0x05." 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 256Kb. The names can be selected from either published names and formats or by creating a new name and assigning the property set a CLSID and FMTID.

2.	A property set may be stored in a single IStream instance or in an IStorage instance containing multiple streams. In the case of an IStorage, 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 and/or provide programmatic access to the property values. If there is no such class, the the CLSID should be set to CLSID_NULL. For a property set that uses an IStorage instance, either set the CLSID of the IStorage to be the same as that stored in the CONTENTS stream or to CLSID_NULL (the value in a newly created IStorage).

4.	Define the property set, assigning it unique format and property IDs. A property set can be defined as an extension of an existing property set by allocating a new format ID and property IDs that do not conflict with the base set. The serialized representation includes the base set and the extension, and both format IDs. As noted earlier, this allows readers to skip over unknown extensions while still extracting information from the properties that they do understand.

5.	You have the option of specifying human-readable 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, unless the property set definition indicates otherwise. For example, the Document Summary Information property set's definition says that it can only be stored in a named IStream. In cases where you are searching for a property set and you don't know whether it is a storage or stream, look for an IStream with your property set name first. If that fails, look for an IStorage.

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 (FormatID_AnimalInfo) and others containing medical information (FormatID_MedicalInfo).

IStorage (File): "C:\OLE\REVO.DOC"

IStorage: "\005AnimalInfo", CLSID = CLSID_AnimalApp

IStream: "CONTENTS"

WORD dwByteOrder, WORD wFmtVersion, DWORD dwOSVer,

CLSID CLSID_AnimalApp, DWORD cSections...

...

FormatID = FormatID_AnimalInfo

Property: Type = PID_ANIMALTYPE, Type = VT_LPSTR, Value = "Dog"

Property: Type = PID_ANIMALNAME, Type = VT_LPSTR, Value = "Revo"

Property: Type = PID_MEDICALHISTORY, Type = VT_STREAMED_OBJECT,

Value = "MedicalInfo"

...

IStream: "MedicalInfo"

WORD dwByteOrder, WORD wFmtVersion, DWORD dwOSVer,

CLSID CLSID_AnimalApp, DWORD cSections...

...

FormatID = CLSID_MedicalInfo

Property: Type = PID_VETNAME, Type = VT_LPSTR, Value = "Dr. Woof"

Property: Type = PID_LASTEXAM, Type = VT_DATE, Value = ...

Note that the class IDs of the IStorage 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 ID of CLSID_AnimalApp can understand the meaning of the data in the property sets.


The Document Summary Information Property Set

OLE defines a standard common property set for storing summary information about documents. The Document Summary Information property set must be stored in an IStream instance off of the root IStorage; it is not valid to store the property set in the "CONTENTS" stream of a named IStorage.

All shared property sets are identified by a stream or storage name prepended with "\005" (or 0x05) to show it is a property set shareable among applications and the Document Summary Information property set is no exception. The name of the stream that contains the Document Summary Information property set is:

"\005SummaryInformation"

The FMTID for the Document Summary Information property set is:

F29F85E0-4FF9-1068-AB91-08002B27B3D9

Use the DEFINE_GUID macro to define the FMTID for the property set:

DEFINE_GUID(FormatID_SummaryInformation, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9);

On an Intel byte-ordered machine, the FMTID has the following representation:

E0 85 9F 4F 68 10 AB 91 08 00 2B 27 B3 D9

The following table shows the property names for the Document Summary Information property set, along with the respective property IDs and type indicators.

Property Name	Property ID	Property ID Code	Type
Title	PID_TITLE	0x00000002	VT_LPSTR
Subject	PID_SUBJECT	0x00000003	VT_LPSTR
Author	PID_AUTHOR	0x00000004	VT_LPSTR
Keywords	PID_KEYWORDS	0x00000005	VT_LPSTR
Comments	PID_COMMENTS	0x00000006	VT_LPSTR
Template	PID_TEMPLATE	0x00000007	VT_LPSTR
Last Saved By	PID_LASTAUTHOR	0x00000008	VT_LPSTR
Revision Number	PID_REVNUMBER	0x00000009	VT_LPSTR
Total Editing Time	PID_EDITTIME	0x0000000A	VT_FILETIME
Last Printed	PID_LASTPRINTED	0x0000000B	VT_FILETIME
Create Time/Date (*)	PID_CREATE_DTM	0x0000000C	VT_FILETIME
Last saved Time/Date (*)	PID_LASTSAVE_DTM	0x0000000D	VT_FILETIME
Number of Pages Number of Words Number of Characters	PID_PAGECOUNT PID_WORDCOUNT PID_CHARCOUNT	0x0000000E 0x0000000F 0x00000010	VT_I4 VT_I4 VT_I4
Thumbnail	PID_THUMBNAIL	0x00000011	VT_CF
Name of Creating Application	PID_APPNAME	0x00000012	VT_LPSTR
Security	PID_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.


Guidelines for Implementing the Document Summary Information Property Set

The following guidelines pertain to the Document Summary Information property set described in the preceding section:

•	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.

•	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 appropriately. An application should not display any information about a password protected document or allow modifications to enforced read-only or locked-for-annotations documents. Applications should warn the user about read-only recommended if the user attempts to modify properties:

Security Level	Value
None	0
Password protected	1
Read-only recommended	2
Read-only enforced	3
Locked for annotations	4


Why Unicode?

For the past 20 years or so, except in the case of some mainframe computers, the American Standard Code for Information Interchange (ASCII) has been the dominant standard for coding information on computers and related equipment. The American National Standards Institute defines ASCII as a 7-bit code with sufficient codes (128) to provide for 26 lowercase letters, 26 uppercase letters, 10 numbers, punctuation, symbols, and control codes. The limitations of the ASCII-provided character set become obvious when attempting to deal with information written in languages other than English (and actually English as well, since no code is provided for the pound sterling sign). A clear limitation is the lack of codes for the accented characters used by many European languages, characters derived from the original Latin alphabet. Further east, languages such as Greek, Hebrew, and Hindi don't use the Latin alphabet at all, severely limiting the usefulness of the ASCII character set. The same problem applies to the written languages of the Far East; in particular Chinese, Japanese, and Korean.

Due to the 7-bit, 128-character limitation of ASCII and the fact that many systems use 8-bit bytes, the IBM® extended-character set later added an eighth bit to extend the ASCII character set by 128 characters. The IBM set includes several accented letters used in European languages, plus lowercase Greek letters that are useful for coding mathematical expressions. The IBM set also includes a set of line-draw characters, but it still falls short of satisfying the requirements of all "Latin-based" languages. As a result, additional extended-character sets, called code pages, were developed that mapped the 8-bit codes to specific sets of characters to satisfy the character-set requirements of different languages and keyboards.

To facilitate coding in an increased number of languages, another 8-bit extended character set was developed. This set, based on an International Standards Organization (ISO) standard and referred to as the ANSI character set, replaced the line-draw characters of the IBM extended set with additional accented characters. While the ANSI set enables the coding of an increased number of Latin-based languages, it leaves the non-Latin languages basically unsupported.

Yet another solution to the ASCII limitation is offered by double-byte character sets (DBCS). DBCS define some characters as requiring one byte and others as requiring two, where those requiring two bytes have a first-byte value of greater than 0x80. Although the size of this character set enables the coding of both Latin-based and non-Latin-based languages, there are multiple standards for it. Plus, DBCS string operations require a little more processing because the lengths of the DBCS characters are not uniform.

So enters Unicode™, containing the original 8-bit ASCII codes plus eight more bits for a total of 16 bits per character. Unicode solves the aforementioned problems, as well as several not mentioned, by providing a single uniform 16-bit system that eliminates the multiple 256-character code mappings required by ASCII and the variable character length inherent in DBCS. With a total character space of 65,536 and with 34,000 currently mapped characters, Unicode is sufficient to represent the most common characters and logo graphs used in the world's written languages and still provide for future growth.


Benefits Provided by Unicode

Many applications can benefit from increased speed, portability, and functionality, not to mention the ease of creating versions of the product for the international market, by processing data as Unicode rather than as ANSI, ASCII, or DBCS characters. Unicode applications can consume more space for data storage, ranging from slightly more space to as much as double, depending on factors such as the type of data stored and the use of data compression. However, this increased storage requirement is offset somewhat by a decrease in the amount of code needed to handle DBCS characters or the substitution of different code pages.

An application that handles DBCS characters has to do extra processing to ensure that it handles one character at a time, since a character can consist of either one or two bytes. For example, an application can't simply allocate 6 bytes for a 6-character string, nor can it skip over a string of characters to a known point unless it examines each of the characters being skipped over. This extra handling is especially time consuming when moving backward over a string, since finding the previous character is not a trivial task in most DBCS schemes.

Some additional performance benefits are possible with Unicode in that the Windows NT-based kernel doesn't have to translate the application's resources (menus, strings, dialog boxes, and so on) to ANSI from their stored Unicode form. Unicode is the native code set of Windows NT, but the Win32 subsystem provides both ANSI and Unicode support. Character strings in the system, including object names, path names, and file and directory names, are represented with 16-bit Unicode characters. The Win32 subsystem converts any ANSI characters it receives into Unicode strings before manipulating them. It then converts them back to ANSI, if necessary, upon exit from the system. If you convert your application to Unicode, the system will not have to perform this conversion and you will see a performance increase.

When you use Unicode, you no longer have to keep a separate version of your application for each language that you support and you can remove all those #ifdefs from your code that depend on the country or the language. Instead, you simply create separate resources containing the different error messages, informational strings, and so on that your application needs for each locality. Also, multiple versions of the source files may not be needed, so fixing a bug for the English version automatically fixes it for the Greek and Spanish versions.

Since a Unicode application can process and display textual information used by virtually every country in the world, such an application may be more useful in handling documents written in different languages. For instance, a person in Greece can write a document using Greek characters and anyone using the same Unicode application will be able to see the document exactly as composed by the originator. This is in contrast to viewing some barely readable document because the code page in use doesn't support the Greek alphabet.

And finally, you can make your Unicode application more portable by using portable type definitions rather than the char type. For instance, some compilers default to a signed character, others to an unsigned character. This difference in compilers can cause problems when moving an application from another environment, such as Unix, or from one Win32 platform to another. Using portable types, you can also write an application so that it can be compiled to handle either ASCII or Unicode. This technique of using portable types allows an application to be small and fast in countries where a single ASCII code page is sufficient, yet a recompile allows it to be fast in countries where Unicode is the preferred character set, such as in Greece, Russia, Japan, and Israel.


Common Questions About Unicode

Although each developer has his or her own list of priorities when making a decision about changes to a code base, each of the following questions is commonly asked when considering conversion to Unicode.


Will My Application be Larger if I Convert to Unicode?

One point commonly raised when talking about converting an application to Unicode is a concern about doubling the size of the application. It's true that your application might double if what you are converting is pure text. However, most applications are not pure text; rather they are mostly code. Since Unicode uses 16 bits to represent each character and ANSI only uses 8 bits to do the same, it follows that all strings will be twice as long under Unicode than under ANSI. Also, bear in mind that compression can usually offset this increase in size.


What if My Unicode Application Needs to Get ANSI Input?

Just because your application is Unicode does not mean that it no longer works with ANSI applications. There are two functions in particular: MultiByteToWideChar and WideCharToMultiByte, that convert between Unicode and non-Unicode input. It is best to use the wide character C run-time functions to read or write Unicode data and to use the regular C run-time functions to read or write existing ANSI files in conjunction with MultiByteToWideChar and WideCharToMultiByte. These mapping functions are supported by all versions of the Win32 API.

If your application is a console application (a character-based Win32 application) or uses putc, getc or other C run-time write functions, you will need to retain ANSI write while doing file I/O. This is because CMD.EXE writes ANSI to pipes, stdin, and, stdout. In future versions of Windows NT, there will be a Unicode mode for the command shell, CMD.EXE.

Conversion from ANSI to Unicode and back is guaranteed in Windows NT, but characters outside the ANSI character set will not be converted. In general, all code pages in MS-DOS or Windows can be converted to Unicode. The UCONVERT sample demonstrates this conversion; however, not all the available mapping tables are activated in UCONVERT. If your application needs to translate from other tables, you can install these tables; just follow the directions given in the sample. Installing other mapping tables may be particularly useful for applications that get data from non-Windows NT and non-Windows sources such as mainframes that handle EBCDIC data.


What if I Need my Application to Run on a Non-Unicode System?

If you are keeping a common code base between Win32 and 16-bit Windows for an application, you can still take advantage of Unicode. You can use the generic type definitions (TCHAR, LPTSTR, LPTCH, and so on), the TEXT macro, and the header files from the Win32 SDK, which make it possible to write code that can be compiled both as an ANSI and as a UNICODE application. In source files, all strings are ANSI. The TEXT macro causes the compiler to convert the strings only if UNICODE is specified in a #define statement. Similarly, the wide versions of the Win32 API are only used if UNICODE is specified in a #define statement.

Also, Win32s does not support Unicode. However, you can still design your program as a Unicode program and explicitly convert back and forth between ANSI and Unicode using the mapping functions MultiByteToWideChar and WideCharToMultiByte. While this may seem like extra work, it does offer two major advantages:

•	You can maintain a Unicode file format for an application, for both the Win31 version running Win32s and the NT version, the only limitation being that the Win32s version cannot display or edit all of the extra characters

•	You can put the core code in a DLL and it can be maintained as a single executable image. This reduces the amount of testing you would need to do than if you had to have two executables for your application; one Unicode and one ANSI.


What Support is There for Unicode in the Win32 SDK?

Neither the Win32 SDK tools nor Visual C++ (VC++) supports Unicode text. The C/C++ language specification maintains that the source files it is given to compile are to be written in 7-bit ANSI code. The resource compiler does not accept Unicode text, but Win32 resources are translated to Unicode in the RC compiler and the resultant binary image is in Unicode. It is also possible to specify L-quoted strings in your resource file. By default, the characters listed between the double quotation marks are ANSI characters and escape sequences are interpreted as byte escape sequences. If the string is preceded by the L prefix, the string is a wide-character string and escape sequences are interpreted as two-byte escape sequences that specify Unicode characters. If a double quotation mark is required in the text, you must include the double quotation mark twice or use the \" escape sequence.

Note  Although the message compiler has flags for Unicode, the flags are not implemented.

You are not confined to ANSI-only strings in your resources. You can use UCONVERT, a utility included with the Win32 SDK, to convert to and from Unicode text in your resources. The source for this utility is also included with the Win32 SDK.

The Help system for Win32, WINHLP32.EXE, is not Unicode enabled and does not accept Unicode input.


What Font Support is There for Unicode in Windows NT?

Windows NT does not provide font support for all of the Unicode characters. The L_10646.TTF font supports over 1300 Unicode characters, and most of the True Type fonts include a Unicode encoding table that maps a Unicode character point to a particular graphical glyph in the font; for example, it maps 0x0040 to the picture of an "a". The Lucida Sans Serif font is currently available for installation, but is not automatically added when you install the system. If you want to use this font, you will need to install it via the Control Panel, or your application will need to add it via a call to AddFontResource.

Several third parties are currently offering TrueType fonts that map to corresponding sections of Unicode.


Which File Systems Support Unicode?

The NTFS file system supports Unicode filenames. The FAT and HPFS file systems do not support Unicode filenames and are not expected to support Unicode in the future. If you need filename support for Unicode, use the NTFS file system.


Does the Win32 Console Support Unicode?

The Win32 console on Windows NT is Unicode enabled. However, it is not possible to use Unicode fonts within the console. Most Unicode characters are represented by the default character of the System font if a Unicode character is specified in the console. For more information, see "Applications and Redirected Output" later in this appendix.


Unicode Programming Considerations

In the context of this appendix, a Unicode application is one that processes Unicode characters rather than SBCS (single byte character set) or DBCS characters, and that has been compiled and linked with the set of Win32 and C run-time functions that process data as Unicode characters or strings. This is done automatically if the UNICODE and _UNICODE constants are defined to the compiler, either directly on the command line or via the Makefile. Note that an application must be recompiled, not relinked, because the Win32 function and data-type names are different depending upon how these constants are defined. The SDK's Win32 programming Help file contains topics under "String Manipulation and Unicode" that describe the mechanics of doing this.


String Operations

There are several details to consider when converting an application so that it handles Unicode as its internal character representation. Probably the most significant problem to deal with is the fact that a character is no longer a single byte but a 16-bit word. For example, most people have written the following lines of code:

cch = strlen(pch);
pchNew = malloc(cch+1);

The problem with this piece of code is that you cannot move it to a system where the size of a character is not the same as the size of a byte.

Another cause of problems related to the byte versus-character assumption is the Win32 API itself. Where the Win32 API function or message mentions a string pointer, the function or message always expects a character count, not a byte count. While the documents make this quite clear, most code overlooks this subtle difference. For instance, consider the following piece of code:

TCHAR	buffer[MAX_PATH];
cch = GetWindowText(hwnd, buffer, sizeof(buffer));

Even though this code works correctly for ANSI applications and looks portable, it isn't. The problem here is the sizeof operator. For an ANSI application the sizeof operator works fine since the size of an ANSI character is the same size as a byte. However, for a Unicode application, where TCHAR is a 16-bit word, running this code might result in corrupted memory storage beyond the end of buffer. This is because sizeof(buffer) returns MAX_PATH*sizeof(WCHAR), which is twice the number of characters as the number of characters represented by buffer.


Data Formats

Another area of concern is that of external data formats-that is, file formats. Many file formats assume SBCS or DBCS format or have small limits on internal offsets. Having to process a file that is SBCS/DBCS does not mean that the applications that read and write it cannot be Unicode. It is quite practical to process data internally as Unicode and convert to and from SBCS/DBCS when writing or reading such a file. Obviously, such a file must have some indication of the character set associated with it in order for the processing application to correctly handle the data.


Input/Output Operations

Another data format of interest is C run-time I/O streams or pipes for standard input and output. Since most current console applications are built using the C run-time standard I/O functions, they have no capacity to handle Unicode standard in and standard out. Applications that are run as console applications with I/O going to standard in and standard out can process data internally as Unicode and use the GetConsole[Input/Output]CP to get the OEM codepage to use for translation to and from DBCS strings.

The Win32 and C run-time header files for Win32 provide portable methods to prevent the aforementioned types of problems. Using the TCHAR and LPTSTR types is preferable to char or CHAR and other character types. The _TEXT or TEXT macros allow portable declarations of character constants and strings (although strings should be placed in resources for better localization and portability). Additionally, the lstrXXX functions and the C run-time library's _tcsXXX functions allow portable processing of strings. However, since the Win32 header files use the constant UNICODE while the C run-time library uses _UNICODE (for ANSI compatibility), both constants must be defined if an application uses both Win32 and C run-time header files.


Applications and Redirected Output

In converting some console applications to Unicode, note that ReadConsole and WriteConsole do not work when the handle passed to them is not a handle to a console stream. As a result, the application must pay attention to redirection, and most applications and files that take and write redirected output expect ANSI. The following code fragment consists of a set of macros and a couple of functions that handle this situation on Windows NT. To use this code, replace all calls to printf with GenOut. If you have more than one argument, you can write a routine that handles variable arguments. The output calls will go to the Unicode console function if the console is not redirected, or to an ANSI/DBCS file if it is redirected. Input redirection can be similarly handled by calling FileIsConsole(STD_INPUT_HANDLE).

WCHAR ConBuf[1024];

#define GenOut(LPTSTR lpFormat) {wvsprintf(ConBuf, lpFormat,

   (LPTSTR)(&lpFormat+1)); \

  ConsolePrint(ConBuf, wcslen(ConBuf));}

...

FileIsConsole(

  int     fh

  )

{

  DWORD dwMode;

  return GetConsoleMode(GetSTdHandle(fh), &dwMode);

}

DWORD ConsolePrint(

  LPWSTR   pch,

  int     cch

  )

{

  int     cchOut;

  int     err;

  CHAR    *pchOemBuffer;

  if (FileIsConsole(STD_OUTPUT_HANDLE)) {

    err = WriteConsole(

          GetStdHandle(STD_OUTPUT_HANDLE),

          pch, cch,

          &cchOut, NULL);

    if (!err || cchOut != cch)

      goto try_again;

  }

  else if ( cch != 0) {

try_again:

    cchOut = WideCharToMultiByte(CP_OEMCP, 0, pch, cch, NULL, 0, NULL,NULL);

    if (cchOut == 0)

      return 0;

    if ((pchOemBuffer = (CHAR *)malloc(cchOut)) != NULL) {

      WideCharToMultiByte(CP_OEMCP, 0, pch, cch,

            pchOemBuffer, cchOut, NULL, NULL);

      WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),

            pchOemBuffer, cchOut, &cch, NULL);

      free(pchOemBuffer);

    }

  }

  return cchOut;

}

In the preceding example, the conversion to ANSI/DBCS uses the default OEM codepage. This can be replaced with the ANSI codepage or an actual OEM codepage. Since existing files contain OEM characters, OEM is often the desired conversion codepage. This is an important point if your application is communicating with systems running MS-DOS®, MS-DOS-based Windows, or OS/2®.


Converting to Unicode

The basic steps for converting your application to Unicode from ANSI are shown in the following procedure. Except for the first one, you can perform these steps in any order, although not all steps may be necessary for all applications. Whenever Win32® provides a portable version of a specific function or type, use the portable version. For example, given the choice between LPWSTR and LPTSTR for wide-character strings, use LPTSTR. Doing so allows you to use the same code when compiling for Unicode or ANSI.

1.	To compile code for Unicode, you need to define UNICODE for the Win32 header files (using #define statement). The #define statement must appear before the #include <WINDOWS.H> statement and any included C run-time headers.

2.	Define _UNICODE for the C run-time library. The #define statement must appear before the #include <WINDOWS.H> statement and any included C run-time headers. Since the C run-times adhere to the naming conventions of the ANSI C standard, a leading underscore is required.

3.	Replace all occurrences of CHAR and char with TCHAR.

4.	Replace all occurrences of LPSTR with LPTSTR.

5.	Replace all occurrences of LPCH with LPTCH.

6.	Prefix all literal strings with L or use the TEXT macro. The TEXT macro places an L before the constant if Unicode is defined.

7.	Replace your C run-time ANSI-specific functions with their Unicode equivalents. (Refer to the table of equivalent functions later in this document.)

8.	Be aware of functions that are byte-based. For example, if you have a function that searches through a buffer for strings that are less than or greater than each other, you have to move your index to the string by the character size (sizeof(TCHAR)) rather than a simple increment, ++.

9.	If you used memset to fill a character buffer, use wcsset instead.

10.	Most Win32 functions requiring or returning the size of a string parameter, use a count of characters. The only exceptions occur where a pointer may not be a string pointer at each call.

Memory allocation functions require or return the size of buffers in bytes. You need to multiply or divide by sizeof(TCHAR) to get between the size of a string and the size of the memory to hold it. In allocating string buffers, the size is often incremented by one to hold the NUL terminator. (NUL, not NULL is the name of the ASCII NUL character.) The one in these expressions needs to be replaced by sizeof(TCHAR).

With the new header functions, Unicode strings are always arrays of 16-bit quantities. Except in rare circumstances, such as when reading mixed binary/text data into buffers, the compiler's type checking will ensure that only Unicode is passed to functions that expect Unicode.

11.	To mark a string as Unicode in a file, insert 0xFEFF, known as BOM, in the string and/or file. A Unicode program on a big-endian processor such as the Macintosh will write 0xFFFE. So finding a 0xFFFE indicates that the bytes in the file are in the opposite order than what a program expects. 0xFEFF means little endian and that the file is byte-ordered in the way a program would expect it to be.

One method of determining whether or not a file is Unicode is by the use of 0xFEFF as "introducer" to the file. Since ASCII files rarely start with 0xFE followed by 0xFF, it's an indication that the file is Unicode.

12.	To get command-line arguments, use the function GetCommandLine rather than using the lpCmdLine parameter passed to WinMain. The lpCmdLine parameter is specified as LPSTR, which is an ANSI string. The system determines whether or not an application is Unicode once the application registers a window class. Since WinMain is called before a window class is registered, WinMain cannot be defined to accept Unicode input.


Unicode Equivalents to ANSI Functions

The following table lists the functions in Windows and the C run-time library that have Unicode equivalents, most of which are string-manipulation functions that handle the differences in the character sets. For more detailed information on any given function, refer to the Win32 SDK for Windows NT Help and the Microsoft C/C++ language Help.

It is important to note which functions are character-based and which functions are byte-based since a Unicode character is 16 bits and an ANSI character is 8 bits. If you use a function that returns a number of bytes, such as the dwBytesRead returned by ReadFile, divide that number by 2 to determine the number of characters read.

Functions in Windows and the C Run-time Library and their Unicode Equivalents

ANSI	Unicode	Functionality	Byte or Character-based
AnsiNext	CharNext	Moves to the next character in a string.	Character
AnsiPrev	CharPrev	Moves to the previous character in a string.	Character
AnsiUpper / AnsiUpperBuff	CharUpper / CharUpperBuff	Converts a character or character string to uppercase.	Not applicable.
AnsiLower / AnsiLowerBuff	CharLower / CharLowerBuff	Converts a character or character string to lowercase.	Not applicable.
AnsiToOem / AnsiToOemBuff	CharToOem / CharToOemBuff	Translates a string to OEM characters.	Character
OemToAnsi / OemToAnsiBuff	OemtoChar / OemtoCharBuff	Translates an OEM string.	Character
atoi / atof / atol / atold	wcstombs	Converts a character string to an integer. Use wcstombs to convert the wide-character string to a multi-byte string before calling atoi.	Not applicable
OpenFile	CreateFile / CreateFileEx	Opens a file. OpenFile is not Unicode. Use CreateFile or CreateFileEx instead.	Not applicable
_lclose	CloseHandle	Closes an open file. Use CloseHandle on a file created by a call to CreateFile or CreateFileEx.	Not applicable
_lread	ReadFile	Reads a file. Use ReadFile instead for Unicode files.	Returned number of bytes read is byte-based.
_lwrite	WriteFile	Writes a file. Use WriteFile instead for Unicode files.	The number of bytes to write and the number of bytes written is byte-based.
memset	wcsset	Sets a buffer to the specified character. Use this function for binary buffers.	Byte
strcat	lstrcat or wcscat	Concatenates two strings.	Not applicable
strchr	wcschr	Finds the first occurrence of a character in a string.	Not applicable
strcmp	lstrcmp or wcscmp	Compares two strings. Note that lstcmp is not exactly equivalent to wcscmp. The function lstrcmp uses the collating tables that are built into the codepage/locale; while wcscmp does a lexicographic comparison (by value only).	Not applicable
strcmpi	lstrcmpi or wcscmp	Compares two strings regardless of case (lower or upper). Note that lstcmpi is not exactly equivalent to wcscmpi. The function lstrcmpi uses the collating tables that are built into the codepage/locale; while wcscmpi does a lexicographic comparison (by value only).	Not applicable
strcoll	wcscoll	Compares two strings based upon the current locale.	Not applicable
strcpy	lstrcpy or wcscpy	Copies a string into the given buffer.	Not applicable
strcspn	wcscspn	Returns the index of the first character in the first string that is specified by the second string.	Character
strdup	wcsdup	Duplicates the given string, allocating space for it and returning its address.	Not applicable
strftime	wcsftime	Formats the current time, tm, structure and converts it to a string.	Not applicable
stricmp	wcsicmp	Compares two strings regardless of case.	Not applicable
strlen	lstrlen or wcslen	Returns the length, in bytes, of the specified string.	Byte
strlwr	wcslwr	Converts any uppercase characters in the given string to lowercase.	Not applicable
strncat	wcsncat	Appends the specified number, in characters, of the second string to the first string specified.	Character
strncmp	wcsncmp	Compares the first number of characters in the two given strings.	Character
strncpy	wcsncpy	Copies the first number of characters in the second string to the first string.	Character
strnicmp	wcsnicmp	Compares the first number of characters in two strings regardless of case.	Character
strnset	wcsnset	Sets the first number of characters in the specified string.	Character
strpbrk	wcspbrk	Finds the first occurrence of string 1 in string 2.	Not applicable
strrchr	wcsrchr	Finds the last occurrence of the given character in the string specified.	Not applicable
strrev	wcsrev	Reverses the order of the characters in the specified string.	Not applicable
strset	wcsset	Sets all of the characters in the specified string to the specified character.	Not applicable
strspn	wcsspn	Returns the index of the first character in string 1 that does not belong to the set of characters specified in string 2.	Character
strstr	wcsstr	Returns a pointer to the first occurrence of string 1 in string 2.	Not applicable
strtod	wcstod	Converts the given string to a double precision value.	Not applicable
strtok	wcstok	Finds the next token in the first string as given by the set of characters passed in the second string.	Not applicabl.
strtol	wcstol	Converts the given string to a long integer value.	Not applicable
stroul	wcstoul	Converts the given string to an unsigned long integer value.	Not applicable
strxform	wcsxfrm	Transforms the strings specified by string 2 based on the current locale and stores the result in string 1.	Character
strupr	wcsupr	Converts any lowercase characters in the given string to uppercase.	Not applicable
sprintf	wsprintf	Formats and stores a series of characters and values in the given buffer. Note that wsprintf does not accurately return the count of characters printed for Unicode strings. Rather, it returns the count of inidividual Unicode characters and because some Unicode characters take two columns to display (such as the Japanese version of Windows NT), column alignment is difficult.	Not applicable
vsprintf	vswprintf	Functions similarly to sprintf except that it takes a variable argument list. Note that wsprintf does not accurately return the count of characters printed for Unicode strings. Rather, it returns the count of inidividual Unicode characters and because some Unicode characters take two columns to display (such as the Japanese version of Windows NT), column alignment is difficult.	Not applicable
The print function wsprintf supports Unicode by providing new and changed data types in its format specifications. These changes affect the way that wsprintf interprets the corresponding passed-in character. The following table lists the changes to the wsprintf function for Unicode support.

Format Specifications for wsprintf

Format Specification	ANSI Version	Unicode Version
c	CHAR	WCHAR
C	WCHAR	CHAR
hc, hC	CHAR	CHAR
hs, hS	LPSTR	LPSTR
lc, lC	WCHAR	WCHAR
ls, lS	LPWSTR	LPWSTR
s	LPSTR	LPWSTR
S	LPWSTR	LPSTR


Using PORTOOL to Help Convert to Unicode

You can use PORTOOL to assist in the conversion to Unicode on Windows NT. First alter the PORT.INI file to add the wide-character version of the string functions to the API section and the Unicode character types to the TYPES section. Below is the exact text you need to put directly into your PORT.INI file to make it suitable for Unicode conversion.

[APIS]

  MessageBox=MessageBox;Change any literal stings to precede with L

    or TEXT;;

  GetTextExtent=GetTextExtentPoint;Make sure the text string passed is

    LPCTSTR;;

  LocalAlloc=LocalAlloc;Byte-based!;Multiply or divide by

    sizeof(TCHAR) to get between the size of a string and the size

    of the memory to hold it;

  LocalReAlloc=LocalReAlloc;Byte-based!;Multiply or divide by

    sizeof(TCHAR) to get between the size of a string and the size

    of the memory to hold it;

  GlobalAlloc=GlobalAlloc;Byte-based!;Multiply or divide by

    sizeof(TCHAR) to get between the size of a string and the size

    of the memory to hold it;

  GlobalReAlloc=GlobalReAlloc;Byte-based!;Multiply or divide by

    sizeof(TCHAR) to get between the size of a string and the size

    of the memory;

  AnsiNext=CharNext;Replace call to AnsiNext with CharNext;;

  AnsiPrev=CharPrev;Replace call to AnsiPrev with CharPrev;;

  AnsiUpper=CharUpper;Replace call to AnsiUpper with CharUpper;;

  AnsiUpperBuff=CharUpperBuff;Replace call to AnsiUpperBuff with

    CharUpperBuff;;

  AnsiLower=CharLower;Replace call to AnsiLower with CharLower;;

  AnsiLowerBuff=CharLowerBuff;Replace call to AnsiLowerBuff with

    CharLowerBuff;;

  OemToAnsi=OemToChar;Replace call to OemToAnsi with OemToChar;;

  OemToAnsiBuff=OemToCharBuff;Replace call to OemToAnsiBuff with

    OemToCharBuff;;

  AnsiToOem=CharToOem;Replace call to AnsiToOem with AnsiToChar;;

  AnsiToOemBuff=CharToOemBuff;Replace call to AnsiToOemBuff with

    AnsiToCharBuff;;

  DialogBox=DialogBox;Change any literal stings to precede with L

    or TEXT;;

  CreateWindow=CreateWindow;Change any literal strings to precede

    with L or TEXT;;

  RegisterClass=RegisterClass;Change any literal stings to precede

    with L or TEXT;;

  atoi=wcstombs;atoi takes a 'char' - use wcstombs on string first

     before passing on;;

  OpenFile=CreateFile;OpenFile does not support Unicode;Use CreateFile

    or CreateFileEx;

  lread=ReadFile;lread is not Unicode aware;use ReadFile instead;

  lwrite=WriteFile;lwrite is not Unicode aware;use WriteFile instead;

  lclose=CloseHandle;close open file handles with CloseHandle;;

  memset=wcsset;Is this a text buffer?;Use wcsset for character

    buffers;

  strcat=lstrcat;use wide-character version;lstrcat or wcscat;

  strchr=wcschr;use wide-character version;wcschr;

  strcmp=lstrcmp;use wide-character version;lstrmcp or wcscmp;

  strcmpi=lstrcmpi;use wide-character version;lstrcmpi;

  strcoll=wcscoll;use wide-character version;wcscoll;

  strcpy=lstrcpy;use wide-character version;lstrcpy or wcscpy;

  strcspn=wcscspn;use wide-character version;wcscpn;

  strdup=wcsdup;use wide-character version;wcsdup;

  strftime=wcsftime;use wide-character version;wcsftime;

  stricmp=wcsicmp;use wide-character version;wcsicmp;

  strlen=lstrlen;use wide-character version;lstrlen or wcslen;

  strlwr=wcslwr;use wide-character version;wcslwr;

  strncat=wcsncat;use wide-character version;wcsncat;

  strncmp=wcsncmp;use wide-character version;wcsncmp;

  strncpy=wcsncpy;use wide-character version;wcsncpy;

  strnicmp=wcsnicmp;use wide-character version;wcsnicmp;

  strnset=wcsnset;use wide-character version;wcsnset;

  strpbrk=wcspbrk;use wide-character version;wcspbrk;

  strrchr=wcsrchr;use wide-character version;wcsrchr;

  strrev=wcsrev;use wide-character version;wcsrev;

  strset=wcsset;use wide-character version;wcsset;

  strspn=wcsspn;use wide-character version;wcsspn;

  strstr=wcsstr;use wide-character version;wcsstr;

  strtod=wcstod;use wide-character version;wcstod;

  strtok=wcstok;use wide-character version;wcstok;

  strtol=wcstol;use wide-character version;wcstol;

  strtoul=wcstoul;use wide-character version;wcstoul;

  strxform=wcsxfrm;use wide-character version;wcsxfrm;

  sprintf=wsprintf;use wide-character version;wsprintf;

  vsprintf=wvsprintf;use wide-character version;wvsprintf;

[TYPES]

  CHAR=TCHAR;Replace CHAR with TCHAR;;

  LPSTR=LPTSTR;Replace LPSTR with LPTSTR;;

  LPCH=LPTCH;Replace LPCH with LPTCH;;

  NPSTR=LPTSTR;Replace NPSTR with LPTSTR;;

  PSTR=LPTSTR;Replace PSTR with LPTSTR;;

  char=TCHAR;Replace char with TCHAR;;


References

The following publications will provide you with additional infomation about Unicode. The last three references on the list refer to Unicode specifically as used in Windows NT.

•	The Unicode Standard: Worldwide Character Encoding, Version 1.0, Volumes 1 and 2, Addison-Welsley Publishing Company: 1991, 1992, ISBN 0201567881

•	Microsoft Win32 Programmer's Reference, Volume 2, Chapter 53, String Manipulation and Unicode

•	Move Over, ASCII! Unicode is Here, by Charles Petzold, PC Magazine, several issues beginning on 10/26/93

•	Application Programming for Windows NT, Murray & Pappas, Osborne McGrawHill

•	Advanced Windows NT, Jeffrey Richter, MS Press

•	Mastering Windows NT programming, Myers & Hamer, SYBEX


Overview of OLE Automation

OLE Automation is a technology that lets you manipulate an application's objects from outside the application. OLE Automation uses OLE's component object model, but may be implemented independently from the rest of OLE. Using OLE Automation, you can:

•	Create applications that expose objects to programming tools and macro languages.

•	Create and manipulate objects exposed in one application from another application.

•	Create tools that access and manipulate objects. These tools can include embedded macro languages, external programming tools, object browsers, and compilers.

The objects an application exposes are called OLE Automation objects. Applications and programming tools that access those objects are called OLE Automation controllers. OLE Automation objects and controllers interact like this:

This section includes the following topics:

Why Expose Objects?
What Is an OLE Automation Object?
Components of OLE Automation
Files You Need
Exposing OLE Automation Objects
Creating OLE Automation Controllers


Why Expose Objects?

Exposing objects provides a way to operate your application's tools programmatically. This allows your customers to use a programming tool to automate repetitive tasks that you might not anticipate.

OLE Automation offers advantages over implementations of macro languages included within some applications:

•	Exposed objects from many applications are available in a single programming environment. In this way, systems integrators can assemble the best pieces from different applications to create new, specialized applications.

•	Exposed objects are accessible from any macro language or programming tool that implements OLE Automation. Customers may choose a programming tool based on their current knowledge, rather than learning a new language for each application.

•	Object names can remain consistent across versions of an application.

•	Object names can automatically conform to the user's national language.


What Is an OLE Automation Object?

An OLE Automation object is an instance of a class within your application that you wish to manipulate programmatically, such as with a macro language. These may be new classes whose sole purpose is to collect and expose data and functions in a way that makes sense to your customers. In an application that manages documents, the OLE Automation objects might look like this diagram:

Each of the objects in the illustration has member functions that uniquely apply to that object. The object becomes programmable when you expose the member functions. OLE Automation defines two types of members that you may expose for an object:

•	Methods are member functions that perform an action on an object. For example, a Document object might provide a Print method.

•	Properties are pairs of member functions that set and return information about the state of an object. For example, a Drawing object might have a Color property.

For example, the following objects could be exposed by implementing the listed methods and properties for each object:

OLE Automation object	Methods	Properties
Application	Help Quit Save Repeat Undo	ActiveDocument Application Caption DefaultFilePath Documents Height Name Parent Path Printers StatusBar Top Value Visible Width
Document	Activate Close NewWindow Print PrintPreview RevertToSaved Save SaveAs	Application Author Comments FullName Keywords Name Parent Path ReadOnly Saved Subject Title Value
To provide access to more than one instance of an object, you can expose a collection object. A collection object manages other objects. All collection objects support iteration over the objects they manage. For example, an application with a multiple document interface (MDI) might expose a Documents collection object with the following methods and properties:

Collection object	Methods	Properties
Documents	Add Close Item Open	Application Count Parent
Guidelines for organizing and naming the OLE Automation objects your applications exposes are described in Standards and Guidelines.


Components of OLE Automation

OLE Automation includes components used to create applications that expose objects; components used to create programming tools, such as object browsers, that read type libraries; and components used to create programming tools that create type libraries.

The following interfaces are used to create applications that expose OLE Automation objects:

Interface	Purpose
IUnknown	Defines a set of member functions that identify and keep track of exposed objects. All OLE objects derive from IUnknown.
IClassFactory	Defines a set of member functions that allow OLE to create objects.
IDispatch	Defines a set of member functions that allow OLE Automation to get information about your application's objects and to invoke methods and get or set properties.
IEnumVARIANT	Defines a set of member functions you must implement to expose collections of objects. This interface allows iteration over a collection of objects.
For more information about these interfaces and the services they provide, see Exposing OLE Automation Objects. Reference information on IDispatch and IEnumVariant appears in Dispatch Interfaces.

Default implementations of Dispatch functions are provided by OLEAUT32.DLL (Win32) and OLE2DISP.DLL (Win16).

Type description interfaces are used by OLE Automation controllers that read compiler or object browser type libraries:

Interface	Purpose
ITypeInfo	Gets information about the members of an object described in a type library.
ITypeLib	Gets information about the set of objects contained in a type library.
See Type Description Interfaces, for more information on the ITypeInfo and ITypeLib interfaces.

Type building interfaces are used to write programming tools that create type libraries:

Interface	Purpose
ICreateTypeInfo	Creates type information within a type library.
ICreateTypeLib	Creates a type library.
ITypeComp	Provides a fast way to access information compilers need when binding to and instantiating data types and interfaces.
See Type Building Interfaces, for a reference to the ICreateTypeInfo, ICreateTypeLib, and ITypeComp interfaces.

The default implementations of the type description and type building interfaces are provided by OLEAUT32.DLL (Win32), and TYPELIB.DLL (Win16).

Data manipulation functions create and manipulate strings, arrays, and variant data types. Data manipulation functions are provided by OLEAUT32.DLL (Win32) and OLE2DISP.DLL (Win16).  Chapter 6, "Data Manipulation Functions," provides reference information on these functions.

National language support functions provide a way to evaluate and act on language differences. This allows programmers to provide localized object, method, property, and argument names for their applications that expose objects. National language support functions are provided as part of the Win32 NLS API (32-bit) and OLE2NLS.DLL (Win16).

Automation tools are used to develop applications that expose objects:

Automation tool	Purpose
DISPTEST.EXE	Tests objects exposed in an application by manipulating them with Visual Basic language constructs.
MKTYPLIB.EXE	Builds type libraries from interface descriptions.
For information on DISPTEST.EXE, see DispTest. Object Description Language, describes MKTYPLIB.EXE.


Files You Need

To expose or access OLE Automation objects, you need the following files, which are provided with OLE. The file names shown in bold are required by your application at run time.

32-bit filenames	16-bit filenames	Purpose
None	DISPTEST.EXE  DISP200.DLL MSOLE2.VBX MSOLE2.BAS VB.HLP AUTOLOAD.MAK	Accesses exposed objects using Visual Basic language constructs. Use these files when testing applications that expose objects as described in Packaging Scripts Using DISPTEST.EXE.
OLEAUT32.DLL	TYPELIB.DLL	Accesses type libraries.
MKTYPLIB.EXE	MKTYPLIB.EXE	Builds type libraries from interface descriptions.
None	OLE2.REG	Registers OLE and OLE Automation. OLE is a system component on Win32, and therefore no .REG file is required.
OLEAUT32.DLL OLEAUT32.LIB OLEAUTO.H	OLE2DISP.DLL OLE2DISP.LIB DISPATCH.H	Provides functions for creating OLE Automation objects and retrieving active objects at run time. Accesses OLE Automation objects by invoking methods and properties.
None	OLE2NLS.DLL OLE2NLS.LIB OLENLS.H	Provides functions for applications that support multiple national languages. Note that on Win32, NLS functionality is provided by the Win32 NLS API.
OLE32.DLL OLE32.LIB OLE2.H	OLE2.DLL OLE2.LIB OLE2.H	Provides OLE functions that may be used by OLE objects or containers.
OLEPRX32.DLL	OLE2PROX.DLL	Coordinates object access across processes.
OLE32.DLL  OLE32.LIB OLE2.H	COMPOBJ.DLL COMPOBJ.LIB OLE2.H COMPOBJ.H	Supports component object creation and access.
OLE32.DLL  OLE32.LIB OLE2.H	STORAGE.DLL STORAGE.LIB OLE2.H STORAGE.H	Supports access to subfiles, such as type libraries, within compound documents.


Exposing OLE Automation Objects

The following figure shows the interfaces and member functions you need to implement in order to expose OLE Automation objects:

The member functions you must implement are shown in bold. The interfaces (IUnknown, IDispatch, IClassFactory, and IEnumVARIANT) are defined in the indicated header files.

When you implement OLE Automation in your application, you must write code to initialize your objects, implement the objects, and reset OLE when your application terminates.

To initialize your exposed objects

1.	Initialize OLE.

2.	Register the class factories of your exposed objects.

3.	Register the active object when the class factory is asked to create an object.

To implement your exposed objects

1.	Write the classes for your exposed objects.  This involves implementing the IDispatch interface and the properties and methods of the object.

2.	Create a type library that describes the properties and methods of the exposed objects..

3.	Create a registration file (.REG) for your application.

To reset OLE when your application terminates

1.	Release the registration of the class factories and revoke the active object.

2.	Reset OLE.

These tasks are described in greater detail in Exposing OLE Automation Objects.


Creating OLE Automation Controllers

There are several strategies for accessing OLE Automation objects:

•	Use existing OLE Automation controllers, such as Microsoft Visual Basic or DispTest, to create packaged scripts.

•	Write code within an application that accesses another application's objects through OLE Automation.

•	Create new OLE Automation controllers, such as compilers or type information browsers, that support OLE Automation.

•	Revise existing programming tools, such as an embedded macro language, to become OLE Automation controllers.

This section includes the following topics:

Packaging Scripts Using DISPTEST.EXE
Packaging Scripts Using Microsoft Visual Basic
Creating Applications and Tools that Access
Objects

The following figure shows the interfaces you use when accessing exposed objects through DispTest.

The following figure shows the interfaces you use when you create compilers or browsers that access type libraries.


Packaging Scripts Using DISPTEST.EXE

To help you get a feel for manipulating your objects programmatically, OLE Automation provides DispTest (DISPTEST.EXE). DispTest lets you create programs that access and manipulate your application's OLE Automation objects.

You can call programs written with DispTest directly from an application that exposes its objects. This figure shows how this was done for the sample program DISPCALC.EXE:

To access an exposed object from DispTest

1.	Start DispTest.

2.	Add code to declare a variable of the type Object. For example:

Dim Calculator As Object

3.	Add code in an event procedure to create an instance of the object and to manipulate the object using its properties and methods. For example:

Sub Form_Click ( )

Const OP_MULT = 3

Set Calculator = CreateObject("Dispcalc.Application")

Calculator.Accum = 1	' Initialize accumulator

For x = 1 to 9	' Compute factorial of 9

Calculator.Opnd  = x	 	' Set the operand

Calculator.Op=OP_MULT	' Set operation to be performed.

Calculator.Eval	' Evaluate

Next x

Calculator.Display	' Display result

End Sub

4.	From the Run menu, choose Start and trigger the event. (In this case, click the form.)

For more information on creating packaged scripts with DispTest, see Accessing OLE Automation Objects and Object Description Language. For the syntax and semantics of the language used by DispTest, see the Help file VB.HLP.


Packaging Scripts Using Microsoft Visual Basic

Microsoft Visual Basic, version 3.0 or later, improves upon DispTest. DispTest is intended as a tool for testing programmable interfaces; Visual Basic provides a complete development environment for creating stand-alone applications.

Visual Basic supports all of the language constructs used by DispTest and includes the following improvements over DispTest:

•	Lets you create and access databases.

•	Lets you create stand-alone .EXE files.

•	Provides superior run-time performance.

•	Includes more complete documentation.

This document describes accessing OLE Automation objects using DispTest; however, all the procedures and code shown for DispTest also work with Visual Basic.


Creating Applications and Tools that Access Objects

OLE Automation provides interfaces for accessing exposed objects from an application or programming tool written in C or C++. When you access exposed objects using C or C++, initialize OLE, create the exposed object, manipulate methods and properties of the exposed object, and release OLE when your application or programming tool terminates.

To initialize and create the object

1.	Initialize OLE.

2.	Create an instance of the exposed object.

To manipulate methods and properties

1.	Get the method and property IDs from the object's type information.

2.	Invoke the methods and properties using the IDs.

To release OLE when your application or programming tool terminates

1.	Release the object.

2.	Release OLE.

These tasks are described in greater detail in Accessing OLE Automation Objects.


Exposing OLE Automation Objects

When you implement OLE Automation in your application, you must write code to do the following tasks:

•	Initialize your exposed objects.

•	Implement your exposed objects.

•	Release OLE when the application terminates.

This section provides a quick overview of these tasks, then demonstrates this procedure using the Hello, DispCalc, DspCalc2, and SPoly2 samples from the OLE 2 SDK. Throughout the following topics, the filename for the example precedes the example in parentheses.


Hello World Example
"Hello World" Example

The fastest way to implement OLE Automation objects is to use the standard implementation of IDispatch that OLE Automation provides through the dispatch function CreateStdDispatch. The following sections demonstrate how you expose a simple class. The code shown is abridged to emphasize the essential parts; see the Hello sample in the OLE 2 SDK for a complete listing.


Initializing Class ID

In a header file, define and allocate the class IDs for the top-level OLE Automation objects and the application's type library. OLE provides the DEFINE_GUID macro to do this. For example (CLSID.H):

DEFINE_GUID(CLSID_CHello,

                0xD3CE6D43,

                0xF1AF,

                0x1068,

                0x9F,

                0xBB,

                0x08,

                0x00,

                0x2B,

                0x32,

                0x37,

                0x2A);

DEFINE_GUID(LIBID_HELLOTLB,

                0xD3CE6D44,

                0xF1AF,

                0x1068,

                0x9F,

                0xBB,

                0x08,

                0x00,

                0x2B,

                0x32,

                0x37,

                0x2A);

The class ID is the mechanism by which OLE identifies objects and type libraries within the system. Class IDs appear in the system registration database (.REG). You generate GUIDs by running GUIDGEN.EXE, provided in the OLE 2 SDK.


Initializing OLE

When your application starts, initialize OLE and the class. For example (MAIN.CPP):

HRESULT

InitOle()

{

    HRESULT hresult;

    IClassFactory FAR* pcf;

    if((hresult = OleInitialize(NULL)) != NOERROR)

      return hresult;

    // Create the single global instance of CHello

    if((g_phello = CHello::Create()) == NULL){

      hresult = ResultFromScode(E_OUTOFMEMORY);

      return hresult;

    }

    // Create an instance of the class factory for CHello.

    // Exit if an error occurs.

    pcf = CHelloCF::Create();

    if (pcf == NULL) {

      UninitOle();

      return hresult;

    }

    // Register the class factory.  Exit if an error occurs.

    hresult = CoRegisterClassObject(

                       CLSID_CHello,

                       pcf,

                       CLSCTX_LOCAL_SERVER,

                       REGCLS_MULTIPLEUSE,

                       &g_dwCHelloCF);

    if (hresult != NOERROR) {

      pcf->Release();

      UninitOle();

      return hresult;

    }

    pcf->Release();

    // If execution has reached this spot,

    // then no errors have occurred.

    return NOERROR;

}

The preceding code initializes the OLE DLLs using OleInitialize and the default memory allocator (indicated by the parameter NULL). It calls CHelloCF::Create to create an instance of the object's class factory, and then registers the class factory with OLE using the CoRegisterClassObject function.  Note that the example also calls CHello::Create. This call creates a single global instance of CHello that will be returned by calls to CHelloCF::CreateInstance (not shown). Alternatively, the program could omit the call to CHello::Create, and instead create a new instance of CHello with each call to CHelloCF::CreateInstance.

CoRegisterClassObject tells OLE which classes OLE can create. You only need to create a class factory and call CoRegisterClassObject for OLE Automation objects that can be created from OLE Automation controllers. Most applications provide this ability for their Application and Document objects.


Registering the Active Object

After your application creates an instance of the object, register the active object. For example:

hresult = RegisterActiveObject(

        g_phello,

        CLSID_CHello,

        NULL,

        g_dwRegister);

Registering an active object lets OLE Automation controllers retrieve an object that is already running, rather than create a new instance of the object.


Implementing IUnknown

As with all other OLE 2 objects, you must create the object's IUnknown interface. For example (HELLO.CPP):

STDMETHODIMP

CHello::QueryInterface(REFIID riid, void FAR* FAR* ppv)

{

    // riid identifies an interface type.  Do we support it?

    if (IsEqualIID(riid, IID_IUnknown))

    // Someone is asking us for our controlling IUnknown.

    // CHello is that IUnknown, so "this" is the answer.

    *ppv = this;

    else if (IsEqualIID(riid, IID_IDispatch) ||

         IsEqualIID(riid, IID_DHello))

        // Someone is asking us for our IDispatch or dispinterface.

        // We simply defer to the standard dispatch interface

        // we created earlier.

        return m_disp_interface->QueryInterface(IID_IDispatch, ppv);

    else if (IsEqualIID(riid, IID_IHello))

    // Someone is asking us for our IHelloPro interface.

    // We simply defer to the standard dispatch interface

    // we created earlier.

        *ppv = &m_prog_interface;

    else {

    // They must have asked for another interface -- something

       // we don't support.  Report the error.

          *ppv = NULL;

        return ResultFromScode(E_NOINTERFACE);

    }

    // If execution reached this point then no error occurred.

    AddRef();

    return NOERROR;

}

STDMETHODIMP_(ULONG) CHello::AddRef() { . . . }

STDMETHODIMP_(ULONG) CHello::Release() { . . . }


Implementing IClassFactory

Create the object's class factory. For example (HELLO.CPP):

IClassFactory FAR*

CHelloCF::Create()

{

    return new FAR CHelloCF();

}

STDMETHODIMP

CHelloCF::CreateInstance(

    IUnknown FAR* punkOuter,

    REFIID riid,

    void FAR* FAR* ppv)

{

    extern CHello FAR* g_phello;

    return g_phello->QueryInterface(riid, ppv);

}

STDMETHODIMP CHelloCF::QueryInterface(. . .) {. . .}

STDMETHODIMP_(ULONG) CHelloCF::AddRef() { . . . }

STDMETHODIMP_(ULONG) CHelloCF::Release() { . . . }

STDMETHODIMP CHelloCF::LockServer(. . .) { . . . }

A class factory is a class that is capable of creating other instances of a class. In this example, the class factory returns the same instance every time CHello::CreateInstance is called. Alternatively, the application could create and return a new instance each time. The class factory is used earlier in this section with CoRegisterClassObject.


Implementing IDispatch

Load the object's type information and create the IDispatch interface. For example (HELLO.CPP):

IUnknown FAR*

CreateDispatchInterface(

  IUnknown FAR*    punkController,

  void FAR* pProgInterface)

{

    HRESULT hresult;

    ITypeLib FAR* ptlib;

    ITypeInfo FAR* ptinfo;

    IUnknown FAR* punkStdDisp;

    // Load the hellotlb type library.

    hresult = LoadRegTypeLib(LIBID_HELLOTLB, 1, 0, 0x0409, &ptlib);

    if (hresult != NOERROR) {

      #define TLB_NAME OLESTR("hello.tlb")

    // If it wasn't registered, try to load it from the

    // path/current directory. If it succeeds, it will have

    // registered the type library for use for the next time.

     if((hresult = LoadTypeLib(TLB_NAME, &ptlib)) != NOERROR)

         return NULL;

    }

    // Get Dispatch TypeInfo from the type library.

    // Exit if an error occurs.

    hresult = ptlib->GetTypeInfoOfGuid(IID_IHello, &ptinfo);

    if (hresult != NOERROR)

        return NULL;

    ptlib->Release();

    // Create a standard dispatch object.

    // Release the pointer to the previously created TypeInfo.

    // Exit if an error occurred.

    hresult = CreateStdDispatch(punkController,

                    pProgInterface,

                ptinfo,

                  &punkStdDisp);

    ptinfo->Release();

    if (hresult != NOERROR)

        return NULL;

    // If execution has reached this point, then no errors occurred.

    return punkStdDisp;

}


Creating the Type Information

Describe the object's type information using the object description language (ODL) and compile the description to create a type library. This sample shows the description for the CHello object's properties and methods (HELLO.ODL). The HELLO.ODL file is compiled using MkTypLib to create a type library (HELLOTL.TLB) and a header file (HELLOTL.H).

/*

 *    hello.odl

IDispatch*    Hello.Application    {D3CE6D43-F1AF-1068-9FBB-08002B32372A}

  Type library:    hello.tlb        {D3CE6D44-F1AF-1068-9FBB-08002B32372A}

  Interface:    IHello                {D3CE6D45-F1AF-1068-9FBB-08002B32372A}

*/

[

  uuid(D3CE6D44-F1AF-1068-9FBB-08002B32372A),

  helpstring("OLE Automation Hello 1.0 Type Library"),

  lcid(0x0409),

  version(1.0)

]

library Hello

{

#ifdef WIN32

    importlib("stdole32.tlb");

#else

    importlib("stdole.tlb");

#endif

    [

     // The uuid for the interface IID_IHelloPro.

     odl,

     uuid(D3CE6D45-F1AF-1068-9FBB-08002B32372A),

     helpstring("Hello")

    ]

    interface _IHello : IUnknown

    {

      [id(0), propput]

      void HelloMessage([in] BSTR b);

      [id(0), propget,helpstring("The message that will be displayed.")]

      BSTR HelloMessage();

      BSTR SayHello(void);

    }

    [

      uuid(D3CE6D46-F1AF-1068-9FBB-08002B32372A),

      helpstring("Hello DispInterface")

    ]

    dispinterface _DHello

    {

      interface _IHello;

    }

    [ // The uuid for the class we expose.  Same as registry

      // entry for hello.hello

      uuid(D3CE6D43-F1AF-1068-9FBB-08002B32372A),

      helpstring("Hello")

    ]

    coclass Hello

    {

      dispinterface _DHello;

      interface _IHello;

    }

};


Releasing Objects and OLE

When your application ends, it should release the class factory, active object, and OLE. For example:

void

UninitOle()

{

    // If a class factory was successfully created earlier,

    // then tell OLE that the object is no longer available.

    if(g_dwCHelloCF != 0)

      CoRevokeClassObject(g_dwCHelloCF);

    hresult = RevokeActiveObject (gdw_Registerm NULL);

    // Cause the remaining typeinfo to be released

    if (g_phello != NULL)

      g_phello->Release();

    // Tell OLE we are done using them.

    OleUninitialize();

}

CoRevokeClassObject tells OLE that the object is no longer available for creation. OleUninitialize tells OLE that this application is no longer using the OLE DLLs. (If no other applications are using the DLLs, they are unloaded from memory.)


Creating a Registration File

Create an OLE registration file for your application. The system registration database contains a list of all the OLE objects in your system. OLE uses this database to locate objects and to ascertain their capabilities. For example (HELLO.REG):

REGEDIT

; Registration information for the hello application

;

; IDispatch*    Hello.Application {D3CE6D43-F1AF-1068-9FBB-08002B32372A}

; Type library: hello.tlb         {D3CE6D44-F1AF-1068-9FBB-08002B32372A}

; Interface:    IHello            {D3CE6D45-F1AF-1068-9FBB-08002B32372A}

;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello.Application (defaults to Hello.Application.1)

HKEY_CLASSES_ROOT\Hello.Application =

        OLE Automation Hello Application

HKEY_CLASSES_ROOT\Hello.Application\Clsid =

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello.Application.1

HKEY_CLASSES_ROOT\Hello.Application.1 =

        OLE Automation Hello 1.0 Application

HKEY_CLASSES_ROOT\Hello.Application.1\Clsid =

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello 1.0

HKEY_CLASSES_ROOT\CLSID\{D3CE6D43-F1AF-1068-9FBB-08002B32372A} =

        IDispatch Hello Example

HKEY_CLASSES_ROOT\CLSID\{D3CE6D43-F1AF-1068-9FBB-08002B32372A}\

        ProgID = Hello.Application.1

HKEY_CLASSES_ROOT\CLSID\{D3CE6D43-F1AF-1068-9FBB-08002B32372A}\

        VersionIndependentProgID = Hello.Application

HKEY_CLASSES_ROOT\CLSID\{D3CE6D43-F1AF-1068-9FBB-08002B32372A}\

        LocalServer = hello.exe /Automation

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello TypeLib

HKEY_CLASSES_ROOT\TypeLib\{D3CE6D44-F1AF-1068-9FBB-08002B32372A}

HKEY_CLASSES_ROOT\TypeLib\{D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0 =

        OLE Automation Hello 1.0 Type Library

HKEY_CLASSES_ROOT\TypeLib\{D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0\HELPDIR =

;Localized language is US English

HKEY_CLASSES_ROOT\TypeLib\{D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0\409\win16 =

        hello.tlb

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A} = _DHello

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A}\

        ProxyStubClsid = {00020420-0000-0000-C000-000000000046}

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A}\NumMethod = 7

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A}\BaseInterface =

        {00020400-0000-0000-C000-000000000046}

This registration entry should be merged with the system registration database when your customer installs your application. To merge the preceding file with the registration database, use the following command line:

regedit hello.reg

Note  If you are developing your application in C, you must also initialize the VTBLs to the OLE interfaces your application supports. For more information, see the OLE 2 Programmer's Reference, Volume 1.


Limitations of the Hello World Example
Limitations of the "Hello World" Example

The "Hello World" example was simplified for demonstration purposes. It has the following limitations:

•	Uses only scalar argument types. OLE Automation also supports methods and properties that accept arguments of complex types, including arrays, references to objects, and formatted data.

•	Does not raise user-defined exceptions on Invoke. The CreateStdDispatch function provides predefined exception codes.

•	Does not expose a collection of objects. Collections are supported through the IEnumVARIANT interface.

•	Supports one national language. CreateStdDispatch does not accommodate multiple localized member names.

The rest of this chapter explains the preceding steps and these more advanced features in greater detail.


Creating Class IDs

You need a unique class ID (CLSID) for each object you expose for creation. CLSIDs are universally unique identifiers (UUIDs, also called GUIDs). Run the GUIDGEN utility included in the OLE2\BIN directory to generate UUIDs.

Exposing a class for creation means that an instance of the object may be created directly through OLE Automation. For example, an application might expose a top-level object for creation, but have many other programmable objects that can be created or destroyed by accessing properties and methods of the top-level object. In this case, only the top-level object needs a class factory.

The CLSID identifies the object to OLE. The CLSID is included in your application and must be registered with the user's system when your application is installed. OLE provides the DEFINE_GUID macro to define a CLSID within your application, as in the following example (SPOLY sample, CLSID.H):

DEFINE_GUID(CLSID_CPoly, 0x00020462, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);


Initializing OLE

OLE2.DLL provides functions to initialize OLE for your application and to initialize your application's objects for creation using OLE Automation.

To initialize OLE

1.	Check the registration database to verify that your application is registered and, if it isn't, register your application.

2.	Initialize the OLE DLLs by calling OleInitialize.

3.	Create an instance of each class factory you expose.

4.	Call CoRegisterClassObject for each class your application exposes for creation.

The following example initializes OLE, then registers the CPoint and CPoly classes using their respective class factories (SPOLY sample, MISC.CPP):

STDAPI

InitOle()

{

    HRESULT hresult;

    if((hresult = OleInitialize(NULL)) != NOERROR)

      goto LError0;

    // Register the CPoint Class Factory

    if((g_ppointCF = CPointCF::Create()) == NULL){

      hresult = ResultFromScode(E_OUTOFMEMORY);

      goto LError1;

    }

    hresult = CoRegisterClassObject(

      CLSID_CPoint,

      g_ppointCF,

      CLSCTX_LOCAL_SERVER,

      REGCLS_MULTIPLEUSE,

      &g_dwPointCF);

    if(hresult != NOERROR)

      goto LError1;

    // Register the CPoly Class Factory.

    if((g_ppolyCF = CPolyCF::Create()) == NULL){

      hresult = ResultFromScode(E_OUTOFMEMORY);

      goto LError1;

    }

    hresult = CoRegisterClassObject(

      CLSID_CPoly,

      g_ppolyCF,

      CLSCTX_LOCAL_SERVER,

      REGCLS_MULTIPLEUSE,

      &g_dwPolyCF);

    if(hresult != NOERROR)

      goto LError1;

    g_ppolyCF->Release();

    g_ppointCF->Release();

    return NOERROR;

LError1:;

    if(g_ppolyCF != NULL)

      g_ppolyCF->Release();

    if(g_ppointCF != NULL)

      g_ppointCF->Release();

    UninitOle();

LError0:;

    return hresult;

}

The OLE 2 Programmer's Reference, Volume 1 provides more information on the OleInitialize and CoRegisterClassObject functions. Inside OLE 2 provides more information on verifying your application's entries in the registration database.


Initializing the Active Object

OLEAUT32.DLL provides several functions to identify and retrieve the running instance of an object or application through OLE Automation:

•	RegisterActiveObject — Sets the active object for an application. (Use when application starts.)

•	RevokeActiveObject — Revokes the active object. (Use when application ends.)

•	GetActiveObject — Retrieves a pointer to the object that is active. (In DispTest, this is implemented by the GetObject function.)

Applications can have more than one active object at a time. To be initialized as the active object, an object must:

•	Have a class factory (that is, the object provides an interface for creating instances of itself).

•	Identify its class factory by a ProgID in the system registry.

•	Be registered by a call to RegisterActiveObject when the object is created or becomes active.

When the active object is closed or deactivated, it should be revoked by a call to RevokeActiveObject.

Document-based, user-interactive applications should have one top-level object called the Application object. The Application object allows Automation controllers that access the application's programmable interface to do several things:

•	Bind to the application if it is already running, as with the DispTest GetObject function.

•	Create a new instance of the application, as with the DispTest CreateObject function.

•	Treat the application as an available prenamed object that exposes functionality.

•	Navigate the objects provided by the application.

OLE defines two command-line switches that may be used when starting an application:

•	/Embedding — indicates that an application was started by an OLE container application.

•	/Automation — indicates that the application was started for programmatic access.

You should expose the Application object's class factory only if the application is launched with the /Automation switch. OLE applies this switch if it appears on the command line or in the application's registration entry. If you expose the Application object's class factory, make sure to specify the REGCLS_SINGLEUSE attribute in the call to CoRegisterClassObject.

When a multiple-document interface (MDI) application starts, it typically registers the class factories for each of the objects it creates that can be linked or embedded, specifying REGCLS_MULTIPLEUSE. This attribute allows the existing application instance to be used later, when instances of those objects need to be created. Each new Application object, however, requires a new instance of the application to be launched, and therefore should specify REGCLS_SINGLEUSE. If the application registered its class factory using REGCLS_MULTIPLEUSE, the next CreateObject call that tried to create the application would get an existing copy.

The following chart shows how applications should expose their Application and Document objects.

Command line	MDI application	SDI application
/Embedding	Expose class factories for document classes, but not for the application. Call RegisterActiveObject for the Application object.	Expose class factories for document class, but not for the application. Call RegisterActiveObject for the active object.
/Embedding /Automation	Expose class factories for document classes. Expose class factory for the application using RegisterClassObject. Call RegisterActiveObject for the Application object.	Expose class factory for the Application object using RegisterClassObject. Do not expose class factory for document class. Call RegisterActiveObject for the active object.
No OLE switches	Expose class factories for document classes, but not for the application. Call RegisterActiveObject for the Application object.	Call RegisterActiveObject for the active object.


Creating the IUnknown Interface

IUnknown defines three member functions you must implement for each object you expose. The prototypes for these functions reside in OLE2.H:

•	QueryInterface — identifies which OLE interfaces the object supports.

•	AddRef — increments a member variable that tracks the number of references to an object.

•	Release — decrements the member variable that tracks the instances of an object. If an object has zero references, Release frees the object.

These functions provide an interface by which OLE can access your objects. Implement these functions according to the requirements discussed in the
OLE 2 Programmer's Reference, Volume 1.

The IUnknown interface for the CPoly object looks like this (SPOLY sample, CPOLY.CPP):

STDMETHODIMP

CPoly::QueryInterface(REFIID riid, void FAR* FAR* ppv)

{

    if(IsEqualIID(riid, IID_IUnknown) ||

       IsEqualIID(riid, IID_IDispatch)) {

      *ppv = this;

      AddRef();

      return NOERROR;

    }

    *ppv = NULL;

    return ResultFromScode(E_NOINTERFACE);

}

STDMETHODIMP_(unsigned long)

CPoly::AddRef()

{

    return ++m_refs;

}

STDMETHODIMP_(unsigned long)

CPoly::Release()

{

    POLYLINK FAR* FAR* pppolylink, FAR* ppolylinkDead;

    if(--m_refs == 0){

      Reset(); // release all CPoints

      // remove ourselves from the global list of polygons

      for( pppolylink = &g_ppolylink;

          *pppolylink != NULL;

       pppolylink = &(*pppolylink)->next)

      {

    if((*pppolylink)->ppoly == this){

      ppolylinkDead = *pppolylink;

      *pppolylink = (*pppolylink)->next;

      delete ppolylinkDead;

      break;

    }

      }

      --g_cPoly;

      SBprintf(g_psb, TSTR("#poly = %d"), g_cPoly);

      delete this;

      return 0;

    }

    return m_refs;

}


Exposing Objects for Creation with IClassFactory

Before OLE Automation can create an object, it must create the object's class. IClassFactory creates an instance of an object's class, so that instances of the object may be created.

You need to implement IClassFactory for objects that may be created explicitly using OLE Automation. For example, an application might expose an Application object for creation, but have many other programmable objects that can be created or destroyed by referencing the Application object. In this case, only the Application object needs a class factory.

For each class factory, you need to implement two member functions. The prototypes for these functions reside in OLE2.H.

•	CreateInstance — creates an instance of the object's class.

•	LockServer — prevents the object's server from shutting down, even if the last instance of the object is released. LockServer can improve the performance of applications that create and release objects frequently.

These member functions provide services for OLE API functions. The IClassFactory implementation for the CPoly object looks like this (CPOLY.CPP):

CPolyCF::CPolyCF()

{

    m_refs = 0;

}

IClassFactory FAR*

CPolyCF::Create()

{

    CPolyCF FAR* pCF;

    if((pCF = new FAR CPolyCF()) == NULL)

      return NULL;

    pCF->AddRef();

    return pCF;

}

STDMETHODIMP

CPolyCF::CreateInstance(

    IUnknown FAR* punkOuter,

    REFIID iid,

    void FAR* FAR* ppv)

{

    HRESULT hresult;

    CPoly FAR *ppoly;

    UNUSED(punkOuter);

    if((ppoly = CPoly::Create()) == NULL){

      *ppv = NULL;

      return ResultFromScode(E_OUTOFMEMORY);

    }

    hresult = ppoly->QueryInterface(iid, ppv);

    ppoly->Release();

    return hresult;

}

STDMETHODIMP

CPolyCF::LockServer(BOOL fLock)

{

    UNUSED(fLock);

    return NOERROR;

}

The object's class factory must also implement an IUnknown interface. For example (CPOLY.CPP):

STDMETHODIMP

CPolyCF::QueryInterface(REFIID riid, void FAR* FAR* ppv)

{

    if(!IsEqualIID(riid, IID_IUnknown)){

      if(!IsEqualIID(riid, IID_IClassFactory)){

        *ppv = NULL;

        return ResultFromScode(E_NOINTERFACE);

      }

    }

    *ppv = this;

    ++m_refs;

    return NOERROR;

}

STDMETHODIMP_(unsigned long)

CPolyCF::AddRef(void)

{

    return ++m_refs;

}

STDMETHODIMP_(unsigned long)

CPolyCF::Release(void)

{

    if(--m_refs == 0){

      delete this;

      return 0;

    }

    return m_refs;

}

The class factory implementation of IUnknown should resemble the preceding example.

Note  Class factories are described more extensively in the OLE 2 Programmer's Reference, Volume 1 and in Inside OLE 2.


Creating the IDispatch Interface

IDispatch provides mechanisms to access and retrieve information about an object's methods and properties. In addition to the member functions inherited from IUnknown, you need to implement the following member functions within the class definition of each object you expose through OLE Automation:

•	GetTypeInfoCount  — for objects that support IDispatch, the type information count is always 1.

•	GetTypeInfo — retrieves a description of the object's programmable interface.

•	GetIDsOfNames — maps the name of a method or property to a dispatch ID used when invoking the method or property.

•	Invoke — accesses the object's methods and properties.

You may implement IDispatch at one of three levels:

•	By calling the CreateStdDispatch function.

•	By adding the member functions to your class and delegating to the DispInvoke and DispGetIDsOfNames functions.

•	By implementing the member functions without delegating to the dispatch functions.

Implementing IDispatch by Calling CreateStdDispatch
Implementing IDispatch by Delegating
Implementing IDispatch Without the Dispatch Functions


Implementing IDispatch by Calling CreateStdDispatch

Using CreateStdDispatch is the fastest way to implement the IDispatch interface for an object. It is useful if your OLE Automation object uses only the standard dispatch exception codes and supports one national language.

The CreateStdDispatch function used in the DspCalc2 sample implements these member functions for the CCalc object by calling CreateStdDispatch on the loaded type information (DSPCALC2.CPP):

// Some error handling code omitted...

// Try to load the type library from the information in the registry

if((hresult = LoadRegTypeLib(LIBID_DspCalc2, 1, 0, 0x0409, &ptlib)) !=     NOERROR){

    #define TLB_NAME OLESTR("dspcalc2.tlb")

    // If not registered, try to load from the path/current directory.

    // If this succeeds, the type library will be registered for use

    // the next time.

    if((hresult = LoadTypeLib(TLB_NAME, &ptlib)) != NOERROR)

        goto LError0;

    }

if((hresult = ptlib->GetTypeInfoOfGuid(IID_ICalculator, &ptinfo)) !=     NOERROR){

    goto LError0;

    }

    // Create an aggregate with an instance of the default

    // implementation of IDispatch that is initialized with our

    // TypeInfo.

    //

    hresult = CreateStdDispatch(

      pcalc,                            // controlling unknown

      &(pcalc->m_arith),                // vtable* to dispatch on

      ptinfo,

      &punkStdDisp);


Implementing IDispatch by Delegating

Delegating to the dispatch functions DispInvoke and DispGetIDsOfNames allows your object to handle special situations before or after calling the provided functions. Using DispInvoke and DispGetIDsOfNames allows you to support multiple national languages and to create application-specific exceptions that are passed back to OLE Automation controllers.

The SPoly2 sample application implements the IDispatch interface using these dispatch functions (CPOLY.CPP):

STDMETHODIMP

CPoly::Invoke(

    DISPID dispidMember,

    REFIID riid,

    LCID lcid,

    unsigned short wFlags,

    DISPPARAMS FAR* pdispparams,

    VARIANT FAR* pvarResult,

    EXCEPINFO FAR* pexcepinfo,

    unsigned int FAR* puArgErr)

{

    UNUSED(lcid);

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    return DispInvoke(

      this, m_ptinfo,

      dispidMember, wFlags, pdispparams,

      pvarResult, pexcepinfo, puArgErr);

}

STDMETHODIMP

CPoly::GetTypeInfoCount(unsigned int FAR* pctinfo)

{

    // This object has a single *introduced* interface

    *pctinfo = 1;

    return NOERROR;

}

STDMETHODIMP

CPoly::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)

{

    UNUSED(lcid);

    if(itinfo != 0)

      return ResultFromScode(DISP_E_BADINDEX);

    m_ptinfo->AddRef();

    *pptinfo = m_ptinfo;

    return NOERROR;

}

STDMETHODIMP

CPoly::GetIDsOfNames(

    REFIID riid,

    OLECHAR FAR* FAR* rgszNames,

    unsigned int cNames,

    LCID lcid,

    DISPID FAR* rgdispid)

{

    UNUSED(lcid);

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);

}


Implementing IDispatch Without the Dispatch Functions

Implementing IDispatch without using CreateStdDispatch, DispInvoke, or DispGetIDsOfNames allows you to create an implementation tailored specifically to your application. For example, you may want to implement your own dispatch functions to handle consistently recurring needs within your application.

The SPoly sample implements GetIDsOfNames and Invoke without using the dispatch functions (CPOLY.CPP):

STDMETHODIMP

CPoly::GetIDsOfNames(

    REFIID riid,

    OLECHAR FAR* FAR* rgszNames,

    unsigned int cNames,

    LCID lcid,

    DISPID FAR* rgdispid)

{

static PARAMDESC rgpdAddPoint[] = {

    {OLESTR("X")}, {OLESTR("Y")}

};

static MEMBERDESC rgmdCPoly[] = {

    {OLESTR("DRAW"),           IDMEMBER_CPOLY_DRAW,            NULL,        0},

    {OLESTR("RESET"),       IDMEMBER_CPOLY_RESET,            NULL,        0},

    {OLESTR("ADDPOINT"),       IDMEMBER_CPOLY_ADDPOINT,       rgpdAddPoint,2},

    {OLESTR("ENUMPOINTS"),     IDMEMBER_CPOLY_ENUMPOINTS,    NULL,        0},

    {OLESTR("GETXORIGIN"),  IDMEMBER_CPOLY_GETXORIGIN,    NULL,        0},

    {OLESTR("SETXORIGIN"),  IDMEMBER_CPOLY_SETXORIGIN,    NULL,        0},

    {OLESTR("GETYORIGIN"),  IDMEMBER_CPOLY_GETYORIGIN,    NULL,        0},

    {OLESTR("SETYORIGIN"),  IDMEMBER_CPOLY_SETYORIGIN,    NULL,        0},

    {OLESTR("GETWIDTH"),    IDMEMBER_CPOLY_GETWIDTH,        NULL,        0},

    {OLESTR("SETWIDTH"),    IDMEMBER_CPOLY_SETWIDTH,        NULL,        0},

    {OLESTR("get_red"),        IDMEMBER_CPOLY_GETRED,        NULL,        0},

    {OLESTR("set_red"),        IDMEMBER_CPOLY_SETRED,        NULL,        0},

    {OLESTR("get_green"),   IDMEMBER_CPOLY_GETGREEN,        NULL,        0},

    {OLESTR("set_green"),   IDMEMBER_CPOLY_SETGREEN,        NULL,        0},

    {OLESTR("get_blue"),    IDMEMBER_CPOLY_GETBLUE,        NULL,        0},

    {OLESTR("set_blue"),    IDMEMBER_CPOLY_SETBLUE,        NULL,        0},

    {OLESTR("DUMP"),            IDMEMBER_CPOLY_DUMP,            NULL,        0}

};

    // this object only exposes a "default" interface.

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    return SPolyGetIDsOfNames(

      rgmdCPoly, DIM(rgmdCPoly), rgszNames, cNames, lcid, rgdispid);

}

STDMETHODIMP

CPoly::Invoke(

    DISPID dispidMember,

    REFIID riid,

    LCID lcid,

    unsigned short wFlags,

    DISPPARAMS FAR* pdispparams,

    VARIANT FAR* pvarResult,

    EXCEPINFO FAR* pexcepinfo,

    unsigned int FAR* puArgErr)

{

    HRESULT hresult;

    VARIANTARG varg0, varg1;

    VARIANT varResultDummy;

    UNUSED(lcid);

    UNUSED(pexcepinfo);

    if(wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET |                         DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))

      return ResultFromScode(E_INVALIDARG);

    // this object only exposes a "default" interface.

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    // It makes the following code simpler if the caller

    // ignores the return value. Some implementations

    // deal with this differently.

    if(pvarResult == (VARIANT FAR*)NULL)

      pvarResult = &varResultDummy;

    VariantInit(&varg0);

    VariantInit(&varg1);

    // assume the return type is void, unless we find otherwise.

    VariantInit(pvarResult);

    switch(dispidMember){

    case IDMEMBER_CPOLY_DRAW:

      Draw();

      break;

//. . . case statements omitted here . . .

    case IDMEMBER_CPOLY_GETBLUE:

      V_VT(pvarResult) = VT_I2;

      V_I2(pvarResult) = get_blue();

      break;

    case IDMEMBER_CPOLY_SETBLUE:

      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);

      if(hresult != NOERROR)

        return hresult;

      set_blue(V_I2(&varg0));

      break;

    default:

      return ResultFromScode(DISP_E_MEMBERNOTFOUND);

    }

    return NOERROR;

}


Creating the Programmable Interface

An object's programmable interface comprises the properties and methods it defines. Organizing the objects, properties, and methods that an application exposes is like creating an object-oriented framework for an application. Standards and Guidelines discusses some of the concepts behind naming and organizing the programmable elements your application may expose.

Creating Methods
Creating Properties
Passing Formatted Data
The Application Object
The Value Property
Returning Objects
Restricting Access


Creating Methods

A method performs an action on an object and may return a value. Like C-language functions, methods can take any number of arguments, any of which may be optional. Arguments may be passed by value or by reference.

In the SPoly2 sample, the class for the CPoly object includes the AddPoint method shown below (CPOLY.CPP):

HRESULT METHODCALLTYPE

CPoly::AddPoint(short x, short y)

{

    CPoint FAR* ppoint;

    POINTLINK FAR* ppointlink;

    ppoint = CPoint::Create();

    if(ppoint == (CPoint FAR*)NULL)

      return ResultFromScode(E_OUTOFMEMORY);

    ppoint->SetX(x);

    ppoint->SetY(y);

    ppointlink = new FAR POINTLINK;

    if(ppointlink == (POINTLINK FAR*)NULL){

      delete ppoint;

      return ResultFromScode(E_OUTOFMEMORY);

    }

    ppointlink->ppoint = ppoint;

    ppointlink->next = (POINTLINK FAR*)NULL;

    if(m_ppointlinkLast == (POINTLINK FAR*)NULL){

      m_ppointlink = m_ppointlinkLast = ppointlink;

    }else{

      m_ppointlinkLast->next = ppointlink;

      m_ppointlinkLast = ppointlink;

    }

    ++m_cPoints;

    return NOERROR;

}


Creating Properties

A property represents an attribute of an object. Most properties have a pair of accessor functions — a function to get the property value and a function to set the property. Properties that are read-only or write-only, however, may have only one accessor function. Properties may receive arguments, though this is less common than with methods.

In the DspCalc2 sample, the CCalc object exposes the Accum property. This property is implemented by the following accessor functions (DSPCALC2.CPP):

STDMETHODIMP_(void)

CArith::put_Accum(long l)

{

    m_accum = l;

}

STDMETHODIMP_(long)

CArith::get_Accum()

{

    return m_accum;

}

The accessor functions for a single property have the same dispatch ID (DISPID). The purpose of each function is indicated by attributes set for the function. These attributes can be set in the METHODDATA structure or in the ODL description of the function as shown in the following table:

Purpose of function	METHODDATA flag (wFlags)	ODL attribute
Return a value	DISPATCH_PROPERTYGET	propget
Set a value	DISPATCH_PROPERTYPUT	propput
Set a reference	DISPATCH_PROPERTYPUTREF	propputref
In the DispCalc sample, attributes are set with the METHODDATA structure (IDATA.CPP). In the DspCalc2 sample, attributes are set in the ODL description (CALCTYPE.ODL). For example, the following two descriptions are equivalent:

// Using METHODDATA -- resides in a file compiled with C compiler

static METHODDATA rgmdataCalc[]

{

    {"Accum", &pdataACCUM, DISPID_VALUE, IMETH_ACCUM, CC_PASCAL, 1,

        DISPATCH_PROPERTYPUT, VT_EMPTY},

    {"Accum", 0, DISPID_VALUE, IMETH_ACCUM + 1, CC_PASCAL, 0,

     DISPATCH_PROPERTYGET, VT_I4}

};

// Using ODL -- resides in .ODL file compiled with MkTypLib.

interface _ICalculator : IUnknown

    {

      [propput]

      void Accum([in] long l);

      [propget, helpstring("The value stored in the calculator")]

      long Accum();

    }


Passing Formatted Data

In many situations, an application may need to accept formatted data as an argument to a method or property. Examples include a bitmap, formatted text, or a spreadsheet range. When handling formatted data, the application should pass an object that implements the OLE IDataObject interface.

Using this interface, applications can retrieve data of any Clipboard format. Because an IDataObject can provide data of more than one format, a caller can provide data of several formats, and let the called object choose which format is most appropriate (like the Clipboard).

If the data object implements IDispatch, it should be passed using the VT_DISPATCH flag. If the data object does not support IDispatch, it should be passed with the VT_UNKNOWN flag.


The Application Object

Document-based, user-interactive applications that provide OLE Automation objects should expose an Application object.

The Application object identifies the application and provides a way for OLE Automation controllers to bind to and navigate an application's exposed objects. The Application object is identified by the [appobject] attribute in the object description language (ODL) script.

In addition, the Application object is initialized as the active object when the application starts. See Initializing the Active Object for details.

See Standards and Guidelines for more on recommended objects, properties, and methods.


The Value Property

The Value property defines the default behavior of an object if no property or method is specified. For example, the following DispTest code returns the default property of the CCalc object. In the case of CCalc, this is the number displayed on the calculator.

Dim Calc As Object

Set Calc = CreateObject("DispCalc.CCalc")

Print Calc

The Value property is identified by the dispatch ID DISPID_VALUE. In an ODL file, the Value property has the attribute id(0).

See Standards and Guidelines for more on recommended objects, properties, and methods.


Returning Objects

To return an object from a property or method, the application should return a pointer to the object's implementation of the IDispatch interface. The data type of the return value should be VT_DISPATCH.


Restricting Access

OLE Automation provides several ways of restricting access to objects. The simplest approach is not to document the properties and methods you do not want end users to see. In addition, you can prevent a property or method from appearing in type library browsers by specifying the restricted attribute in ODL or by specifying the flag FUNCFLAG_FRESTRICTED in the FUNCDESC structure for the item.

Restricted properties and methods can be invoked by OLE Automation controllers, but are not visible to the end user who may be using a language such as Visual Basic. To restrict access even further, you can require a password argument for a method that returns an object. The returned object can provide methods and properties to perform actions that should not be available to all customers.

The following figure shows the Security method, which returns an object with the method Access and the properties Owner and Lock. Security, Access, Owner, and Lock all have the restricted attribute. In addition, the Security method requires an argument, key, to gain access to subordinate methods and properties.


Creating Collection Objects

The defining characteristic of a collection object is the ability to iterate over the items it contains. OLE Automation defines the IEnumVARIANT interface to provide a standard way for OLE Automation controllers to iterate over collections. In addition, a collection object must expose a read-only property named _NewEnum to let OLE Automation controllers know that the object supports iteration. The _NewEnum property returns an enumerator object that supports IEnumVariant.


Implementing IEnumVARIANT

The IEnumVARIANT interface provides a way to iterate through the objects contained by a collection object. The enumerator object that supports IEnumVariant is returned by the _NewEnum property of the collection object. Collection objects do not need an IClassFactory implementation, because they are built from elements that have their own class factories.

The IEnumVARIANT interface defines these member functions:

•	Next — retrieves one or more elements in a collection, starting with the current element.

•	Skip — skips over one or more elements in a collection.

•	Reset — resets the current element to the first element in the collection.

•	Clone — copies the current state of the enumeration so you can return to the current element after using Skip or Reset.

In the SPoly2 example, CEnumPoint is the iteration interface for CPoint objects. CEnumPoint implements the Next, Skip, Reset, and Clone member functions in addition to the member functions inherited from IUnknown and IDispatch (CENUMPT.CPP):

STDMETHODIMP

CEnumPoint::Next(

    unsigned long celt,

    VARIANT FAR* rgvar,

    unsigned long FAR* pceltFetched)

{

    long ix;

    unsigned int i;

    HRESULT hresult;

    for(i = 0; i < celt; ++i)

      VariantInit(&rgvar[i]);

    for(i = 0; i < celt; ++i){

      if(m_iCurrent == m_celts){

        hresult = ResultFromScode(S_FALSE);

        goto LDone;

      }

      ix = m_iCurrent++;

      hresult = SafeArrayGetElement(m_psa, &ix, &rgvar[i]);

      if(FAILED(hresult))

        goto LError0;

    }

    hresult = NOERROR;

LDone:;

    if (pceltFetched != NULL)

      *pceltFetched = i;

    return hresult;

LError0:;

    for(i = 0; i < celt; ++i)

      VariantClear(&rgvar[i]);

    return hresult;

}

STDMETHODIMP

CEnumPoint::Skip(unsigned long celt)

{

    m_iCurrent += celt;

    if(m_iCurrent > m_celts)

     m_iCurrent = m_celts;

    return (m_iCurrent == m_celts)

      ? ResultFromScode(S_FALSE) : NOERROR;

}

STDMETHODIMP

CEnumPoint::Reset()

{

    m_iCurrent = 0;

    return NOERROR;

}

STDMETHODIMP

CEnumPoint::Clone(IEnumVARIANT FAR* FAR* ppenum)

{

    HRESULT hresult;

    SAFEARRAY FAR* psa;

    CEnumPoint FAR* penum;

    hresult = SafeArrayCopy(m_psa, &psa);

    if(FAILED(hresult))

      return hresult;

    hresult = CEnumPoint::Create(psa, &penum);

    if(FAILED(hresult))

      goto LError0;

    penum->m_iCurrent = m_iCurrent;

    *ppenum = penum;

    return NOERROR;

LError0:

    SafeArrayDestroy(psa);

    return hresult;

}

See Standards and Guidelinesfor more information about collection objects.


Implementing the _NewEnum Property

The _NewEnum property identifies an object as supporting iteration through the IEnumVARIANT interface. The _NewEnum property has the following requirements:

•	Must be named _NewEnum and not be localized.

•	Must return a pointer to the enumerator object's IUnknown interface (VT_Unknown).

•	Must have DISPID = DISPID_NEWENUM (-4)

See Standards and Guidelines for more information about collection objects.


Creating Type Information

Type information is the OLE Automation standard for describing exposed objects, properties, and methods to an application or programming tool that accesses an exposed object. You provide type information in one of two ways:

•	As a type library written in Microsoft Object Description Language (ODL) and compiled by MkTypLib.

•	As a data structure exported at run time.

OLE Automation also supports the creation of alternative tools that compile and access type information. See Type Building Interfaces, for more information on creating such tools.

The following figure illustrates the differences between exporting type information and creating a type library.

Type libraries provide more features than type information exported from the application itself. Using type libraries is the standard way to describe a programmable interface. The following table compares the relative advantages of type libraries and exported type information.

Type library	Exported type information
Provides run-time and controller compile-time access to information.	Provides run-time access only.
Supports browsing and provides Help information on objects, properties, and methods.	Can't be browsed and does not provide Help.
Type information is stored in a file that is separate from the .EXE file.  However, the type library may be bound with a DLL using the resource compiler.	Type information is part of the .EXE file or the DLL.


Creating a Type Library

A type library stores complete type information for all of an application's exposed objects. It may be included as a resource in a DLL or remain as a stand-alone file (.TLB).

To create a type library

1.	Write an object description script (.ODL) for the objects you expose.

2.	Build the type library (.TLB) and class description header file (.H) from the script using MkTypLib.


Writing an Object Description Script

An object description script is essentially an annotated header file. The following example shows CALCTYPE.ODL, the object description script for the DspCalc2 sample.

[

  uuid(00020470-0000-0000-C000-000000000046),

  helpstring("OLE Automation DspCalc2 1.0 Type Library"),

  lcid(0x0409),

  version(1.0)

]

library DspCalc2

{

#ifdef WIN32

    importlib("stdole32.tlb");

#else

    importlib("stdole.tlb");

#endif

    typedef enum operators {

      OP_NONE = 0,

      OP_PLUS,

      OP_MINUS,

      OP_MULT,

      OP_DIV

    } OPERATORS;

    [

      uuid(00020441-0000-0000-C000-000000000046),

      helpstring("DspCalc2"),

      odl

    ]

    interface _ICalculator : IUnknown

    {

      [propput]

      void Accum([in] long l);

      [propget, helpstring("The value stored in the calculator")]

      long Accum();

      [propput]

      void Opnd([in] long l);

      [propget]

      long Opnd();

      [propput]

      void Op([in] OPERATORS op);

      [propget]

      OPERATORS Op();

      boolean Eval(void);

      void Clear(void);

      void Display(void);

      void Quit(void);

      [vararg] boolean Button([in]SAFEARRAY(VARIANT) psa);

    }

    [

      uuid(00020442-0000-0000-C000-000000000046),

      helpstring("DspCalc2")

    ]

    dispinterface _DCalculator

    {

      interface _ICalculator;

    }

    [

      uuid(00020469-0000-0000-C000-000000000046),

      helpstring("DspCalc2"),

      appobject

    ]

    coclass Calculator

    {

      dispinterface _DCalculator;

      interface _ICalculator;

    }

}

Note that the Accum, Opnd, and Op properties have arguments with the [in] attribute. These arguments supply a value and are read-only. If an argument returns a value, it should have the [out] attribute. Arguments that both supply and return a value should have both attributes.

For more information on the Microsoft Object Description Language, see Object Description Language.


Building the Type Library

The MkTypLib tool builds type libraries. MkTypLib is described in detail in Object Description Language.

To create a type library from an object description script

1.	From Windows, run the MkTypLib tool on the script. For example:

MKTYPLIB /TLB output.tlb /H output.h inscript.odl

Note that MkTypLib requires Windows. If you run it from NMAKE, you must use the WX server support provided with OLE. For example, start WXSRVR in Windows and use the following command line in your NMAKE script:

WX MKTYPLIB /o odlscrip.err /TLB output.tlb /h output.h inscript.odl

Alternately, you can add MkTypLib to the Tools menu in the Microsoft C programming environment. If you do this, be sure to check the Arguments check box, so the programming environment will prompt you for the options and input file before running the tool.

After creating the type library (.TLB), you can include it in the resource step of building your application or leave it as a stand-alone file. In either case, the resulting filename must be included in the registration file. See "Creating a Registration File" later in this section for information on registering the type library.

To build an application using a type library

1.	Include the header file output by MkTypLib in your project.

2.	Compile the project.

3.	Optionally, use the Resource Compiler to bind the type library with your compiled project. You can bind the type library with DLLs, but not with .EXE files. For example, to bind a type library named OUTPUT.TLB with a DLL, use the following statement in the .RC file for the DLL:

        1 typelib output.tlb

The following figure illustrates the process.


Exporting Type Information from Your Application

To export type information, you create C data definitions, then compile and link those definitions with your application. Type information exported from an application is a subset of the information available in a type library.

The data structures you must create are:

•	INTERFACEDATA — information about the interface (in this case, the class CPoly).

•	METHODDATA — each element contains information about a method or property in an interface.

•	PARAMDATA — each element contains information about one argument for a method or property.

The INTERFACEDATA structure has two fields. The first is a pointer to an array of METHODDATA structures, and the second is the number of members in the array (Spoly2 sample, TDATA.CPP).

INTERFACEDATA NEAR g_idataCPoly =

{

    rgmdataCPoly, DIM(rgmdataCPoly)

};

In the preceding example, rgmdataCPoly[] is an array of METHODDATA structures for the Cpoly object. This array contains all the methods for the CPoly object, including the Draw method, as follows:

static METHODDATA NEAR rgmdataCPoly[] =

{

    // void CPoly::Draw(void)

    {

    OLESTR("Draw"),

    NULL,

    IDMEMBER_CPOLY_DRAW,

    IMETH_CPOLY_DRAW,

    CC_CALL,

    0,

    DISPATCH_METHOD,

    VT_EMPTY

    }

    .

    .        // Structures for other members omitted.

    .

};

Note that the second member of the structure is NULL. If the Draw method required an argument, the second member would be a pointer to the array of parameters, and its type information would also include a PARAMDATA structure, as for the SetX and SetY methods in the following example. This example shows the data structures in the order in which they must appear in source code.

static PARAMDATA NEAR rgpdataCPointSetX[] =

{

    { OLESTR("X"), VT_I2 }

};

static PARAMDATA NEAR rgpdataCPointSetY[] =

{

    { OLESTR("Y"), VT_I2 }

};

static METHODDATA NEAR rgmdataCPoint[] =

{

    // CPoint::GetX()

    {

    OLESTR("GetX"),

    NULL,

    IDMEMBER_CPOINT_GETX,

    IMETH_CPOINT_GETX,

    CC_CALL,

    0,

    DISPATCH_METHOD,

    VT_I2

    },

    // CPoint::SetX()

    {

    OLESTR("SetX"),

    rgpdataCPointSetX,

    IDMEMBER_CPOINT_SETX,

    IMETH_CPOINT_SETX,

    CC_CALL,

    DIM(rgpdataCPointSetX),

    DISPATCH_METHOD,

    VT_EMPTY

    },

.

.                // Other methods omitted.

.

};

INTERFACEDATA NEAR g_idataCPoint =

{

    rgmdataCPoint, DIM(rgmdataCPoint)

};

To translate these data definitions into type information, you must call CreateDispTypeInfo when you create the first instance of an object. For example:

CPoly FAR*

CPoly::Create()

{

    HRESULT hresult;

    CPoly FAR* ppoly;

    ITypeInfo FAR* ptinfo;

    POLYLINK FAR* ppolylink;

    extern INTERFACEDATA NEAR g_idataCPoly;

    if((ppolylink = new FAR POLYLINK) == (POLYLINK FAR*)NULL)

      return (CPoly FAR*)NULL;

    if((ppoly = new FAR CPoly()) == (CPoly FAR*)NULL)

      return (CPoly FAR*)NULL;

    ppoly->AddRef();

    // create and attach its TypeInfo

    hresult = CreateDispTypeInfo(

        &g_idataCPoly,

        LOCALE_SYSTEM_DEFAULT,

        &ptinfo);

    if(hresult != NOERROR)

      goto LError0;

    ppoly->m_ptinfo = ptinfo;

    // push the new polygon onto the front of the polygon list.

    ++g_cPoly;

    ppolylink->ppoly = ppoly;

    ppolylink->next = g_ppolylink;

    g_ppolylink = ppolylink;

    SBprintf(g_psb, TSTR("#poly = %d"), g_cPoly);

    return ppoly;

LError0:;

    ppoly->Release();

    return NULL;

}


Creating a Registration File

Before an application can use OLE and OLE Automation, the OLE objects must be registered with the user's system registration database. OLE provides sample registration files to perform this task for the OLE objects and the sample applications.

To create a registration file

1.	Copy DISPCALC.REG.

2.	Rename and edit the file, adding entries for your application.

The registration file provides information about the application, application object, classes of objects, type libraries, and interfaces. Each entry starts with the constant HKEY_CLASSES_ROOT, which represents the root key of the entire registration database, then supplies specific information for an object.

The following sections provide a brief overview of the syntax used in registering OLE Automation applications. For detailed information, refer to the OLE 2 Programmer's Reference, Volume 1.

Registering the Application
Registering Classes (Objects)
Registering Type Library Information
Registration File Example


Registering the Application

The registration file uses the following syntax for the application:

\ AppName.ObjectName[.VersionNumber] = human_readable_string
\ AppName.ObjectName\CLSID = {UUID}

AppName

The name of the application.

ObjectName

The name of the object to be registered, in this case, Application.

VersionNumber

The optional version number of the object. If you omit a version number, version 1 is registered by default.

human_readable_string

A string that describes the application, as you want it to appear to users. The recommended maximum length is 40 characters.

UUID

The universally unique ID for the application class (CLSID). To generate a universally unique ID for your class, run the utility GUIDGEN.EXE provided in the OLE2\BIN directory.

Example

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello.Application (defaults to Hello.Application.1)

HKEY_CLASSES_ROOT\Hello.Application =

        OLE Automation Hello Application

HKEY_CLASSES_ROOT\Hello.Application\Clsid =

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello.Application.1

HKEY_CLASSES_ROOT\Hello.Application.1 =

        OLE Automation Hello 1.0 Application

HKEY_CLASSES_ROOT\Hello.Application.1\Clsid =

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}


Registering Classes (Objects)

The registration file uses the following syntax for each class of each object your application exposes:

\CLSID\{UUID} = human_readable_string
\CLSID\{UUID}\ProgID = AppName.ObjectName.VersionNumber \CLSID\{UUID}\VersionIndependentProgID = AppName.ObjectName
\CLSID\{UUID}\LocalServer = filepath[/Automation]

human_readable_string

A string that describes the object, as you want it to appear to users. The recommended maximum length is 40 characters.

AppName

The name of the application, as specified previously in the application registration string.

ObjectName

The name of the object to be registered.

VersionNumber

The version number of the object.

UUID

The universally unique ID for the application class (CLSID). To generate a universally unique ID for your class, run the utility GUIDGEN.EXE provided in the OLE2\BIN directory.

filepath

The pathname of the application file that contains the object. The optional /Automation switch tells the application it was launched for automation purposes. For more information on /Automation, see Initializing the Active Object.

The progammatic IDs (ProgID and VersionIndependentProgID) are used by other programmers to gain access to the objects you expose. These IDs should follow consistent naming guidelines across all your applications. They may contain up to 39 characters, must contain no punctuation (except for the period), and must not start with a digit.

Version-independent names consist of an AppName.ObjectName, without a version number; for example, Word.Document or Excel.Chart.

Version-dependent names consist of an AppName.ObjectName.VersionNumber, such as Excel.Application.5.

The filepath you register should give the full path and name, so that applications need not rely on the MS-DOS PATH variable to find the object.

Example

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello 1.0

HKEY_CLASSES_ROOT\CLSID\

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A} =

        IDispatch Hello Example

HKEY_CLASSES_ROOT\CLSID\

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}\ProgID =

        Hello.Application.1

HKEY_CLASSES_ROOT\CLSID\

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}\

        VersionIndependentProgID = Hello.Application

HKEY_CLASSES_ROOT\CLSID\

        {D3CE6D43-F1AF-1068-9FBB-08002B32372A}\LocalServer =

        hello.exe /Automation


Registering Type Library Information

In addition to entering a programmability name for each class, applications that support programmability need to register their type libraries. All type library information is registered under the top level key \TypeLib. The information registered for a type library is as follows:

\TypeLib\{libUUID}
\TypeLib\{libUUID}\major.minor = human_readable_string
\TypeLib\{libUUID}\major.minor\HELPDIR = [helpfile_path] \TypeLib\{libUUID}\major.minor\lcid\platform = localized_typelib_filename

libUUID

The universally unique ID of the type library.

major.minor

The two-part version number of the type library. If only the minor version number increases, all the features of the previous type library are supported in a compatible way. If the major version number changes, code that compiled against the type library must be recompiled. The version number of the type library may differ from the version number of the application.

human_readable_string

A string that describes the type library, as you want it to appear to users. The recommended maximum length is 40 characters.

helpfile_path

The directory where the Help file(s) for the types in the type library are located. Note that if the application supports type libraries for multiple languages, the libraries may refer to different filenames in the Help file directory.

lcid

The hexadecimal string representation of the locale ID (LCID). It is one to four hexadecimal digits with no 0x prefix and no leading zeros. The LCID may have a neutral sublanguage.

platform

The target operating system platform: Win16, Win32, or Mac.

localized_typelib_filename

The full name of the localized type library.

Using the LCID specifier, an application can explicitly register the filenames of type libraries for different languages. This allows the application to find the desired language without having to open all type libraries with a given name. To find the type library for Australian English (309), the application would first look for it and, if that fails, look for an entry for Standard English (9). If there is no entry for Standard English, the application looks for the LANG_SYSTEM_DEFAULT (0). For more information on locale support, refer to the Windows NT documentation for the NLS interface.

Note  You can generate the correct registration entries for a type library by calling the RegisterTypeLib function on the type library. You can then use REGEDIT, supplied with the Windows SDK, to write the registration entries to a text file from your system registration database.

Example

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info Hello TypeLib

HKEY_CLASSES_ROOT\TypeLib\

        {D3CE6D44-F1AF-1068-9FBB-08002B32372A}

HKEY_CLASSES_ROOT\TypeLib\

        {D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0 =

        OLE Automation Hello 1.0 Type Library

HKEY_CLASSES_ROOT\TypeLib\

        {D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0\HELPDIR =

;Localized language is US English

HKEY_CLASSES_ROOT\TypeLib\

        {D3CE6D44-F1AF-1068-9FBB-08002B32372A}\1.0\409\win16 =

        hello.tlb


Registering Interface Information

Applications that add interfaces need to register the interfaces under the \Interface key. The information registered for a type library is as follows:

\Interface\{IID} = InterfaceName
\Interface\{IID}\ProxyStubClsid = CLSID
\Interface\{IID}\NumMethod = cmethods

[\Interface\{IID}\BaseInterface = InterfaceID]

IID

The universally unique ID of the interface.

InterfaceName

The name of the interface.

CLSID

The UUID associated with the proxy/stub implementation of the interface, used internally by OLE for interprocess communication. Automation objects use the proxy/stub implementation of IDispatch.

cMethods

The number of methods this interface supports.

InterfaceID

The UUID of the interface from which this was derived. Specifying the BaseInterface key, but omitting a value after the equals sign, means the interface was derived from nothing. Omitting the key entirely means the interface was derived from IUnknown.

Example

The ProxyStubClsid in the following example refers to the proxy/stub implementation of IDispatch. The example specifies NumMethods = 7, because IDispatch has 7 methods (3 from IUnknown and 4 of its own). In the last line, the BaseInterface is the UUID of IDispatch.

HKEY_CLASSES_ROOT\Interface\

        {D3CE6D46-F1AF-1068-9FBB-08002B32372A} = _DHello

HKEY_CLASSES_ROOT\Interface\

        {D3CE6D46-F1AF-1068-9FBB-08002B32372A}\ProxyStubClsid =

        {00020420-0000-0000-C000-000000000046}

HKEY_CLASSES_ROOT\Interface\

        {D3CE6D46-F1AF-1068-9FBB-08002B32372A}\NumMethod = 7

HKEY_CLASSES_ROOT\Interface\

        {D3CE6D46-F1AF-1068-9FBB-08002B32372A}\BaseInterface =

        {00020400-0000-0000-C000-000000000046}


Registration File Example

DspCalc2 uses the following entries to register its Application object (Dspcalc2.Application) with the system (DSPCALC2.REG):

REGEDIT

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info DspCalc2.Application (defaults to DspCalc2.Application.1

HKEY_CLASSES_ROOT\Dspcalc2.Application =

        OLE Automation DspCalc2 Application

HKEY_CLASSES_ROOT\Dspcalc2.Application\Clsid =

        {00020469-0000-0000-C000-000000000046}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info DspCalc2.Application.1

HKEY_CLASSES_ROOT\Dspcalc2.Application.1 =

        OLE Automation DspCalc2 1.0 Application

HKEY_CLASSES_ROOT\Dspcalc2.Application.1\Clsid =

        {00020469-0000-0000-C000-000000000046}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; registration info DspCalc2 1.0

HKEY_CLASSES_ROOT\CLSID\

        {00020469-0000-0000-C000-000000000046} =

        OLE Automation DspCalc2 1.0 Application

HKEY_CLASSES_ROOT\CLSID\

        {00020469-0000-0000-C000-000000000046}\ProgID =

        DspCalc2.Application.1

HKEY_CLASSES_ROOT\CLSID\

        {00020469-0000-0000-C000-000000000046}\

        VersionIndependentProgID = Dspcalc2.Application

HKEY_CLASSES_ROOT\CLSID\

        {00020469-0000-0000-C000-000000000046}\LocalServer =

        dspcalc2.exe /Automation


Releasing OLE

To release OLE on exit

1.	Release the active object by calling RevokeActiveObject.

2.	Release the classes of your exposed objects by calling CoRevokeClassObject.

3.	Reset OLE by calling OleUninitialize.

The following code releases an active Ccalc object, releases the CCalc class, then unloads OLE (DSPCALC2.CPP):

HRESULT

UninitOle()

{

    if(g_dwRegisterCCalc != 0)

      RevokeActiveObject(g_dwRegisterCCalc, NULL);

    if(g_dwCCalcCF != 0)

      CoRevokeClassObject(g_dwCCalcCF);

    // cause the remaining typeinfo to be released

    if(g_pcalc != NULL)

      g_pcalc->Release();

    OleUninitialize();

    return NOERROR;

}


Supporting Multiple National Languages

Applications sometimes need to expose objects with names that differ across localized versions of the product. The names pose a problem for programming languages that want to access these objects, because late binding will be sensitive to the locale of the application. The IDispatch interface provides a range of solutions that vary in cost of implementation and quality of language support. All methods of the IDispatch interface that are potentially sensitive to language are passed a locale ID (LCID), which identifies the local language context.

Following are some of the possible approaches a class implementation may take:

•	Accept any LCID and use the same member names in all locales. This is acceptable if the exposed interface will typically be accessed only by very advanced users. For example, the member names for OLE 2 interfaces will never be localized.

•	Accept all LCIDs supported by all versions of the product. In this case, the implementation of GetIDsOfNames would need to interpret the passed array of names based on the given LCID. This is the most acceptable solution because it allows users to write code in their natural language and run the code on any localized version of the application.

•	Simply return an error (DISP_E_UNKNOWNLCID) if the caller's LCID doesn't match the localized version of the class. This would prevent users from being able to write late-bound code that runs on machines with different localized implementations of the class.

•	Recognize the particular version's localized names, as well as one language that is recognized in all versions. For example, a French version might accept French and English names, where English is the language supported in all versions. Users who want to write code that runs in all countries would have to use English.

At the very least, the application must check the LCID before interpreting member names. Also note that Invoke is passed an LCID, which allows methods that take parameters whose meaning is dependent on the locale to interpret them properly in the caller's national language. For example, a spreadsheet application might interpret the arguments to a SetFormula method differently depending on the LCID.

The following macro is defined in WINNT.H for creating LCIDs:

/*

 * LCID creation/extraction macros:

 *

 *    MAKELCID - construct locale ID from language ID and

 *                 country code.

 */

#define MAKELCID(l) \

        ((DWORD)(((WORD)(l)) | (((DWORD)((WORD)(0))) << 16)))

There are two predefined LCID values: LOCALE_SYSTEM_DEFAULT is the system default locale, and LOCALE_USER_DEFAULT is the current user's locale.

Another macro constructs a language ID:

/*

 * Language ID creation/extraction macros:

 *

 *    MAKELANGID - construct language ID from primary language ID and

 *                 sublanguage ID

*/

#define MAKELANGID(p, s) ((((USHORT)(s)) << 10) | (USHORT)(p))

If your application receives an LCID it does not recognize, it should use LANG_SYSTEM_DEFAULT (0x0000).

The following three combinations of primary language ID and sublanguage ID have special semantics:

PRIMARYLANGID	SUBLANGID	Result
LANG_NEUTRAL	SUBLANG_NEUTRAL	User default language
LANG_NEUTRAL	SUBLANG_SYS_DEFAULT	System default language
LANG_NEUTRAL	SUBLANG_DEFAULT	User default language
For primary language IDs, the range 0x200 to 0x3ff is user definable. The range 0x000 to 0x1ff is reserved for system use. For sublanguage IDs, the range 0x20 to 0x3f is user definable. The range 0x00 to 0x1f is reserved for system use. If your application receives an LCID it does not recognize, it should use LANG_SYSTEM_DEFAULT (0x0000).

The following table lists the primary language IDs supported by OLE Automation. For more information on national language support, refer to your operating system documentation.

Primary Language IDs

Language	PRIMARYLANGID
Neutral	0x00
Chinese	0x04
Czech	0x05
Danish	0x06
Dutch	0x13
English	0x09
Finnish	0x0b
French	0x0c
German	0x07
Greek	0x08
Hungarian	0x0e
Icelandic	0x0F
Italian	0x10
Japanese	0x11
Korean	0x12
Norwegian	0x14
Polish	0x15
Portuguese	0x16
Russian	0x19
Slovak	0x1b
Spanish	0x0a
Swedish	0x1d
Turkish	0x1F
The following table lists the sublanguage IDs supported by OLE Automation. For more information on national language support, refer to your operating system documentation.

Sublanguage IDs

Sublanguage	SUBLANGID
Neutral	0x00
Default	0x01
System Default	0x02
Chinese (Simplified)	0x02
Chinese (Traditional)	0x01
Czech	0x01
Danish	0x01
Dutch	0x01
Dutch (Belgian)	0x02
English (US)	0x01
English (UK)	0x02
English (Australian)	0x03
English (Canadian)	0x04
English (Irish)	0x06
English (New Zealand)	0x05
Finnish	0x01
French	0x01
French (Belgian)	0x02
French (Canadian)	0x03
French (Swiss)	0x04
German	0x01
German (Swiss)	0x02
German (Austrian)	0x03
Greek	0x01
Hungarian	0x01
Icelandic	0x01
Italian	0x01
Italian (Swiss)	0x02
Japanese	0x01
Korean	0x01
Norwegian (Bokmal)	0x01
Norwegian (Nynorsk)	0x02
Polish	0x01
Portuguese	0x02
Portuguese (Brazilian)	0x01
Russian	0x01
Slovak	0x01
Spanish (Castilian) (1)	0x01
Spanish (Mexican)	0x02
Spanish (Modern) (1)	0x03
Swedish	0x01
Turkish	0x01
1 The only difference between Spanish (Castilian) and Spanish (Modern) is the sort ordering. The LCType values are all the same.


Accessing OLE Automation Objects

Applications that access OLE Automation objects are called OLE Automation controllers. OLE Automation controllers can create OLE Automation objects that reside in another application, and they can manipulate those objects using the properties and methods that the OLE Automation object supports.

You can create OLE Automation controllers using DispTest, Microsoft Visual Basic, Microsoft Visual C++, and other programming languages that are compatible with the OLE 2 DLLs.

You can access exposed objects through either the IDispatch or ITypeComp interface. To bind to exposed objects at run time, you use IDispatch. To bind to exposed objects at compile time, you use ITypeComp.

To access exposed objects for OLE Automation using IDispatch

1.	Initialize OLE.

2.	Instantiate the object you want to access. If the object's application is not yet running, OLE starts it. The object's application creates the object.

3.	Obtain a reference to the object's IDispatch interface (if it has implemented one).

4.	Manipulate the object through the methods and properties exposed in its IDispatch interface.

5.	Terminate the object by invoking the appropriate method in its IDispatch interface or by releasing all references to the object.

6.	Uninitialize OLE.

The following topics illustrate various ways of accessing OLE objects with the sample application DispCalc. In the next few sections, accessing DispCalc with DispTest is contrasted to accessing it directly with the OLE interfaces.

Manipulating Remote Objects with DispTest
Accessing Remote Objects Using OLE Interfaces
Other OLE Features of DispTest
Creating an Invisible Object
Using Existing Objects
Limitations in DispTest
Using Visual Basic for Scripting Your Application
Creating Type Information Browsers


Manipulating Remote Objects with DispTest

DispTest is a tool, similar to Visual Basic,™ whose programming language manipulates the exposed OLE objects of other applications. Internally, DispTest accomplishes this by using the OLE Automation IDispatch interface. For this reason, it is a good example of the programming approach appropriate for end users who are familiar with high-level programming languages, or who use macro languages to automate applications.

Note  DISPTEST.EXE is a subset of Microsoft Visual Basic, version 3.0, that is provided for testing OLE Automation objects. Visual Basic provides a more complete programming environment for creating Windows applications. DispTest is a 16-bit tool. Testing 32-bit applications with DispTest will use the 16:32 interoperability feature of OLE Automation. Optionally, you can use a different 32-bit OLE Automation server or controller for your testing.

With DispTest, implementing and initializing the remote object is straightforward, requiring only that the program declare an object variable and assign the return of a CreateObject or GetObject function call to the variable. The syntax for the appropriate DispTest statements is as follows:

Dim ObjectVar As Object
Set ObjectVar = CreateObject("ProgID.Objectname")
Set ObjectVar = GetObject("Filename", "ProgID.Objectname")

Use Dim to declare a single variable, or ReDim to declare an array of object variables. GetObject can be used to reestablish the reference to the most recently used object that corresponds to the Filename and ProgID.Objectname specification. For example, if the Automation controller needs an existing instance of an object instead of a new instance, it could use GetObject.

The DispCalc sample application included in the OLE 2 SDK accepts only mouse input; it has no keyboard interface. The figure below shows a simple DispTest application called CalcKey that adds a keyboard interface to DispCalc. The form appears as a title bar with a system box. (The form also includes an invisible command button that has no purpose except to make sure that, when a user presses the Enter key, the message is passed to the CalcKey form.) CalcKey illustrates creation and manipulation of DispCalc from within DispTest.

The following CalcKey code manipulates DispCalc at its highest level, through its Button and Quit methods. In Form_Load, CalcRef.accum is set to 0 to illustrate setting a property in the remote object:

' Module-level declaration.

Dim CalcRef As Object

Sub Form_Load()

  Set CalcRef = CreateObject("DISPCALC.Application")

    ' Enable form-level key trapping

  Keypreview = True

    ' Make sure Enter is trapped by making command button

    ' the Default

  Command1.Default = True

    ' Clear DispCalc's accumulator by setting the accum

    ' property to 0

  CalcRef.accum = 0

End Sub

Sub Form_KeyDown(Keycode As Integer, Shift As Integer)

   If Shift = 1 And Keycode = 187 Then Keycode = 107

   ' "+" sign

   ' Figure out which key was pressed and send to DispCalc

   If 12 <= Keycode And Keycode <= 46 Then

   ' NumLock not on

        Call ConvertForNumPad(Keycode)

   ' so map keys

   End If

   Select Case Keycode

    Case 96 To 105           ' Keypad w/ NumLock on

      ' Calculate character constant DispCalc expects

        AsciiChar = Keycode - 48

      'End If

    Case 48 To 57    ' Keyboard number row

      AsciiChar = Keycode

    Case Is = 42, 106

    ' DispCalc"*"

      AsciiChar = 42

    Case Is = 43, 107

    ' DispCalc "+"

      AsciiChar = 43

    Case Is = 45, 109, 189

    ' DispCalc "-"

      AsciiChar = 45

    Case Is = 47, 111, 191, 220

    ' DispCalc "/"

      AsciiChar = 47

    Case Is = 187, 13

    ' DispCalc "="

      AsciiChar = 61

    Case Asc("c"), Asc("C")

      AsciiChar = Asc("c")

    Case Else

      Beep

   End Select

   ' Send the key on to DispCalc

    CalcRef.Button AsciiChar

End Sub

Sub Form_Unload (Cancel As Integer)

    ' If user chooses Close from CalcKey's system box,

    ' quits DispCalc.

    CalcRef.Quit

End Sub

The preceding code waits for the user to press a key. If Keycode represents a key on the keypad, for example, an equal sign (=), the letter "c" (to clear the calculator), or Enter, the Keycode is converted to an argument appropriate to DispCalc's Button method. (Button looks like a method within the language, but it is defined within the code of the remote object.) To write this code in Visual Basic, you would need to know the program name and class name of the object, and have documentation of the object's Button and Quit methods in order to call them appropriately. For example, the documentation would need to specify the precise name of the Button and Quit methods and the type of any arguments expected. If the object provides a type library, you could also obtain the information by viewing the library with an object browser. A sample browser, TIBROWSE, is provided in the OLE 2 SDK.

Other Help sections show C++ code that uses the same type of access method as the preceding code, but without benefit of DispTest's powerful CreateObject and GetObject statements.


Accessing Remote Objects Using OLE Interfaces

This section discusses using OLE interfaces to directly access a remote object like DispCalc. Although the process is more complicated than the preceding DispTest code, the approach is similar. Note, however, that this section shows the minimum code necessary to access and manipulate a remote object. A more object-oriented approach is shown in the sample application DispDemo, in which a class mirrors the remote object.

From a C or C++ program, the CoCreateInstance, IUnknown::QueryInterface, IDispatch::GetIDsOfNames, and IDispatch::Invoke functions shown in the following table can be used to specify, initialize, and prepare to manipulate a remote object. OleInitialize and OleUninitialize are necessary to start and release OLE.

Function	Purpose	Interface
OleInitialize	Initializes OLE	OLE API function
CoCreateInstance	Creates an instance of the class represented by the specified class ID (CLSID_CCalc in the code below). Also places a pointer to the IUnknown interface in a caller-allocated variable (punk in code below) to use in a QueryInterface call	Component object API function
QueryInterface	Uses the punk obtained from CoCreateInstance, when you specify IID_IDispatch, to check that IDispatch has been implemented for the object. If so, places a pointer in a caller-allocated variable (pdisp in example that follows) to the IDispatch implementation for the remote object	IUnknown
GetIDsOfNames	Uses the IDispatch pointer from QueryInterface to give access to the dispids for names of exposed properties, methods, and their parameters	IDispatch
Invoke	Executes the method, or sets or gets the property of the remote object	IDispatch
Release	Decrements the reference count for an IUnknown or IDispatch object	IUnknown
OleUninitialize	Uninitializes OLE	OLE API function
The following fragments could appear in a windows or QuickWin type program, but the function must be preceded by the #include directives shown. Error checking is omitted to save space, but would normally be used where an HRESULT is returned. The macro DEFINE_GUID places a universally unique ID in the CLSID type variable CLSID_CCalc, which is passed to CoCreateInstance.

The code that follows initializes OLE, creates an instance of DispCalc, and retrieves a pointer to IUnknown. It calls QueryInterface on the IUnknown pointer (punk) to determine whether IDispatch has been implemented for DispCalc. If it has, a pointer to DispCalc's IDispatch interface is placed in pdisp.

#include <windows.h>

#include <ole2.h>

DEFINE_GUID(CLSID_CCalc, 0x00020467, 0x84A9, 0x1068,

    0xF1, 0x00, 0xDD, 0x01, 0x0E, 0xDF, 0x05)

#include <oleauto.h>

#include "dispcalc.h"

void sendnumbers(void)

{

    int count, buttoncode ;

    HRESULT hresult;

    IUnknown FAR* punk;

    IDispatch FAR* pdisp = (IDispatch FAR*)NULL;

    OLECHAR FAR* pbpushName = "Button";   // Button method name

    OLECHAR FAR* pquitName = "Quit";      // Quit method name

    OLECHAR FAR* paccumName = "Accum";    // Accum property name

    OLECHAR FAR* pdisplayName = "Display";// Display method name

    DISPID dispidBUTTON;     //Dispatch ID for Button method

    DISPID dispidQUIT;       //Dispatch ID for Quit method

    DISPID dispidaccum;      //Dispatch ID for Accum property

    DISPID dispidDISPLAY;    //Dispatch ID for Display method

     // Use this for methods that expect no arguments

    DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};

    DISPPARAMS dispparams;

    VARIANTARG varg[1] ; // Single element array to hold

                         // variant arg

    // Initialize OLE

    hresult = OleInitialize(NULL);

    if (hresult != NOERROR)

      goto EndAll;

    // Pass in the DispCalc class ID

    // & retrieve pointer to IUnknown

    hresult = CoCreateInstance(CLSID_CCALC, NULL,

            CLSCTX_LOCAL_SERVER,

            IID_IUnknown,

            (void FAR* FAR*)&punk);

    // Use the punk to find out if IDispatch

    // is implemented for DispCalc

    hresult = punk->QueryInterface(IID_IDispatch,

                                  (void FAR* FAR*)&pdisp);

    // Release the pointer to IUnknown

    punk->Release();

    // Retrieve a dispatch ID for the Button method

    hresult = pdisp->GetIDsOfNames(

        IID_NULL,

        &pbpushName,

        1, LOCALE_SYSTEM_DEFAULT,

        &dispidBUTTON) ;

.

.

.

After GetIDsOfNames supplies the appropriate dispatch ID, you can operate DispCalc by calling its Button method with Invoke. However, because Button takes an argument, you need to fill a DISPPARAMS structure to pass as the fifth argument to Invoke. DISPPARAMS structures have the following form:

typedef struct FARSTRUCT tagDISPPARAMS{

    VARIANTARG FAR* rgvarg;        // Array of arguments

    DISPID FAR* rgdispidNamedArgs; // dispids of named arguments

    UINT cArgs;                    // Number of arguments

    UINT cNamedArgs;               // Number of named arguments

} DISPPARAMS;

The named arguments fields are discussed elsewhere, and do not apply to DispCalc. The cArgs field specifies the count of positional arguments; the rgvarg field is a pointer to an array of VARIANTARG structures. Each element of the array specifies one of the arguments whose position in the array corresponds to its position in the parameter list of the method definition. Although VARIANTARG has five fields, only the first and fifth are used. The first specifies the argument's type, and the fifth specifies its value.

typedef struct FARSTRUCT tagVARIANT VARIANTARG;

struct FARSTRUCT tagVARIANT{

    VARTYPE vt;

    unsigned short wReserved1;

    unsigned short wReserved2;

    unsigned short wReserved3;

    union {

      short       iVal;      // VT_I2.

.    // The rest of this union specifies numerous other types

.

    };

} VARIANTARG;

To call DispCalc's Button method, the vt and iVal fields of the VARIANTARG rgvarg structure are filled with VT_I2 (short integer) and the actual value of the argument (initialized below to 0). NULL and 0 are assigned to the named argument fields, and 1 is assigned to cArgs, the field representing the count of positional arguments. Continuing with the sendnumbers function shown above, significant DISPPARAMS fields are filled; then Invoke is called to place numbers on the DispCalc display:

.

.

.

    VariantInit(&varg);             // Initialize variant.

    // First put together the initial DISPPARAMS

    // type parameter list

    dispparams.rgvarg = &varg;      // Put address in VARIANTARG

    dispparams.rgvarg[0].vt = VT_I2;// Put type in VARIANTARG

    dispparams.rgvarg[0].iVal = 0;  // Put value in VARIANTARG

    dispparams.rgdispidNamedArgs = NULL;// Don't use named args

    dispparams.cArgs = 1;           // Only one positional arg

    dispparams.cNamedArgs = 0;      // No named args

    // Codes for number buttons =

    // ASCII char codes for numbers 1-9

    buttoncode = 48;                // ASCII code for zero

    // Call invoke with a different ASCII char code

    // each iteration

    for (count = 1; count < 9; count++){// Start for loop

                                        // Put a new value into

                                        // the argument

        dispparams.rgvarg[0].iVal = buttoncode + count;

    // Call Invoke on DispCalc's IDispatch implementation.

    // Each call puts another number into the DispCalc's

    // display: 123456789

        hresult = pdisp->Invoke(

        dispidBUTTON,

        IID_NULL,

        LOCALE_SYSTEM_DEFAULT,

        DISPATCH_METHOD,

        &dispparams, NULL, NULL, NULL);

    }       // End of for loop

.

.

.

You can also access DispCalc's properties and other exposed methods, such as the Accum property (the calculator's accumulator) and the Display method (which displays the accumulator). Setting or retrieving a property is similar to invoking a method, except that the fourth argument to Invoke must specify one of the following: DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYGET, or DISPATCH_PROPERTYPUTREF. In addition, to set a property you must supply a named parameter with DISPID = DISPID_PROPERTYPUT. The named parameter represents the value to be assigned to the property. The same DISPPARAMS structure can be used because the type of the argument is the same;   a new value is simply assigned to the rgvarg[0].ival member.

The next fragment continues the sendnumbers function, placing a value in the accumulator, then displaying it. Note the use of dispparamsNoArgs to represent a DISPPARAMS structure that has no arguments. Defining such a structure simplifies using Invoke for a method that has no arguments.

    // First retrieve a dispatch ID for the Accum property:

    hresult = pdisp->GetIDsOfNames(

        IID_NULL,

        &paccumName,

        1, LOCALE_SYSTEM_DEFAULT,

        &dispidACCUM) ;

    // Now use invoke to put a new value in the accum property.

    // First put new value into the dispparams argument value field

    dispparams.rgvarg[0].iVal = 3;

    // Now call Invoke with DISPATCH_PROPERTYPUT as arg #4

    hresult = pdisp->Invoke(

        dispidACCUM,

        IID_NULL,

        LOCALE_SYSTEM_DEFAULT,

        DISPATCH_PROPERTYPUT,                        // IMPORTANT!

        &dispparams, NULL, NULL, NULL);

    // You have to use the Display method to show value in accumulator

    // First, retrieve a dispatch ID for the Display method

    hresult = pdisp->GetIDsOfNames(

        IID_NULL,

        &pdisplayName,

        1, LOCALE_SYSTEM_DEFAULT,

        &dispidDISPLAY) ;

    // Use invoke to show the new value by calling the Display method

    hresult = pdisp->Invoke(

        dispidDISPLAY,

        IID_NULL,

        LOCALE_SYSTEM_DEFAULT,

        DISPATCH_METHOD,

        &dispparamsNoArgs, NULL, NULL, NULL);

    // Close DispCalc by invoking its Quit method;

    //  First retrieve a dispatch ID for the Quit method

    hresult = pdisp->GetIDsOfNames(

        IID_NULL,

        &pquitName,

        1, LOCALE_SYSTEM_DEFAULT,

        &dispidQUIT) ;

    hresult = pdisp->Invoke(

        dispidQUIT,

        IID_NULL,

        LOCALE_SYSTEM_DEFAULT,

        DISPATCH_METHOD,

        &dispparamsNoArgs, NULL, NULL, NULL);

// Release the calculator

EndAll:;

    pdisp->Release();  // Decrement refcount for the object

}

To write this code, you need access to several pieces of DispCalc documentation:

•	Class ID for DispCalc. Stored in the external variable CLSID_CCalc, this ID is generated with the DEFINE_GUID macro.

•	Name of the Button method and whether it expects an integer representing a character constant.

•	Name of the Accum property and whether it can be both set and retrieved.

•	Name of the Display and Quit methods and whether they expect arguments.


Other OLE Features of DispTest

As shown at the beginning of this chapter, the OLE Automation features of DispTest let you do much more than share data. From within DispTest, you can easily access and manipulate objects that are exposed by other applications. You can use DispTest to orchestrate custom solutions that utilize data and features from applications that support OLE Automation.

Similarly, the OLE toolkit contains an OLE custom control (MSOLE2.VBX) that can be incorporated into DispTest applications to permit using embedded and linked OLE objects in DispTest applications.


Creating an Invisible Object

In Manipulating Remote Objects with DispTest, DispTest was used to access and program a keyboard interface for DispCalc to make it easier to use. The DispCalc keyboard is displayed because it has the Visible property. By default, however, objects are not visible, and some objects are never displayed to the user. For example, a word processing application may expose its spelling checker engine as an object. This object supports a method called CheckWord that takes a string as an argument. If the string is spelled correctly, True is returned; otherwise, the method returns False. If the string is spelled incorrectly, then you could pass it to another (hypothetical) method called SuggestWord that takes a misspelled word as an argument and returns a suggestion for its correct spelling. The code might look something like this:

Sub CheckSpelling ()

    Dim ObjVar As Object

    Dim MyWord, Result

    MyWord = "potatoe"

    ' Create the object.

    Set ObjVar = CreateObject("WordProc.SpellCheck")

    ' Check the spelling.

    Result = ObjVar.CheckWord MyWord

    ' If False, get suggestion.

    If Not Result Then

        MyWord = ObjVar.SuggestWord MyWord

    End If

End Sub

In the above example, the spelling checker is never displayed to the user. Its capabilities are exposed through the properties and methods of the spelling checker object.

As shown in the example, you create and reference invisible objects the same way as any other type of object. Use the CreateObject function to create the object and then reference the object using an object variable.


Using Existing Objects

Objects can exist on your system in several ways. Applications that create OLE Automation objects can save objects in files on disk. The following sections show you how to access objects that already exist on your system.

Activating an Object from a File
Manipulating Objects
Limitations in DispTest

Some applications that supply objects support linking and embedding as well as OLE Automation. Using the OLE control, you can create and display linked and embedded objects in a DispTest application. If the objects also support OLE Automation, you can access their properties and methods in DispTest using the Object property. The Object property returns the object in the OLE control. This property refers to an OLE object in the same way an object variable created using the CreateObject or GetObject functions refers to the object variable.

For example, an OLE control named Ole1 contains an object that supports OLE Automation. This object has an Insert method, a Select method, and a Bold property. In this case, you could write the following code to manipulate the OLE control's object:

' Insert text in the object.

Ole1.Object.Insert "Hello, world."

' Select the text.

Ole1.Object.Select

' Format the text as bold.

Ole1.Object.Bold = True


Activating an Object from a File

Many OLE Automation applications allow the user to save objects in files. For example, a spreadsheet application that supports worksheet objects allows the user to save the worksheet in a file. The same application may also support a chart object that the user can save in a file. To activate an object that has been saved in a file, you use the GetObject function.

To activate an object from a file, first declare an object variable, then call the GetObject function using the following syntax:

GetObject (filename[, class])

The filename argument is a string containing the full path and name of the file you want to activate. For example, an application named SPDSHEET.EXE creates an object that was saved in a file called REVENUE.SPD. The following code invokes SPDSHEET.EXE, loads the file REVENUE.SPD, and assigns REVENUE.SPD to an object variable:

Dim Spreadsheet As Object

Set Spreadsheet = GetObject("C:\ACCOUNTS\REVENUE.SPD")

If the filename argument is omitted, the GetObject function returns the currently active object of the specified class. For example:

Set Spreadsheet = GetObject (,"SpdSheet.Application")

If there is no active object of the class SpdSheet.Application, an error occurs.

In addition to activating an entire file, some applications let you activate part of a file. To activate part of a file, add an exclamation point (!) or a backslash (\) to the end of the filename, followed by a string that identifies the part of the file you want to activate. Refer to the object's documentation for information on how to create this string.

For example, SPDSHEET.EXE is a spreadsheet application that uses R1C1 syntax. The following code could be used to activate a range of cells within REVENUE.SPD:

Set Spreadsheet = GetObject("C:\ACCOUNTS\REVENUE.SPD!R1C1:R10C20")

These examples invoke an application and activate an object. Notice that in these examples the application name (SPDSHEET.EXE) is never specified. When you use GetObject to activate an object, the registry files determine the application to invoke and the object to activate based on the filename or class you provide.

Some files, however, support more than one class of object. Suppose the spreadsheet file, REVENUE.SPD, supports three different classes of objects: an Application object, a Worksheet object, and a Toolbar object, all of which are part of the same file. To specify which object to activate, you supply an argument for the optional class parameter. Here's an example of activating the toolbar object in the file REVENUE.SPD:

Set Spreadsheet = GetObject("C:\REVENUE.SPD", "SPDSHEET.TOOLBAR")

Note  When you use the GetObject function and do not include the class of the object to be activated, the default object of the specified file is activated.


Manipulating Objects

Once you have created a variable that references an OLE object, the object can be manipulated in DispTest the same way as any DispTest object (such as a control). You use the object.property or object.method syntax to get and set the object's properties or to perform the object's methods.


Accessing an Objects Properties
Accessing an Object's Properties

To assign a value to a property of an object, put the object variable and property name on the left side of an assignment and the desired property setting on the right side. For example:

Dim ObjVar as Object

Dim RowPos, ColPos

Set ObjVar = CreateObject("MyApplication.MyObjectType")

ObjVar.Text = "Hello, world"

ObjVar.Cell(RowPos, ColPos) = "This property accepts two arguments."

You can also retrieve property values from an object:

Dim X

X = ObjVar.Text

X = ObjVar.Range(12, 32)

' Sets the font for ObjVar.Selection.

ObjVar.Selection.Font = 12

Note  All arguments to OLE Automation objects use the VARIANT data type. When retrieving a value from a property or method, OLE Automation objects always return values with the VARIANT data type.

If you assign a variable with a data type other than VARIANT when setting a property value or performing a method, the variable is coerced to the VARIANT data type when the property is set or the method is performed.


Invoking Methods

In addition to getting and setting properties, you can manipulate an object using the methods it supports. Some methods may return a value, as in the following example:

' IsBold method returns True or False.

X = ObjVar.Text.IsBold

If X Then

    ObjVar.Text = "The text is bold."

Else

    ObjVar.Text = "The text is not bold."

End If

Methods that do not return a value behave like subroutines.  For example:

' This method requires two arguments.

ObjVar.Move XPos, YPos

If you assign such a method to a variable, an error occurs.

Some objects contain subobjects. For example, a cell could be considered a subobject of a spreadsheet object. You can include multiple objects, properties, and methods on the same line of code using the dot syntax, just as you would with a DispTest object (for example, Form.Control.Property). For example:

ObjVar.Cell(1,1).FontBold = True


Limitations in DispTest

Some OLE objects support features that cannot be accessed using DispTest. The following sections discuss those features.

Arrays and Arrays of User-defined Types
Named Arguments
Collection Objects


Arrays and Arrays of User-defined Types

Some objects have properties and methods that either return an array of data or a user-defined type, or take an array of data or user-defined type as an argument. The following is a list of DispTest's limitations when using arrays or user-defined types with OLE objects. You can't:

•	Use an array or a user-defined type as an argument to a method.

•	Set a property using an array or a user-defined type.

•	Assign an array variable or a variable of a user-defined type to the return value of a property or method.

When a property or method returns an array, you can use the LBound and UBound functions to ascertain the size of an array. You can then access the individual elements of the array. For example:

Dim I, Value

For I = LBound(ObjVar.Selection) To UBound(ObjVar.Selection)

    Value = ObjVar.Selection(I)

Next


Named Arguments

Some objects have methods that require many arguments, some of which are optional. These methods are typically used to display one of the application's dialogs. The arguments for the method correspond to selections in the dialog. Such methods often allow you to set any desired arguments using predefined names. When calling a method using named arguments, the order in which the arguments appear does not matter.

In DispTest, however, you can't use named arguments. When calling a method that supports named arguments, you must specify each argument in the correct order. If you want to omit an optional argument, leave it blank.

For example, an object supports a method called FileOpen that is used to display the object's File Open dialog. The following code shows how the method is called using named arguments and how to call the method from within DispTest:

Not Valid in DispTest

' When using named arguments, the order does not matter.

FileOpen Name := "MYDOC.DOC", ReadOnly := 0, Password := "Mahler"

FileOpen ReadOnly := 0, Name := "MYDOC.DOC", Password := "Mahler"

Valid in DispTest

' The arguments must be specified in the correct order.

ObjVar.FileOpen "MYDOC.DOC", 0, "Mahler"

' To omit the second argument, insert a comma in its place.

ObjVar.FileOpen "MYDOC.DOC", , "Mahler"


Collection Objects

A collection object contains zero or more items. For example, an Application object may have a Menus object that is a collection of all the menus in the application. Collection objects are usually specified using the plural form of an object. For example, the collection object for Row objects would be called Rows.

All collection objects support a property called Count. This property returns the number of elements in the collection. For example, in the code
x = ObjVar.Rows.Count, x equals the number of rows in the object referenced by the object variable ObjVar.

You specify an individual element of a collection object using an index. For example, in a 1-based collection object, Rows(1) specifies the first row in an object and Rows(Rows.Count) specifies the last row.

Iterating Through Collection Objects

Keep the following in mind when writing code that iterates through a collection of an OLE object:

•	There is no guarantee that the iterator of a collection object is numeric.

•	If a collection object does provide a numeric iterator, there is no guarantee that the numbers are contiguous.

•	If a change is made to an element of a collection object, the positions of the elements may change. This differentiates collection objects from arrays, in which the positions of the elements are guaranteed to be the same unless explicitly changed.

DispTest and Visual Basic, version 3.0, do not provide a mechanism to reliably iterate over objects contained by a collection object. To iterate over a collection using these tools, the collection object must provide a contiguous numeric Index method or some other mechanism to iterate over the objects in the collection.

In the following example, Index accepts the numbers 1 to Windows.Count. Other collection objects may or may not support this method. Consult the application's object documentation to see if a particular collection object supports this method.

For i = 1 to Windows.Count

    Window(i).Minimize    ' Index returns a contained object

Next i

Some collection objects do not use numeric iterators. For example, an object has a collection of styles. Instead of using a numeric iterator, the Styles collection might use string subscripts. In code, the collection might look like this:

ObjVar.Styles("Normal")

ObjVar.Styles("Ex")

ObjVar.Styles("Heading")

Visual Basic, Applications edition (shipped in Microsoft Excel and other applications), provides a For Each language construct to reliably iterate over the elements in all types of collections. For example:

For Each x In Windows

    x.Minimize

Next x


Using Visual Basic for Scripting Your Application

Visual Basic, version 3.0, is an OLE Automation controller. It lets you create stand-alone .EXE files that you can distribute to your customers. This makes it easy to customize your applications for specific customer needs. In fact, completely new applications can easily be assembled from OLE Automation objects using Visual Basic.

Visual Basic includes database access, performance enhancements, additional documentation, and many other advantages over DispTest.


Creating Type Information Browsers

Although you can use the type description interfaces directly to expose objects in your applications, an easier way to do this is to write an Object Description Language (.ODL) source file, then process that file with MkTypLib to produce a type library (see Object Description Language for more information on MkTypLib and .ODL source files). Typically, the type description interfaces are used by type browsers, compilers, and interpreters to read type libraries created with MkTypLib.

The following example shows how a browser might access a type library, in the course of displaying type information to a user. The example is a Windows-based type browser that presents a dialog box from which user selects the type information items to display. The XAddDlgItemList, XClrDlgItemList, and XSetDlgItemText functions manage the dialog box. This code is culled from the sample application TIBROWSE (TIBROWSE.CPP).

/***

*tibrowse.cpp

*/

.

.  // Portions omitted for brevity.

.

ITypeLib  FAR *g_ptlib = NULL;

ITypeInfo FAR *g_ptinfoCur = NULL;

TYPEATTR  FAR *g_ptattrCur = NULL;

OLECHAR * g_rgszTKind[] = {

    OLESTR("Enum"),          // TKIND_ENUM

    OLESTR("Struct"),        // TKIND_RECORD

    OLESTR("Module"),        // TKIND_MODULE

    OLESTR("Interface"),     // TKIND_INTERFACE

    OLESTR("Dispinterface"), // TKIND_DISPATCH

    OLESTR("Coclass"),       // TKIND_COCLASS

    OLESTR("Typedef"),       // TKIND_ALIAS

    OLESTR("Union"),         // TKIND_UNION

};

void

OpenTypeLib(OLECHAR FAR *sztlib)

{

    unsigned int utypeinfoCount, i;

    BSTR bstrName;

    TLIBATTR FAR* ptlibattr;

.

.

.

    /* load the type library */

    CHECKRESULT(LoadTypeLib(sztlib, &g_ptlib));

    /* get library attributes for the fun of it */

    CHECKRESULT(g_ptlib->GetLibAttr(&ptlibattr));

    /* release library attributes */

    g_ptlib->ReleaseTLibAttr(ptlibattr);

    /* Now add each of the names to the type list */

    utypeinfoCount = g_ptlib->GetTypeInfoCount();

    for(i = 0; i < utypeinfoCount; i++){

      CHECKRESULT(g_ptlib->GetDocumentation(

                           i, &bstrName, NULL, NULL, NULL));

      ASSERT(bstrName);

      XAddDlgItemList(IDC_TYPELIST, bstrName);

      SysFreeString(bstrName);

    }

}

/***

*void SetSelectedType

*Purpose:

* When the user selects a different type, this function updates the

* dialog by changing the member list and the help for the type.

* It also sets g_ptinfoCur to refer to the typeinfo.

*

***********************************************************************/

void

SetSelectedType(unsigned long dwIndex)

{

    HRESULT hr;

    BSTR bstrDoc;

    OLECHAR FAR* psz;

    OLECHAR szBuf[40];

    unsigned long dwHelpContext;

    TYPEKIND tkind;

    if(g_ptinfoCur != NULL){

      g_ptinfoCur->ReleaseTypeAttr(g_ptattrCur);

      g_ptinfoCur->Release();

    }

.

.

.

    // Note: The index in the list box is conveniently the same as the

    // one to pass to GetTypeInfo/GetTypeInfoType

    // TypeInfo type can be obtained without actually loading
    // the typeinfo

    CHECKRESULT(g_ptlib->GetTypeInfoType(

                         (unsigned int)dwIndex, &tkind));

    XSetDlgItemText(IDC_TYPEKIND, g_rgszTKind[tkind]);

    CHECKRESULT(g_ptlib->GetTypeInfo(

                         (unsigned int)dwIndex, &g_ptinfoCur));

    CHECKRESULT(g_ptinfoCur->GetTypeAttr(&g_ptattrCur));

    // GUID

    hr = StringFromCLSID(g_ptattrCur->guid, &psz);

    ASSERT(hr == NOERROR);

    XSetDlgItemText(IDC_GUID, psz);

    MemFree(psz);

    // Version

    SPRINTF(szBuf, OLESTR("%u.%02u"),

      g_ptattrCur->wMajorVerNum, g_ptattrCur->wMinorVerNum);

    XSetDlgItemText(IDC_VERSION, szBuf);

    CHECKRESULT(

      g_ptlib->GetDocumentation(

        (unsigned int)dwIndex,

        NULL, &bstrDoc,

        &dwHelpContext, NULL));

    // Help Context

    SPRINTF(szBuf, OLESTR("%ld"), dwHelpContext);

    XSetDlgItemText(IDC_HELPCONTEXT, szBuf);

    // Documentation string

    psz = (bstrDoc != NULL) ? bstrDoc : OLESTR("<none>");

    XSetDlgItemText(IDC_HELPSTRING, psz);

    SysFreeString(bstrDoc);

    FillMemberList(g_ptinfoCur, g_ptattrCur, IDC_MEMBERLIST);

    XClrDlgItemList(IDC_PARAMLIST);

}

/***

*void FillMemberList

*Purpose:

* Sets the current typeinfo to the typeinfo indexed by dwIndex, and

* then fills in the list box with the members of the type.

***********************************************************************/

void

FillMemberList(

    ITypeInfo FAR *ptinfo,

    TYPEATTR FAR *ptypeattr,

    int ctlid)

{

    MEMBERID memid;

    BSTR bstrName;

    unsigned int i;

    FUNCDESC FAR *pfuncdesc;

    VARDESC  FAR *pvardesc;

    /* Now add all of the functions and all of the vars.

     * This is somewhat roundabout.

     * For each one, we need to get the funcdesc or the vardesc.

     * From that we get the MEMBERID, and finally can get to the name.

     */

    for(i = 0; i < ptypeattr->cFuncs; i++){

      CHECKRESULT(ptinfo->GetFuncDesc(i, &pfuncdesc));

      memid = pfuncdesc->memid;

      CHECKRESULT(ptinfo->GetDocumentation(memid, &bstrName, NULL, NULL,

         NULL));

      ptinfo->ReleaseFuncDesc(pfuncdesc);

      pfuncdesc = NULL;

      ASSERT(bstrName);

      XAddDlgItemList(ctlid, bstrName);

      SysFreeString(bstrName);

    }

    for(i = 0; i < ptypeattr->cVars; i++)

    {

      CHECKRESULT(ptinfo->GetVarDesc(i, &pvardesc));

      memid = pvardesc->memid;

      CHECKRESULT(ptinfo->GetDocumentation(memid, &bstrName, NULL, NULL,

        NULL));

      ptinfo->ReleaseVarDesc(pvardesc);

      pvardesc = NULL;

      ASSERT(bstrName);

      XAddDlgItemList(ctlid, bstrName);

      SysFreeString(bstrName);

    }

}

/***

*void SetSelectedMember

*Purpose:

*  When a member of a type is selected, update the Help to be the Help

*  of the member.  If the member is a function, update the parameter

*  list to reflect that it is a function.

*

***********************************************************************/

void

SetSelectedMember(unsigned long dwIndex)

{

    MEMBERID memid;

    /* In any case, we'll need to clear out the parameter list. */

    XClrDlgItemList(IDC_PARAMLIST);

    if(g_ptattrCur == NULL)

      return;

    /* if this is a function, fill the param list, otherwise just fill

     * in the item info.

     */

    if(dwIndex < g_ptattrCur->cFuncs){

      unsigned short i;

      unsigned int cNames;

      FUNCDESC FAR *pfuncdesc;

      const unsigned int MAX_NAMES = 40;

      BSTR rgNames[MAX_NAMES];

      CHECKRESULT(g_ptinfoCur->GetFuncDesc((unsigned int) dwIndex,

         &pfuncdesc));

      memid = pfuncdesc->memid;

      UpdateMemberInfo(memid);

      CHECKRESULT(g_ptinfoCur->GetNames(memid, rgNames, MAX_NAMES,

        &cNames));

      for(i = 1; i < cNames; i++){

        ASSERT(rgNames[i])

        XAddDlgItemList(IDC_PARAMLIST, rgNames[i]);

        SysFreeString(rgNames[i]);

      }

      ASSERT(rgNames[0]);

      SysFreeString(rgNames[0]);

      g_ptinfoCur->ReleaseFuncDesc(pfuncdesc);

    }

    else

    {

      VARDESC FAR *pvardesc;

      CHECKRESULT( g_ptinfoCur->GetVarDesc(

         (unsigned int)(dwIndex - g_ptattrCur->cFuncs), &pvardesc));

      memid = pvardesc->memid;

      UpdateMemberInfo(memid);

      g_ptinfoCur->ReleaseVarDesc(pvardesc);

    }

}

/***

*void UpdateMemberInfo

*Purpose:

*  sets fields on the dialog (such as help string and help context) from

*  the type information.

***********************************************************************/

void

UpdateMemberInfo(MEMBERID memid)

{

    BSTR bstrDoc;

    OLECHAR buf[40];

    unsigned long dwHelpContext;

    /* get the member information */

    CHECKRESULT(g_ptinfoCur->GetDocumentation(

      memid, NULL, &bstrDoc, &dwHelpContext, NULL));

    /* update the help string displayed in the dialog */

    XSetDlgItemText(IDC_HELPSTRING, (bstrDoc != NULL) ?

                                 bstrDoc : OLESTR("<none>"));

    SysFreeString(bstrDoc);

    /* update the help context displayed in the dialog */

    SPRINTF(buf, OLESTR("%ld"), dwHelpContext);

    XSetDlgItemText(IDC_HELPCONTEXT, buf);

}


Standards and Guidelines

The topics in this section describe the standard OLE Automation objects and discuss naming guidelines for creating objects that are unique to applications, especially user-interactive applications that support a multiple-document interface. However, if your OLE Automation object is not user-interactive or supports only a single-document interface, you should adapt the standards and guidelines as appropriate.

•	Standard objects comprise a standard set of objects defined by OLE. They should be used as appropriate to your application. The objects described in these sections are oriented toward document-based, user-interactive applications. Other applications (such as non-interactive database servers) may have requirements that differ from those described here.

•	Naming guidelines are recommendations meant to improve consistency across applications.

The following topics cover each of these points in turn. Examples are shown in a hypothetical syntax derived from DispTest and Visual Basic.

Standard Objects
Naming Conventions

Note  These standards and guidelines are subject to change.


Standard Objects

The following table lists the OLE Automation standard objects and their methods and properties. Although none of these objects is required, user-interactive applications with subordinate objects should include an Application object.

Object name	Description
Application	Top-level object; provides a standard way for OLE Automation controllers to retrieve and navigate an application's subordinate objects.
Document	Provides a way to open, print, change, and save an application document.
Documents	Provides a way to iterate over and select open documents in multiple-document interface (MDI) applications.
Font	Describes fonts that are used to display or print text.
The following figure shows how the standard objects fit into the organization of the objects an application provides.

The following sections describe the standard properties and methods for all objects, all collection objects, and each of the standard objects. These sections list only the standard methods and properties for each object, as well as the standard arguments for those properties and methods.

Note  You may define additional application-specific properties and methods for each object. You may also provide additional optional arguments for any of the listed properties or methods; however, the optional arguments should follow the standard arguments in a positional argument list.

All Objects
All Collection Objects
Application Object
Document Object
Documents Collection Object
Font Object


All Objects

All objects, including the Application object and collection objects, must provide the following properties:

Property name	Return Type	Description
Application	VT_DISPATCH	Returns the Application object; read only
Parent	VT_DISPATCH	Returns the creator of the object; read only.
Note  The Application and Parent properties of the Application object return the Application object.


All Collection Objects

A collection provides a set of objects over which iteration can be performed. All collection objects must provide the following properties:

Property name	Return type	Description
Count	VT_I4	Returns the number of items in the collection; read only.
_NewEnum	VT_DISPATCH	A special propertymethod that returns an enumerator object that implements IEnumVARIANT. Required.
The following topics are included in this section:

Collection Methods
Kinds of Collections


Collection Methods

Methods for collections are described in the following table. The Item method is required; other methods are optional.

Method name	Return type	Description
Add	VT_DISPATCH or VT_EMPTY	Adds an item to a collection. Returns VT_DISPATCH if object is created (object can't exist outside the collection) or VT_EMPTY if no object is created (object can exist outside the collection).
Item	Varies with type of collection	Returns the indicated item in the collection.  Required. The Item method may take one or more arguments to indicate the element within the collection to return. Item is the default member (DISPID_VALUE) for the collection object.
Remove	VT_EMPTY	Removes an item from a collection. Uses indexing arguments in the same way as the Item method.
All collection objects must provide at least one form of indexing through the Item method. The dispatch ID of the Item method is  DISPID_VALUE. Because it is the default member, it can be used in the following convenient form:

ThirdDef = MyWords(3).Definition 	' Equivalent to
' MyWords.Item(3).Definition

The Item method takes one or more arguments to indicate the index. Indexes may be numbers, strings, or other types. For example:

DogDef = MyWords("dog").Definition

Important  Within the application's type library, the _NewEnum property has a special dispatch ID: DISPID_NEWENUM. The name "_NewEnum" should not be localized.

The Add method may take one or more arguments. For example, if MyWord is an object with the properties Letters and Definition:

Dim MyWord As New Word

Dim MyDictionary as Words

MyWord = "dog"

MyWord.Letters = "Dog"

MyWord.Definition = "My best friend."

MyDictionary.Add MyWord

MyDictionary.Remove("Dog")

For more information on creating collection objects, see Exposing OLE Automation Objects.


Kinds of Collections

The standard for collections allows you to describe two kinds of collections, depending on whether it makes sense for a collected object to exist outside the collection.

If the Object Can't Exist Outside the Collection

In some cases, it isn't logical for a collected object to exist independently of its collection. For example, an application's Documents collection contains all currently open Document objects. Opening a document means adding it to the collection, and closing the document means removing it from the collection. All open documents are, by definition, part of the collection; the application cannot have open documents that aren't part of the collection. The relationship between the collection and the members of the collection can be shown in the following ways:

•	Documents.Add creates an object and adds it to the collection. Since an object is created, a reference to it is returned:

Set MyDoc = Documents.Add

•	Document.Close removes an object from the collection:

Set SomeDoc = Documents(3)

SomeDoc.Close

If the Object Can Exist Outside the Collection

In other cases, it is logical for the collected objects to exist outside the collection. For example, a Mail application might have Name objects, and many collections of these Name objects. The Name object would have the user's email name, the user's full name, and possibly other information. The Name object could have the following string-type properties:

•	EmailName

•	FullName

The application might have the following collections of Name objects:

•	 A collection for the "to" list on each piece of mail

•	 A collection of the names of all of the people to whom a user has sent mail

The collections of Name objects could be indexed by using either the EmailName or the FullName.

For such a collection, it is not logical for Add to create an object because the object already exists. Therefore, the Add method should take an object as an argument and should not return a value. Assuming the existence of two collections (AddressBook and ToList), a user might execute the following code to add a Name object to the ToList collection:

Dim Message as Object

Dim AddressBook as Object

Dim NameRef as Object

...

Set NameRef = AddressBook.Names("Fred Funk")

Message.ToList.Add   NameRef

The Name object already exists and is contained in the AddressBook collection. The first line of code gets a reference to the Name object for "Fred Funk" and makes NameRef point to it. The second line of code adds a reference to the object to the ToList collection. No new object is created, so no reference is returned from the Add method. Unlike the relationship between Documents and Document, there is no way for the collected object (the Name) to know how to remove itself from the collections in which it is contained. Thus, to remove an item from a collection, the Remove method is used:

Message.ToList.Remove("Fred Funk")

This line of code removes the Name object that has the FullName "Fred Funk". The "Fred Funk" object may exist in other collections. These other collections will be unaffected.


Application Object

The Application object should have the properties listed in the following table. The Application, FullName, Name, Parent, and Visible properties are required; other properties are optional.

Property name	Return type	Description
ActiveDocument	VT_DISPATCH,  VT_EMPTY	Returns the active document object or VT_EMPTY if none; read only.
Application	VT_DISPATCH	Returns the Application object; read only.  Required.
Caption	VT_BSTR	Sets or returns the title of the application window; read/write.  Setting the Caption to VT_EMPTY returns control to the application.
DefaultFilePath	VT_BSTR	Sets or returns the default path specification used by the application for opening files; read/write.
Documents	VT_DISPATCH	Returns a collection object for the open documents; read only.
FullName	VT_BSTR	Returns the file specification for the application, including path; read only. For example, C:\DRAWDIR\SCRIBBLE.  Required.
Height	VT_R4	Sets or returns the distance between the top and bottom edge of the main application window; read/write.
Interactive	VT_BOOL	Sets or returns True if the application accepts actions from the user, and False otherwise; read/write.
Left	VT_R4	Sets or returns the distance between the left edge of the physical screen and the main application window; read/write.
Name	VT_BSTR	Returns the name of the application, such as "Microsoft Excel"; read only. The Name property is the default member (DISPID_VALUE) for the Application object. Required.
Parent	VT_DISPATCH	Returns the Application object; read only. Required.
Path	VT_BSTR	Returns the path specification for the application's executable file; read only. For example, C:\DRAWDIR if the executable is C:\DRAWDIR\SCRIBBLE.EXE.
StatusBar	VT_BSTR	Sets or returns the text displayed in the status bar; read/write.
Top	VT_R4	Sets or returns the distance between the top edge of the physical screen and main application window; read/write.
Property name	Return type	Description
Visible	VT_BOOL	Sets or returns whether the application is visible to the user; read/write. The default is False when the application is started with /Automation command-line switch. Required.
Width	VT_R4	Sets or returns the distance between the left and right edges of the main application window; read/write.
If a type library is used, the Application object should be the object that has the [appobj] attribute specified. Some OLE Automation controllers will use this information to allow unqualified access to the Application object's members. For this reason, it is important to avoid overloading the Application object with too many members.

The Application object should provide the following methods. The Quit method is required; other methods are optional.

Method name	Return type	Description
Help	VT_EMPTY	Displays online Help. May take three optional arguments: helpfile (VT_BSTR), helpcontextID (VT_I4), and helpstring (VT_BSTR). The helpfile argument specifies the Help file to display; if omitted, the main Help file for the application is displayed. The helpcontextID and helpstring arguments specify a Help context to display, and only one of them may be supplied; if both are omitted, the default Help topic is displayed.
Quit	VT_EMPTY	Exits the application and closes all open documents. Required.
Repeat	VT_EMPTY	Repeats the previous action in the user interface.
Undo	VT_EMPTY	Reverses the previous action in the user interface.


Document Object

If your application is document-based, it should provide a Document object named "Document". Use a different name only if "Document" is inappropriate, for example, if your application uses highly technical or otherwise specialized terminology within its user interface.

The Document object should have the properties listed in the table that follows. The Application, FullName, Name, Parent, Path, and Saved properties are required; other properties are optional.

Property name	Return type	Description
Application	VT_DISPATCH	Returns the Application object; read only. Required.
Author	VT_BSTR	Sets or returns the summary information about the document's author; read/write.
Comments	VT_BSTR	Sets or returns summary information comments for the document; read/write.
FullName	VT_BSTR	Returns the file specification of the document, including path; read only. Required.
Keywords	VT_BSTR	Sets or returns summary information keywords associated with the document; read/write.
Name	VT_BSTR	Returns the filename of the document, not including the file's path specification; read only.
Parent	VT_DISPATCH	Returns the parent of the Document object; read only. Required.
Path	VT_BSTR	Returns the path specification for the document, not including the filename or filename extension; read only. Required.
ReadOnly	VT_BOOL	Returns True if the file is read only and False otherwise; read only.
Saved	VT_BOOL	Returns True if the document has never been saved, but has not changed since it was created. Returns True if it has been saved and has not changed since last save. Returns False if it has never been saved and has changed since it was created; or if it was saved, but has changed since last save. Read only; required.
Subject	VT_BSTR	Sets or returns the summary information about the subject of the document; read/write.
Title	VT_BSTR	Sets or returns the summary information about the title of the document; read/write.
The Document object should have the following methods. The Activate, Close, Print, Save, and SaveAs methods are required; other methods are optional.

Method name	Return type	Description
Activate	VT_EMPTY	Activates the first window associated with the document. Required.
Close	VT_EMPTY	Closes all windows associated with the document and removes the document from the Documents collection. Required. Takes two optional arguments, saveChanges (VT_BOOL) and fileName (VT_BSTR). The fileName argument specifies the name of the file in which to save the document.
NewWindow	VT_EMPTY	Creates a new window for the document.
Print	VT_EMPTY	Prints the document. Required. Takes three optional arguments: from (VT_I2), to (VT_I2), and copies (VT_I2). The from and to arguments specify the page range to print. The copies argument specifies the number of copies to print.
PrintOut	VT_EMPTY	Same as Print method, but provides an easier way to use the method in Visual Basic, version 3.0, because Print is a Visual Basic 3.0 keyword.
PrintPreview	VT_EMPTY	Previews the pages and page breaks of the document. Equivalent to choosing Print Preview from the File menu.
RevertToSaved	VT_EMPTY	Reverts to the last saved copy of the document, discarding any changes.
Save	VT_EMPTY	Saves changes to the file specified in the document's FullName property. Required.
SaveAs	VT_EMPTY	Saves changes to a file. Required. Takes one optional argument, filename (VT_BSTR). The filename argument may optionally include a path specification.


Documents Collection Object

If your application is MDI document-based, you should provide a Documents collection object. Use the name "Documents" for this collection unless that name is inappropriate for your application.

The Documents collection object should have all of the following properties.

Property name	Return type	Description
Application	VT_DISPATCH	Returns the Application object; read only.
Count	VT_I4	Returns the number of items in the collection; read only.
_NewEnum	VT_DISPATCH	A special property that returns an enumerator object that implements IEnumVARIANT.
Parent	VT_DISPATCH	Returns the parent of the Documents collection object; read only.
The Documents collection object should have all of the following methods.

Method name	Return type	Description
Add	VT_DISPATCH	Creates a new document and adds it to the collection. Returns the document that was created.
Close	VT_EMPTY	Closes all documents in the collection.
Item	VT_DISPATCH or VT_EMPTY	Returns a Document object from the collection or returns VT_EMPTY if the document does not exist. Takes an optional argument, index, which may be a string (VT_BSTR) indicating the document name, a number (VT_I4) indicating the ordered position within the collection, or either (VT_VARIANT). If index is omitted, returns the Document collection. Item is the default member (DISPID_VALUE).
Open	VT_DISPATCH or VT_EMPTY	Opens an existing document and adds it to the collection. Returns the document that was opened or VT_EMPTY if the object could not be opened. Takes one required argument, filename, and one optional argument, password. Both arguments have type VT_BSTR.


Font Object

The Font object should have the following properties. The Application, Bold, Italic, Parent, and Size properties are required; other properties are optional.

Property name	Return type	Description
Application	VT_DISPATCH	Returns the Application object; read only. Required.
Bold	VT_BOOL	Sets or returns True if the font is boldface, and False otherwise; read/write. Required.
Color	VT_I4	Sets or returns the RGB color of the font; read/write.
Italic	VT_BOOL	Sets or returns True if the font is italic, and False otherwise; read/write. Required.
Name	VT_BSTR	Returns the name of the font; read only.
OutlineFont	VT_BOOL	Sets or returns True if the font is scaleable, and False otherwise. For example, bitmapped fonts are not scaleable, whereas TrueType fonts are scaleable; read/write.
Parent	VT_DISPATCH	Returns the parent of the Font object; read only.  Required.
Shadow	VT_BOOL	Sets or returns True if the font appears with a shadow, and False otherwise; read/write.
Size	VT_R4	Sets or returns the size of the font in points; read/write. Required.
Strikethrough	VT_BOOL	Sets or returns True if the font appears with a line running through it, and False otherwise; read/write.
Subscript	VT_BOOL	Sets or returns True if the font is subscripted, and False otherwise; read/write.
Superscript	VT_BOOL	Sets or returns True if the font is superscripted and False otherwise; read/write.


Naming Conventions

Choose names for your exposed objects, properties, and methods that can be easily understood by the users of your application. The guidelines in this section apply to all the items you expose:

•	Objects (implemented as classes in your application)

•	Properties and methods (implemented as members of a class)

•	Named arguments (implemented as named parameters in a member function)

•	Constants and enumerations (implemented as settings for properties and methods)

Use entire words or syllables

It is easier for users to remember complete words than to remember whether you abbreviated Window as Wind, Wn, or Wnd.

When you need to abbreviate because an identifier would be too long, try to use complete initial syllables. For example, use AltExpEval instead of AlternateExpressionEvaluation.

Use	Don't use
Application	App
Window	Wnd
Use mixed case

All identifiers should use mixed case, rather than underscores, to separate words.

Use	Don't use
ShortcutMenus	Shortcut_Menus, Shortcutmenus, SHORTCUTMENUS, SHORTCUT_MENUS
BasedOn	basedOn
Use the same word you use in the interface

Use consistent terminology; don't use names like HWND that are based on Hungarian notation. Try to use the same word your users would use to describe a concept.

Use	Don't use
Name	Lbl
Use the correct plural for the class name

Collection classes should use the correct plural for the class name. For example, if you have a class named Axis, you should store the collection of Axis objects in an Axes class. Similarly, a collection of Vertex objects is stored in a Vertices class. In rare cases where English uses the same word for the plural, append the word "Collection."

Use	Don't use
Axes	Axiss
SeriesCollection	CollectionSeries
Windows	ColWindow
Using plurals rather than inventing new names for collections reduces the number of items a user must remember. It also simplifies the selection of names for collections.

Note, however, that for some collections this may not be appropriate, especially where a set of objects exists independently of the collection. For example, a Mail program might have a Name object that exists in multiple collections: ToList, CCList, and so forth. In this case, you might specify the individual name collections as ToNames and CCNames.


Dispatch Interfaces

The dispatch interfaces provide a way to expose and access objects within an application. The dispatch interfaces include:

•	IDispatch interface — Exposes objects, methods, and properties to OLE Automation programming tools and other applications.

•	Dispatch functions — Simplify the implementation of an IDispatch interface. You can use these functions to automatically generate an IDispatch interface.

•	IEnumVARIANT interface — Provides a way for OLE Automation controllers to iterate over collection objects.

The following topics are included in this section:

Overview of Interfaces
Overview of Functions
IDispatch Interface
IDispatch::GetIDsOfNames
IDispatch::GetTypeInfo
IDispatch::GetTypeInfoCount
IDispatch::Invoke
Dispatch Interface Creation Functions
CreateDispTypeInfo
CreateStdDispatch
DispGetIDsOfNames
DispGetParam
DispInvoke
Active Object Registration Functions
GetActiveObject
RegisterActiveObject
RevokeActiveObject
IEnumVARIANT Interface
IEnumVARIANT::Clone
IEnumVARIANT::Next
IEnumVARIANT::Reset
IEnumVARIANT::Skip


Overview of Interfaces

The following table describes the member functions of each of the dispatch interfaces.

Interface	Member name	Purpose
IDispatch	Invoke	Provides access to properties and methods exposed by the object.
	GetIDsOfNames	Maps a single member name and an optional set of argument names to a corresponding set of integer DISPIDs which may then be used on subsequent calls to Invoke.
	GetTypeInfo	Retrieves the type information for an object.
	GetTypeInfoCount	Retrieves the number of type information interfaces that an object provides (either 0 or 1).
IEnumVARIANT	Clone	Creates a copy of the current enumeration state.
	Next	Gets the next item(s) in the enumeration sequence and returns them through the array.
	Reset	Resets the enumeration sequence to the beginning.
	Skip	Skips over the next item(s) in the enumeration sequence.


Overview of Functions

The dispatch functions are summarized in the following table. These functions are provided in OLEAUT32.DLL. The header file is OLEAUTO.H and the import library is OLEAUT32.LIB.

Category	Function name	Purpose
Dispatch interface creation	CreateDispTypeInfo	Creates simplified type information for an object.
	CreateStdDispatch	Creates a standard IDispatch implementation for an object.
	DispGetIDsOfNames	Converts a set of names to DISPIDs.
	DispGetParam	Retrieves and coerces elements from a DISPPARAMS structure.
	DispInvoke	Calls a member function of an IDispatch interface.
Active object initialization	GetActiveObject	Retrieves an instance of an object that is initialized with OLE.
	RegisterActiveObject	Initializes a running object with OLE. (Use when application starts.)
	RevokeActiveObject	Revokes a running application's initialization with OLE. (Use when application ends.)


IDispatch Interface

Implemented by	Used by	Header filename	Import library name
Applications that expose programmable objects.	Applications that access programmable objects	OLEAUTO.H	None
The IDispatch interface is implemented by OLE Automation objects to expose them for access by OLE Automation controllers, such as Microsoft Visual Basic. The object's properties and methods can be accessed using IDispatch::GetIDsOfNames and IDispatch::Invoke.

The following examples show how to access an OLE Automation object through the IDispatch interface. Note that the code is abbreviated and omits error handling.

// Declarations of variables used

DEFINE_GUID(CLSID_Hello, //...portions omitted for brevity...

HRESULT hresult;

IUnknown * punk;

IDispatch * pdisp;

OLECHAR FAR* szMember = "SayHello";

DISPID dispid;

DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}

In the following lines, OleInitialize loads the OLE DLLs and CoCreateInstance initializes the OLE Automation object's class factory. For more information on these two functions, see the OLE 2 Programmer's Reference, Volume 1.

// Initialize OLE DLLs

hresult = OleInitialize(NULL);

// OLE function CoCreateInstance starts application using GUID

hresult = CoCreateInstance(CLSID_Hello, &punk);

QueryInterface checks if the object supports IDispatch. (As with any call to QueryInterface, the returned pointer must be released when it is no longer needed.)

// QueryInterface to see if it supports IDispatch.

hresult = punk->QueryInteface(IID_IDispatch, &pdisp);

GetIDsOfNames retrieves the dispatch identifier for the indicated method or property (szMember).

// Retrieve the dispatch identifier for the SayHello method.

// Use defaults where possible.

hresult = pdisp->GetIDsOfNames(

IID_NULL,

&szMember,

1,

LOCALE_SYSTEM_DEFAULT,

&dispid);

In the following call to Invoke, the dispatch identifier (dispid) indicates the property or method to invoke. The SayHello method takes no parameters, so the fifth argument (&dispparamsNoArgs), contains NULL and 0, as initialized in a previous example. To invoke a property or method that requires parameters, you would need to supply the parameters in the DISPPARAMS structure.

// Invoke the method. Use defaults where possible.

hresult = pdisp->Invoke(

dispid,

IID_NULL,

LOCALE_SYSTEM_DEFAULT,

DISPATCH_METHOD,

&dispparamsNoArgs,

NULL,

NULL,

NULL);

The following topic is included in this section:

Data Types, Structures, and Enumerations


Data Types, Structures, and Enumerations

The IDispatch interface uses these data types and structures:

Name	Purpose
BSTR	A length-prefixed string.
CALLCONV	Identifies the calling convention used by a member function.
CURRENCY	Provides a precise data type of monetary data.
DISPID	Identifies a method or property to Invoke.
DISPPARAMS	Contains arguments passed to a method or property.
EXCEPINFO	Describes an error that occurred during Invoke.
INTERFACEDATA	Describes the members of an interface.
LCID	Provides locale information for international string comparisons and localized member names.
METHODDATA	Describes a method or property.
PARAMDATA	Describes a parameter to a method.
VARIANT	Describes a VARIANTARG that can't have the VT_BYREF bit set. Because VT_BYREF is not set, data of type VARIANT can't be passed within DISPPARAMS.
VARIANTARG	Describes arguments that may be passed within DISPPARAMS.
VARTYPE	Identifies the available variant types.
The following topics are included in this section:

BSTR
CALLCONV
CURRENCY
DISPID
DISPPARAMS
EXCEPINFO
INTERFACEDATA
LCID
METHODDATA
PARAMDATA
VARIANT and VARIANTARG
VARTYPE


BSTR

A length-prefixed string used by OLE Automation data manipulation functions.

typedef OLECHAR *BSTR;

For details on this data type, see Data Manipulation Functions.


CALLCONV

Identifies the calling convention used by a member function described in METHODDATA.

typedef enum tagCALLCONV {

      CC_CDECL = 1

    , CC_MSCPASCAL

    , CC_PASCAL = CC_MSCPASCAL

    , CC_MACPASCAL

    , CC_STDCALL

    , CC_RESERVED

    , CC_SYSCALL

    , CC_MPWCDECL

    , CC_MPWPASCAL

    , CC_MAX	/* end of enum marker */

} CALLCONV;

For the Windows 16-bit operating system, functions implemented with the CC_CDECL calling convention can't have return type float or double. This includes functions returning DATE, which is a floating-point type.


CURRENCY

A currency number is stored as an 8-byte, two's complement integer, scaled by 10,000 to give a fixed-point number with 15 digits to the left of the decimal point and 4 digits to the right. This representation provides a range of ±922337203685477.5807. The currency data type is extremely useful for calculations involving money, or for any fixed-point calculation where accuracy is particularly important.

typedef CY CURRENCY;

The data type is defined as a structure for working with currency more conveniently:

typedef struct tagCY

  {

  unsigned long Lo;

  long Hi;

  }

CY;


DISPID

Used by IDispatch::Invoke to identify methods, properties, and arguments.

typedef LONG DISPID;

The following DISPIDs have special meaning:

DISPID	Description
DISPID_VALUE	The default member for the object. This property or method is invoked when an OLE Automation controller specifies the object name without a property or method.
DISPID_NEWENUM	The _NewEnum property. This special, restricted method is required for collection objects. It returns the collection object and should have the flag FUNCFLAG_FRESTRICTED.
DISPID_EVALUATE	The Evaluate method.  This method is implicitly invoked when the OLE Automation controller encloses the arguments in square brackets. For example, the following two lines are equivalent: x.[A1:C1].value = 10 x.Evaluate("A1:C1").value = 10 In the preceding example, the Evaluate method has the dispatch ID DISPID_EVALUATE.
DISPID_PROPERTYPUT	The parameter that receives the value of an assignment in a property "put."
DISPID_UNKNOWN	Value returned by IDispatch::GetIDsOfNames to indicate that a member or parameter name was not found.


DISPPARAMS

Used by IDispatch::Invoke to contain the arguments passed to a method or property. For more information, see IDispatch::Invoke.

typedef struct FARSTRUCT tagDISPPARAMS{

VARIANTARG FAR* rgvarg;	// Array of arguments

    DISPID FAR* rgdispidNamedArgs;	// Dispatch IDs of named arguments

    unsigned int cArgs;	// Number of arguments

    unsigned int cNamedArgs;	// Number of named arguments

} DISPPARAMS;


EXCEPINFO

Describes an exception that occurred during IDispatch::Invoke. See the section "IDispatch::Invoke" for more information on exceptions.

typedef struct FARSTRUCT tagEXCEPINFO {

    unsigned short wCode;    /* An error code describing the error. */

unsigned short wReserved;

BSTR bstrSource;	    	/*Source of the exception.*/

    BSTR bstrDescription;   	/* Textual description of the error. */

    BSTR bstrHelpFile;     	/* Help file path. */

    unsigned long dwHelpContext;  /* Help context ID. */

void FAR* pvReserved;

    /* Pointer to function that fills in Help and description info */

HRESULT (STDAPICALLTYPE FAR* pfnDeferredFillIn)(struct tagEXCEPINFO

 FAR*);

SCODE scode;	/* An SCODE describing the error. */

} EXCEPINFO, FAR* LPEXCEPINFO;

Name	Type	Description
wCode	unsigned short	An error code identifying the error. Error codes should be greater than 1000.  Either this field or the scode field must be filled in; the other must be set to 0.
wReserved	unsigned short	Reserved value. This value should be set to 0.
bstrSource	BSTR	A textual, human-readable name of the source of the exception. Typically this will be an application name.  This field should be filled in by the implementor of IDispatch.
bstrDescription	BSTR	A textual, human-readable description of the error intended for the customer. If no description is available, NULL should be used.
bstrHelpFile	BSTR	The fully qualified drive, path, and filename of a Help file with more information about the error. If no Help is available, NULL should be used.
dwHelpContext	unsigned long	The Help context of the topic within the Help file. This field should be filled in if and only if the bstrHelpFile field is not NULL.
pvReserved	void FAR*	Must be set to NULL.
pfnDeferredFillIn		Use of this field allows an application to defer filling in the bstrDescription, bstrHelpFile, and dwHelpContext fields until they are needed. This field might be used, for example, if loading the string for the error is a time-consuming operation. To use deferred fill-in, the application should put a function pointer in this slot and not fill any of the other fields except wCode (which is required in any case). To get additional information, the caller passes the EXCEPINFO structure back to the pexcepinfo callback function, which fills in the additional information. If deferred fill-in is not desired, this field should be set to NULL. When the OLE Automation object and the OLE Automation controller are in different processes, the OLE Automation object will call pfnDeferredFillIn before returning to the controller.
scode	SCODE	An SCODE describing the error. Either this field or wCode (but not both) must be filled in; the other must be set to 0.


INTERFACEDATA

Describes the OLE Automation object's properties and methods.

typedef struct FARSTRUCT tagINTERFACEDATA {

    METHODDATA FAR* pmethdata;	// Pointer to an array of METHODDATAs

    unsigned int cMembers;	// Count of members

} INTERFACEDATA;


LCID

Identifies a locale for national language support. Locale information is used for international string comparisons and localized member names. For information on LCIDs, see Supporting Multiple National Languages.

typedef unsigned long LCID;


METHODDATA

Used to describe a method or property.

typedef struct FARSTRUCT tagMETHODDATA {

OLECHAR FAR* szName;	// Member name

PARAMDATA FAR* ppdata;	// Pointer to array of PARAMDATAs

DISPID dispid;	// Member ID

unsigned int iMeth;	// Method index

CALLCONV cc;	// Calling convention

unsigned int cArgs;	// Count of arguments

unsigned short wFlags;	// Whether this is a method or
// a property get, put, or putref

VARTYPE vtReturn;	// Return type

} METHODDATA;

Name	Type	Description
szName	OLECHAR  FAR*	The method name.
ppdata	PARAMDATA FAR*	The parameters for the method. The first parameter is ppdata[0] and so on.
dispid	DISPID	The ID of the method as used in IDispatch.
iMeth	unsigned int	The index of the method in the interface's vtable. The indexes start with 0.
cc	CALLCONV	The calling convention. The CDecl and Pascal calling conventions are supported by the dispatch interface creation functions, such as CreateStdDispatch.
cArgs	unsigned int	The number of arguments for the method.
wFlags	unsigned short	Flags that indicate whether the method is used for getting or setting a property. The flags are the same as in IDispatch::Invoke. DISPATCH_METHOD indicates this is not used for a property. DISPATCH_PROPERTYGET indicates the method is used to get a property value. DISPATCH_PROPERTYPUT indicates the method is used to set the value of a property. DISPATCH_PROPERTYPUTREF indicates the method is used to make the property refer to a passed-in object.
vtReturn	VARTYPE	Return type for the method.


PARAMDATA

Used to describe a parameter accepted by a method or property.

typedef struct FARSTRUCT tagPARAMDATA {

OLECHAR FAR* szName;	// Parameter name

VARTYPE vt;	// Parameter type

} PARAMDATA;

Name	Type	Description
szName	OLECHAR FAR *	The parameter name. Names should follow reasonable standard conventions for programming language access; that is, no embedded spaces or control characters, and 32 characters or fewer. This is a localized name, because each type description describes names for a particular locale.
vt	VARTYPE	The VARTYPE that will be used by the receiver. If more than one parameter type is accepted, VT_VARIANT should be specified.


VARIANT and VARIANTARG

Use VARIANTARG to describe arguments passed within DISPPARAMS. Use VARIANT to specify variant data that can't be passed by reference; the VARIANT type may not have the VT_BYREF bit set.

typedef struct FARSTRUCT tagVARIANT VARIANT;

typedef struct FARSTRUCT tagVARIANT VARIANTARG;

typedef struct tagVARIANT  {

VARTYPE vt;

unsigned short wReserved1;

unsigned short wReserved2;

unsigned short wReserved3;

union {

short        iVal;             /* VT_I2                */

long         lVal;             /* VT_I4                */

float        fltVal;           /* VT_R4                */

double       dblVal;           /* VT_R8                */

VARIANT_BOOL bool;             /* VT_BOOL              */

SCODE        scode;            /* VT_ERROR             */

CY           cyVal;            /* VT_CY                */

DATE         date;             /* VT_DATE              */

BSTR         bstrVal;          /* VT_BSTR              */

IUnknown     FAR* punkVal;     /* VT_UNKNOWN           */

IDispatch    FAR* pdispVal;    /* VT_DISPATCH          */

        SAFEARRAY	 FAR* parray;	   /* VT_ARRAY|*           */

short        FAR* piVal;       /* VT_BYREF|VT_I2       */

long         FAR* plVal;       /* VT_BYREF|VT_I4       */

float        FAR* pfltVal;     /* VT_BYREF|VT_R4       */

double       FAR* pdblVal;     /* VT_BYREF|VT_R8       */

VARIANT_BOOL FAR* pbool;       /* VT_BYREF|VT_BOOL     */

SCODE        FAR* pscode;      /* VT_BYREF|VT_ERROR    */

CY           FAR* pcyVal;      /* VT_BYREF|VT_CY       */

DATE         FAR* pdate;       /* VT_BYREF|VT_DATE     */

BSTR         FAR* pbstrVal;    /* VT_BYREF|VT_BSTR     */

IUnknown FAR* FAR* ppunkVal;   /* VT_BYREF|VT_UNKNOWN  */

IDispatch FAR* FAR* ppdispVal; /* VT_BYREF|VT_DISPATCH */

SAFEARRAY  FAR* FAR* parray;   /* VT_ARRAY|*           */

VARIANT      FAR* pvarVal;     /* VT_BYREF|VT_VARIANT  */

void         FAR* byref;       /* Generic ByRef        */

};

} ;

To simplify extracting values from VARIANTARGs, OLE Automation provides a set of functions for manipulating this type. Use of these functions is highly recommended to ensure that applications apply consistent coercion rules. These functions are described in Data Manipulation Functions.

The vt value governs the interpretation of the union as follows:

Value	Description
VT_EMPTY	No value was specified.  If an argument is left blank, you should not return VT_EMPTY for the argument. Rather, you should return the VT_ERROR: DISP_E_MEMBERNOTFOUND.
VT_EMPTY | VT_BYREF	Illegal.
VT_I2	A 2-byte integer value is stored in iVal.
VT_I2 | VT_BYREF	A reference to a 2-byte integer was passed; a pointer to the value is in piVal.
Value	Description
VT_I4	A 4-byte integer value is stored in lVal.
VT_I4 | VT_BYREF	A reference to a 4-byte integer was passed; a pointer to the value is in plVal.
VT_R4	An IEEE 4-byte real value is stored in fltVal.
VT_R4 | VT_BYREF	A reference to an IEEE 4-byte real was passed; a pointer to the value is in pfltVal.
VT_R8	An 8-byte IEEE real value is stored in dblVal.
VT_R8 | VT_BYREF	A reference to an 8-byte IEEE real was passed; a pointer to its value is in pdblVal.
VT_CY	A currency value was specified. A currency number is stored as an 8-byte, two's complement integer, scaled by 10,000 to give a fixed-point number with 15 digits to the left of the decimal point and 4 digits to the right. The value is in cyVal.
VT_CY | VT_BYREF	A reference to a currency value was passed; a pointer to the value is in pcyVal.
VT_BSTR	A string was passed; it is stored in bstrVal. This pointer must be obtained and freed via the BSTR functions, which are described in Chapter 6, "Data Manipulation Functions."
VT_BSTR | VT_BYREF	A reference to a string was passed. A BSTR* which points to a BSTR is in pbstrVal. The referenced pointer must be obtained or freed via the BSTR functions.
VT_NULL	A propagating NULL value was specified. This should not be confused with the NULL pointer. The NULL value is used for tri-state logic as with SQL.
VT_NULL | VT_BYREF	Illegal.
VT_ERROR	An SCODE was specified. The type of the error is specified in scode. Generally, operations on error values should raise an exception or propagate the error to the return value, as appropriate.
VT_ERROR | VT_BYREF	A reference to an SCODE was passed. A pointer to the value is in pscode.
VT_BOOL	A Boolean (True/False) value was specified. A value of 0xFFFF (all bits one) indicates True; a value of 0 (all bits zero) indicates False. No other values are legal.
VT_BOOL | VT_BYREF	A reference to a Boolean value. A pointer to the Boolean value is in pbool.
Value	Description
VT_DATE	A value denoting a date and time was specified. Dates are represented as double-precision numbers, where midnight, January 1, 1900 is 2.0, January 2, 1900 is 3.0, and so on. The value is passed in date. This is the same numbering system used by most spreadsheet programs, although some incorrectly believe that February 29, 1900 existed, and thus set January 1, 1900 to 1.0. The date can be converted to and from an MS-DOS representation using VariantTimeToDosDateTime, discussed in Data Manipulation Functions.
VT_DATE | VT_BYREF	A reference to a date was passed. A pointer to the value is in pdate.
VT_DISPATCH	A pointer to an object was specified. The pointer is in pdispVal. This object is only known to implement IDispatch; the object can be queried as to whether it supports any other desired interface by calling QueryInterface on the object. Objects that do not implement IDispatch should be passed using VT_UNKNOWN.
VT_DISPATCH | VT_BYREF	A pointer to a pointer to an object was specified. The pointer to the object is stored in the location referred to by ppdispVal.
VT_VARIANT	Illegal. VARIANTARGs must be passed by reference.
VT_VARIANT | VT_BYREF	A pointer to another VARIANTARG is passed in pvarVal. This referenced VARIANTARG will never have the VT_BYREF bit set in vt, so only one level of indirection can ever be present. This value can be used to support languages that allow functions to change the types of variables passed by reference.
VT_UNKNOWN	A pointer to an object that implements the IUnknown interface is passed in punkVal.
VT_UNKNOWN | VT_BYREF	A pointer to a pointer to the IUnknown interface is passed in ppunkVal. The pointer to the interface is stored in the location referred to by ppunkVal.
VT_ARRAY | <anything>	An array of data type <anything> was passed. (VT_EMPTY and VT_NULL are illegal types to combine with VT_ARRAY). The pointer in pByrefVal points to an array descriptor, which describes the dimensions, size, and in-memory location of the array. The array descriptor is never accessed directly, but instead is read and modified using the functions described in Data Manipulation Functions.


VARTYPE

VARTYPE is an enumeration type used in VARIANT, TYPEDESC, OLE property sets, and safe arrays. The enumeration constants listed below are valid in the vt field of a VARIANT structure.

typedef unsigned short VARTYPE;

enum VARENUM{

VT_EMPTY,	 	// Not specified

VT_NULL, 	// Propagating null

VT_I2, 	// Two-byte signed integer

VT_I4, 	// Four-byte signed integer

VT_R4, 	// Real, with four-byte precision

VT_R8, 	// Real

VT_CY, 	// Currency

VT_DATE, 	// Date

VT_BSTR,	// String

VT_DISPATCH,	// IDispatch *

VT_ERROR, 	// Error

VT_BOOL, 	// Boolean

VT_VARIANT,	// A VARIANT

VT_UNKNOWN	// A pointer to the an implementation of IUnknown

.

. //other constants that are not valid in VARIANTs

.

};

VT_RESERVED	= (int) 0x8000

// By reference: a pointer to the data is passed

VT_BYREF  	= (int) 0x4000

VT_ARRAY 	= (int) 0x2000	// A safe array of the data is passed


IDispatch::GetIDsOfNames

HRESULT IDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
REFIID riid
OLECHAR FAR* FAR* rgszNames
unsigned int cNames
LCID lcid
DISPID FAR* rgdispid

Maps a single member and an optional set of argument names to a corresponding set of integer DISPIDs which may be used on subsequent calls to IDispatch::Invoke. The dispatch function DispGetIDsOfNames provides a standard implementation of GetIDsOfNames.

Parameter

riid

Reserved for future use. Must be NULL.

rgszNames

Passed-in array of names to be mapped.

cNames

Count of the names to be mapped.

lcid

The locale context in which to interpret the names.

rgdispid

Caller-allocated array, each element of which contains an ID corresponding to one of the names passed in the rgszNames array. The first element represents the member name; the subsequent elements represent each of the member's parameters.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_OUTOFMEMORY	Out of memory.
DISP_E_UNKNOWNNAME	One or more of the names were not known. The returned array of DISPIDs contains DISPID_UNKNOWN for each entry that corresponds to an unknown name.
DISP_E_UNKNOWNLCID	The LCID was not recognized.
Comments

An IDispatch implementation may choose to associate any positive integer ID value with a given name. Zero is reserved for the default, or Value property, and −1 is reserved to indicate an unknown name. For example, if GetIDsOfNames is called and the implementation does not recognize one or more of the names, it will return DISP_E_UNKNOWNNAME and the rgdispid array will contain DISPID_UNKNOWN for the entries which correspond to the unknown names.

The member and parameter DISPIDs must remain constant for the lifetime of the object. This allows a client to discover the DISPIDs once and cache them for later use.

When GetIDsOfNames is called with more than one name, the first name (rgszNames[0]) corresponds to the member name, and subsequent names correspond to parameter names on that member.

The same name may map to different DISPIDs depending on context. For example, a name may have a DISPID when it is used as a member name with a particular interface, a different DISPID as a member of a different interface, and different mapping for each time it appears as a parameter.

Instead of using GetIDsOfNames, an IDispatch client can map names to DISPIDs using the type information interfaces described in Chapter 8, "Type Description Interfaces." This allows a client to bind to members at compile time and avoid calling GetIDsOfNames at run time.

The implementation of GetIDsOfNames must be case insensitive. Clients that need case-sensitive name mapping should use the type information interfaces to map names to DISPIDs, rather than calling GetIDsOfNames.

Example

This code from the SPoly2 sample file CPOLY.CPP implements the GetIDsOfNames member function for the CPoly class. (OLE Automation object.)

STDMETHODIMP

CPoly::GetIDsOfNames(

    REFIID riid,

    OLECHAR FAR* FAR* rgszNames,

    unsigned int cNames,

    LCID lcid,

    DISPID FAR* rgdispid)

{

UNUSED(lcid);

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);

}

The code that follows calls GetIDsOfNames to get the DISPID of the CPoly Draw method. (OLE Automation controller.)

HRESULT hresult;

IDispatch FAR* pdisp = (IDispatch FAR*)NULL;

DISPID dispid;

OLECHAR FAR* szMember = "draw";

hresult = pdisp->GetIDsOfNames(

IID_NULL,

&szMember,

1, LOCALE_SYSTEM_DEFAULT,

&dispid) ;

See Also

CreateStdDispatch, DispGetIDsOfNames


IDispatch::GetTypeInfo

HRESULT IDispatch::GetTypeInfo(itinfo, lcid, pptinfo)
unsigned int itinfo
LCID lcid
ITypeInfo FAR* FAR* pptinfo

Retrieves type information for an interface.

Parameter

itinfo

Reserved for future use. Always pass 0.

lcid

The locale ID for the type information. An object may be able to return different type information for different languages. This is important for classes that support localized member names. For classes that don't support localized member names, this parameter can be ignored.

pptinfo

Receives a pointer to the type information object requested.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success; the TypeInfo element exists.
DISP_E_BADINDEX	Failure; itinfo argument was not 0.
TYPE_E_ELEMENTNOTFOUND	Failure; itinfo argument was not 0.
Example

This code from the SPoly2 sample file CPOLY.CPP creates type information and implements the GetTypeInfo member function for the CPoly class. (OLE Automation object.)

// These lines from CPoly::Create create pointer to the type
// information returned by GetTypeInfo.

//

hresult = CreateDispTypeInfo(&g_idataCPoly, LOCALE_SYSTEM_DEFAULT,
&ptinfo);

    ppoly->m_ptinfo = ptinfo;

// Retrieve the type information for this object.

STDMETHODIMP

CPoly::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)

{

    if(itinfo != 0)

      return ResultFromScode(DISP_E_BADINDEX);

    m_ptinfo->AddRef();

    *pptinfo = m_ptinfo;

    return NOERROR;

}

See Also

CreateStdDispatch, CreateDispTypeInfo


IDispatch::GetTypeInfoCount

HRESULT IDispatch::GetTypeInfoCount(pctinfo)
unsigned int FAR* pctinfo

Retrieves the number of type information interfaces that an object provides
(either 0 or 1).

Parameter

pctinfo

Points to location that receives the number of type information interfaces that the object provides. If the object provides type information, this number is 1; otherwise the number is 0.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
E_NOTIMPL	Failure
Comments

The function may return zero, which indicates that the object does not provide any type information. In this case, the object may still be programmable through IDispatch, but does not provide type information for browsers, compilers, or other programming tools that access type information. This may be useful for hiding an object from browsers or for preventing early binding on an object.

Example

This code from the SPoly2 sample file CPOLY.CPP implements the GetTypeInfoCount member function for the CPoly class. (OLE Automation object.)

STDMETHODIMP

CPoly::GetTypeInfoCount(unsigned int FAR* pctinfo)

{

    // This object has a single *introduced* interface

    *pctinfo = 1;

    return NOERROR;

}

See Also

CreateStdDispatch


IDispatch::Invoke

HRESULT IDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult,  pexcepinfo, puArgErr)
DISPID dispidMember
REFIID riid
LCID lcid
unsigned short wFlags
DISPPARAMS FAR* pdispparams
VARIANT FAR* pvarResult
EXCEPINFO FAR* pexcepinfo
unsigned int FAR* puArgErr

Provides access to properties and methods exposed by the object. The dispatch function DispInvoke provides a standard implementation of IDispatch::Invoke.

Parameter

dispidMember

Identifies the member. Dispatch identifiers may be retrieved using GetIDsOfNames.

riid

Reserved for future use. Must be IID_NULL.

lcid

The locale context in which to interpret arguments. The lcid is used by the GetIDsOfNames function, and is also passed to Invoke to allow the object to interpret its method arguments in a locale-specific way. Applications that don't support multiple national languages can ignore this parameter. See Supporting Multiple National Languages for more information.

wFlags

Flags describing the context of the Invoke call, as follows:

Value	Description
DISPATCH_METHOD	The member was accessed as a method. If there is a property of the same name, both this and the DISPATCH_PROPERTYGET flag may be set.
DISPATCH_PROPERTYGET	The member is being retrieved as a property or data member.
DISPATCH_PROPERTYPUT	The member is being changed as a property or data member.
DISPATCH_PROPERTYPUTREF	The member is being changed via a reference assignment, rather than a value assignment. This value is valid only when the property accepts a reference to an object.
pdispparams

Pointer to a structure containing an array of arguments, array of argument dispatch IDs for named arguments, and counts for number of elements in the arrays. See the Comments section for a description of the DISPPARAMS structure.

pvarResult

Pointer to where the result is to be stored, or NULL if the caller expects no result. This argument is ignored if DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF is specified.

pexcepinfo

Pointer to a structure containing exception information. This structure should be filled in if DISP_E_EXCEPTION is returned.

puArgErr

If the result is DISP_E_TYPEMISMATCH, this parameter indicates the index within rgvarg of the argument with incorrect type. If more than one argument has an error, puArgErr should contain the number of the first argument with an error. Arguments are stored in pdispparams->rgvarg in reverse order, so the first argument is the one with the highest index in the array. See the Comments section for details.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADPARAMCOUNT	The number of elements provided in DISPPARAMS is different from the number of arguments accepted by the method or property.
DISP_E_BADVARTYPE	One of the arguments in DISPPARAMS is not a valid variant type.
DISP_E_EXCEPTION	The application needs to raise an exception. In this case the structure passed in pexcepinfo should be filled in.
DISP_E_MEMBERNOTFOUND	The requested member does not exist.
DISP_E_NONAMEDARGS	This implementation of IDispatch does not support named arguments.
DISP_E_OVERFLOW	One of the arguments in DISPPARAMS could not be coerced to the specified type.
DISP_E_PARAMNOTFOUND	One of the parameter IDs does not correspond to a parameter on the method. In this case puArgErr should be set to the first argument that contains the error.
DISP_E_TYPEMISMATCH	One or more of the arguments could not be coerced. The index within rgvarg of the first parameter with the incorrect type is returned in the puArgErr parameter.
DISP_E_UNKNOWNINTERFACE	The interface ID passed in riid is not IID_NULL.
DISP_E_UNKNOWNLCID	The member being accessed interprets string arguments according to the locale ID (LCID), and the LCID is not recognized. If the LCID is not needed to interpret arguments, this error should not be returned.
DISP_E_PARAMNOTOPTIONAL	A required parameter was omitted.
You can define your own errors using the MAKE_SCODE macro.

Comments

The dispidMember argument identifies which member to access. The dispatch IDs that identify members are defined entirely by the implementor of the class, and can be ascertained using documentation, the IDispatch::GetIDsOfNames function, or the ITypeInfo interface.

Arguments to the method being invoked are passed using the DISPPARAMS structure. It consists of a pointer to an array of arguments represented as variants, dispatch IDs of arguments that were passed as named arguments, and counts for the number of arguments in each array.

typedef struct FARSTRUCT tagDISPPARAMS{

    VARIANTARG FAR* rgvarg;	// Array of arguments

    DISPID FAR* rgdispidNamedArgs; 	// Dispatch IDs of named arguments

    unsigned int cArgs;	// Number of arguments

    unsigned int cNamedArgs; 	// Number of named arguments

} DISPPARAMS;

The arguments to the member are passed in the array rgvarg[], with the number of arguments passed in cArgs. Elements within the array rgvarg may be changed if they have the VT_BYREF flag set; otherwise they should be considered read only.

The arguments are in the array from last to first, so rgvarg[0] has the last argument and rgvarg[cArgs −1] has the first argument.

A dispatch invocation can have named arguments as well as positional arguments. If cNamedArgs is 0, all the elements of rgvarg[] represent positional arguments. If cNamedArgs is nonzero, each element of rgdispidNamedArgs[] contains the DISPID of a named argument whose value is in the matching element of rgvarg[]. The named arguments are always contiguous in rgdispidNamedArgs, and their values are in the first cNamedArgs elements of rgvarg.

The DISPID of an argument is its zero-based position in the argument list. For example, the following method takes three arguments:

BOOL _export CDECL

CCredit::CheckCredit(BSTR bstrCustomerID,	// DISPID = 0
 BSTR bstrLenderID,	// DISPID = 1
 CURRENCY cLoanAmt)	// DISPID = 2

{

... // Code omitted.

}

If you include the DISPID with each named argument, you may pass the named arguments to Invoke in any order. For example, if a method is to be invoked with two positional arguments, followed by three named arguments (A, B, and C) using the following hypothetical syntax, then cArgs would be 5, and cNamedArgs would be 3:

object.method("arg1", "arg2", A := "argA", B := "argB", C: = "argC")

The first positional argument would be in rgvarg[4]. The second positional argument would be in rgvarg[3]. The ordering of named arguments should be immaterial to the IDispatch implementor, but these are also generally passed in reverse order. The argument named A would be in rgvarg[2], with the DISPID of A in rgdispidNamedArgs[2]. The argument named B would be in rgvarg[1], with the corresponding DISPID in rgdispidNamedArgs[1]. The argument named C would be in rgvarg[0], with the DISPID corresponding to C in rgdispidNamedArgs[0]. The following diagram illustrates the arrays and their contents.

In pvarResult, the caller passes a pointer to a caller-allocated VARIANTARG where the return value should be placed. If no return value is expected, the caller passes NULL. The callee fills in this value with the return value. In general, the return value will not have VT_BYREF set. However, implementors may set this bit and return a pointer to the return value, if the lifetime of the return value is the same as that of the object.

Invoke must return DISP_E_MEMBERNOTFOUND if one of the following conditions occurs:

•	It can't find a member with the specified name DISPID and matching cArgs, and the member is not optional.

•	The referenced member is a void function and the caller didn't set pvarResult to NULL.

•	The referenced member is not a void function and the caller set pvarResult to NULL.

If Invoke finds a match, but can't convert one of the arguments to the desired type, it should return DISP_E_TYPEMISMATCH, setting *puArgErr to the index within rgvarg of the argument with the inappropriate type. For example, if an OLE Automation method expects a reference to a double-precision number as an argument, but receives a reference to an integer, the argument should be coerced. If, however, the method receives a date, IDispatch::Invoke should return DISP_E_TYPEMISMATCH.

OLE Automation provides functions to perform standard conversions of VARIANT, and these should be used for consistent operation. Only if these functions fail should DISP_E_TYPEMISMATCH be returned. For more information on performing conversions, see Data Manipulation Functions.

Property members are accessed the same way method members are accessed, except that the DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT flag is set in wFlags for properties, whereas the DISPATCH_METHOD flag is set for methods. Note that some languages can't distinguish between retrieving a property and calling a method; both the DISPATCH_PROPERTYGET and DISPATCH_METHOD flags will be set in this case.

If the caller wants to get a property's current value, wFlags contains DISPATCH_PROPERTYGET, dispidMember corresponds to the property's name, cArgs = 0, and pvarResult points to the caller-allocated VARIANT which is to receive the property's value.

If the caller wants to change the property's value, wFlags contains DISPATCH_PROPERTYPUT, dispidMember corresponds to the property's name, rgvarg[0] contains the new value, rgdispidNamedArgs[0] = DISPID_PROPERTYPUT, cArgs = 1, cNamedArgs = 1, and pvarResult = NULL.

Indexed properties of any dimension are dealt with in a similar way, by passing additional arguments which are the indexes. In this case, if an element of an indexed property is being changed, the new value of the property is the first element in the rgvarg[] vector. For example, if a put operation on an indexed property is trying to change the value of Prop[1,2] to 3, rgvarg[0] would contain 3, rgvarg[1] would contain 2, and rgvarg[2] would contain 1.

Use the DISPATCH_PROPERTYPUTREF flag to indicate a property or data member that should be set by reference rather than by value.

The caller is responsible for releasing all strings and objects referred to by rgvarg[] or placed in *pvarResult. As with other parameters that are passed by value, if the callee needs to maintain access to a string after returning, the string should be copied. Similarly, if the callee needs access to a passed object pointer after returning, it must call AddRef on that object. A common example occurs when an object property is changed to refer to a new object, using the DISPATCH_PROPERTYPUTREF flag.

Important  The callee should not change values in the passed rgvarg array.

Raising Exceptions During Invoke

When an error occurs during the execution of IDispatch::Invoke, the implementor can choose to communicate the error either through the normal return value or by raising an exception. An exception is a special situation that is normally dealt with by jumping to the nearest enclosing exception handler.

If IDispatch::Invoke returns DISP_E_EXCEPTION, it is indicating to its caller that an exception has occurred, and that information about the cause of the exception or error has been placed in the structure pointed to by pexcepinfo. The information can be used by the caller to understand the cause of the exception and deal with it as necessary.

The information placed in the exception information structure includes an error code number which identifies the kind of exception, a string which describes the error in a human-readable way, and a Help file and Help context number which can be passed to WinHelp for more detail about the error. At a minimum, the error code number must be filled in with a valid number.

If you think of IDispatch as another way to call C++ style methods in an interface, EXCEPINFO models the throwing of an exception or longjmp() call by such a method.

Example

This code from the SPoly2 sample file CPOLY.CPP implements the Invoke member function for the CPoly class. (OLE Automation object.)

STDMETHODIMP

CPoly::Invoke(

    DISPID dispidMember,

    REFIID riid,

    LCID lcid,

    unsigned short wFlags,

    DISPPARAMS FAR* pdispparams,

    VARIANT FAR* pvarResult,

    EXCEPINFO FAR* pexcepinfo,

    unsigned int FAR* puArgErr)

{

 UNUSED(lcid);

 if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

DispInvoke(

      this, m_ptinfo,

      dispidMember, wFlags, pdispparams,

      pvarResult, pexcepinfo, puArgErr);

}

This code calls the SPoly2 Invoke member function to invoke the Draw method on a CPoly object. (OLE Automation controller.)

HRESULT hresult;

IUnknown FAR* punk;
IDispatch FAR* pdisp = (IDispatch FAR*)NULL;

    OLECHAR FAR* szMember = "draw";

    DISPID dispid;

    DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};

hresult = CoCreateInstance(CLSID_CPoly2, NULL, CLSCTX_LOCAL_SERVER,
IID_Unknown, (void FAR* FAR*)&punk);

hresult = punk->QueryInterface(IID_IDispatch,
(void FAR* FAR*)&pdisp);

hresult = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_SYSTEM_DEFAULT, &dispid) ;

hresult = pdisp->Invoke(

dispid,

IID_NULL,

LOCALE_SYSTEM_DEFAULT,

DISPATCH_METHOD,

&dispparamsNoArgs, NULL, NULL, NULL);

See Also

CreateStdDispatch, DispInvoke, DispGetParam


Dispatch Interface Creation Functions

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose programmable objects	OLEAUTO.H	OLEAUT32.LIB
OLE Automation provides these functions to simplify creating OLE Automation objects.


CreateDispTypeInfo

HRESULT CreateDispTypeInfo (pinterfacedata, lcid, pptinfo)
INTERFACEDATA pinterfacedata
LCID lcid
ITypeInfo FAR* FAR* pptinfo

Creates simplified type information for use in an implementation of IDispatch.

Parameter

pinterfacedata

The interface description that this type information describes.

lcid

The locale ID (LCID) for the names used in the type information.

pptinfo

Output parameter that will contain a pointer to a type information implementation for use in DispGetIDsOfNames and DispInvoke.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	The interface is supported.
E_INVALIDARG	Either the interface description or the locale ID is invalid.
E_OUTOFMEMORY	There was insufficient memory to complete the operation.
Comments

Type information may be constructed at run time using CreateDispTypeInfo and the INTERFACEDATA defined to describe the object being exposed.

CreateDispTypeInfo does not return all the type information described in Chapter 9, "Type Building Interfaces." The argument pInterfaceData is not a complete description of an interface. It does not include Help information, comments, optional parameters, and other type information that is useful in different contexts. The type information returned by this function is primarily designed to automate the implementation of IDispatch.

The recommended method for providing type information about an object is to describe the object using the Object Description Language (ODL) and to compile the object description into a type library using MkTypLib.

To use type information from a type library, use the LoadTypeLib and GetTypeInfoOfGuid functions instead of CreateDispTypeInfo. For more information on LoadTypeLib and GetTypeInfoOfGuid, see Type Description Interfaces.

Example

This code from the DispCalc sample file DISPCALC.CPP creates type information from INTERFACEDATA defined in IDATA.CPP to expose the CCalc object.

CCalc FAR*

CCalc::Create()

{

    HRESULT hresult;

    CCalc FAR* pcalc;

    CArith FAR* parith;

    ITypeInfo FAR* ptinfo;

    IUnknown FAR* punkStdDisp;

extern INTERFACEDATA NEARDATA g_idataCCalc;

    if((pcalc = new FAR CCalc()) == NULL)

      return NULL;

    pcalc->AddRef();

    parith = &(pcalc->m_arith);

    // Build a TypeInfo for the functionality on this object that

    // is being exposed for external programmability.

    hresult = CreateDispTypeInfo(

      &g_idataCCalc, LOCALE_SYSTEM_DEFAULT, &ptinfo);

    if(hresult != NOERROR)

      goto LError0;

    // Create an aggregate with an instance of the default

    // implementation of IDispatch that is initialized with our

    // TypeInfo.

    hresult = CreateStdDispatch(

      pcalc,	// controlling unknown

      parith,	// instance to dispatch on

      ptinfo,	// typeinfo describing the instance

      &punkStdDisp);

    ptinfo->Release();

    if(hresult != NOERROR)

      goto LError0;

    pcalc->m_punkStdDisp = punkStdDisp;

    return pcalc;

LError0:;

    pcalc->Release();

    return NULL;

}


CreateStdDispatch

HRESULT CreateStdDispatch (punkOuter, pvThis, ptinfo, ppunkStdDisp)
IUnknown FAR* punkOuter
void FAR* pvThis
ITypeInfo FAR* ptinfo
IUnknown FAR* FAR* ppunkStdDisp

Creates a standard implementation of the IDispatch interface through a single function call. This greatly simplifies exposing objects through OLE Automation.

Parameters

punkOuter

Pointer to the object's IUnknown implementation.

pvThis

Pointer to the object to expose.

ptinfo

Pointer to the type information that describes the exposed object.

ppunkStdDisp

Pointer to the location where the implementation of the IDispatch interface for this object is returned. This pointer is NULL if the function fails.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	One of the first three arguments is invalid.
E_OUTOFMEMORY	There was insufficient memory to complete the operation.
Comments

You can use CreateStdDispatch when you create an object instead of implementing the IDispatch member functions for the object. The implementation that CreateStdDispatch creates has these limitations:

•	Supports one national language

•	Supports only dispatch-defined exception codes returned from Invoke

CreateDispTypeInfo and CreateStdDispatch comprise the minimum set of dispatch components you need to call to expose an object using type information provided by the INTERFACEDATA structure.

LoadTypeLib, GetTypeInfoOfGuid, and CreateStdDispatch comprise the minimum set of functions you need to call to expose an object using a type library. For more information on LoadTypeLib and GetTypeInfoOfGuid, see Type Description Interfaces.

Example

This code from the DispCalc sample file DISPCALC.CPP implements the IDispatch interface for the CCalc class using CreateStdDispatch. (OLE Automation object.)

CCalc FAR*

CCalc::Create()

{

    HRESULT hresult;

    CCalc FAR* pcalc;

    CArith FAR* parith;

    ITypeInfo FAR* ptinfo;

    IUnknown FAR* punkStdDisp;

extern INTERFACEDATA NEARDATA g_idataCCalc;

    if((pcalc = new FAR CCalc()) == NULL)

      return NULL;

    pcalc->AddRef();

    parith = &(pcalc->m_arith);

    // Build a TypeInfo for the functionality on this object that

    // is being exposing for external programmability.

    hresult = CreateDispTypeInfo(

      &g_idataCCalc, LOCALE_SYSTEM_DEFAULT, &ptinfo);

    if(hresult != NOERROR)

      goto LError0;

    // Create an aggregate with an instance of the default

    // implementation of IDispatch that is initialized with our

    // TypeInfo.

    hresult = CreateStdDispatch(

      pcalc,	// controlling unknown

      parith,	// instance to dispatch on

      ptinfo,	// typeinfo describing the instance

      &punkStdDisp);

    ptinfo->Release();

    if(hresult != NOERROR)

      goto LError0;

    pcalc->m_punkStdDisp = punkStdDisp;

    return pcalc;

LError0:;

    pcalc->Release();

    return NULL;

}

The following code calls the CCalc IDispatch member functions implemented by CreateStdDispatch. (OLE Automation controller.)

    long x = 4;

    HRESULT hresult;

    IUnknown FAR* punk;

    IDispatch FAR* pdisp = (IDispatch FAR*)NULL;

    OLECHAR FAR* szMember = "accum";

    DISPID dispid;

    DISPPARAMS FAR* pdispparams;

    DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};

    hresult = CoCreateInstance(CLSID_DispCalc, NULL,
CLSCTX_LOCAL_SERVER, IID_IUnknown, (void FAR* FAR*)&punk);

    hresult = punk->QueryInterface(IID_IDispatch,
(void FAR* FAR*)&pdisp);

    punk->Release();

    szMember = "accum";

    hresult = pdisp->GetIDsOfNames(

IID_NULL,

        &szMember,

        1, LOCALE_SYSTEM_DEFAULT,

        &dispid) ;

hresult = DispBuildParams(&pdispparams, 0, NULL, "I", x);

hresult = pdisp->Invoke(

        dispid,

        IID_NULL,

        LOCALE_SYSTEM_DEFAULT,

        DISPATCH_PROPERTYPUT,

        pdispparams, NULL, NULL, NULL);

DispFreeParams(pdispparams);


DispGetIDsOfNames

HRESULT DispGetIDsOfNames (ptinfo, rgszNames, cNames, rgdispid)
ITypeInfo * ptinfo
OLECHAR FAR* FAR* rgszNames
unsigned int	cNames
DISPID FAR* rgdispid

Uses type information to convert a set of names to DISPIDs. This is the recommended implementation of IDispatch::GetIDsOfNames.

Parameter

ptinfo

Pointer to the type information for an interface. Note that this type information will be specific to one interface and language code, so it is not necessary to pass an IID or LCID to this function.

rgszNames

An array of name strings. This can be the array passed to DispInvoke in the DISPPARAMS structure. If cNames is greater than one, the first name will be interpreted as a method name and subsequent names will be interpreted as parameters to that method.

cNames

The number of elements in rgszNames.

rgdispid

Pointer to an array of DISPIDs that will be filled in by this function. The first id will correspond to the method name; subsequent names will be interpreted as parameters to the method.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	The interface is supported.
E_INVALIDARG	One of the arguments is invalid.
DISP_E_UNKNOWNNAME	One or more of the given names were not known. The returned array of DISPIDs will contain DISPID_UNKNOWN for each entry that corresponds to an unknown name.
Other returns	Any of the ITypeInfo::Invoke errors may also be returned.
Example

This code from the SPoly2 sample file CPOLY.CPP implements the member function GetIDsOfNames for the CPoly class using DispGetIDsOfNames.

STDMETHODIMP

CPoly::GetIDsOfNames(

    REFIID riid,

    OLECHAR FAR* FAR* rgszNames,

    unsigned int cNames,

    LCID lcid,

    DISPID FAR* rgdispid)

{

    UNUSED(lcid);

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);

}

See Also

CreateStdDispatch, IDispatch::GetIDsOfNames


DispGetParam

HRESULT DispGetParam(dispparams, iPosition, vt, pvarResult, puArgErr)
DISPPARAMS FAR* dispparams
unsigned int iPosition
VARTYPE vt
VARIANT FAR* pvarResult
unsigned int FAR* puArgErr

Retrieves a parameter from the dispparams structure, checking both named parameters and positional parameters, and coerces it to the specified type.

Parameter

dispparams

Pointer to the parameters passed to IDispatch::Invoke.

iPosition

The position of the parameter in the parameter list. DispGetParam starts at the end of the array, so if iPosition is 0, the last parameter in the array will be returned.

vt

The type to which the argument should be coerced, when it is copied into pvarResult.

pvarResult

Pointer to the variant into which to pass the parameter. This must be a valid VARIANT; any existing contents will be released in the standard way. The contents of this VARIANT should be freed with VariantFree.

puArgErr

On return, pointer to the index of the argument that caused a DISP_E_TYPEMISMATCH error. This pointer should be returned to Invoke to indicate the position of the argument in DISPPARAMS that caused the error.

Return Value

The SCODE obtained from the HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADVARTYPE	The variant type vt is not supported.
DISP_E_OVERFLOW	The retrieved parameter could not be coerced to the specified type.
DISP_E_PARAMNOTFOUND	Could not find the parameter indicated by  iPosition.
DISP_E_TYPEMISMATCH	Could not coerce argument to specified type.
E_INVALIDARG	One of the arguments was invalid.
E_OUTOFMEMORY	Insufficient memory to complete operation.
Example

This code from the SPoly sample file CPOINT.CPP shows using DispGetParam to set X and Y properties.

STDMETHODIMP

CPoint::Invoke(

    DISPID dispidMember,

    REFIID riid,

    LCID lcid,

    unsigned short wFlags,

    DISPPARAMS FAR* pdispparams,

    VARIANT FAR* pvarResult,

    EXCEPINFO FAR* pexcepinfo,

    unsigned int FAR* puArgErr)

{

    unsigned int uArgErr;

    HRESULT hresult;

    VARIANTARG varg0;

    VARIANT varResultDummy;

    UNUSED(lcid);

    UNUSED(pexcepinfo);

    // Make sure the wFlags are legal

    if(wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET | 	DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))

      return ResultFromScode(E_INVALIDARG);

    // This object only exposes a "default" interface.

    if(!IsEqualIID(riid, IID_NULL))

      return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    // It simplifies the following code if the caller

// ignores the return value.

    if(puArgErr == NULL)

      puArgErr = &uArgErr;

    if(pvarResult == NULL)

      pvarResult = &varResultDummy;

    VariantInit(&varg0);

    // Assume the return type is void, unless we find otherwise.

    VariantInit(pvarResult);

    switch(dispidMember){

    case IDMEMBER_CPOINT_GETX:

      V_VT(pvarResult) = VT_I2;

      V_I2(pvarResult) = GetX();

      break;

    case IDMEMBER_CPOINT_SETX:

      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);

      if(hresult != NOERROR)

        return hresult;

      SetX(V_I2(&varg0));

      break;

    case IDMEMBER_CPOINT_GETY:

      V_VT(pvarResult) = VT_I2;

      V_I2(pvarResult) = GetY();

      break;

    case IDMEMBER_CPOINT_SETY:

      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);

      if(hresult != NOERROR)

        return hresult;

      SetY(V_I2(&varg0));

      break;

    default:

      return ResultFromScode(DISP_E_MEMBERNOTFOUND);

    }

    return NOERROR;

}

See Also

CreateStdDispatch,
IDispatch::Invoke


DispInvoke

HRESULT DispInvoke(_this, ptinfo, dispidMember, wFlags, pparams,
pvarResult, pexcepinfo, puArgErr)
void FAR* _this
ITypeInfo FAR* ptinfo
DISPID dispidMember
unsigned short wFlags
DISPPARAMS FAR* pparams
VARIANT FAR* pvarResult
EXCEPINFO	pexcepinfo
unsigned int FAR* puArgErr

Automatically calls member functions on an interface, given type information for the interface. A developer can describe an interface with type information and implement IDispatch::Invoke for the interface using this single call.

Parameter

 _this

Pointer to an implementation of the IDispatch interface described by ptinfo.

ptinfo

Pointer to the type information describing the interface.

dispidMember

The member to invoke.

wFlags

Flags describing the context of the Invoke call. See the following table for a complete list of flags.

Value	Description
DISPATCH_METHOD	The member was accessed as a method. If there is ambiguity, both this and the DISPATCH_PROPERTYGET flag may be set.
DISPATCH_PROPERTYGET	The member is being retrieved as a property or data member.
DISPATCH_PROPERTYPUT	The member is being changed as a property or data member.
DISPATCH_PROPERTYPUTREF	The member is being changed via a reference assignment, rather than a value assignment.
Other returns	Any of the ITypeInfo::Invoke errors may also be returned.
pparams

Pointer to the parameters passed to IDispatch::Invoke.

pvarResult

Pointer to the result VARIANT passed to IDispatch::Invoke.

pexcepinfo

Pointer to the EXCEPINFO structure passed to IDispatch::Invoke. This is not currently used by DispInvoke.

puArgErr

If the result is DISP_E_TYPEMISMATCH, puArgErr indicates the index within rgvarg of the argument with incorrect type. If more than one argument has an error, puArgErr should contain the number of the first argument with an error. Remember that arguments are placed in pdispparams->rgvarg in reverse order, so the first argument is the one with the highest index in the array.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADPARAMCOUNT	The number of elements provided in DISPPARAMS is different from the number of arguments accepted by the method or property.
DISP_E_BADVARTYPE	One of the arguments in DISPPARAMS is not a valid variant type.
DISP_E_EXCEPTION	The application wishes to raise an exception. In this case the structure passed in pexcepinfo should be filled in.
DISP_E_MEMBERNOTFOUND	The requested member does not exist.
DISP_E_NONAMEDARGS	This implementation of IDispatch does not support named arguments.
DISP_E_OVERFLOW	One of the arguments in DISPPARAMS could not be coerced to the specified type.
DISP_E_PARAMNOTFOUND	One of the parameter IDs does not correspond to a parameter on the method. In this case puArgErr should be set to the first argument that contains the error.
DISP_E_PARAMNOTOPTIONAL	A required parameter was omitted.
DISP_E_TYPEMISMATCH	One or more of the arguments could not be coerced. In this case the index within rgvarg of the first parameter with the incorrect type is returned in the puArgErr parameter.
DISP_E_UNKNOWNINTERFACE	The interface ID passed in riid is not NULL.
DISP_E_UNKNOWNLCID	The member being accessed interprets string arguments according to the locale ID (LCID), and the LCID is not recognized. If the LCID is not needed to interpret arguments, this error should not be returned.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Insufficient memory to complete operation.
Comments

The parameter _this is a pointer to an implementation of the interface being deferred to. DispInvoke builds a stack frame, coerces parameters using standard coercion rules, pushes them on the stack, and calls the correct member function in the virtual function table.

Example

STDMETHODIMP CCalc::CDisp::Invoke(

    DISPID dispidMember,

    REFIID riid,

    LCID lcid,

    unsigned short wFlags,

    DISPPARAMS FAR* pdispparams,

    VARIANT FAR* pvarResult,

    EXCEPINFO FAR* pexcepinfo,

    unsigned int FAR* puArgErr)

{

    if(riid != IID_NULL)

      return ReportResult(0, DISP_E_UNKNOWNINTERFACE, 0, 0);

    if(lcid != m_pcalc->m_lcid)

      return ReportResult(0, DISP_E_UNKNOWNLCID, 0, 0);

    return DispInvoke(

      &m_pcalc->m_arith,

      m_pcalc->m_ptinfo,

      dispidMember, wFlags, pdispparams,

      pvarResult, pexcepinfo, puArgErr);

}

See Also

CreateStdDispatch, IDispatch::Invoke


Active Object Registration Functions

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose or access programmable objects	OLEAUTO.H	OLEAUT32.LIB
These functions let you identify a running instance of an object. Because they use the OLE object table (GetRunningObjectTable), they require OLE32.DLL to be loaded.

When an application is started with the /Automation switch, it should initialize its Application object as the active object by calling RegisterActiveObject, after it initializes OLE (OleInitialize).

Applications may also initialize other top-level objects as the active object. For example, an application that exposes a document object may want to let OLE Automation controllers retrieve and modify the currently active document.

For more information on initializing the active object, see Exposing OLE Automation Objects.

This section includes the following functions:

GetActiveObject
RegisterActiveObject
RevokeActiveObject


GetActiveObject

HRESULT GetActiveObject(rclsid, pvreserved, ppunk)
REFCLSID rclsid
void FAR* pvreserved
IUnknown FAR* FAR* ppunk

Retrieves a pointer to a running application initialized with OLE.

Parameters

rclsid

Pointer to the class ID of the active object from the OLE registration database.

pvreserved

Reserved for future use. Must be NULL.

ppunk

On return, a pointer to the requested active object.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
Other returns	Failure


RegisterActiveObject

HRESULT RegisterActiveObject (punk, rclsid, pvreserved, pdwRegister)
IUnknown FAR* punk
REFCLSID rclsid
void FAR* pvreserved
unsigned long FAR* pdwRegister

Registers an object (punk) as the active object for the class indicated by rclsid.

Parameters

punk

Pointer to the IUnknown interface of the active object.

rclsid

Pointer to the class ID of the active object.

pvreserved

Reserved for future use. Must be NULL.

pdwRegister

Pointer to a handle used when initializing and revoking an active object. This handle is created by RegisterActiveObject and passed to RevokeActiveObject when you want to end an object's status as active.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
Other returns	Failure


RevokeActiveObject

HRESULT RevokeActiveObject (dwRegister, pvreserved)
unsigned long dwRegister
void FAR* pvreserved

Ends an object's status as active.

Parameters

dwRegister

A handle previously returned by RegisterActiveObject.

pvreserved

Reserved for future use. Must be NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
Other returns	Failure


IEnumVARIANT Interface

Implemented by	Used by	Header filename	Import library name
Applications that expose collections of objects	Applications that access collections of objects	OLEAUTO.H	None
The IEnumVARIANT interface provides a method for enumerating a collection of variants. It can be used to enumerate over heterogeneous collections of objects and intrinsic types. Similarly, IEnumVARIANT allows enumerating by clients that can't know (or do not need to know) the specific type, or types, of the elements in the collection.

The following is the definition that results from expanding the parameterized type IEnumVARIANT:

 interface IEnumVARIANT : IUnknown {

virtual HRESULT Next(unsigned long celt,
VARIANT FAR* rgvar,
unsigned long FAR* pceltFetched) = 0;

virtual HRESULT Skip(unsigned long celt) = 0;

virtual HRESULT Reset() = 0;

virtual HRESULT Clone(IEnumVARIANT FAR* FAR* ppenum) = 0;

};

See the sample file CENUMPT.CPP in SPOLY or SPOLY2 for an example of how to implement a collection of objects using IEnumVARIANT.


IEnumVARIANT::Clone

HRESULT IEnumVARIANT::Clone(ppenum)
IEnumVARIANT FAR* FAR* ppenum

Creates a copy of the current enumeration state.

Parameter

ppenum

On return, points to the location of the clone enumerator.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_OUTOFMEMORY	Insufficient memory to complete the operation.
Comments

Using this function, you can record a particular point in the enumeration sequence, then return to it at a later time. Notice that the enumerator returned is of the same actual interface as the one that is being cloned.

There is no guarantee that exactly the same set of VARIANTs will be enumerated the second time as was enumerated the first. Although an exact duplicate is desirable, the outcome depends on the collection being enumerated. Some collections (for example, an enumeration of the files in a directory) will simply find it too impractical to maintain this condition.

Example

STDMETHODIMP CEnumPoint::Clone(IEnumVARIANT FAR* FAR* ppenum)

{

    HRESULT hresult;

    SAFEARRAY FAR* psa;

    CEnumPoint FAR* penum;

    hresult = SafeArrayCopy(m_psa, &psa);

    if(FAILED(hresult))

      return hresult;

    hresult = CEnumPoint::Create(psa, &penum);

    if(FAILED(hresult))

      goto LError0;

    // Assert(penum->m_celts == m_celts);

    penum->m_iCurrent = m_iCurrent;

     *ppenum = penum;

return NOERROR;

LError0:

    SafeArrayDestroy(psa);

    return hresult;

}


IEnumVARIANT::Next

HRESULT IEnumVARIANT::Next(celt, rgvar, pceltFetched)
unsigned long celt
VARIANT FAR* rgvar
unsigned long FAR* pceltFetched

Attempts to get the next celt items in the enumeration sequence and return them through the array pointed to by rgvar.

Parameter

celt

The number of elements to be returned.

rgvar

An array of at least size celt in which the elements are to be returned.

pceltFetched

Pointer to the number of elements returned in rgvar, or NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	The number of elements returned is celt.
S_FALSE	The number of elements returned by is less than celt.
Comments

If fewer than the requested number of elements remain in the sequence, return only the remaining elements; the actual number of elements returned is passed through *pceltFetched (unless it is NULL).

Example

STDMETHODIMP CEnumPoint::Next(

    unsigned long celt,

    VARIANT FAR* rgvar,

    unsigned long FAR* pceltFetched)

{

    unsigned int i;

    long ix;

    HRESULT hresult;

    for(i = 0; i < celt; ++i)

      VariantInit(&rgvar[i]);

    for(i = 0; i < celt; ++i){

      if(m_iCurrent == m_celts){

        hresult = ResultFromScode(S_FALSE);

goto LDone;

      }

      ix = m_iCurrent++;

      hresult = SafeArrayGetElement(m_psa, &ix, &rgvar[i]);

      if(FAILED(hresult))

goto LError0;

    }

    hresult = NOERROR;

LDone:;

 if (pceltFetched != NULL)

*pceltFetched = i;

return hresult;

LError0:;

    for(i = 0; i < celt; ++i)

      VariantClear(&rgvar[i]);

    return hresult;

}


IEnumVARIANT::Reset

HRESULT IEnumVARIANT::Reset()

Resets the enumeration sequence to the beginning.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
S_FALSE	Failure
Comments

There is no guarantee that exactly the same set of VARIANTs will be enumerated the second time as was enumerated the first. Although an exact duplicate is desirable, the outcome depends on the collection being enumerated. Some collections (for example, an enumeration of the files in a directory) will simply find it too impractical to maintain this condition.

Example

STDMETHODIMP CEnumPoint::Reset()

{

    m_iCurrent = 0;

    return NOERROR;

}


IEnumVARIANT::Skip

HRESULT IEnumVARIANT::Skip(celt)
unsigned long celt

Attempts to skip over the next celt elements in the enumeration sequence.

Parameter

celt

The number of elements to be skipped.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	The specified number of elements was skipped.
S_FALSE	The end of the sequence was reached first.
Example

STDMETHODIMP CEnumPoint::Skip(unsigned long celt)

{

    m_iCurrent += celt;

    if(m_iCurrent > m_celts)

     m_iCurrent = m_celts;

    return (m_iCurrent == m_celts)

      ? ResultFromScode(S_FALSE) : NOERROR;

}


Data Manipulation Functions

The data manipulation functions access and manipulate the arrays, strings, and variant types of data used by OLE Automation.

The following topics are included in this section:

Overview of Functions
Array Manipulation Functions
Data Types and Structures
String Manipulation Functions
Variant Manipulation Functions
Low-Level Variant Conversion Functions
Time Conversion Functions


Overview of Functions

The data manipulation functions are summarized in the following table. All of these functions are provided in OLEAUT32.DLL. The header file is OLEAUTO.H and the import library is OLEAUT32.LIB.

Category	Function name	Purpose
Array manipulation	SafeArrayAccessData	Increments the lock count of an array and returns a pointer to array data.
	SafeArrayAllocData	Allocates memory for a safe array based on a descriptor created with SafeArrayAllocDescriptor.
	SafeArrayAllocDescriptor	Allocates memory for a safe array descriptor.
	SafeArrayCopy	Copies an existing array.
	SafeArrayCreate	Creates a new array descriptor.
	SafeArrayDestroy	Destroys an array descriptor.
	SafeArrayDestroyData	Frees memory used by the data elements in a safe array.
	SafeArrayDestroyDescriptor	Frees memory used by a safe array descriptor.
	SafeArrayGetDim	Returns the number of dimensions in an array.
	SafeArrayGetElement	Retrieves an element of an array.
	SafeArrayGetElemsize	Returns the size of an element.
	SafeArrayGetLBound	Retrieves the lower bound for a given dimension.
	SafeArrayGetUBound	Retrieves the upper bound for a given dimension.
	SafeArrayLock	Increments the lock count of an array.
	SafeArrayPutElement	Assigns an element into an array.
	SafeArrayRedim	Resizes a safe array.
	SafeArrayUnAccessData	Frees a pointer to array data and decrements the lock count of the array.
	SafeArrayUnlock	Decrements the lock count of an array.
String manipulation	SysAllocString	Creates and initializes a string.
	SysAllocStringByteLen	Creates a zero-terminated string of  a specified length (Win32 only).
	SysAllocStringLen	Creates a string of a specified length.
	SysFreeString	Frees a previously created string.
	SysReAllocString	Changes the size and value of a string.
	SysReAllocStringLen	Changes the size of an existing string.
	SysStringByteLen	Returns the length of a string in bytes (Win32 only).
	SysStringLen	Returns the length of a string.
Variant manipulation	VariantChangeType	Converts a variant to another type.
	VariantChangeTypeEx	Converts a variant to another type using a locale ID.
	VariantClear	Releases resources and sets a variant to VT_EMPTY.
	VariantCopy	Copies a variant.
	VariantCopyInd	Copies variants that may contain a pointer.
	VariantInit	Initializes a variant.
	Low-level conversion functions	Convert specific types of variants to other variant types. These functions are called by VariantChangeType and VariantChangeTypeEx.
Time conversion	DosDateTimeToVariantTime	Converts MS-DOS date and time representations to a variant time.
	VariantTimeToDosDateTime	Converts a variant time to  MS-DOS date and time representations.


Array Manipulation Functions

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose or access programmable objects	OLEAUTO.H	OLEAUT32.LIB
The arrays passed by IDispatch::Invoke within VARIANTARGs are called safe arrays. Safe arrays contain information about the number of dimensions and bounds within them. When an array is an argument or the return value of a function, the parray field of VARIANTARG points to an array descriptor. This array descriptor should not be accessed directly unless you are creating arrays containing elements with nonvariant data types. Instead, use the functions SafeArrayAccessData and SafeArrayUnaccessData to access the data.

The base type of the array is indicated by VT_ tag | VT_ARRAY. The data referred to by an array descriptor is stored in column-major order, which is the same ordering scheme used by Visual Basic and FORTRAN, but different from C and Pascal. Column-major order means that the leftmost dimension (as specified in a programming language syntax) changes first.

Following are the definitions of the safe array descriptor and the functions to use when accessing the data in the descriptor and the array itself.

Data Types and Structures
SAFEARRAY
SAFEARRAYBOUND
SafeArrayAccessData
SafeArrayAllocData
SafeArrayAllocDescriptor
SafeArrayCopy
SafeArrayCreate
SafeArrayDestroy
SafeArrayDestroyData
SafeArrayDestroyDescriptor
SafeArrayGetDim
SafeArrayGetElement
SafeArrayGetElemsize
SafeArrayGetLBound
SafeArrayGetUBound
SafeArrayLock
SafeArrayPutElement
SafeArrayRedim
SafeArrayUnaccessData
SafeArrayUnlock


Data Types and Structures

SAFEARRAY
SAFEARRAYBOUND


SAFEARRAY

typedef struct FARSTRUCT tagSAFEARRAY {

    unsigned short cDims;     // Count of dimensions in this array.

    unsigned short fFeatures; // Flags used by the SafeArray

                              // routines documented below:

#if defined(WIN32)

    // Size of an element of the array;

    // does not include size of pointed-to data.

    unsigned long cbElements;

    // Number of times the array has been locked
    // without corresponding unlock.

    unsigned long cLocks;

#else

    unsigned short cbElements;

    unsigned short cLocks;

    unsigned long handle;       // Unused but kept for compatibility

#endif

    void HUGEP* pvData;         // Pointer to the data.

    SAFEARRAYBOUND rgsabound[1];// One bound for each dimension.

} SAFEARRAY;

Note that the definition varies depending on the target operating system platform. On Windows NT Workstations, both the cbElements and cLocks parameters are unsigned long integers, and the handle parameter is omitted. On other platforms, cbElements and cLocks are unsigned short integers, and the handle parameter has been retained for compatibility with earlier software.

The array rgsabound is stored with the leftmost dimension in rgsabound[0] and the rightmost dimension in rgsabound[cDims − 1]. If an array were specified in C-like syntax as a[2][5], it would have two elements in the rgsabound vector. Element 0 has an lLbound of 0 and a cElements of 2. Element 1 has an lLbound of 0 and a cElements of 5.

The fFeatures flags describe attributes of an array that may affect how the array is released. This allows freeing the array without referencing its containing variant. The bits are accessed using the following constants:

#define FADF_AUTO      0x0001 // Array is allocated on the stack

#define FADF_STATIC    0x0002 // Array is statically allocated

#define FADF_EMBEDDED  0x0004 // Array is embedded in a structure

#define FADF_FIXEDSIZE 0x0010 // Array may not be resized or

                              // reallocated

#define FADF_BSTR      0x0100 // An array of BSTRs

#define FADF_UNKNOWN   0x0200 // An array of IUnknown*

#define FADF_DISPATCH  0x0400 // An array of IDispatch*

#define FADF_VARIANT   0x0800 // An array of VARIANTs

#define FADF_RESERVED  0xF0E8 // Bits reserved for future use

For more information, see Accessing OLE Automation Objects.


SAFEARRAYBOUND

typedef struct tagSAFEARRAYBOUND {

    unsigned long cElements;

    long lLbound;

} SAFEARRAYBOUND;

This structure represent the bounds of one dimension of the array. The lower bound of the dimension is represented by lLbound, and cElements represents the number of elements in the dimension.


SafeArrayAccessData

HRESULT SafeArrayAccessData (psa, ppvdata)
SAFEARRAY FAR* psa
void HUGEP* FAR* ppvdata

Increments the lock count of an array and retrieves a pointer to the array data.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

ppvdata

On exit, points to the pointer to the array data. Note that arrays may be larger than 64K, so huge pointers must be used in Windows version 3.1 or later.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be locked.


SafeArrayAllocData

HRESULT SafeArrayAllocData(psa)
SAFEARRAY FAR* psa

Allocates memory for a safe array based on a descriptor created with SafeArrayAllocDescriptor.

Parameters

psa

Points to an array descriptor created by SafeArrayAllocDescriptor.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be locked.
Example

The following example creates a safe array using the SafeArrayAllocDescriptor and SafeArrayAllocData functions.

SAFEARRAY FAR* FAR* ppsa;

unsigned int ndim =  2;

HRESULT hresult = SafeArrayAllocDescriptor(

                          ndim, ppsa);

if(FAILED(hresult))

    return ERR_OutOfMemory;

(*ppsa)->rgsabound[0].lLbound = 0;

(*ppsa)->rgsabound[0].cElements = 5;

(*ppsa)->rgsabound[1].lLbound = 1;

(*ppsa)->rgsabound[1].cElements = 4;

hresult = SafeArrayAllocData(*ppsa);

if(FAILED(hresult)) {

    SafeArrayDestroyDescriptor(*ppsa)

    return ERR_OutOfMemory;

}

See Also

SafeArrayAllocData, SafeArrayDestroyData, SafeArrayDestroyDescriptor


SafeArrayAllocDescriptor

HRESULT SafeArrayAllocDescriptor(cDims, ppsaOut)
unsigned int cDims
SAFEARRAY FAR* FAR*  ppsaOut

Allocates memory for a safe array descriptor.

Parameters

cDims

The number of dimensions of the array.

ppsaOut

Points to a location in which to store the created array descriptor.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be locked.
Comments

This function lets you create safe arrays containing elements with data types other than those provided by SafeArrayCreate. After creating an array descriptor using SafeArrayAllocDescriptor, set the element size in the array descriptor and call SafeArrayAllocData to allocate memory for the array elements.

Example

The following example creates a safe array using the SafeArrayAllocDescriptor and SafeArrayAllocData functions.

SAFEARRAY FAR * FAR * ppsa;

unsigned int ndim =  2;

HRESULT hresult = SafeArrayAllocDescriptor(

                          ndim, ppsa);

if(FAILED(hresult))

    return ERR_OutOfMemory;

(*ppsa)->rgsabound[0].lLbound = 0;

(*ppsa)->rgsabound[0].cElements = 5;

(*ppsa)->rgsabound[1].lLbound = 1;

(*ppsa)->rgsabound[1].cElements = 4;

hresult = SafeArrayAllocData(*ppsa);

if(FAILED(hresult)) {

    SafeArrayDestroyDescriptor(*ppsa)

    return ERR_OutOfMemory;

}

See Also

SafeArrayAllocData, SafeArrayDestroyData, SafeArrayDestroyDescriptor


SafeArrayCopy

HRESULT SafeArrayCopy(psa, ppsaOut)
SAFEARRAY FAR*  psa
SAFEARRAY FAR* FAR* ppsaOut

Creates a copy of an existing safe array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

ppsaOut

Points to a location in which to return the new array descriptor.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_OUTOFMEMORY	There was insufficient memory to create the copy.
Comments

SafeArrayCopy calls the string or variant manipulation functions if the array to copy contains either of those data types. If the array being copied contains object references, the reference counts for those objects are incremented.

See Also

SysAllocStringLen, VariantCopy, VariantCopyInd


SafeArrayCreate

SAFEARRAY * SafeArrayCreate(vt, cDims, rgsabounds)
VARTYPE vt
unsigned int cDims
SAFEARRRAYBOUND FAR* rgsabound

Creates a new array descriptor, allocates and initializes the data for the array, and returns a pointer to the new array descriptor.

Parameters

vt

The base type of the array (that is, the VARTYPE of each element of the array). The VARTYPE is restricted to a subset of the variant types. Neither the VT_ARRAY nor the VT_BYREF flag can be set. VT_EMPTY and VT_NULL are not valid base types for the array. All other types are legal.

cDims

Number of dimensions in the array. This cannot be changed after the array is created.

rgsabound

Points to a vector of bounds (one for each dimension) to allocate for the array.

Return Value

Points to the array descriptor, or NULL if the array could not be created.

Example

HRESULT PASCAL __export

    CPoly::EnumPoints(IEnumVARIANT FAR* FAR* ppenum)

{

    unsigned int i;

    HRESULT hresult;

    VARIANT var;

    SAFEARRAY FAR* psa;

    CEnumPoint FAR* penum;

    POINTLINK FAR* ppointlink;

    SAFEARRAYBOUND rgsabound[1];

    rgsabound[0].lLbound = 0;

    rgsabound[0].cElements = m_cPoints;

    psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);

    if(psa == NULL){

      hresult = ReportResult(0, E_OUTOFMEMORY, 0, 0);

      goto LError0;

    }

.

.    // Code omitted here.

.

LError0:;

    return hresult;

}


SafeArrayDestroy

HRESULT SafeArrayDestroy(psa)
SAFEARRAY FAR* psa

Destroys an existing array descriptor and all the data in the array. If objects are stored in the array, Release is called on each object in the array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The array is currently locked.
E_INVALIDARG	The item pointed to by psa is not a safe array descriptor.
Example

STDMETHODIMP_(ULONG) CEnumPoint::Release()

{

    if(--m_refs == 0){

      if(m_psa != NULL)

        SafeArrayDestroy(m_psa);

      delete this;

      return 0;

    }

    return m_refs;

}


SafeArrayDestroyData

HRESULT SafeArrayDestroyData(psa)
SAFEARRAY FAR* psa

Destroys all the data in a safe array. If objects are stored in the array, Release is called on each object in the array.

Parameters

psa

Points to an array descriptor.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The array is currently locked.
E_INVALIDARG	The item pointed to by psa is not a safe array descriptor.
Comments

This function is typically used when freeing safe arrays that contain elements with data types other than variants.

See Also

SafeArrayAllocDataSafeArrayAllocDescriptor,
SafeArrayDestroyDescriptor


SafeArrayDestroyDescriptor

HRESULT SafeArrayDestroyDescriptor(psa)
SAFEARRAY FAR* psa

Destroys a descriptor of a safe array.

Parameters

psa

Points to a safe array descriptor.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The array is currently locked.
E_INVALIDARG	The item pointed to by psa is not a safe array descriptor.
Comments

This function is typically used to destroy the descriptor of a safe array that contains elements with data types other than variants. Destroying the array descriptor does not destroy the elements in the array. Call SafeArrayDestroyData to free the elements before destroying the array descriptor.

See Also

SafeArrayAllocData,
SafeArrayAllocDescriptor,
SafeArrayDestroyData


SafeArrayGetDim

unsigned int SafeArrayGetDim(psa)
SAFEARRAY FAR* psa

Returns the number of dimensions in the array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

Return Value

Returns the number of dimensions in the array.

Example

HRESULT

CEnumPoint::Create(

    SAFEARRAY FAR* psa,

    CEnumPoint FAR* FAR* ppenum)

{

    long lBound;

    HRESULT hresult;

    CEnumPoint FAR* penum;

    // Verify that the SafeArray is the proper shape.

    if(SafeArrayGetDim(psa) != 1)

      return ReportResult(0, E_INVALIDARG, 0, 0);

.

.    // Code omitted here.

.

}


SafeArrayGetElement

HRESULT SafeArrayGetElement(psa, rgIndices, pvData)
SAFEARRAY FAR* psa
long FAR* rgIndices
void FAR* pvData

Retrieves a single element of the array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

rgIndices

Points to a vector of indexes for each dimension of the array. The rightmost (least-significant) dimension is rgIndices[0]. The leftmost dimension is stored at rgIndices[psa->cDims − 1].

pvData

Points to the location to place the element of the array.

Comments

This function automatically calls SafeArrayLock and SafeArrayUnlock before and after retrieving the element. The caller must provide a storage area of the correct size to receive the data. If the data element is a string, object, or variant, a copy of the element is made in the correct way.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADINDEX	The specified index is invalid.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Memory could not be allocated for the element.
Example

STDMETHODIMP CEnumPoint::Next(

    ULONG celt,

    VARIANT FAR rgvar[],

    ULONG FAR* pceltFetched)

{

    unsigned int i;

    long ix;

    HRESULT hresult;

    for(i = 0; i < celt; ++i)

      VariantInit(&rgvar[i]);

    for(i = 0; i < celt; ++i) {

      if (m_iCurrent == m_celts) {

        hresult = ReportResult(0, S_FALSE, 0, 0);

        goto LDone;

      }

      ix = m_iCurrent++;

      hresult = SafeArrayGetElement(

                m_psa, &ix, &rgvar[i]);

      if(FAILED(hresult))

        goto LError0;

    }

    hresult = NOERROR;

LDone:;

    *pceltFetched = i;

    return hresult;

LError0:;

    for(i = 0; i < celt; ++i)

      VariantClear(&rgvar[i]);

    return hresult;

}


SafeArrayGetElemsize

unsigned int SafeArrayGetElemsize(psa)
SAFEARRAY FAR* psa

Returns the size, in bytes, of the elements of a safe array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.


SafeArrayGetLBound

HRESULT SafeArrayGetLBound(psa, nDim, plLbound)
SAFEARRAY FAR* psa
unsigned int nDim
long FAR* plLbound

Returns the lower bound for any dimension of a safe array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

nDim

The array dimension for which to get the lower bound.

plLbound

Points to the location to return the lower bound.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADINDEX	The specified index is out of bounds.
E_INVALIDARG	One of the arguments is invalid.
Example

HRESULT

CEnumPoint::Create(

    SAFEARRAY FAR* psa,

    CEnumPoint FAR* FAR* ppenum)

{

    long lBound;

    HRESULT hresult;

    CEnumPoint FAR* penum;

    // Verify that the SafeArray is the proper shape.

    hresult = SafeArrayGetLBound(psa, 1, &lBound);

    if(FAILED(hresult))

      return hresult;

.

.    // Code omitted here.

.

}


SafeArrayGetUBound

HRESULT SafeArrayGetUBound(psa, nDim, plUbound)
SAFEARRAY FAR* psa
unsigned int nDim
long FAR* plUbound

Returns the upper bound for any dimension of a safe array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate().

nDim

The array dimension for which to get the upper bound.

plUbound

Points to the location to return the upper bound.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADINDEX	The specified index is out of bounds.
E_INVALIDARG	One of the arguments is invalid.
Example

HRESULT

CEnumPoint::Create(

    SAFEARRAY FAR* psa,

    CEnumPoint FAR* FAR* ppenum)

{

    long lBound;

    HRESULT hresult;

    CEnumPoint FAR* penum;

    // Verify that the SafeArray is the proper shape.

    hresult = SafeArrayGetUBound(psa, 1, &lBound);

    if(FAILED(hresult))

      goto LError0;

.

.    // Code omitted

.

LError0:;

    penum->Release();

    return hresult;

}


SafeArrayLock

HRESULT SafeArrayLock(psa)
SAFEARRAY FAR* psa

Increments the lock count of an array and places a pointer to the array data in pvData of the array descriptor.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

Comments

The pointer in the array descriptor is valid until SafeArrayUnlock is called. Calls to SafeArrayLock can be nested; an equal number of calls to SafeArrayUnlock are required.

An array cannot be deleted while it is locked.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be locked.


SafeArrayPutElement

HRESULT SafeArrayPutElement(psa, rgIndices, pvData)
SAFEARRAY FAR* psa
long FAR* rgIndices
void FAR* pvData

Assigns a single element into the array.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

rgIndices

Points to a vector of indexes for each dimension of the array. The rightmost (least significant) dimension is rgIndices[0]. The leftmost dimension is stored at rgIndices[psa->cDims − 1].

pvData

Points to the data to assign into the array. VT_DISPATCH, VT_UNKNOWN, and VT_BSTR variant types are pointers and do not require another level of indirection.

Comments

This function automatically calls SafeArrayLock and SafeArrayUnlock before and after assigning the element. If the data element is a string, object, or variant, a copy of the element is made correctly. If the existing element is a string, object or variant, it is cleared correctly.

Note that you can have multiple locks on an array, so you can put elements into an array while the array is locked by other operations.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADINDEX	The specified index was invalid.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Memory could not be allocated for the element.
Example

HRESULT PASCAL __export CPoly::EnumPoints(

    IEnumVARIANT FAR* FAR* ppenum)

{

    unsigned int i;

    HRESULT hresult;

    VARIANT var;

    SAFEARRAY FAR* psa;

    CEnumPoint FAR* penum;

    POINTLINK FAR* ppointlink;

    SAFEARRAYBOUND rgsabound[1];

    rgsabound[0].lLbound = 0;

    rgsabound[0].cElements = m_cPoints;

    psa = SafeArrayCreate(

            VT_VARIANT, 1, rgsabound);

.

.    // Code omitted here.

.

      V_VT(&var) = VT_DISPATCH;

      hresult =

        ppointlink->ppoint->QueryInterface(

            IID_IDispatch, (void FAR* FAR*)&V_DISPATCH(&var));

      if(hresult != NOERROR)

        goto LError1;

      ix[0] = i;

      SafeArrayPutElement(psa, ix, &var);

      ppointlink = ppointlink->next;

    }

    hresult = CEnumPoint::Create(psa, &penum);

    if(hresult != NOERROR)

      goto LError1;

    *ppenum = penum;

    return NOERROR;

LError1:;

    SafeArrayDestroy(psa);

LError0:;

    return hresult;

}


SafeArrayRedim

HRESULT SafeArrayRedim(psa, psaboundNew)
SAFEARRAY FAR* psa
SAFEARRAYBOUND FAR* psaboundNew

Changes the least significant (rightmost) bound of a safe array.

Parameters

psa

Points to an array descriptor.

psaboundNew

Points to a new safe array bound structure containing the new array bound. Only the least significant dimension of an array may be changed.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The array is currently locked.
E_INVALIDARG	The item pointed to by psa is not a safe array descriptor.
Comments

If you reduce the bound of an array, SafeArrayRedim deallocates the array elements outside the new array boundary. If you increase the bound of an array, SafeArrayRedim allocates and initializes the new array elements. The data is preserved for elements that exist in both the old and the new array.


SafeArrayUnaccessData

HRESULT SafeArrayUnaccessData(psa)
SAFEARRAY FAR*  psa

Decrements the lock count of an array and invalidates the pointer retrieved by SafeArrayAccessData.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be unlocked.


SafeArrayUnlock

HRESULT SafeArrayUnlock(psa)
SAFEARRAY FAR* psa

Decrements the lock count of an array so it can be freed or resized.

Parameters

psa

Points to an array descriptor created by SafeArrayCreate.

Comments

This function is called after access to the data in an array is finished.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
E_UNEXPECTED	The array could not be unlocked.


String Manipulation Functions

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose or access programmable objects	OLEAUTO.H	OLEAUT32.LIB
To handle strings that are allocated by one component and freed by another, OLE Automation defines a special set of functions. These functions use the following data type:

typedef OLECHAR FAR* BSTR;

These strings are zero-terminated, and in most cases they can be treated just like CHAR * strings. However, a BSTR can be queried for its length rather than scanned, so it may contain embedded null characters. The length is stored as an integer at the memory location preceding the data in the string. Instead of reading this location directly, applications should use the supplied functions to access the length of a BSTR. Because BSTR variables may contain multiple null characters, they can be used to pass binary data.

A NULL pointer is a valid value for a BSTR variable; by convention it is always treated the same as a pointer to a BSTR that contains zero characters. Also, by convention, calls to functions that take a BSTR reference parameter must pass either a NULL pointer or a pointer to an allocated BSTR. If the implementation of a function that takes a BSTR reference parameter assigns a new BSTR to the parameter, it must free the previously referenced BSTR.

This section includes the following functions:

SysAllocString
SysAllocStringByteLen
SysAllocStringLen
SysFreeString
SysReAllocString
SysReAllocStringLen
SysStringByteLen
SysStringLen


SysAllocString

BSTR SysAllocString(sz)
OLECHAR FAR* sz

Allocates a new string and copies the passed string into it. Returns NULL if insufficient memory exists or if NULL is passed in.

Parameter

sz

A zero-terminated string to copy.

Return Value

If successful, points to a BSTR containing the string. If insufficient memory exists or sz was NULL, returns NULL.

Strings created with SysAllocString should be freed with SysFreeString.

Example

inline void CStatBar::SetText(OLECHAR FAR* sz)

{

    SysFreeString(m_bstrMsg);

    m_bstrMsg = SysAllocString(sz);

}


SysAllocStringByteLen

BSTR SysAllocStringByteLen(psz, len)
char FAR*  psz
unsigned int len

Allocates a new string of len bytes, copies len bytes from the passed string into it, and then appends a null character. Valid for Win32 systems only.

Parameter

psz

A zero-terminated string to copy, or NULL to keep the string uninitialized.

len

Number of bytes to copy from psz. A null character is placed afterwards, making a total of len+1 bytes allocated.

Return Value

Points to a copy of the string or NULL, if insufficient memory exists.

Comments

If psz is NULL, a string of the requested length is allocated, but not initialized. The string psz can contain embedded null characters and need not end with a null. The returned string should later be freed with SysFreeString.


SysAllocStringLen

BSTR SysAllocStringLen(pch, cch)
OLECHAR FAR* pch
unsigned int cch

Allocates a new string, copies cch characters from the passed string into it, and then appends a null character.

Parameter

pch

A pointer to cch characters to copy, or NULL to keep the string uninitialized.

cch

Number of characters to copy from pch*. A null character is placed afterwards, making a total of cch+1 characters allocated.

Return Value

Points to a copy of the string or NULL, if insufficient memory exists.

Comments

If pch is NULL, a string of the requested length is allocated, but not initialized. The string pch can contain embedded null characters and need not end with a null. The returned string should later be freed with SysFreeString.


SysFreeString

void SysFreeString(bstr)
BSTR bstr

Frees a string previously allocated by SysAllocString, SysAllocStringByteLen, SysReAllocString, SysAllocStringLen, or SysReAllocStringLen.

Parameter

bstr

A BSTR allocated earlier or NULL. If NULL, the function simply returns.

Return Value

None.

Example

CStatBar::~CStatBar()

{

    SysFreeString(m_bstrMsg);

}


SysReAllocString

BOOL SysReAllocString(pbstr, sz)
BSTR FAR* pbstr
OLECHAR FAR* sz

Allocates a new BSTR and copies the passed string into it, then frees the BSTR currently referenced by pbstr and resets pbstr to point to the new BSTR. Returns False if insufficient memory exists.

Parameter

pbstr

Points to a variable containing a BSTR.

sz

A zero-terminated string to copy.

Return Value

Returns False if insufficient memory exists.


SysReAllocStringLen

BOOL SysReAllocStringLen(pbstr, pch, cch)
BSTR FAR* pbstr
OLECHAR FAR* pch
unsigned int cch

Creates a new BSTR containing a specified number of characters from an old BSTR and frees the old BSTR.

Parameter

pbstr

Points to a variable containing a BSTR.

pch

Points to cch characters to copy, or NULL to keep the string uninitialized.

cch

Number of characters to copy from pch*. A null character is placed afterwards, making a total of cch+1 characters allocated.

Return Value

Returns True if the string is successfully reallocated, or False if insufficient memory exists.

Comments

Allocates a new string, copies cch characters from the passed string into it, and then appends a null character. Frees the BSTR currently referenced by pbstr and resets pbstr to point to the new BSTR. If pch is NULL, a string of length cch is allocated, but not initialized.

The string pch can contain embedded null characters and need not end with a null.


SysStringByteLen

unsigned int SysStringByteLen(bstr)
BSTR bstr

Returns the length in bytes of a BSTR. Valid for Win32 systems only.

Parameter

bstr

A BSTR previously allocated. It cannot be NULL.

Return Value

The number of bytes in bstr, not including a terminating null character.

Comments

The value returned may be different from fstrlen(bstr), if the BSTR was allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, and the passed-in characters included a null character in the first len characters. For a BSTR allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns the number of bytes specified in the len parameter at allocation time.

Example

// Draw the status message

//

TextOut(

  hdc,

  rcMsg.left + (m_dxFont / 2),

  rcMsg.top +

        ((rcMsg.bottom - rcMsg.top - m_dyFont) / 2),

  m_bstrMsg, SysStringByteLen(m_bstrMsg));


SysStringLen

unsigned int SysStringLen(bstr)
BSTR bstr

Returns the length of a BSTR.

Parameter

bstr

A BSTR previously allocated. It cannot be NULL.

Return Value

The number of characters in bstr, not including a terminating null character.

Comments

The value returned may be different from _fstrlen(bstr), if the BSTR was allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, and the passed-in characters included a null character in the first cch characters. For a BSTR allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns the number of characters specified in the cch parameter at allocation time.

Example

// Draw the status message

//

TextOut(

  hdc,

  rcMsg.left + (m_dxFont / 2),

  rcMsg.top +

        ((rcMsg.bottom - rcMsg.top - m_dyFont) / 2),

  m_bstrMsg, SysStringLen(m_bstrMsg));


Variant Manipulation Functions

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose or access programmable objects	OLEAUTO.H	OLEAUT32.LIB
These functions are provided to allow applications to manipulate VARIANTARG variables. Applications that implement IDispatch should test each VARIANTARG for all permissible types by attempting to coerce the variant to each type (using VariantChangeType or VariantChangeTypeEx). If objects are allowed, the application should always test for object types before other types. If an object type is expected, the application must test (via IUnknown::QueryInterface) whether the object is of the type desired.

Although applications can access and interpret the VARIANTARGs without these functions, using them ensures uniform conversion and coercion rules for all implementors of IDispatch. (For example, these functions automatically coerce numeric arguments to strings and vice versa, when necessary.)

Because variants can contain strings, references to scalars, references to objects and arrays, certain data ownership rules must be obeyed. All the functions described in this section conform to the following rules:

1.	All VARIANTARGs must be initialized using VariantInit before use.

2.	For the types VT_I2, VT_I4, VT_R4, VT_R8, VT_BOOL, VT_ERROR, VT_CY, VT_DATE, data is stored within the VARIANT structure. Any pointers to the data become invalid when the type of the variant is changed.

3.	For VT_BYREF | any type, the memory pointed to by the variant is owned and freed by the caller of the function.

4.	For VT_BSTR, there is only one owner for the string. All strings in variants must be allocated with the SysAllocString function. When releasing or changing the type of a variant with the VT_BSTR type, SysFreeString is called on the contained string.

5.	For VT_ARRAY | any type, the rule is analogous to the rule for VT_BSTR. All arrays in variants must be allocated with SafeArrayCreate. When releasing or changing the type of a variant with the VT_ARRAY flag set, SafeArrayDestroy is called.

6.	For VT_DISPATCH and VT_UNKNOWN, the pointed-to objects have reference counts that are incremented when they are placed in a variant. When releasing or changing the type of the variant, Release is called on the pointed-to object.

This section includes the following functions:

VariantChangeType
VariantChangeTypeEx
VariantClear
VariantCopy
VariantCopyInd
VariantInit


VariantChangeType

HRESULT VariantChangeType(pvargDest, pvargSrc, wFlags, vtNew)
VARIANTARG FAR* pvargDest
VARIANTARG FAR* pvargSrc
unsigned short wFlags
VARTYPE vtNew

This function converts a variant from one type to another.

Parameter

pvargDest

Points to the VARIANTARG to receive the coerced type. This may be the same as pvargSrc to convert in place.

pvargSrc

Points to the source VARIANTARG to be coerced.

wFlags

Flags that control the coercion. The only defined flag is VARIANT_NOVALUEPROP, which prevents the function from attempting to coerce an object to a fundamental type by getting the Value property. Applications should set this flag only if necessary, because it makes the behavior inconsistent with other applications.

vtNew

The type to coerce to. If the return code is S_OK, the vt field of the *pvargDest is guaranteed to be equal to this value.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADVARTYPE	The variant type vtNew is not a valid type of variant.
DISP_E_OVERFLOW	The data pointed to by pvargSrc does not fit in the destination type.
DISP_E_TYPEMISMATCH	The argument could not be coerced to the specified type.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Memory could not be allocated for the conversion.
Comments

The VariantChangeType function handles coercions between the fundamental types (including numeric-to-string and string-to-numeric coercions). A variant that has VT_BYREF set is coerced to a value by fetching the referenced value. An object is coerced to a value by invoking the object's Value property (DISPID_VALUE).

Typically, the implementor of IDispatch::Invoke determines which member is being accessed, then calls VariantChangeType to get the value of one or more arguments. For example, if the IDispatch call specifies a SetTitle member, which takes one string argument, the implementor would call VariantChangeType to attempt to coerce the argument to VT_BSTR. If VariantChangeType did not return an error, the argument could then be fetched directly from the bstrVal field of the VARIANTARG. If VariantChangeType returned DISP_E_TYPEMISMATCH, the implementor would set *puArgErr to 0 (indicating the argument in error) and return DISP_E_TYPEMISMATCH from IDispatch::Invoke.

Note that you should not attempt to change the type of a VARIANTARG in the rgvarg array in place. The callee should not change the contents of this array.

Arrays of one type cannot be converted to arrays of another type with this function.

See Also

VariantChangeTypeEx


VariantChangeTypeEx

HRESULT VariantChangeTypeEx(pvargDest, pvargSrc, lcid, wFlags, vtNew)
VARIANTARG FAR* pvargDest
VARIANTARG FAR* pvargSrc
LCID lcid
unsigned short wFlags
VARTYPE vtNew

This function converts a variant from one type to another using a locale ID.

Parameter

pvargDest

Points to the VARIANTARG to receive the coerced type. This may be the same as pvargSrc to convert in place.

pvargSrc

Points to the source VARIANTARG to be coerced.

lcid

The locale ID for the variant to coerce. The locale ID is useful when the type of the source or destination VARIANTARG is VT_BSTR, VT_DISPATCH, or VT_DATE.

wFlags

Flags that control the coercion. The only defined flag is VARIANT_NOVALUEPROP, which prevents the function from attempting to coerce an object to a fundamental type by getting its Value property. Applications should set this flag only if necessary, because it makes the behavior inconsistent with other applications.

vtNew

The type to coerce to. If the return code is S_OK, the vt field of the *pvargDest is guaranteed to be equal to this value.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADVARTYPE	The variant type vtNew is not a valid type of variant.
DISP_E_OVERFLOW	The data pointed to by pvargSrc does not fit in the destination type.
DISP_E_TYPEMISMATCH	The argument could not be coerced to the specified type.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Memory could not be allocated for the conversion.
Comments

The VariantChangeTypeEx function handles coercions between the fundamental types (including numeric to string and string to numeric coercions). To change a type with the VT_BYREF flag set to one without VT_BYREF set, VariantChangeTypeEx fetches the referenced value. Objects are coerced to fundamental types by fetching the value of the Value property.

Typically, the implementor of IDispatch::Invoke determines which member is being accessed, then calls VariantChangeType to get the value of one or more arguments. For example, if the IDispatch call specifies a SetTitle member, which takes one string argument, the implementor would call VariantChangeTypeEx to attempt to coerce the argument to VT_BSTR. If VariantChangeTypeEx did not return an error, the argument could then be fetched directly from the bstrVal field of the VARIANTARG. If VariantChangeTypeEx returned DISP_E_TYPEMISMATCH, the implementor would set *puArgErr to 0 (indicating the argument in error) and return DISP_E_TYPEMISMATCH from IDispatch::Invoke.

Note that you should not attempt to change the type of a VARIANTARG in the rgvarg array in place, because the callee should not change the contents of this array.

Arrays of one type cannot be converted to arrays of another type with this function.

See Also

VariantChangeType


VariantClear

HRESULT VariantClear(pvarg)
VARIANTARG FAR* pvarg

Clears a variant.

Parameter

pvarg

Points to the VARIANTARG to clear.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The variant contains an array that is locked.
DISP_E_BADVARTYPE	The variant type pvarg is not a valid type of variant.
E_INVALIDARG	One of the arguments is invalid.
Comments

This function clears a VARIANTARG by setting the vt field to VT_EMPTY and the wReserved field to 0. The current contents of the VARIANTARG are released first. If the vt field is VT_BSTR, the string is freed; if the vt field is VT_DISPATCH, the object is released. If the vt field has the VT_ARRAY bit set, the array is freed. The function should be used to clear variables of type VARIANTARG (or VARIANT) before the memory containing the VARIANTARG is freed (as when a local goes out of scope).

Example

for(i = 0; i < celt; ++i)

    VariantClear(&rgvar[i]);


VariantCopy

HRESULT VariantCopy(pvargDest, pvargSrc)
VARIANTARG FAR* pvargDest
VARIANTARG FAR* pvargSrc

Frees the destination VARIANT and makes a copy of the source VARIANT.

Parameter

pvargDest

Points to the VARIANT to receive the copy.

pvargSrc

Points to the VARIANT to be copied.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The variant contains an array that is locked.
DISP_E_BADVARTYPE	The source and destination have an invalid variant type (usually uninitialized).
E_OUTOFMEMORY	Memory could not be allocated for the copy.
E_INVALIDARG	The argument pvargSrc was VT_BYREF.
Comments

First, any memory owned by pvargDest is freed as in VariantClear (note that pvargDest must point to a valid initialized variant, and not simply an uninitialized memory location). Then, pvargDest receives an exact copy of the contents of pvargSrc. If pvargSrc is a VT_BSTR, a copy of the string is made. If pvargSrc is a VT_ARRAY, the entire array is copied. If pvargSrc is a VT_DISPATCH or VT_UNKNOWN, AddRef is called to increment the object's reference count.


VariantCopyInd

HRESULT VariantCopyInd(pvarDest, pvargSrc)
VARIANT FAR* pvarDest
VARIANTARG FAR* pvargSrc

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.

Parameters

pvarDest

Points to the VARIANT to receive the copy.

pvargSrc

Points to the VARIANTARG to be copied.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_ARRAYISLOCKED	The variant contains an array that is locked.
DISP_E_BADVARTYPE	The source and destination have an invalid variant type (usually uninitialized).
E_OUTOFMEMORY	Memory could not be allocated for the copy.
E_INVALIDARG	The argument pvargSrc was VT_ARRAY.
Comments

This function is useful when you want to make a copy of a variant and guarantee that it is not VT_BYREF, such as when handling arguments within an implementation of IDispatch::Invoke.

For example, if the source is a (VT_BYREF | VT_I2), the destination will be a ByVal VT_I2. The same is true for all legal VT_BYREF combinations including VT_VARIANT.

If pvargSrc is (VT_BYREF | VT_VARIANT) and the contained variant is also VT_BYREF, the contained variant is also dereferenced.

This function frees any existing contents of pvarDest.


VariantInit

void VariantInit(pvarg)
VARIANTARG FAR* pvarg

Initializes a variant.

Parameter

pvarg

Points to the VARIANTARG to initialize.

Comments

The VariantInit function initializes the VARIANTARG by setting the vt field to VT_EMPTY and the wReserved field to 0. Unlike VariantClear, this function does not interpret the current contents of the VARIANTARG. VariantInit should be used to initialize new local variables of type VARIANTARG (or VARIANT).

Example

for(i = 0; i < celt; ++i)

    VariantInit(&rgvar[i]);


Low-Level Variant Conversion Functions

OLEAUT32.DLL provides the following low-level functions for converting variant data types. These functions are used by the higher-level variant manipulation functions, such as VariantChangeType, but may be called directly as well.

Convert to type	From type	Function
short	short	None
	long	VarI2FromI4(lIn, psOut)
	float	VarI2FromR4(fltIn, psOut)
	double	VarI2FromR8(dblIn, psOut)
	CURRENCY	VarI2FromCy(cyIn, psOut)
	DATE	VarI2FromDate(dateIn, psOut)
	OLECHAR FAR*	VarI2FromStr(strIn, lcid, dwFlags, psOut)
	IDispatch FAR*	VarI2FromDisp(pdispIn, lcid, dwFlags, psOut)
	BOOL	VarI2FromBool(boolIn, psOut)
long	short	VarI4FromI2(sIn, plOut)
	long	None
	float	VarI4FromR4(fltIn, plOut)
	double	VarI4FromR8(dblIn, plOut)
	CURRENCY	VarI4FromCy(cyIn, plOut)
	DATE	VarI4FromDate(dateIn, plOut)
	OLECHAR FAR*	VarI4FromStr(strIn, lcid, dwFlags, plOut)
	IDispatch FAR*	VarI4FromDisp(pdispIn, lcid, dwFlags, plOut)
	BOOL	VarI4FromBool(boolIn, plOut)
float	short	VarR4FromI2(sIn, prOut)
	long	VarR4FromI4(lIn, prOut)
	float	None
	double	VarR4FromR8(dblIn, prOut)
	CURRENCY	VarR4FromCy(cyIn, prOut)
	DATE	VarR4FromDate(dateIn, prOut)
	OLECHAR FAR*	VarR4FromStr(strIn, lcid, dwFlags, prOut)
Convert to type	From type	Function
	IDispatch FAR*	VarR4FromDisp(pdispIn, lcid, dwFlags, prOut)
	BOOL	VarR4FromBool(boolIn, prOut)
double	short	VarR8FromI2(sIn, pdblOut)
	long	VarR8FromI4(lIn, pdblOut)
	float	VarR8FromR4(fltIn, pdblOut)
	double	None
	CURRENCY	VarR8FromCy(cyIn, pdblOut)
	DATE	VarR8FromDate(dateIn, pdblOut)
	OLECHAR FAR*	VarR8FromStr(strIn, lcid, dwFlags, pdblOut)
	IDispatch FAR*	VarR8FromDisp(pdispIn, lcid, dwFlags, pdblOut)
	BOOL	VarR8FromBool(boolIn, pdblOut)
DATE	short	VarDateFromI2(sIn, pdateOut)
	long	VarDateFromI4(lIn, pdateOut)
	float	VarDateFromR4(fltIn, pdateOut)
	double	VarDateFromR8(dblIn, pdateOut)
	CURRENCY	VarDateFromCy(cyIn, pdateOut)
	DATE	None
	OLECHAR FAR*	VarDateFromStr(strIn, lcid, dwFlags, pdateOut)
	IDispatch FAR*	VarDateFromDisp(pdispIn, lcid, dwFlags, pdateOut)
	BOOL	VarDateFromBool(boolIn, pdateOut)
CURRENCY	short	VarCyFromI2(sIn, pcyOut)
	long	VarCyFromI4(lIn, pcyOut)
	float	VarCyFromR4(fltIn, pcyOut)
	double	VarCyFromR8(dblIn, pcyOut)
	CURRENCY	None
	DATE	VarCyFromDate(dateIn, pcyOut)
Convert to type	From type	Function
	OLECHAR FAR*	VarCyFromStr(strIn, lcid, dwFlags, pcyOut)
	IDispatch FAR*	VarCyFromDisp(pdispIn, lcid, dwFlags, pcyOut)
	BOOL	VarCyFromBool(boolIn, pcyOut)
BSTR	short	VarBstrFromI2(sIn, lcid, dwFlags, pbstrOut)
	long	VarBstrFromI4(lIn, lcid, dwFlags, pbstrOut)
	float	VarBstrFromR4(fltIn, lcid, dwFlags, pbstrOut)
	double	VarBstrFromR8(dblIn, lcid, dwFlags, pbstrOut)
	CURRENCY	VarBstrFromCy(cyIn, lcid, dwFlags, pbstrOut)
	DATE	VarBstrFromDate(dateIn, lcid, dwFlags, pbstrOut)
	OLECHAR FAR*	None
	IDispatch FAR*	VarBstrFromDisp(pdispIn, lcid, dwFlags, pbstrOut)
	BOOL	VarBstrFromBool(boolIn, lcid, dwFlags, pbstrOut)
BOOL	short	VarBoolFromI2(sIn, pboolOut)
	long	VarBoolFromI4(lIn, pboolOut)
	float	VarBoolFromR4(fltIn, pboolOut)
	double	VarBoolFromR8(dblIn, pboolOut)
	CURRENCY	VarBoolFromCy(cyIn, pboolOut)
	DATE	VarBoolFromDate(dateIn, pboolOut)
	OLECHAR FAR*	VarBoolFromStr(strIn, lcid, dwFlags, pboolOut)
	IDispatch FAR*	VarBoolFromDisp(pdispIn, lcid, dwFlags, pboolOut)
	BOOL	None
Parameters

sIn, lIn, fltIn, dblIn, cyIn, dateIn, strIn, pdispIn, boolIn

The value to coerce. These parameters have following data types:

Parameter	Data type
sIn	short
lIn	long
fltIn	float
dblIn	double
cyIn	CURRENCY
dateIn	DATE
strIn	OLECHAR FAR*
pdispIn	IDispatch FAR*
boolIn	BOOL
lcid

For conversions from string and VT_DISPATCH input, the locale ID to use for the conversion. dwFlags

One or more of the following flags:

Flag	Description
LOCALE_NOUSEROVERRIDE	Use the system default locale settings, rather than custom user locale settings.
VAR_TIMEVALUEONLY	Omit the date portion of a VT_DATE and return the time only. Applies to conversions to or from dates.
VAR_DATEVALUEONLY	Omit the time portion of a VT_DATE and return the time only. Applies to conversions to or from dates.
psOut, plOut, pfltOut, pdblOut, pcyOut, pstrOut, pdispOut, pboolOut

A pointer to the coerced value. These parameters have following data types:

Parameter	Data type
psOut	short
plOut	long
pfltOut	float
pdblOut	double
pcyOut	CURRENCY
pdateOut	DATE
pstrOut	OLECHAR FAR*
pdispOut	IDispatch FAR*
pboolOut	BOOL
Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
DISP_E_BADVARTYPE	The input parameter is not a valid type of variant.
DISP_E_OVERFLOW	The data pointed to by the output parameter does not fit in the destination type.
DISP_E_TYPEMISMATCH	The argument could not be coerced to the specified type.
E_INVALIDARG	One of the arguments is invalid.
E_OUTOFMEMORY	Memory could not be allocated for the conversion.


Time Conversion Functions

OLEAUT32.DLL provides the following functions to convert between dates and times stored in MS-DOS format and the variant representation.

This section includes the following functions:

DosDateTimeToVariantTime
VariantTimeToDosDateTime


DosDateTimeToVariantTime

int DosDateTimeToVariantTime(wDOSDate, wDOSTime, pvtime)
unsigned short wDOSDate
unsigned short wDOSTime
double FAR* pvtime

Converts the MS-DOS representation of time to the date and time representation stored in variant.

Parameters

wDOSDate

The MS-DOS date to convert.

wDOSTime

The MS-DOS time to convert.

pvtime

Pointer to the location to store the converted time.

Return Value

One of the following:

Result	Meaning
True	Success
False	Failure
Comments

MS-DOS records file dates and times as packed 16-bit values. An MS-DOS date has the following format:

Bits	Contents
0-4	Day of the month (1-31)
5-8	Month (1 = January, 2 = February, and so on)
9-15	Year offset from 1980 (add 1980 to get actual year)
An MS-DOS time has the following format:

Bits	Contents
0-4	Second divided by 2
5-10	Minute (0-59)
11-15	Hour (0-23 on a 24-hour clock)


VariantTimeToDosDateTime

int VariantTimeToDosDateTime(vtime, pwDOSDate, pwDOSTime)
double vtime
unsigned short FAR* pwDOSDate
unsigned short FAR* pwDOSTime

Converts the variant representation of a date and time to MS-DOS date and time values.

Parameters

vtime

The variant time to convert.

pwDOSDate

Pointer to location to store the converted MS-DOS date.

pwDOSTime

Pointer to location to store the converted MS-DOS time.

Return Value

One of the following:

Result	Meaning
True	Success.
False	Failure.
Comments

See the Comments section of DosDateTimeToVariantTime for a description of the MS-DOS date and time formats.


Object Description Language

Type information is described using the Object Description Language (ODL). Scripts written in ODL are compiled with the MkTypLib tool to create type libraries. MkTypLib is included with the OLE 2 SDK.

This  section provides a reference to ODZL and the MkTypLib tool.

This section includes the following topics:

MkTypLib: Type Library Creation Tool
Creating Type Libraries
Contents and Structure of a Type Library
Invoking MkTypLib
Error Reporting
MkTypLib Source File Syntax
Other Source File Contents
Intrinsic Data Types
Array Definitions
String Definitions
Attribute Descriptions
Filenames
Preprocessing
Reference to MkTypLib Statements and Directives


MkTypLib: Type Library Creation Tool

When you expose OLE Automation objects, you permit interoperability with other vendors' programs. To make public the characteristics (properties and methods) of your objects for use by others, you can:

•	Publish object and type definitions (for example, as printed documentation).

•	Code objects into a compiled .C or .CPP file, so they can be accessed using IDispatch::GetTypeInfo or implementations of the ITypeInfo and ITypeLib interfaces.

•	Place objects in a type library created with MkTypLib and then included as a resource in an application's DLL or as a stand-alone binary file.

.

Tools such as object browsers and compilers access type libraries through the ITypeLib, ITypeInfo, and ITypeComp interfaces. Type library tools (such as MkTypLib) can be created using the ICreateTypeLib and ICreateTypeInfo interfaces.


Creating Type Libraries

MkTypLib accepts a text file (with extension .ODL) as input and produces a type library (a file with a .TLB extension). Type libraries are OLE compound document files containing standard descriptions of data types, modules, and interfaces that can be used to fully expose objects for OLE Automation.

MkTypLib processes scripts written in the Object Description Language (ODL). In addition to type libraries, MkTypLib can optionally output a C or C++ style header file. See Exposing OLE Automation Objects for more information about generating header files. MkTypLib uses the ICreateTypeLib and ICreateTypeInfo interfaces to create type libraries. Type libraries can in turn be accessed by tools such as type browsers and compilers using the ITypeLib and ITypeInfo interfaces.

Application vendors can ship type libraries in one of the following forms:

•	A resource in a DLL. This resource should have the type typelib and the integer ID 1. It must be declared in the resource (.RC) file as follows:

1 typelib mylib.tlb

There can be only one resource of this type. Application developers use the resource compiler to add the .TLB file to their own DLL. A DLL with a type library resource typically has the extension .OLB (object library).

•	A subfile within a compound document file (sometimes called a "docfile"). The subfile must have the name:

"\006typelib"

The name is a quoted string that begins with a control character (ASCII 6) plus the identifier "typelib" (\006 represents 6 within the string, using octal notation).

•	A stand-alone binary file. The .TLB file output by MkTypLib is a binary file.


Contents and Structure of a Type Library

The general syntax for a type library is as follows:

[attributes] library libraryname { librarycontents };

The librarycontents shown in the table below can include data type information such as an alias, enumeration, structure, or union, as well as module, interface, IDispatch interface, and component object class type information that describes an object or objects. Each of these is frequently referred to as a "typeinfo." Type descriptions from other type libraries can be accessed or referenced by specifying the other type libraries with an importlib directive for each library.

Enumerations, structures, and unions must be defined with the typedef keyword. The attributes for a type defined with typedef are enclosed in square brackets following the typedef keyword. If a simple alias typedef has no attributes, it is treated like a #define and the aliasname does not appear in the library. Any attribute (use public if no others are desired) specified between the typedef keyword and the rest of a simple alias definition causes the alias to appear explicitly in the type library. The attributes typically include things like a Help string and Help context.

Module, IDispatch interface (dispinterface), interface, and component object class (coclass) descriptions can include attributes by preceding the keyword with the list of attributes in square brackets. Library elements shown in the following table are described in Reference to MkTypLib Statements and Directives.

Category	Library element	Description
Directive for referencing other type libraries. Must precede all other librarycontents entries.	importlib (lib1)	Specifies an external type library (in this case lib1) whose definitions may be referenced in this type library.
Data type declarations used by the objects in this type library. Must appear before any references to the types.	typedef[attributes]	An alias declared using C syntax. Must have at least one attribute to be included in the type library.
	typedef [attributes] enum	An enumeration declared using C typedef and enum keywords.
	typedef [attributes] struct	A structure declared using C typedef and struct keywords.
	typedef [attributes] union	A union declared using C typedef and union keywords.
DLL description functions (make querying the DLL possible)	[attributes] module	Constants and a group of general data functions whose actions are not restricted to any specified class of objects (functions exposed by a DLL and not accessed through vtable entries).
Interface descriptions	[attributes]  dispinterface	An IDispatch interface describing the methods and properties for an object that can be accessed through IDispatch::Invoke. The property accessor functions and methods are not in the vtable of the interface. Therefore, DispInvoke and CreateStdDispatch can't be used to provide an implementation of IDispatch::Invoke.
	[attributes]  interface	Interface describing functions, including property accessor functions and methods of an object. These functions are in the vtable of the interface, so DispInvoke and CreateStdDispatch can be used to provide an implementation of IDispatch::Invoke.
OLE class descriptions	[attributes] coclass	Specifies a top-level object, with all its interfaces and dispinterfaces, which can be created by OLE's CoCreateInstance or DispTest's CreateObject function.


Invoking MkTypLib

To invoke MkTypLib, choose the Run command from either the Program Manager or the File Manager File menu, then enter the following command line in the dialog box:

MkTypLib [options] ODLfile

MkTypLib creates a type library file (extension .TLB) based on source text input contained in the file specified by ODLfile. It can optionally produce a header file (extension .H), which is a stripped version of the input file suitable for inclusion in C or C++ programs that want to access the types defined in the input file.

The options can be a series of options prefixed with a hyphen (-) or a slash (/) as follows:

Option	Description
/? or /help	Displays command line Help. The ODLfile does not need to be specified in this case.
/align:alignment	Sets the default alignment for types in the library. An alignment value of 1 indicates natural alignment; n indicates alignment on byte n.
/D define[=value]	Defines the name define for the C preprocessor. The value is its optional value. No space is allowed between the equal sign (=) and the value.
/I includedir	Specifies includedir as the directory where include files are located for the C preprocessor.
/tlb filename	Specifies filename as the name of the output .TLB file. If not specified, this is the same as the name of the ODLfile with the extension .TLB.
/h filename	Specifies filename as the name for a stripped version of the input file. This file can be used as a C or C++ header file. The output is defined in the section .H file output.
/Win16  /Win32  /mac /mips /alpha  /ppc /ppc32	Specifies type of output type library to produce. Default is /Win16.
/nologo	Disables display of the copyright banner.
/nocpp	Suppresses invocation of the C preprocessor.
/cpp_cmd cpppath	Specifies cpppath as the command to run the C preprocessor. By default, MkTypLib invokes CL.
/cpp_opt "options"	Specifies options for the C preprocessor. Default is "/C /E /D__MKTYPLIB__."
/o outputfile	Redirects output (for example, error messages) to specified outputfile.
/w0	Disables warnings.


Error Reporting

Although MkTypLib offers minimal error reporting, error messages include accurate line and column number information that can be used with text editors to find mistakes.


MkTypLib Source File Syntax

In general, the description of an alias, enumeration, union, or structure has the following syntax:

typedef [typeattributes] typekind typename {
memberdescriptions
};

The description of an interface, IDispatch interface, component object class, or module has the following syntax:

[typeattributes] typekind typename {
memberdescriptions
};

Note  The square and curly brackets in these descriptions are part of the syntax, not descriptive symbols. The semicolon after the curly brace (}) that terminates the library definition (and all other type definitions) is optional.

The following example illustrates a MkTypLib source file:

// Type library specification with preceding attributes in brackets

// Note the library must have a uuid

[uuid(BFB73340-822A-1068-8849-00DD011087E8),

helpfile("MYOBJS.HLP"), helpstring("This is some Help for the library"), helpcontext(2475)]

library mylibrary {

importlib("STDOLE32.TLB");	// Necessary to support IDispatch interfaces

importlib("MYOBLIB.TLB");	// An imported external library

// A typedef used by objects in the library;

typedef DWORD long;

// An enumeration used by objects in the library

typedef

[uuid(BFB73342-822A-1068-8849-00DD011087E8), helpstring("FarmAnimals are friendly"), helpcontext(234)]
enum {
[helpstring("Moo")] cows = 1,
pigs = 2
} ANIMALS;

// A typedef'd structure used by objects in the library

typedef

[uuid(BFB73343-822A-1068-8849-00DD011087E8), helpstring("A task"), 	helpcontext(1019)]
struct {
DATE startdate;
DATE enddate;
BSTR ownername;
SAFEARRAY (int) subtasks ;
} TASKS;

.

.

.

// A description of interface

[odl]

[uuid(BFB73344-822A-1068-8849-00DD011087E8), version(1.0)]
interface _IHello {
[id(0), propput]

void HelloMessage([in] BSTR b);

      	[id (0), propget, helpstring("HelloMessage property"),

helpcontext(2001)]

      	BSTR HelloMessage();

[id (1), helpstring ("Display HelloMessage"),

helpcontext (2002)]

void SayHello([in] int nTimes);

};

// A description of an object exposed for IDispatch

[ uuid (BFB73345-822A-1068-8849-00DD011087E8),

version(1.0),helpstring("Hello World Example"), 	helpcontext(2480)]

dispinterface _DHello {

interface hello; 	// IHello interface supports
// IDispatch, so declare it here.

// This is shorthand for repeating

// the methods/properties here.
};

// Description of an implemented OLE style class

[ uuid(BFB73346-822A-1068-8849-00DD011087E8), version(1.0),
helpstring("A class"), helpcontext(2481), appobject]
coclass Hello {

interface _IHello;
dispinterface _DHello;
};

};

The preceding example file creates a library named mylibrary. The Help for all of the objects in the library is found in the file MYOBJS.HLP. The overview Help for the library has the context 2475. The example also imports the type library MYOBLIB.TLB; uses typedef with a uuid attribute, an enumeration, and a structure; and then describes the _IHello interface. The _IHello interface has a property named HelloMessage with ID 0, and a method named SayHello with ID 1. Furthermore, the example describes the object _DHello, which can be accessed via IDispatch, and a component object class named Hello. Many of the descriptions are preceded by typical attributes.


Other Source File Contents

This section includes the following topics:

Constants
Identifiers
The Globally Unique ID (GUID)
Comments
Forward Declarations


Constants

A constant can be either numeric or a string, depending on the attribute.

Numerics

Numeric input is usually an integer (in either decimal or in hexadecimal using the standard 0x format), but may also be a single character constant (\0, for example).

Strings

A string is delimited by double-quotation marks (") and may not span multiple lines. The backslash character (\) acts as an escape character. The backslash character followed by any character (even another backslash) prevents the second character from being interpreted with any special meaning; the backslash is not included in the text.

For example, to include a double-quotation mark (") in the text without causing it to be interpreted as the closing delimiter, precede it with a backslash (\"). Similarly, you can use a double backslash (\\) to put a backslash into the text. Some examples of valid strings are:

"commandName"
"This string contains a \"quote\"."
"Here's a pathname: c:\\bin\\binp"

A string can be up to 255 characters long.


Identifiers

Identifiers can be up to 255 characters long, and must conform to C-style syntax. MkTypLib is case sensitive, but a generated type library is case insensitive. Thus, it is possible to define a user-defined type whose name differs from that of a built-in type only by case. However, user-defined type names (and type-member names) that differ only in case refer to the same type or member. Except for property accessor functions, it is illegal for two members of a type to have the same name, regardless of case.


The Globally Unique ID (GUID)

A uuid is a globally unique ID (GUID). This number is created by running the GUIDGEN.EXE command-line program. GUIDGEN will never produce the same number twice, no matter how many times it is run or how many different machines it runs on. Every entity (such as an interface) that needs to be uniquely identified has a GUID.


Comments

C-style comments are ignored by MkTypLib. This includes comments in block form (/*...*/), as well as single-line comments beginning with a double backslash (//). Comments are not preserved in the header file.


Forward Declarations

Forward declarations permit forward references to types. Forward references have the following form:

typedef struct mydata;

interface aninterface;

dispinterface fordispatch;


Intrinsic Data Types

The following data types are recognized by MkTypLib:

Type	Description
int	A signed integer, whose size is system-dependent.
boolean	A data item that can have the values TRUE or FALSE. The size may be system-dependent.
char	8-bit signed data item.
double	64-bit IEEE floating point number.
float	32-bit IEEE floating point number.
long	32-bit signed integer.
short	16-bit signed integer.
wchar_t	Unicode character accepted only for Win32 type libraries.
LPWSTR	Unicode string accepted only for Win32 type libraries.
LPSTR	A zero-terminated string.
CURRENCY	8-byte fixed-point number.
DATE	64-bit floating-point fractional number of days since December 30, 1899.
VARIANT	One of the variant data types as described in , Dispatch Interfaces.
IDispatch *	Pointer to IDispatch interface.
IUnknown *	Pointer to IUnknown interface. (Any OLE interface can be represented by its IUnknown interface.)
void	Allowed only as return type for a function or in a function parameter list to indicate no arguments.
BSTR	A length-prefixed string, as described in Dispatch Interfaces.
SCODE	Built-in error type that corresponds to VT_ERROR. An SCODE does not contain the additional error information provided by HRESULT.
HRESULT	Return type used for reporting error information in interfaces, as described in the OLE 2 Programmer's Reference, Volume 1.
The keyword unsigned may be specified before int, char, short, and long.


Array Definitions

MkTypLib accepts both fixed-size (C-style) arrays (type arrname[size];) and arrays declared as SAFEARRAY. The following syntax can be used for a SAFEARRAY:

SAFEARRAY (elementtype) *arrayname

A function returning a SAFEARRAY has the following syntax:

SAFEARRAY (elementtype) myfunction(parameterlist);


String Definitions

Strings can be declared using the LPSTR type, which indicates a 0-terminated string, and with the BSTR type, which indicates a length-prefixed string (as defined in Dispatch Interfaces.


Attribute Descriptions

MkTypLib accepts the following attributes as indicated:

Attribute Allowed on	Effect	Comments
appobject Coclass	Identifies the Application object.	Indicates that the members of the class may be accessed without qualification when accessing this type library.
bindable Property	Indicates that the property supports data binding.	Refers to the property as a whole, so it must be specified wherever the property is defined. This may mean specifying the attribute on both the property get description and on the property set description. Representations: FUNCFLAG_FBINDABLE, VARFLAG_FBINDABLE
default Member of a coclass	Indicates that the interface or dispinterface represents the default for the source or sink.	Representation: IMPLTYPEFLAG_FDEFAULT
defaultbind Property	Indicates the single bindable property that best represents the object. Used by containers having a user model that involves binding to an object rather than binding to a property of an object.	An object can support data binding but not have this attribute. Properties having defaultbind attribute must also have the bindable attribute. Can't specify defaultbind on more than one property in a dispinterface. Representation: FUNCFLAG_FDEFAULTBIND, VARFLAG_FDEFAULTBIND
displaybind Property	Set on those properties recommended  by the object to be displayed to the user as bindable.	An object can support data binding but not have this attribute. The property that has the displaybind attribute must also have the bindable attribute. Representations: FUNCFLAG_FDISPLAYBIND, VARFLAG_FDISPLAYBIND
dllname(str) Module		Defines the name of the DLL that contains  the module entry points..
entry(entryid) Function in a module	Identifies the entry point in the DLL. If entryid is a string, this is a named entry point. If entryid is a number, the entry point is defined by an ordinal.	Provides a way to obtain the address of a function in a module.
helpcontext(numctxt) Interface, library, dispinterface, struct, enum, union, module, typedef, method, struct member, enum value, property, coclass, const	Retrieved via the GetDocumentation functions in the ITypeLib and ITypeInfo interfaces.	The numctxt is a 32-bit Help context within the Help file.
helpfile(filenam) Library	Retrieved via the GetDocumentation functions in the ITypeLib and ITypeInfo interfaces.	All types in a library share the same Help file.
helpstring(string) Library, interface, dispinterface, struct, enum, union, module, typedef, method, struct member, enum value, property, coclass, const	Retrieved via the GetDocumentation functions in the ITypeLib and ITypeInfo interfaces.
id(num) Method or property in interface or dispinterface	Identifies the DISPID of the member.	The num is a 32-bit integral value.
in parameter	Parameter receives a value.	Specifies an input parameter. The parameter may be a pointer (as with char *) but the value it refers to is not returned.
lcid(numid) library	Identifies the locale for the library.	The numid is a 32-bit locale ID as used in Win32 National Language Support. This is typically entered in hexadecimal.
licensed  Coclass	Indicates that the class is licensed.	Representation: TYPEFLAG_FLICENSED
odl Interface (required)	Identifies an interface as an ODL interface. This attribute must appear on all interfaces.
optional parameter	Parameter may be omitted.	Specifies an optional. Valid only if the parameter is of type VARIANT. All subsequent parameters of the function must be optional.
parameter	Parameter supplies a value.	Specifies an output parameter. The parameter must be a pointer to memory that will receive a result.
propget Functions; methods in interfaces and dispinterfaces	Causes the INVOKEKIND in the funcdesc to be INVOKE_PROPERTYGET.	Specifies a property-accessor function for the property with the same name as the function. At most, one of propget, propput, and propputref can be specified for a function.
propput Functions; methods in interfaces and dispinterfaces	Causes the INVOKEKIND in the funcdesc to be INVOKE_PROPERTYPUT.	Specifies a property-setting function for the property with the same name as the function. At most, one of propget, propput and propputref can be specified.
propputref Functions; methods in interfaces and dispinterfaces	Causes the INVOKEKIND in the funcdesc to be INVOKE_ PROPERTYPUTREF.	Specifies a property-setting function that sets the reference for the property with the name of the function. At most, one of propget, propput and propputref can be specified.
public Aliases declared with typedef	Causes an alias to be included in the type library.	Specifies that an alias created with typedef (and having no other attributes) should be included in the type library. Otherwise, an alias created with typedef (and having no other attributes) is treated as a #define.
readonly	Causes the variable's VARDESC structure to have its wVarFlags element set with VARFLAG_FREADONLY.	If the flag is set, assignment to the variable should not be allowed.
requestedit Property	The property supports the OnRequestEdit notification, raised by a property before it is edited.	An object can support data binding but not have this attribute. Representations: FUNCFLAG_ FREQUESTEDIT, VARFLAG_ FREQUESTEDIT
restricted Member of a coclass	Prevents the interface or dispinterface from being used by a macro programmer.	Allowed on a member of a coclass, independent of whether the member is a dispinterface or interface, and independent of whether the member is a sink or source. A member of a coclass cannot have both the restricted and default attributes. Representation: IMPLTYPEFLAG_FRESTRICTED
restricted Type libraries and members in modules and interfaces	If specified, the FUNCDESC will have its wFuncFlags element set with FUNCFLAG_ FRESTRICTED	Indicates that the function should not be called from macro languages. Corresponds to FUNCFLAG_FRESTRICTED
source Member of a coclass	Specified on a member of a coclass that is called rather than implemented.	Representation: IMPLTYPEFLAG_FSOURCE.
string Struct, member, parameter, property		Included only for compatibility  with IDL; use LPSTR for a zero-terminated string.
uuid(uuidval) Library, dispinterface, struct, enum, union, module, typedef, interface, coclass	This value is returned in the TypeAttr structure retrieved via TypeInfo::GetTypeAttr	Optional for all but library, coclass, and dispinterface. The uuidval is a 16-byte value formatted as hexadecimal digits in the following format: 12345678-1234-1234-1234-123456789ABC.
vararg all functions		Indicates that the last parameter must be a safe array of VARIANT type.
version(versionval) library, struct, module, dispinterface, interface, coclass, enum, union		The argument versionval is a real number of the format n.m, where n is a major version number and m is a minor version number.
Note that attribute specifications follow the typedef keyword when it is used, but precede the descriptions of other types.


Filenames

A filename is a string that represents either a full or partial path. The referenced files are expected to be in directories referenced by the type library registration entries, so partial pathnames are typically used. See Exposing OLE Automation Objects for more information about registration.


Preprocessing

MkTypLib spawns the C preprocessor. The symbol __MKTYPLIB__ is predefined for the preprocessor.


Reference to MkTypLib Statements and Directives

This section includes the following topics:

The coclass Statement
The dispinterface Statement
The enum Statement
The importlib Directive
The interface Statement
The library Statement
The module Statement
The struct Statement
The typedef Statement
The union Statement


The coclass Statement

Syntax

[attributes]
coclass classname {
[attributes2] [interface | dispinterface] interfacename;
...
};

The Microsoft Component Object model defines a class as an implementation that allows QueryInterface between a set of interfaces. The coclass statement describes the GUID and supported interfaces for a component object.

Syntax Elements

attributes

The uuid attribute is required on a coclass. This is the same uuid that is registered as a CLSID in the system registration database. The helpstring, helpcontext, licensed, version, and appobject attributes are accepted, but not required, before a coclass definition. See Attribute Descriptions for more information on the attributes accepted before a coclass definition. The appobject attribute makes the functions and properties of the coclass globally available in the type library.

classname

Name by which the common object is known in the type library.

attributes2

Optional attributes for the interface or dispinterface. The source, default, and restricted attributes are accepted on an interface or dispinterface within a coclass.

interfacename

Either an interface declared with the interface keyword, or a dispinterface declared with the dispinterface keyword.

Examples

[ uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0),
helpstring("A class"), helpcontext(2481), appobject]
coclass myapp {
[source] interface IMydocfuncs;
dispinterface DMydocfuncs;
};

[uuid 00000000-0000-0000-0000-123456789019]

coclass foo

{

  [restricted] interface bar;

  interface baz;

}


The dispinterface Statement

Syntax 1

[attributes]
dispinterface intfname {
properties:
proplist
methods:
methlist
};

Syntax 2

[attributes]
dispinterface intfname {
interface interfacename
};

Defines a set of properties and methods on which you can call IDispatch::Invoke. An IDispatch interface (dispinterface) may be defined by explicitly listing the set of supported methods and properties (Syntax 1) or by listing a single interface (Syntax 2).

Syntax Elements

attributes

The helpstring, helpcontext, uuid, and version attributes are accepted before dispinterface. See Attribute Descriptions for more information on the attributes accepted before a dispinterface definition. Attributes (including the square brackets) may be omitted. The uuid attribute is required.

intfname

The name by which the dispinterface is known in the type library. This name must be unique within the type library.

interfacename

(Syntax 2) The name of the interface to declare as an IDispatch interface.

proplist

(Syntax 1) An optional list of properties supported by the object, declared in the form of variables. This form is shorthand for declaring the property functions in the methods list. See the comments section for details.

methlist

(Syntax 1) A list comprising a function prototype for each method and property in the dispinterface. Any number of function definitions can appear in methlist. A function in methlist has the following form:

[attributes] returntype methname(params);

The following attributes are accepted on a method in a dispinterface: helpstring, helpcontext, string (for compatibility with IDL), bindable, defaultbind, displaybind, propget, propput, propputref, and vararg. If vararg is specified, the last parameter must be a safe array of VARIANT type.

The parameter list is a comma-delimited list, each element of which has the following form:

[attributes] type paramname

The type can be any declared or built-in type, or a pointer to any type. Attributes on parameters are:

in, out, optional, string

If optional appears, it must only be specified on the rightmost parameters, and the types of those parameters must be VARIANT.

Comments

Method functions are specified exactly as described in the section "The module Statement," except that the entry attribute is not allowed. Note that STDOLE32.TLB (STDOLE.TLB on 16-bit systems) must be imported, because a dispinterface inherits from IDispatch.

You can declare properties in either the properties or methods lists. Declaring properties in the properties list does not indicate the type of access the property supports (that is, get, put, or putref). Specify the readonly attribute for properties that don't support put or putref. If you declare the property functions in the methods list, functions for one property all have the same ID.

Using Syntax 1, the properties: and methods: tags are required. The id attribute is also required on each member. For example:

properties:

[id(0)] int Value;	// Default property.

methods

[id(1)] void Show();

Using Syntax 2, interfaces that support IDispatch and are declared earlier in an ODL script can be redeclared as IDispatch interfaces as follows:

dispinterface helloPro {

interface hello;

};

The preceding example declares all of the members of hello and all of the members that hello inherits as supporting IDispatch.

Example

[ uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0), helpstring("Useful help string."), helpcontext(2480)]
dispinterface MyDispatchObject {
properties:
[id(1)] int x;	//An integer property named x
[id(2)] BSTR y;	//A string property named y
methods:
[id(3)] void show();	//No arguments, no result
[id(11)] int computeit(int inarg, double *outarg);
};

[uuid 00000000-0000-0000-0000-123456789012]

dispinterface MyObject

{

properties:

methods:

[id(1), propget, bindable, defaultbind, displaybind]

long x();

[id(1), propput, bindable, defaultbind, displaybind]

void x(long rhs);

}


The enum Statement

Syntax

typedef [attributes] enum [tag] {
enumlist
} enumname;

Syntax Elements

attributes

The helpstring, helpcontext, and uuid attributes are accepted before an enum. The helpstring and helpcontext attributes are accepted on an enumeration element. See Attribute Descriptions for more information on the attributes accepted before an enumeration definition. Attributes (including the square brackets) may be omitted. If uuid is omitted, the enumeration is not uniquely specified in the system.

tag

An optional tag, as with a C enum.

enumlist

List of enumerated elements.

enumname

Name by which the enumeration is known in the type library.

Comments

The enum keyword must be preceded by typedef. The enumeration description must precede other references to the enumeration in the library. If = value is not specified for enumerators, the numbering progresses as with enumerations in C. The type of the enum elements is int, the system default integer, which depends on the target type library specification.

Examples

typedef [uuid(DEADF00D-C0DE-B1FF-F001-A100FF001ED),

        helpstring("Farm Animals are friendly"),

        helpcontext(234)]

enum {
[helpstring("Moo")] cows = 1,
pigs = 2
} ANIMALS;


The importlib Directive

Syntax

importlib(filename);

All importlib directives must precede the other type descriptions in the library. The importlib directives make types that have already been compiled into other type libraries available to the library currently being created.

Syntax Elements

filename

The location of the referenced type library file when MkTypLib is executed.

Comments

The importlib directive makes any type defined in the imported library accessible from within the library being compiled. Ambiguity is resolved as follows. The current library is searched for the type. If the type can't be found, MkTypLib searches the imported library that is lexically first, then the next, and so on. To import a "shadowed" type name, you must enter the name as libname.typename, where libname is the library name as it appeared in the library statement when the library was compiled.

The imported type library should be distributed with the library being compiled.


The interface Statement

Syntax

[attributes]
interface interfacename  [:baseinterface] {
functionlist
};

An interface is a set of procedure definitions. An interface can inherit from any base interface.

Syntax Elements

attributes

The helpstring, helpcontext, odl, uuid, and version attributes are accepted before interface. If the interface is a member of a coclass, the source, default, and restricted attributes are also accepted. See Attribute Descriptions for more information on the attributes accepted before an interface definition.

The odl attribute is required on all interface declarations.

interfacename

The name by which the interface is known in the type library.

baseinterface

The name of the interface that is the base class for this interface.

functionlist

List of function prototypes for each function in the interface. Any number of function definitions can appear in the function list. A function in the function list has the following form:

[attributes] returntype [calling convention]  funcname(params);

The following attributes are accepted on a function in an interface: helpstring, helpcontext, string, propget, propput, propputref, bindable, defaultbind, displaybind, and vararg. If vararg is specified, the last parameter must be a safe array of VARIANT type. The optional calling convention can be any one of __pascal/_pascal/pascal or __cdecl/_cdecl/cdecl, __stdcall/_stdcall/stdcall. In other words, the calling convention specification can include up to two leading underscores.

The parameter list is a comma-delimited list as follows:

[attributes] type paramname

The type can be any previously declared type, built-in type, a pointer to any type, or a pointer to a built-in type. Attributes on parameters are:

in, out, optional, string

If optional appears, it must only be specified on the rightmost parameters, and the types of those parameters must be VARIANT.

Comments

Because the functions described by the interface statement are in the vtable, you can use DispInvoke and CreateStdDispatch to provide an implementation of IDispatch::Invoke. For this reason, interface is more commonly used than dispinterface to describe the properties and methods of an object.

Functions in interfaces are exactly as described in the section "The module Statement," except that the entry attribute is not allowed.

Example

[uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0)]
interface hello : IUnknown
{
void HelloProc([in, string] unsigned char * pszString);
void Shutdown(void);
};


The library Statement

Syntax

[attributes] library libname {
definitions
};

Describes a type library. This description contains all the other information in a MkTypLib input file.

Syntax Elements

attributes

The helpstring, helpcontext, lcid, restricted, and uuid attributes are accepted before a library statement. See Attribute Descriptions for more information on the attributes accepted before a library definition. The uuid attribute is required.

libname

The name by which the type library is known.

definitions

Descriptions of any imported libraries, data types, modules, interfaces, dispinterfaces, and coclasses relevant to the object being exposed.

Comments

The library statement must precede any other type definitions.

Example

[uuid(BFB73349-822A-1068-8849-00DD011087E8), version(1.0)]
[helpfile("MYOBJS.HLP"), helpstring("This is some Help for the library"), helpcontext(2475)]
library Alibrary {
[uuid(BFB7334A-822A-1068-8849-00DD011087E8), version(1.0)]
interface hello
{
void HelloProc([in, string] unsigned char * pszString);
void Shutdown(void);
};

};


The module Statement

Syntax

[attributes]
module modulename {
elementlist
};

A module is a set of procedure definitions. Typically it describes a set of DLL entry points.

Syntax Elements

attributes

The uuid, version, helpstring, helpcontext, and dllname attributes are accepted before a module statement. See Attribute Descriptions for more information on the attributes accepted before a module definition. The dllname attribute is required. If uuid is omitted, the module is not uniquely specified in the system.

modulename

The name of the module.

elementlist

List of constant definitions and function prototypes for each function in the DLL. Any number of function definitions can appear in the function list. A function in the function list has the following form:

[attributes] returntype [calling convention] funcname(params);
[attributes] const constname = constval;

Only the helpstring and helpcontext attributes are accepted for a const.

The following attributes are accepted on a function in a module: helpstring, helpcontext, string, entry (required when a dllname has been specified), propget, propput, propputref, vararg. If vararg is specified, the last parameter must be a safe array of VARIANT type.

The optional calling convention can be one of __pascal/_pascal/pascal, __cdecl/_cdecl/cdecl, or __stdcall/_stdcall/stdcall. The calling convention can include up to two leading underscores.

The parameter list is a comma-delimited list of:

[attributes] type paramname

The type can be any previously declared type or built-in type, or a pointer to any type, or a pointer to a built-in type. Attributes on parameters are:

in, out, optional

If optional appears, it must only be specified on the rightmost parameters, and the types of those parameters must be VARIANT.

Comments

Header file (.H) output for modules is a series of function prototypes. The module keyword and surrounding brackets are stripped out of the .H file output, but a comment (// module modulename) is inserted before the prototypes. The keyword extern is inserted before the declarations.

Example

[uuid(D00BED00-CEDE-B1FF-F001-A100FF001ED),

        helpstring("This is not GDI.EXE"), helpcontext(190), dllname("MATH.DLL")]
module somemodule{
    [helpstring("Color for the frame")]

        unsigned long const COLOR_FRAME = 0xH80000006;

    [helpstring("Not a rectangle but a square"),

        entry(1)] pascal double square([in] double x);
};


The struct Statement

Syntax

typedef [attributes]
struct [tag] {
memberlist
} structname;

Syntax Elements

attributes

The helpstring, helpcontext, uuid, and version attributes are accepted before a struct statement. The helpstring, helpcontext, string attributes are accepted on a structure member. See Attribute Descriptions for more information on the attributes accepted before a structure definition. Attributes (including the square brackets) may be omitted. If uuid is omitted, the structure is not uniquely specified in the system.

tag

An optional tag, as with a C struct.

memberlist

List of structure members defined with C syntax.

structname

Name by which the structure is known in the type library.

Comments

The struct keyword must be preceded with typedef. The structure description must precede other references to the structure in the library. Members of a struct can be of any built-in type, or any type defined lexically as a typedef before the struct. See String Definitions" and Array Definitions for a description of how strings and arrays can be entered.

Example

typedef [uuid(BFB7334B-822A-1068-8849-00DD011087E8), helpstring("A task"), helpcontext(1019)]

struct {
DATE startdate;
DATE enddate;
BSTR ownername;
SAFEARRAY (int) subtasks;

int A_C_array[10];
} TASKS;


The typedef Statement

Syntax

typedef [attributes] basetype aliasname;

Creates an alias for a type.

Syntax Elements

attributes

Any attribute specifications must follow the typedef keyword. If no attributes and no other type (for example enum, struct, or union) are specified, the alias is treated as a #define and does not appear in the type library. If no other attribute is desired, public can be used to explicitly include the alias in the type library. The helpstring, helpcontext, and uuid attributes are accepted before a typedef. See Attribute Descriptions for more information on the attributes accepted before a typedef definition. Attributes (including the square brackets) may be omitted. If uuid is omitted, the typedef is not uniquely specified in the system.

aliasname

Name by which the type will be known in the type library.

basetype

The type for which the alias is defined.

Comments

The typedef keyword must also be used whenever a struct or enum is defined.

Examples

typedef [public]  long DWORD;

The preceding example creates a type description for an alias type with the name DWORD.

typedef enum {
cows = 1,
pigs = 2
} ANIMALS;

The second example creates a type description for an enumeration named "animals." The name recorded for the enum or struct is the typedef name, not the tag for the enumeration. No attributes are required to make sure the alias appears in the type library.


The union Statement

Syntax

typedef [attributes] union [tag] {
memberlist
} unionname;

Syntax Elements

attributes

The helpstring, helpcontext, uuid, and version attributes are accepted before a typedef. The helpstring, helpcontext, safe (on arrays), and string attributes are accepted on a structure member. See Attribute Descriptions for more information on the attributes accepted before a union definition. Attributes (including the square brackets) may be omitted. If uuid is omitted, the union is not uniquely specified in the system.

tag

An optional tag, as with a C union.

memberlist

List of union members defined with C syntax.

unionname

Name by which the union is known in the type library.

Comments

The union keyword must be preceded with typedef. The union description must precede other references to the structure in the library. Members of a union can be of any built-in type, or any type defined lexically as a typedef before the union. See String Definitions and Array Definitions for a description of how strings and arrays can be entered.

Example

[uuid(BFB7334C-822A-1068-8849-00DD011087E8), helpstring("A task"), helpcontext(1019)]
typedef union {
COLOR polycolor;
int	cVertices;
boolean filled;
SAFEARRAY (int) subtasks;
} UNIONSHOP;


Type Description Interfaces

The type description interfaces provide a way to read and bind to the descriptions of objects in a type library. The descriptions are used by OLE Automation controllers when they browse, create, and manipulate OLE Automation objects.

The type description interfaces include:

•	ITypeLib—Used to retrieve information about a type library.

•	ITypeInfo— Used to read the type information within the type library.

•	ITypeComp— Used when creating compilers that use type information.

This section includes the following topics:

Overview of Interfaces
Overview of Functions
ITypeLib Interface
ITypeInfo Interface
ITypeComp Interface
Type Compilation Functions
Type Library Loading and Registration
Functions


Overview of Interfaces

A type library is a container for type descriptions. ITypeLib gives access to information about a type description's containing library. ITypeInfo is used to gain access to each type description contained in a type library. The following table describes the member functions of each of the type description interfaces:

Category	Member name	Purpose
ITypeLib	FindName	Finds occurrences of a type description in a type library.
	GetDocumentation	Retrieves the library's documentation string, name of the complete Help file path and name, and the context ID for the library Help topic in the Help file.
	GetLibAttr [The caller of a method that returns a structure must delete the structure by using the Release methods (ReleaseTLibAttr, ReleaseVarDesc, ReleaseFuncDesc, ReleaseTypeAttr) provided by the interface that created the structure.]	Retrieves the structure containing the library's attributes.
	GetTypeComp	Retrieves a pointer to the ITypeComp for a type library. This enables a client compiler to bind to the library's types, variables, constants and global functions.
	GetTypeInfo	Retrieves the specified type description in the library.
	GetTypeInfoCount	Retrieves the number of type descriptions in the library.
	GetTypeInfoType	Retrieves the type of a type description.
	GetTypeInfoOfGuid	Retrieves the type description corresponding to the specified GUID.
	IsName	Indicates whether a passed-in string contains the name of a type or member described in the library.
	ReleaseTLibAttr	Releases TLIBATTR originally obtained from ITypeLib::GetLibAttr.
ITypeInfo	AddressOfMember	Retrieves the addresses of static functions or variables, such as those defined in a DLL.
	CreateInstance	Creates a new instance of a type that describes a component object class (coclass).
	GetContainingTypeLib	Retrieves the type library containing a specific type description as well as the index of the type description within the type library.
	GetDllEntry	Retrieves a description or specification of an entry point for a function in a DLL.
	GetDocumentation	Retrieves the documentation string, name of the complete Help file path and name, and the context ID for the Help topic for a specified type description.
	GetFuncDesc  [The caller of a method that returns a structure must delete the structure by using the Release methods (ReleaseTLibAttr, ReleaseVarDesc, ReleaseFuncDesc, ReleaseTypeAttr) provided by the interface that created the structure.]	Retrieves the FUNCDESC structure containing information about a specified function.
	GetIDsOfNames	Maps between member names and member IDs and parameter names and parameter IDs.
	GetImplTypeFlags	Retrieves the IMPLTYPE flags for an interface.
	GetMops	Retrieves marshaling information.
	GetNames	Retrieves the variable with the specified member ID, or the name of the function and parameter names corresponding to the specified function ID.
	GetRefTypeInfo	If a type description references other type descriptions, this function retrieves the referenced type descriptions.
	GetRefTypeOfImpl-Type	If the type description describes a component object class, this function retrieves the type description of the specified implemented interface types.
	GetTypeAttr [The caller of a method that returns a structure must delete the structure by using the Release methods (ReleaseTLibAttr, ReleaseVarDesc, ReleaseFuncDesc, ReleaseTypeAttr) provided by the interface that created the structure.]	Retrieves a TYPEATTR structure containing the attributes of the type description.
	GetTypeComp	Retrieves the ITypeComp interface for the type description, which enables a client compiler to bind to the type description's members.
	GetVarDesc ( [) The caller of a method that returns a structure must delete the structure by using the Release methods (ReleaseTLibAttr, ReleaseVarDesc, ReleaseFuncDesc, ReleaseTypeAttr) provided by the interface that created the structure.]	Retrieves a VARDESC structure describing the specified variable.
	Invoke	Invokes a method or accesses a property of an object that implements the interface described by the type description.
	ReleaseFuncDesc	Releases a FUNCDESC previously returned by GetFuncDesc.
	ReleaseTypeAttr	Releases a TYPEATTR previously returned by GetTypeAttr.
	ReleaseVarDesc	Releases a VARDESC previously returned by GetVarDesc.
ITypeComp	Bind	Maps a name to a member of a type, or binds global variables and functions contained in a type library.
	BindType	Binds to the type descriptions contained within a type library.
Type compilation	LHashValOfName	Computes a hash value for a name that can then be passed to: ITypeComp::Bind ITypeComp::BindType ITypeLib::IsName ITypeLib::FindName
	LHashValOfNameSys	Equivalent to LHashValOfName.


Overview of Functions

OLEAUT32.DLL provides functions for loading, registering, and querying type libraries.

Category	Function name	Purpose
Library loading	LoadTypeLib	Loads and registers a type library.
	LoadRegTypeLib	Uses registry information to load a type library.
Library registration	RegisterTypeLib	Adds information about a type library to the system registry.
	QueryPathOfRegTypeLib	Retrieves the path of a registered type library.
Type compilation	LHashValOfNameSys LHashValOfName	Computes a hash value for a name that can then be passed to: ITypeComp::Bind ITypeComp::BindType ITypeLibComp::IsName ITypeLib::FindName.


ITypeLib Interface

This section includes the following topics:

TLIBATTR Structure
ITypeLib::FindName
ITypeLib::GetDocumentation
ITypeLib::GetLibAttr
ITypeLib::GetTypeComp
ITypeLib::GetTypeInfo
ITypeLib::GetTypeInfoCount
ITypeLib::GetTypeInfoOfGuid
ITypeLib::GetTypeInfoType
ITypeLib::IsName
ITypeLib::ReleaseTLibAttr

Implemented by	Used by	Header filename
OLEAUT32.DLL	Tools that need to access the descriptions of objects contained in type libraries	OLEAUTO.H
Data describing a set of objects is stored in a type library. A type library is typically stored as a resource in a DLL; however, it may also be stored in a compound document file.

A type library contains descriptions of a collection of objects, and is accessed via the ITypeLib interface. The descriptions of individual objects are accessed via the ITypeInfo interface. The system registry contains a list of all the installed type libraries. Type library organization is illustrated in the following figure:

The ITypeLib interface provides methods for accessing a library of type descriptions. This interface provides the following:

•	Generalized containment support for type information. ITypeLib allows iteration over the type descriptions contained in the library.

•	Global function and data support. A type library can contain descriptions of a set of modules, each of which is the equivalent of a C or C++ source file that exports data and functions. The type library supports compiling references to this exported data and functions.

•	General information, including a user-readable name for the library and Help for the library as a whole.


TLIBATTR Structure

The following structure contains information about a type library. Information from this structure is used to identify the type library and to provide national language support for member names.

typedef struct FARSTRUCT tagTLIBATTR {
    GUID guid;	// Unique ID of the library

    LCID lcid;	// Language/locale of the library

    SYSKIND syskind;	// Target hardware platform

    unsigned short wMajorVerNum;	// Major version number

    unsigned short wMinorVerNum;	// Minor version number

    unsigned short wLibFlags;	// Library flags

} TLIBATTR, FAR * LPTLIBATTR;

For more information on national language support, see Supporting Multiple National Languages" , and refer to the NLS API reference material in the Windows NT documentation.


ITypeLib::FindName

HRESULT ITypeLib::FindName(szNameBuf, lHashVal, rgptinfo, rgmemid, 	pcFound)
OLECHAR FAR*  szNameBuf
unsigned long lHashVal
ITypeInfo FAR* FAR* rgptinfo
MEMBERID FAR* rgmemid
unsigned int  FAR*pcFound

Finds occurrences of a type description in a type library. This may be used to quickly verify that a name exists in a type library.

Parameter

szNameBuf

The name to search for.

lHashVal

A hash value to speed up the search, computed by LHashValOfName. If lHashVal = 0, the value will be computed for you.

rgptinfo

On return, an array of pointers to the type descriptions that contain the name specified in szNameBuf. May not be NULL.

rgmemid

An array of the MEMBERIDs of the found items; rgmemid[i] is the MEMBERID which indexes into the type description specified by rgptinfo[i]. May not be NULL.

pcFound

On entry, indicates how many instances to look for. For example, you may call with pcFound = 1 in order to find the first occurrence. In this case, the search stops when one is found.

On exit, indicates the number of instances that were found. If the in and out values of pcFound are identical, there may be more type descriptions that contain the name.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_CANTLOADLIBRARY	The library or DLL could not be loaded.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Passing *pcFound = n indicates that there is enough room in the rgptinfo and rgmemid arrays for n (ptinfo, memid) pairs.


ITypeLib::GetDocumentation

HRESULT ITypeLib::GetDocumentation(index, lpbstrName, lpbstrDocString, lpdwHelpContext, lpbstrHelpFile)
int index
BSTR FAR* lpbstrName
BSTR FAR* lpbstrDocString
unsigned long FAR* lpdwHelpContext
BSTR FAR* lpbstrHelpFile

Retrieves the library's documentation string, name of the complete Help file path and name, and the context ID for the library Help topic in the Help file.

Parameter

index

Index of the type description whose documentation is to be returned; if index is −1, then the documentation for the library itself is returned.

lpbstrName

Returns a BSTR allocated by the caller that contains the name of the specified item. If the caller does not need the item name, then lpbstrName can be NULL.

lpbstrDocString

Returns a BSTR allocated by the caller that contains the documentation string for the specified item. If the caller does not need the documentation string, then lpbstrDocString can be NULL.

lpdwHelpContext

Returns the Help context ID associated with the specified item. If the caller does not need the Help context ID, then lpdwHelpContext can be NULL.

lpbstrHelpFile

Returns a BSTR allocated by the caller that contains the fully qualified name of the Help file. If the caller does not need the Help filename, then lpbstrHelpFile can be NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Example

for (i = 0; i < utypeinfoCount; i++)

{

CHECKRESULT(ptlib->GetDocumentation(i, &bstrName, NULL, NULL, NULL));

.

.

.

SysFreeString(bstrName);

}


ITypeLib::GetLibAttr

HRESULT ITypeLib::GetLibAttr (lplptlibattr)
TLIBATTR FAR* FAR* lplptlibattr

Retrieves the structure containing the library's attributes.

Parameter

lplptlibattr

A pointer to a structure containing the library's attributes.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Comments

Use ITypeLib::ReleaseTLibAttr to free the memory occupied by the TLIBATTR structure.


ITypeLib::GetTypeComp

HRESULT ITypeLib::GetTypeComp(lplptcomp)
ITypeComp FAR* FAR* lplptcomp

Enables a client compiler to bind to the library's types, variables, constants and global functions.

Parameter

lplptcomp

Points to a pointer to the ITypeComp instance for this ITypeLib that a client compiler can use to bind to types in the ITypeLib and to the global functions, variables, and constants defined in the ITypeLib.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

The B ind function of the returned TypeComp binds to global functions, variables, constants, enumerated values, and coclass members. The Bind function also binds the names of the TYPEKINDS of TKIND_MODULE, TKIND_ENUM, and TKIND_COCLASS. These names shadow any global names defined within the type information.

ITypeComp::Bind and ITypeComp::BindType accept only unqualified names. ITypeLib::GetTypeComp returns a pointer to the ITypeComp interface which is then used to bind to global elements in the library. The names of some types (TKIND_ENUM, TKIND_MODULE, and TKIND_COCLASS) share the name space with variables, functions, constants, and enumerators. If a member requires qualification to differentiate it from other items in the name space, GetTypeComp can be called successively for each qualifier in order to bind to the desired member. This allows programming language compilers to access members of modules, enumerations, and coclasses, even though the member can't be bound to with a qualified name.


ITypeLib::GetTypeInfo

HRESULT ITypeLib::GetTypeInfo(index, lplpitinfo)
unsigned int	index
ITypeInfo FAR* FAR* lplpitinfo

Retrieves the specified type description in the library.

Parameters

index

Index of the ITypeInfo to be returned.

lplpitinfo

If successful, returns a pointer to the ITypeInfo.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
TYPE_E_ELEMENTNOTFOUND	Index is outside the range of 0 to GetTypeInfoCount() −1.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_REGISTRYACCESS	There was an error accessing the system registration database.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Example

CHECKRESULT(ptlib->GetTypeInfo((unsigned int) dwIndex,

 	&ptypeinfoCur));


ITypeLib::GetTypeInfoCount

unsigned int ITypeLib::GetTypeInfoCount( )

Comments

Returns the number of type descriptions in a type library.


ITypeLib::GetTypeInfoOfGuid

ITypeLib::HResult ITypeLib::GetTypeInfoOfGuid(lpguid, lplpitinfo)
REFGUID lpguid
ITypeInfo FAR* FAR* lplpitinfo

Retrieves the type description corresponding to the specified GUID.

Parameters

lpguid

Pointer to the globally unique ID of the type description.

lplpitinfo

Pointer to a pointer to the ITypeInfo.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
TYPE_E_ELEMENTNOTFOUND	No type description was found in the library with the specified guid.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_REGISTRYACCESS	There was an error accessing the system registration database.
TYPE_E_INVALIDSTATE	The type library could not be opened.


ITypeLib::GetTypeInfoType

HRESULT ITypeLib::GetTypeInfoType(index, ptypekind)
unsigned int index
TYPEKIND FAR* ptypekind

Retrieves the type of a type description.

Parameters

index

The index of the type description within the type library.

ptypekind

A pointer to the TypeKind for the type description.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
TYPE_E_ELEMENTNOTFOUND	Index is outside the range of 0 to GetTypeInfoCount() −1.


ITypeLib::IsName

HResult ITypeLib::IsName(szNameBuf, lHashVal, lpfName)
OLECHAR FAR* szNameBuf
unsigned long lHashVal
BOOL lpfName

Indicates whether a passed-in string contains the name of a type or member described in the library.

Parameter

szNameBuf

The string to test. If IsName is successful, szNameBuf is modified to match the case (capitalization) found in the type library.

lHashVal

The hash value of szNameBuf.

lpfName

Upon return, set to True if szNameBuf was found in the type library.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.


ITypeLib::ReleaseTLibAttr

VOID ITypeLib::ReleaseTLibAttr(lptlibattr)
TLIBATTR FAR* lptlibattr

Releases TLIBATTR originally obtained from ITypeLib::GetLibAttr.

Parameter

lptlibattr

Pointer to the TLIBATTR to be freed.

Comments

Releases the specified TLIBATTR. This TLIBATTR was previously obtained with a call to ITypeLib::GetLibAttr


ITypeInfo Interface

This section includes the following topics:

Type Descriptions
Structures and Enumerations
ITypeInfo::AddressOfMember
ITypeInfo::CreateInstance
ITypeInfo::GetContainingTypeLib
ITypeInfo::GetDllEntry
ITypeInfo::GetDocumentation
ITypeInfo::GetFuncDesc
ITypeInfo::GetIDsOfNames
ITypeInfo::GetImplTypeFlags
ITypeInfo::GetMops
ITypeInfo::GetNames
ITypeInfo::GetRefTypeInfo
ITypeInfo::GetRefTypeOfImplType
ITypeInfo::GetTypeAttr
ITypeInfo::GetTypeComp
ITypeInfo::GetVarDesc
ITypeInfo::Invoke
ITypeInfo::ReleaseFuncDesc
ITypeInfo::ReleaseTypeAttr
ITypeInfo::ReleaseVarDesc

Implemented by	Used by	Header filename
OLEAUT32.DLL	Tools that need to access the descriptions of objects contained in type libraries	OLEAUTO.H
The ITypeInfo interface is usually used for reading information about objects. For example, an object browser tool could use ITypeInfo to extract information about the characteristics and capabilities of objects from type libraries. (An example of typical browser code appears in Accessing OLE Automation Objects.)

Type information interfaces are intended to describe the parts of the application that can be called by outside clients, rather than those that might be used internally to build an application.

The ITypeInfo interface provides access to the following:

The set of function descriptions associated with the type; for interfaces, this contains the set of member functions in the interface.

The set of data member descriptions associated with the type; for structures, this contains the set of fields of the type.

The general attributes of the type such as whether it describes a structure, an interface, and so forth.

You can use the type description of an IDispatch interface to implement the IDispatch interface. See CreateStdDispatch in Dispatch Interfaces for more information.

An ITypeInfo instance provides various information about a type, and is used in different ways. A compiler could use an ITypeInfo to compile references to members of the type. A type interface browser could use it to find information about each member of the type. An IDispatch implementor could use it to provide automatic delegation of IDispatch calls to an interface.


Type Descriptions

The information associated with an object described by ITypeInfo can include a set of functions, a set of data members, and various type attributes. It is essentially the same as the information described by a C++ class declaration. A C++ class declaration can be used to define both interfaces and structures, as well as any combination of functions and data members. In addition to interfaces and structure definitions, the ITypeInfo interface is used to describe other types, including enumerations and aliases. Because the interface to a C file or library is simply a set of functions and variable declarations, ITypeInfo is used to describe these as well.

Type information comprises individual type descriptions. Each type description must have one of the following forms:

Table 8.    Recognized Type Information

Type description category	ODL keyword	Description
alias	typedef	An alias for another type.
enumeration	enum	An enumeration.
structure	struct	A structure.
union	union	A single data item that can have one of a specified group of types.
module	module	Data and functions not accessed through vtable entries.
IDispatch interface	dispinterface	IDispatch properties and methods accessed through IDispatch::Invoke.
OLE interface	interface	OLE member functions accessed through vtable entries.
Component object class	coclass	Description of a component object class description. Specifies an implementation of one or more OLE interfaces and one or more IDispatch interfaces.
Note that all bit flags that are not specifically used should be set to zero for future compatibility.

Alias

An alias has TypeKind = TKIND_ALIAS. An alias is an empty set of functions, an empty set of data members, and a type description which gives the actual type of the alias.

Enumeration

An enumeration has TypeKind = TKIND_ENUM. An enumeration is an empty set of functions and a set of constant data members.

Structure

A structure description has TypeKind = TKIND_RECORD. A structure is an empty set of functions and a set of per-instance data members.

Union

A union description has TypeKind = TKIND_UNION. A union is an empty set of functions and a set of per-instance data members, each of which has an instance offset of zero.

Module

A module has TypeKind = TKIND_MODULE. A module is a set of static functions and a set of static data members.

OLE-Compatible Interfaces

An interface definition has TypeKind = TKIND_INTERFACE. An interface is a set of pure virtual functions and an empty set of data members. If a type description contains any virtual functions, then the pointer to the virtual function table is the first 4 bytes of the instance.

The type information fully describes the member functions in the vtable, including parameter names and types and function return types. It may inherit from no more than one other interface.

Note that with interfaces and dispinterfaces, all members should have different names, except the accessor functions of properties. For property functions having the same name, the documentation string and Help context should be set for only one of the functions (because they conceptually define the same property).

Dispatch-Only Interfaces

These include objects (TypeKind = TKIND_DISPATCH) that support the IDispatch interface with a specification of the dispatch data members (for example, properties) and methods supported through the object's Invoke implementation. All members of the dispinterface should have different IDs, except for the accessor functions of properties.

Component Object Class (coclass) Interfaces

These include objects (TypeKind = TKIND_COCLASS) that support a set of implemented interfaces, which can be of either TKIND_INTERFACE or TKIND_DISPATCH.

The ITypeInfo interface  is used by programming tools and browsers to read the information in type libraries. It can be implemented by applications that wish to expose their objects, but exposing objects through a type library lets OLE Automation controllers browse and retrieve type information without creating an instance of the application. ITypeInfo references many structures and enumerations, described in the following sections.


Structures and Enumerations

Structures and enumerations used by ITypeInfo include the following:

ARRAYDESC
ELEMDESC
FUNCDESC
FUNCFLAGS
FUNCKIND
HREFTYPE
IDLDESC
IDLFLAGS
IMPLTYPEFLAGS
INVOKEKIND
MEMBERID
TYPEATTR
TYPEDESC
TYPEFLAGS
TYPEKIND
VARDESC
VARFLAGS
VARKIND


ARRAYDESC

A pointer to an ARRAYDESC is contained within the TYPEDESC, which describes a C-style array. The TYPEDESC describes the type of the array's elements and information describing the array's dimensions. It is defined as follows:

typedef struct tagARRAYDESC{

TYPEDESC tdescElem;	// Element type.

unsigned short cDims;	// Dimension count.

SAFEARRAYBOUND rgbounds[1];	// Variable length array containing one

// element for each dimension.

} ARRAYDESC;


ELEMDESC

An ELEMDESC structure includes the type description and process-transfer information for a variable, a function, or a function parameter. It is defined as follows:

typedef struct tagELEMDESC{

TYPEDESC tdesc;	// Type of the element.

IDLDESC idldesc;	// Information needed for transferring the

// element between processes.

}  ELEMDESC;


FUNCDESC

A FUNCDESC describes a function, and is defined as follows:

typedef struct tagFUNCDESC {

MEMBERID memid;	// Function member ID

SCODE FAR* lprgscode;	// Legal SCODES for the function.

ELEMDESC FAR* lprgelemdescParam;  // Array of parameter types

FUNCKIND funckind;	// specifies whether the function is 	// virtual, static, or dispatch-only.

INVOKEKIND invkind;	// Invocation kind; indicates if this is a 	//property function and if so, what kind.

CALLCONV callconv;	// Specifies the function's calling 	// convention.

short cParams;	// Count of total number of parameters.

short cParamsOpt;	// Count of optional parameters (detailed

// description below).

short oVft;	// For FUNC_VIRTUAL, specifies the offset in 	// the virtual function table.

short cScodes;	// Count of permitted Scodes.

ELEMDESC elemdescFunc;	// Contains the return type of the function.

unsigned short wFuncFlags; // See below for definition of flags.

}  FUNCDESC;

The field cParams specifies the total number of required and optional parameters.

The cParamsOpt field specifies the form of optional parameters accepted by the function, as follows:

A value of 0 specifies that no optional arguments are supported.

A value of −1 specifies that the method's last parameter is a pointer to a safe array of variants.  Any number of variant arguments greater than cParams − 1 must be packaged by the caller into a safe array and passed as the final parameter. This array of optional parameters must be freed by the caller after control is returned from the call.

Any other number indicates that the last n parameters of the function are variants and need not be specified explicitly by the caller. The parameters left unspecified should be filled in by the compiler or interpreter as variants of type VT_ERROR with the value DISP_E_PARAMNOTFOUND.

The fields cScodes and lprgscode store the count and the set of errors that a function can return. If cScodes = −1 then the set of errors is unknown.
If cScodes = −1 or cScodes = 0, then lprgscode is undefined.


FUNCFLAGS

The FUNCFLAGS enumeration is defined as follows:

typedef enum tagFUNCFLAGS {

FUNCFLAG_FRESTRICTED= 1

      , FUNCFLAG_FSOURCE= 0x2

      , FUNCFLAG_FBINDABLE= 0x4

      , FUNCFLAG_FREQUESTEDIT= 0x8

      , FUNCFLAG_FDISPLAYBIND= 0x10

      , FUNCFLAG_FDEFAULTBIND= 0x20

} FUNCFLAGS;

Value	Description
FUNCFLAG_FRESTRICTED	The function should not be accessible from macro languages. This flag is intended for system-level functions or functions that you do not want type browsers to display.
FUNCFLAG_FSOURCE	The function returns an object that is a source of events.
FUNCFLAG_FBINDABLE	The function that supports data binding.
FUNCFLAG_FDISPLAYBIND	The function that is displayed to the user as bindable; that is, FUNC_FBINDABLE must also be set.
FUNCFLAG_FDEFAULTBIND	The function that best represents the object.  Only one function in a dispinterface may have this attribute.


FUNCKIND

The FUNCKIND enumeration is defined as follows:

typedef enum tagFUNCKIND {

FUNC_VIRTUAL,

FUNC_PUREVIRTUAL,

FUNC_NONVIRTUAL,

FUNC_STATIC,

FUNC_DISPATCH,

} FUNCKIND;

Value	Description
FUNC_PUREVIRTUAL	The function is accessed via the virtual function table and takes an implicit this pointer.
FUNC_VIRTUAL	The function is accessed the same as PUREVIRTUAL, except the function has an implementation.
FUNC_NONVIRTUAL	The function is accessed by static address and takes an implicit this pointer.
FUNC_STATIC	The function is accessed by static address and does not take an implicit this pointer.
FUNC_DISPATCH	The function can be accessed only via IDispatch.


HREFTYPE

HREFTYPE is a handle that identifies a type description.

typedef unsigned long HREFTYPE;


IDLDESC

An IDLDESC contains information needed for transferring a structure element, parameter, or function return value between processes, and is defined as follows:

typedef struct FARSTRUCT tagIDLDESC {

unsigned long dwReserved;

unsigned short wIDLFlags;	/* IN, OUT, and so on */

} IDLDESCtypedef struct tagIDLDESC{

Note that dwReserved is reserved for future use and should be set to NULL.


IDLFLAGS

The IDLFLAGS are defined as follows:

#define IDLFLAG_NONE	0

#define IDLFLAG_FIN	0x1

#define IDLFLAG_FOUT	0x2

Value	Description
IDLFLAG_NONE	Whether the parameter passes or receives information is unspecified. IDispatch interfaces can use this flag.
IDLFLAG_FIN	Parameter passes information from the caller to the callee.
IDLFLAG_FOUT	Parameter returns information from the callee to the caller.
IDLFLAG_FIN | IDLFLAG_FOUT	Parameter passes and returns information.


IMPLTYPEFLAGS

The IMPLTYPEFLAGS are defined as follows:

/* IMPLTYPE Flags */

#define IMPLTYPEFLAG_FDEFAULT	0x1

#define IMPLTYPEFLAG_FSOURCE	0x2

#define IMPLTYPEFLAG_FRESTRICTED	0x4

Value	Description
IMPLTYPEFLAG_FDEFAULT	The interface or dispinterface represents the default for the source or sink.
IMPLTYPEFLAG_FSOURCE	This member of a coclass is called rather than implemented.
IMPLTYPEFLAG_FRESTRICTED	The member should not be displayed or programmable by users.


INVOKEKIND

The INVOKEKIND enumeration is defined as follows:

typedef enum tagINVOKEKIND {

INVOKE_FUNC = DISPATCH_METHOD,

INVOKE_PROPERTYGET = DISPATCH_PROPERTYGET,

INVOKE_PROPERTYPUT = DISPATCH_PROPERTYPUT,

INVOKE_PROPERTYPUTREF = DISPATCH_PROPERTYPUTREF

} INVOKEKIND;

Value	Description
INVOKE_FUNC	The member is called using normal function invocation syntax.
INVOKE_PROPERTYGET	The function is invoked via normal property access syntax.
INVOKE_PROPERTYPUT	The function is invoked via property value assignment syntax. Syntactically, a typical programming language might represent changing a property in the same way as assignment; for example: object.property := value.
INVOKE_PROPERTYPUTREF	The function is invoked via property reference assignment syntax.
Note that in C, value assignment is written as *pobj1 = *pobj2, while reference assignment is written as pobj1 = pobj2. Other languages have other syntactic conventions. A property or data member may support value assignment only, reference assignment only, or both. For a more detailed description of property functions, see Dispatch Interfaces. These enumeration constants are the same constants that are passed to IDispatch::Invoke to specify the way in which a function is invoked.


MEMBERID

MEMBERID identifies the member in a type description. For IDispatch interfaces, this is the same as DISPID.

typedef DISPID MEMBERID;


TYPEATTR

The TYPEATTR structure contains attributes of an ITypeInfo, and is defined as follows:

typedef struct FARSTRUCT tagTYPEATTR {

GUID guid;	// the GUID of the TypeInfo

    LCID lcid;	// locale of member names and doc

// strings

    unsigned long dwReserved;

    MEMBERID memidConstructor;	// ID of constructor,MEMBERID_NIL if

// none

    MEMBERID memidDestructor;	// ID of destructor, MEMBERID_NIL if

// none

    OLECHAR FAR* lpstrSchema;	// reserved for future use

    unsigned long cbSizeInstance;// the size of an instance of 	// this type

    TYPEKIND typekind;	// the kind of type this typeinfo 	// describes

    unsigned short cFuncs;	// number of functions

    unsigned short cVars;	// number of variables / data 	// members

    unsigned short cImplTypes;	// number of implemented interfaces

    unsigned short cbSizeVft;	// the size of this type's virtual 	// func table

    unsigned short cbAlignment;	// byte-alignment for an instance  	// of this type

    unsigned short wTypeFlags;

    unsigned short wMajorVerNum;	// major version number

    unsigned short wMinorVerNum;	// minor version number

    TYPEDESC tdescAlias;	// if typekind == TKIND_ALIAS, 	// specifies the type for which 	// this type is an alias

    IDLDESC idldescType;	// IDL attributes of the 	// described type

} TYPEATTR, FAR* LPTYPEATTR;

The cbAlignment field indicates how addresses are aligned. A value of 0 indicates alignment on the 64K boundary; 1 indicates no special alignment. For other values, n indicates aligned on byte n.


TYPEDESC

A TYPEDESC, which describes the type of a variable, the return type of a function, or the type of a function parameter, is defined as follows:

typedef struct FARSTRUCT tagTYPEDESC {

union {

/* VT_PTR - the pointed-at type */

struct FARSTRUCT tagTYPEDESC FAR* lptdesc;

/* VT_CARRAY */

struct FARSTRUCT tagARRAYDESC FAR* lpadesc;

/* VT_USERDEFINED - this is used to get a TypeInfo for a user-

   defined type */

HREFTYPE hreftype;

}UNION_NAME(u);

VARTYPE vt;

} TYPEDESC;

If the variable is VT_SAFEARRAY or VT_PTR, the union portion of the TYPEDESC contains a pointer to a TYPEDESC that specifies the element type.


TYPEFLAGS

The TYPEFLAGS enumeration is defined as follows:

typedef enum tagTYPEFLAGS {

TYPEFLAG_FAPPOBJECT = 0x01

    , TYPEFLAG_FCANCREATE = 0x02

    , TYPEFLAG_FLICENSED = 0x04

} TYPEFLAGS;

Value	Description
TYPEFLAG_FAPPOBJECT	A type description that describes an Application object.
TYPEFLAG_FCANCREATE	Instances of the type can be created by ITypeInfo::CreateInstance.
TYPEFLAG_FLICENSED	The type is licensed.
TYPEFLAG_FAPPOBJECT may be used on type descriptions with
TypeKind = TKIND_COCLASS, and indicates that the type description describes an Application object.

Members of the Application object are globally accessible in that the Bind method of the ITypeComp instance associated with the library will bind to the members of an Application object just as it does for type descriptions with TypeKind = TKIND_MODULE.

The type description implicitly defines a global variable with the same name and type as the type described by the type description. This variable is also globally accessible. Specifically, when Bind is passed the name of an Application object, a VARDESC is returned describing the implicit variable. The ID of the implicitly created variable is always ID_DEFAULTINST.

When the CreateInstance function of an Application object type description is called, it uses GetActiveObject to retrieve the Application object. If GetActiveObject fails because the application is not running, then CreateInstance calls CoCreateInstance (which should start the application).

When TYPEFLAG_FCANCREATE is True, ITypeInfo::CreateInstance can create an instance of the type. Note that this is currently true only for component object classes for which a GUID has been specified.


TYPEKIND

The TypeKind enumeration is defined as follows:

typedef enum tagTYPEKIND {

      TKIND_ENUM = 0

    , TKIND_RECORD

    , TKIND_MODULE

    , TKIND_INTERFACE

    , TKIND_DISPATCH

    , TKIND_COCLASS

    , TKIND_ALIAS

    , TKIND_UNION

    , TKIND_MAX	/* end of enum marker */

} TYPEKIND;

Value	Description
TKIND_ALIAS	A type that is an alias for another type.
TKIND_COCLASS	A set of implemented component object interfaces.
TKIND_DISPATCH	A set of methods and properties that are accessible via IDispatch::Invoke.
TKIND_ENUM	A set of enumerators.
TKIND_INTERFACE	A type that has virtual functions, all of which are pure.
TKIND_MODULE	A module which can only have static functions and data (for instance, a DLL).
TKIND_RECORD	A struct with no methods.
TKIND_UNION	A union, all of whose members have offset zero.


VARDESC
VeRDESC

A VARDESC structure describes a variable, constant, or data member, and is defined as follows:

typedef struct FARSTRUCT tagVARDESC {

MEMBERID memid;

OLECHAR FAR* lpstrSchema;/* reserved for future use */

union {

/* VAR_PERINSTANCE - the offset of this 	   variable within the instance */

unsigned long oInst;

/* VAR_CONST—the value of the constant */

VARIANT FAR* lpvarValue;

} UNION_NAME(u);

ELEMDESC elemdescVar;

unsigned short wVarFlags;

VARKIND varkind;

} VARDESC


VARFLAGS

The VARFLAGS enumeration is defined as follows:

typedef enum tagVARFLAGS {

      VARFLAG_FREADONLY= 1

      , VARFLAG_FSOURCE= 0x2

      , VARFLAG_FBINDABLE= 0x4

      , VARFLAG_FREQUESTEDIT= 0x8

      , VARFLAG_FDISPLAYBIND= 0x10

      , VARFLAG_FDEFAULTBIND= 0x20

} VARFLAGS;

Value	Description
VARFLAG_READONLY	Assignment to the variable should not be allowed.
VARFLAG_FSOURCE	The variable returns an object that is a source of events.
VARFLAG_FBINDABLE	The variable supports data binding.
VARFLAG_FDISPLAYBIND	The variable is displayed to the user as bindable; that is, VARFLAG_FBINDABLE must also be set.
VARFLAG_FDEFAULTBIND	The variable is the single property that best represents the object.  Only one variable in a dispinterface may have this attribute.


VARKIND

The VARKIND enumeration is defined as follows:

typedef enum tagVARKIND {

VAR_PERINSTANCE,

VAR_STATIC,

VAR_CONST,

VAR_DISPATCH

} VARKIND;

Value	Description
VAR_PERINSTANCE	The variable is a field or member of the type; it exists at a fixed offset within each instance of the type.
VAR_STATIC	There is only one instance of the variable.
VAR_CONST	The VARDESC describes a symbolic constant. There is no memory associated with it.
VAR_DISPATCH	The variable can only be accessed via IDispatch::Invoke.


ITypeInfo::AddressOfMember

HRESULT ITypeInfo::AddressOfMember(memid, invkind, lplpvoid)
MEMBERID memid
INVOKEKIND invkind
VOID FAR* FAR* lplpvoid

Retrieves the addresses of static functions or variables, such as those defined in a DLL.

Parameters

memid

Member ID of the static member whose address is to be retrieved.
The member ID is defined by DISPID.

invkind

Specifies whether the member is a property, and if so, what kind.

lplpvoid

Upon return, points to a pointer to the static member.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_WRONGTYPEKIND	Type mismatch.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_DLLFUNCTIONNOTFOUND	The function could not be found in the DLL.
TYPE_E_CANTLOADLIBRARY	The type library or DLL could not be loaded.
STG_E_OLDDLL	The COMPOBJ.DLL is out of date.
Comments

The addresses are valid until the caller releases its reference to the type description. Note that the invkind parameter can be ignored unless the address of a property function is being requested.


ITypeInfo::CreateInstance

HRESULT ITypeInfo::CreateInstance(punkOuter, riid, ppvObj)
IUnknown FAR* punkOuter
REFIID riid
VOID FAR* FAR* ppvObject

Creates a new instance of a type that describes a component object class (coclass).

Parameters

punkOuter

A pointer to the instance that was created.

riid

An ID for the interface the caller will use to communicate with the resulting object.

ppvObject

On return, points to a pointer to an instance of the created object.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
TYPE_E_WRONGTYPEKIND	Type mismatch.
E_INVALIDARG	One or more of the arguments is invalid.
E_NOINTERFACE	OLE could not find an implementation of one or more required interfaces.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Other returns	Additional errors may be returned from GetActiveObject or CoCreateInstance.
Comments

For types that describe a component object class (coclass), CreateInstance creates a new instance of the class. Normally CreateInstance calls CoCreateInstance with the type description's GUID. For an Application object, it first calls GetActiveObject. If the application is active, GetActiveObject returns the active object; otherwise, if GetActiveObject fails, CreateInstance calls CoCreateInstance.


ITypeInfo::GetContainingTypeLib

HRESULT ITypeInfo::GetContainingTypeLib(lplptlib, lpindex)
ITypeLib FAR* FAR* lplptlib
unsigned int FAR* lpindex

Retrieves the containing type library and the index of the type description within that type library.

Parameters

lplptlib

Upon return, points to a pointer to the containing type library.

lpindex

Upon return, points to the index of the type description within the containing type library.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_NOINTERFACE	OLE could not find an implementation of one or more required interfaces.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.


ITypeInfo::GetDllEntry

HRESULT ITypeInfo::GetDllEntry(memid, invkind, lpbstrDllName, lpbstrName, lpwOrdinal)
MEMBERID memid
INVOKEKIND  invkind
BSTR FAR* lpbstrDllName
BSTR FAR* lpbstrName
unsigned short FAR* lpwOrdinal

Retrieves a description or specification of an entry point for a function in a DLL.

Parameters

memid

ID of the member function whose DLL entry description is to be returned.

invkind

Specifies the kind of member identified by memid. This is important for properties, because one memid can identify up to three separate functions.

lpbstrDllName

Pointer to a BSTR allocated by the callee. If not NULL, the function sets lpbstrDllName to point to the DLL name.

lpbstrName

Pointer to a BSTR allocated by the callee. If not NULL, the function sets lpbstrName to point to the name of the entry point; if the entry point is specified by an ordinal, the passed-in pointer is set to NULL.

lpwOrdinal

If not NULL, and if the function is defined by ordinal, then lpwOrdinal is set to point to the ordinal.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_NOINTERFACE	OLE could not find an implementation of one or more required interfaces.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

The caller passes in a MEMID representing the member function whose entry description is desired. If the function has a DLL entry point, the name of the DLL containing the function, and either its name or ordinal identifier are placed in the  passed-in pointers allocated by the caller. If there is no DLL entry point for the function, an error is returned.


ITypeInfo::GetDocumentation

HRESULT ITypeInfo::GetDocumentation(memid, lpbstrName, lpbstrDocString, lpdwHelpContext, lpbstrHelpFile)
MEMBERID  memid
BSTR FAR* lpbstrName
BSTR FAR* lpbstrDocString
unsigned long FAR* lpdwHelpContext
BSTR FAR* lpbstrHelpFile

Retrieves the documentation string, name of the complete Help file path and name, and the context ID for the Help topic for a specified type description.

Parameters

memid

ID of the member whose documentation is to be returned.

lpbstrName

Pointer to a BSTR allocated by the callee into which the name of the specified item is placed. If the caller does not need the item name, lpbstrName can be NULL.

lpbstrDocString

Pointer to a BSTR allocated by the callee into which the documentation string for the specified item is placed. If the caller does not need the documentation string, lpbstrDocString can be NULL.

lpdwHelpContext

Pointer to the Help context associated with the specified item. If the caller does not need the Help context, the lpdwHelpContext can be NULL.

lpbstrHelpFile

Pointer to a BSTR allocated by the callee into which the fully qualified name of the Help file is placed. If the caller does not need the Help filename, lpbstrHelpFile can be NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Provides access to the documentation for the Member specified by the memid parameter. If the passed-in memid is MEMBERID_NIL, then the documentation for the type description is returned.

Example

CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL,

NULL));

.

.

.

SysFreeString (bstrName);


ITypeInfo::GetFuncDesc

HRESULT ITypeInfo::GetFuncDesc(index, lplpfuncdesc)
unsigned int index
FUNCDESC FAR* FAR* lplpfuncdesc

Retrieves the FUNCDESC structure containing information about a specified function.

Parameters

index

Index of the function whose description is to be returned. The index should be in the range of 0 to 1 less than the number of functions in this type.

lplpfuncdesc

Upon return, points to a pointer to a FUNCDESC that describes the specified function.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Comments

Provides access to a FUNCDESC which describes the function with the specified index. The number of functions in the type is one of the attributes contained in the TYPEATTR structure.

Example

CHECKRESULT(ptypeinfo->GetFuncDesc(i, &pfuncdesc));

idMember = pfuncdesc->elemdescFunc.ID;

CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL, NULL));

ptypeinfo->ReleaseFuncDesc(pfuncdesc);


ITypeInfo::GetIDsOfNames

HRESULT ITypeInfo::GetIDsOfNames(rgszNames, cNames, rgmemid)
OLECHAR FAR* FAR* rgszNames
unsigned int cNames
MEMBERID FAR* rgmemid

Maps between member names and member IDs, and parameter names and parameter IDs.

Parameters

rgszNames

Passed-in pointer to an array of names to be mapped.

cNames

Count of the names to be mapped.

rgmemid

Caller-allocated array in which name mappings are placed.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
DISP_E_UNKNOWNNAME	One or more of the names could not be found.
DISP_E_UNKNOWNLCID	The LCID could not be found in the OLE DLLs.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Maps the name of a member (rgszNames[0]) and its parameters
(rgszNames[1] ...rgszNames[cNames  1]) to the ID of the member (rgid[0]) and the IDs of the specified parameters (rgid[1] ... rgid[cNames  1]). Note that the IDs of parameters are 0 for the first parameter in the member function's argument list, 1 for the second, and so on.

If the type description inherits from another type description, ITypeInfo::GetIDsOfNames recurses on the base type description if necessary.


ITypeInfo::GetImplTypeFlags

HRESULT ITypeInfo:: GetImplTypeFlags(index, pimpltypeflags)

unsigned int index
MEMBERID FAR* pimpltypeflags

Retrieves the IMPLTYPEFLAGS for one item in a type description.

Parameters

index

Index of the item in the type description for which to get the flags.

pimpltypeflags

On return, pointer to the IMPLTYPEFLAGS.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.


ITypeInfo::GetMops

HRESULT ITypeInfo::GetMops(memid, lpbstrMops)
MEMBERID memid
BSTR FAR* lpbstrMops

Retrieves marshaling information.

Parameters

memid

Member ID indicating which marshaling information is sought.

lpbstrMops

Upon return, points to a pointer to the opcode string used in marshaling the fields of the structure described by the referenced type description.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

If the passed-in member ID is MEMBERID_NIL, the function returns the opcode string for marshaling the fields of the structure described by the type description. Otherwise, it returns the opcode string for marshaling the function specified by the index.


ITypeInfo::GetNames

HRESULT ITypeInfo::GetNames(memid, rgbstrNames, cNameMax, lpcName)
MEMBERID memid
BSTR FAR* rgbstrNames
unsigned int cNameMax
unsigned int FAR* lpcName

Retrieves the variable with the specified member ID, or the name of the function and parameter names corresponding to the specified function ID.

Parameters

memid

ID of member whose name (or names) is to be returned.

rgbstrNames

Pointer to caller-allocated array. On return, each of these lpcName elements is filled in to point to a BSTR containing the name (or names) associated with the member.

cNameMax

Length of the passed-in rgbstrNames array.

lpcName

On return, points to number representing the number of names in rgbstrNames array.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

The caller must release the returned BSTR array. If the member ID identifies a property that is implemented with property functions, the property name is returned.

For property put and put reference functions, the right side of the assignment is unnamed. If cNameMax is less than is required to return all the names of parameters of a function, then only the names of the first cNameMax  1 parameters are returned. The names of the parameters are returned in the array in the same order they appear elsewhere in the interface, for example, in the same order they appear in the parameter array associated with the FUNCDESC.


ITypeInfo::GetRefTypeInfo

HRESULT ITypeInfo::GetRefTypeInfo(hreftype, lplptinfo)
HREFTYPE hreftype
ITypeInfo FAR* FAR* lplptinfo

If a type description references other type descriptions, this function retrieves the referenced type descriptions.

Parameters

hreftype

Handle to the referenced type description to be returned.

lplptinfo

Points to a pointer to the referenced type description.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_REGISTRYACCESS	There was an error accessing the system registration database.
TYPE_E_LIBNOTREGISTERED	The type library was not found in the system registration database.
Comments

On return, the second parameter contains a pointer to a pointer to a type description that is referenced by this type description. A type description must have a reference to each type description that occurs as the type of any of its variables, function parameters, or function return types. For example, if the type of a data member is a record type, TypeDesc for that data member contains the hreftype of a referenced type description. To get a pointer to the type description, the reference is passed to GetRefTypeInfo.


ITypeInfo::GetRefTypeOfImplType

HRESULT ITypeInfo::GetRefTypeOfImplType(index, lphreftype)
unsigned int index
HREFTYPE FAR* lphreftype

If this type description describes a component object class, the function retrieves the type description of the specified implemented interface types. For an interface, GetRefTypeOfImplType returns the type information for inherited interfaces, if any exist.

Parameters

index

Index of the implemented type whose handle is returned. The valid range is 0 to the cImplTypes field in the TYPLIBATTR structure.

lphreftype

Upon return, points to a handle for the implemented interface (if any). This handle can be passed to ITypeInfo::GetRefTypeInfo to get the type description.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
TYPE_E_ELEMENTNOTFOUND	Passed index is outside the range 0 to 1 less than the number of function descriptions.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.


ITypeInfo::GetTypeAttr

HRESULT ITypeInfo::GetTypeAttr(lplptypeattr)
TYPEATTR FAR* FAR* lplptypeattr

Retrieves a TYPEATTR structure containing the attributes of the type description.

Parameter

lplptypeattr

Upon return, points to a pointer to a structure that contains the attributes of this type description.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Comments

The number of variables in the type is one of the attributes contained in the TYPEATTR structure. To free the TYPEATTR structure, use ITypeInfo::ReleaseTypeAttr

Example

CHECKRESULT(ptypeinfoCur->GetTypeAttr(&ptypeattrCur));


ITypeInfo::GetTypeComp

HRESULT ITypeInfo::GetTypeComp(lplpcomp)
ITypeComp FAR* FAR* lplpcomp

Retrieves the ITypeComp interface for the type description, which enables a client compiler to bind to the type description's members.

Parameter

lplpcomp

Upon return, points to a pointer to the ITypeComp of the containing type library.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
SCODE	Meaning
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

A client compiler can use the ITypeComp interface to bind to members of the type.


ITypeInfo::GetVarDesc

HRESULT ITypeInfo::GetVarDesc(index, lplpvardesc)
unsigned int index
VARDESC FAR* FAR* lplpvardesc

Retrieves a VARDESC structure describing the specified variable.

Parameters

index

Index of the variable whose description is to be returned. The index should be in the range of 0 to 1 less than the number of variables in this type.

lplpvardesc

Upon return, points to a pointer to a VARDESC that describes the specified variable.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Comments

To free the VARDESC structure, use ReleaseVarDesc.

Example

CHECKRESULT(ptypeinfo->GetVarDesc(i, &pvardesc));

idMember = pvardesc->elemdescVar.ID;

CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL, NULL));

ptypeinfo->ReleaseVarDesc(pvardesc);


ITypeInfo::Invoke

HRESULT ITypeInfo::Invoke(lpvInstance, dispidMember, wFlags,
pdispparams, pvargResult, pexcepinfo, puArgErr)
VOID FAR* lpvInstance
MEMBERID memid
unsigned short wFlags
DISPPARAMS FAR* pdispparams
VARIANT FAR* pvargResult
EXCEPINFO FAR* pexcepinfo
unsigned int FAR* puArgErr

Invokes a method or accesses a property of an object that implements the interface described by the type description.

Parameters

lpvInstance

Pointer to an instance of the interface described by this type description.

memid

Identifies the interface member.

wFlags

Flags describing the context of the invoke call, as follows:

Value	Description
DISPATCH_METHOD	The member was accessed as a method. If there is ambiguity, both this and the DISPATCH_PROPERTYGET flag may be set.
DISPATCH_PROPERTYGET	The member is being retrieved as a property or data member.
DISPATCH_PROPERTYPUT	The member is being changed as a property or data member.
DISPATCH_PROPERTYPUTREF	The member is being changed via a reference assignment, rather than a value assignment. This value is only valid when the property accepts a reference to an object.
pdispparams

Points to a structure containing an array of arguments, an array of DISPIDs for named arguments, and counts for number of elements in the arrays.

pvargResult

Should be NULL if the caller expects no result; otherwise, it should be a pointer to the location at which the result is to be stored. If wFlags specifies DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF, pvargResult is ignored.

pexcepinfo

Points to an exception information structure, which is filled in only if DISP_E_EXCEPTION is returned. May not be NULL.

puArgErr

If Invoke returns DISP_E_TYPEMISMATCH, puArgErr indicates the index (within rgvarg) of the argument with incorrect type. If more than one argument has an error, puArgErr indicates only the first argument with an error. Note that arguments in pdispparams->rgvarg appear in reverse order, so the first argument is the one having the highest index in the array. Can't be NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_REGISTRYACCESS	There was an error accessing the system registration database.
TYPE_E_LIBNOTREGISTERED	The type library was not found in the system registration database.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_WRONGTYPEKIND	Type mismatch.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_BADMODULEKIND	The module does not support Invoke.
Other returns	Any of the IDispatch::Invoke errors may also be returned.
Comments

Use Invoke to access a member of an object or invoke a method that implements the interface described by this type description. For objects that support the IDispatch interface, Invoke can be used to implement IDispatch::Invoke. ITypeInfo::Invoke takes a pointer to an instance of the class. Otherwise, its parameters are the same as IDispatch::Invoke, except that ITypeInfo::Invoke omits the REFIID and LCID parameters. When called, ITypeInfo::Invoke performs the actions described by the IDispatch::Invoke parameters on the specified instance.


ITypeInfo::ReleaseFuncDesc

VOID ITypeInfo::ReleaseFuncDesc(lpfuncdesc)
FUNCDESC FAR* lpfuncdesc

Releases a FUNCDESC previously returned by GetFuncDesc.

Parameter

lpfuncdes

Pointer to the FUNCDESC to be freed.

Comments

ReleaseFuncDesc releases a FUNCDESC that was returned via ITypeInfo::GetFuncDesc

Example

ptypeinfoCur->ReleaseFuncDesc(pfuncdesc);


ITypeInfo::ReleaseTypeAttr

VOID ITypeInfo::ReleaseTypeAttr(lptypeattr)
TYPEATTR FAR* lptypeattr

Releases a TYPEATTR previously returned by GetTypeAttr.

Parameter

lptypeattr

Pointer to the TYPEATTR to be freed.

Comments

ReleaseTypeAttr releases a TYPEATTR that was returned via ITypeInfo::GetTypeAttr.


ITypeInfo::ReleaseVarDesc

VOID ITypeInfo::ReleaseVarDesc(lpvardesc)
VARDESC FAR* lpvardesc

Releases a VARDESC previously returned by GetVarDesc.

Parameter

lpvardesc

Pointer to the VARDESC to be freed.

Comments

ReleaseVarDesc releases a VARDESC that was returned via ITypeInfo::GetVarDesc

Example

VARDESC     FAR *pvardesc;

CHECKRESULT(ptypeinfo->GetVarDesc(i, &pvardesc));

idMember = pvardesc->elemdescVar.id;

CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL, NULL));

ptypeinfo->ReleaseVarDesc(pvardesc);


ITypeComp Interface

This section includes the following topics:

Structures and Enumerations
ITypeComp::Bind
ITypeComp::BindType

Implemented by	Used by	Header filename
OLEAUT32.DLL	Tools that compile references to objects contained in type libraries	OLEAUTO.H
Binding is the process of mapping names to types and type members. The ITypeComp interface provides a fast way to access information that compilers need when binding to and instantiating structures and interfaces.


Structures and Enumerations

BINDPTR

A union containing a pointer to a FUNCDESC, VARDESC, or an ITypeComp interface.

typedef union tagBINDPTR {

    FUNCDESC FAR* lpfuncdesc;

    VARDESC FAR* lpvardesc;

    ITypeComp FAR* lptcomp;

} BINDPTR;

DESCKIND

Identifies the type of the type description being bound to.

typedef enum tagDESCKIND {

DESCKIND_NONE,

DESCKIND_FUNCDESC,

DESCKIND_VARDESC,

DESCKIND_TYPECOMP,

DESCKIND_IMPLICITAPPOBJ

} DESCKIND;

Comments

Value	Description
DESCKIND_NONE	Indicates that no match was found.
DESCKIND_FUNCDESC	Indicates that a FUNCDESC has been returned.
DESCKIND_VARDESC	Indicates that a VARDESC has been returned.
DESCKIND_TYPECOMP	Indicates that a TYPECOMP has been returned.
DESCKIND_IMPLICITAPPOBJ	Indicates that an IMPLICITAPPOBJ has been returned.


ITypeComp::Bind

HRESULT ITypeComp::Bind(szName, lHashVal, wFlags, lplptinfo, lpdesckind, lpbindptr)
OLECHAR FAR* szName
unsigned long lHashVal
unsigned short wFlags
ITypeInfo FAR* FAR* lplptinfo
DESCKIND FAR* lpdesckind
BINDPTR FAR* lpbindptr

Maps a name to a member of a type, or binds global variables and functions contained in a type library.

Parameters

szName

Name to be bound.

lHashVal

Hash value for the name computed by LHashValOfNameSys

wFlags

Flags word containing one or more of the INVOKE flags defined in the INVOKEKIND enumeration. Specifies whether the name was referenced as a method or as a property. When binding to a variable, specify the INVOKE_PROPERTYGET flag.

lplptinfo

If a FUNCDESC or VARDESC was returned, then lplptinfo points to a pointer to the type description that contains the item to which it is bound.

desckind

Specifies whether the name bound to a VARDESC, a FUNCDESC, or a TYPECOMP. Points to DESCKIND_NONE if there was no match.

lpbindptr

Upon return, contains a pointer to the bound-to VARDESC, FUNCDESC, or ITypeComp.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_AMBIGUOUSNAME	More than one instance of this name occurs in the type library.
Comments

Used for binding to the variables and methods of a type or for binding to the global variables and methods contained within a type library. The returned BINDPTR (lplpbindptr) points to a VARDESC, a FUNCDESC, or an ITypeComp as specified by the DESCKIND. If a data member or method is bound to, then lplptinfo points to the containing type description. It is possible that Bind will bind to a nested binding context, in which case an ITypeComp instance and a NULL type description pointer are returned. For example, if the ITypeComp instance of a type library is passed the name of a type description with TypeKind TKIND_MODULE, TKIND_ENUM, or TKIND_COCLASS, then the ITypeComp instance of that type description is returned.

This provides support for compiling references to the members of a type description that are qualified by the name of the type description. For example, a module function can be referenced by modulename.functionname.

As with other methods of ITypeComp, ITypeInfo, and ITypeLib, the caller is responsible for releasing any returned object instances or structures. If a VARDESC or FUNCDESC is returned, the caller is responsible for deleting it via the returned type description and releasing the type description instance itself; otherwise, if an ITypeComp instance is returned, the caller must release it.

A special case for binding occurs when a client calls a type library's Bind method, passing it the name of a member of an Application object class (that is, a class that has the TYPEFLAG_FAPPOBJECT flag set). In this case, DESCKIND_IMPLICITAPPOBJ is returned along with a VARDESC that describes the Application object and the ITypeInfo of the Application object class.

It is the client's responsibility to get the ITypeComp of the Application object class and reinvoke its Bind method with the name initially passed to the type library's ITypeComp.

The caller should use the returned ITypeInfo pointer (lplptinfo) to get the address of the member.

Note that the wflags parameter is the same as the wflags parameter in IDispatch::Invoke.


ITypeComp::BindType

HRESULT ITypeComp::BindType(szName, lHashVal, lplpitinfo, lplpitcomp)
OLECHAR FAR* szName
unsigned long lHashVal
ITypeInfo FAR* FAR* lplpitinfo
ITypeComp FAR* FAR* lplpitcomp

Binds to the type descriptions contained within a type library.

Parameters

szName

Name to be bound.

lHashVal

Hash value for the name computed by LHashValOfName.

lplpitinfo

Upon return, contains a pointer to a pointer to an ITypeInfo of the type to which it was bound.

lplpitcomp

 Reserved for future use. Pass NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
SCODE	Meaning
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_AMBIGUOUSNAME	More than one instance of this name occurs in the type library.
Comments

Used for binding a type name to the ITypeInfo which describes that type. This function is invoked on the ITypeComp returned by an ITypeLib::GetTypeComp to bind to types defined within that library. It could also be used in the future for binding to nested types.


Type Compilation Functions

This section includes the following topics:

LHashValOfName
LHashValOfNameSys


LHashValOfName

unsigned long LHashValOfName(lcid, szName)
LCID lcid
OLECHAR FAR* szName

Computes a hash value for a name that can then be passed to ITypeComp::Bind, ITypeComp::BindType, ITypeLib::FindName, or ITypeLib::IsName.

Parameters

lcid

The locale ID for the string.

szName

String whose hash value is to be computed.

Return Value

A 32-bit hash value representing the name passed in.

Comments

This function is equivalent to LHashValOfNameSys.  The OLEAUTO.H header file contains macros that define LHashValOfName as LHashValOfNameSys with the target operating system (syskind), based on your build preprocessor flags.

LHashValOfName computes a 32-bit hash value for a name which can then be passed to ITypeComp::Bind, ITypeComp::BindType, ITypeLib::FindName, or ITypeLib::IsName.  The returned hash value is independent of the case of the characters in szName as long as the language of the name is one of the languages supported by the National Language Specification API.  In general, for any two strings, if those strings match when a case-insensitive comparison is done using any language, then they will produce the same hash value. Specifically, for all locales except Japanese, Korean, and Chinese LCIDs, the hash value is insensitive to case and spacing characters. This behavior is similar to CompareString with NORM_IGNORECASE and NORM_IGNORENONSPACE.  In Japanese, Korean, and Chinese LCIDs, the hash value is insensitive to case, width, and kanatype. This behavior is similar to CompareString with NORM_IGNORECASE, NORM_IGNORENONSPACE, and NORM_IGNOREKANATYPE.

Refer to Supporting Multiple National Languages,Exposing OLE Automation Objects, for more information.


LHashValOfNameSys

unsigned long LHashValOfName(syskind, lcid, szName)
SYSKIND syskind
LCID lcid
OLECHAR FAR* szName

Computes a hash value for a name that can then be passed to ITypeComp::Bind, ITypeComp::BindType, ITypeLib::FindName, or ITypeLib::IsName

Parameters

syskind

The SYSKIND of the target operating system.

lcid

The locale ID for the string.

szName

String whose hash value is to be computed.

Return Value

A 32-bit hash value representing the name passed in.


Type Library Loading and Registration Functions

This section includes the following topics:

LoadTypeLib
LoadRegTypeLib
RegisterTypeLib
QueryPathOfRegTypeLib


LoadTypeLib

HRESULT LoadTypeLib(szFileName, lplptlib)
OLECHAR FAR* szFileName
ITypeLib FAR* FAR* lplptlib

Loads and registers a type library.

Parameters

szFileName

Contains the name of the file from which LoadTypeLib should attempt to load a type library.

lplptlib

On return, contains a pointer to a pointer to the loaded type library.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_UNKNOWNLCID	The LCID is not supported by this system.
TYPE_E_CANTLOADLIBRARY	The type library or DLL could not be loaded.
Other returns	All FACILITY_STORAGE errors may also be returned.
Comments

LoadTypeLib loads and registers a type library (usually created with MkTypLib) that is stored in the specified file. First, LoadTypeLib tries to load the file as a compound-document file and extract the type library from its root. If that fails, it tries to load the type library from the substream name. If both these attempts fail, it tries to load the file as a DLL and extract the type library from the first resource of type typelib.

If szFileName specifies only a filename, with no path, LoadTypeLib will search for the file.

If the type library is already loaded, LoadTypeLib increments the type library's reference count and returns a pointer to the type library.


LoadRegTypeLib

HRESULT LoadRegTypeLib(guid, wVerMajor,  wVerMinor, lcid, lplptlib)
REFGUID guid
unsigned short wVerMajor
unsigned short wVerMinor
LCID lcid
ITypeLib FAR* FAR* lplptlib

Uses registry information to load a type library.

Parameters

guid

ID of the library being loaded.

wVerMajor

Major version number of library being loaded.

wVerMinor

Minor version number of library being loaded.

lcid

National language code of library being loaded.

lplptlib

On return, points to a pointer to the loaded type library.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_INVALIDSTATE	The type library could not be opened.
TYPE_E_UNKNOWNLCID	The LCID is not supported by this system.
TYPE_E_CANTLOADLIBRARY	The type library or DLL could not be loaded.
Other returns	All FACILITY_STORAGE and system registry errors may also be returned.
Comments

LoadRegTypeLib defers to LoadTypeLib to load the file.

LoadRegTypeLib compares the requested version numbers against those found in the system registry and takes one of the following actions:

If one of the registered libraries exactly matches both the requested major and minor version numbers, then that type library is loaded.

If one or more registered type libraries exactly match the requested major version number and have a greater minor version number than that requested, the one with the greatest minor version number is loaded.

If none of the registered type libraries exactly match the requested major version number or if none of those which do exactly match the major version number also have a minor version number greater than or equal to the requested minor version number, then LoadRegTypeLib returns an error.

For a list of supported LCIDs, see Supporting Multiple National Languages.


RegisterTypeLib

HRESULT RegisterTypeLib(ptlib, szFullPath, szHelpDir)
ITypeLib FAR* ptlib
OLECHAR FAR* szFullPath
OLECHAR FAR* szHelpDir

Adds information about a type library to the system registry.

Parameters

ptlib

Pointer to the type library being registered.

szFullPath

Fully qualified path specification for the type library being registered.

szHelpDir

Directory in which the Help file for the library being registered can be found. May be NULL.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_REGISTRYACCESS	The system registration database could not be opened.
TYPE_E_INVALIDSTATE	The type library could not be opened.
Comments

RegisterTypeLib can be used during application initialization to correctly register the application's type library.


QueryPathOfRegTypeLib

HRESULT QueryPathOfRegTypeLib(guid, wVerMajor, wVerMinor, lcid, lpBstrPathName)
REFGUID guid
unsigned short wVerMajor
unsigned short wVerMinor
LCID lcid
LPBSTR lpBstrPathName

Retrieves the path of a registered type library.

Parameters

guid

ID of the library whose path is to be queried.

wVerMajor

Major version number of the library whose path is to be queried.

wVerMinor

Minor version number of the library whose path is to be queried.

lcid

National language code for the library whose path is to be queried.

lpBstrPathName

Caller-allocated BSTR in which the type library name is returned.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
Comments

Returns the fully qualified filename specified for the type library in the registry. The caller allocates the BSTR that is passed in, and must free it after use.


Type Building Interfaces

Use the type-building interfaces, ICreateTypeInfo and ICreateTypeLib, to build tools that can be used to automate the process of describing objects and types (each of which is called a type description, as described in Type Description Interfaces) and creating type libraries (collections of type descriptions). For example, the tool MkTypLib uses these interfaces to create type libraries. See Object Description Language for more information on MkTypLib.

Usually, you would not need to write custom implementations of these interfaces; MkTypLib itself uses the default implementation supplied in OLEAUT32.DLL. If you want to create tools similar to MkTypLib, calling the default implementation should suffice.

This section includes the following topic:

ICreateTypeInfo Interface

Implemented by	Used by	Header filename	Import library name
OLEAUT32.DLL	Applications that expose programmable objects	OLEAUTO.H	OLEAUT32.LIB


ICreateTypeInfo Interface

This section includes the following topics:

Structures
ICreateTypeInfo::AddFuncDesc
ICreateTypeInfo::AddImplType
ICreateTypeInfo::AddRefTypeInfo
ICreateTypeInfo::AddVarDesc
ICreateTypeInfo::DefineFuncAsDllEntry
ICreateTypeInfo::LayOut
ICreateTypeInfo::SetAlignment
ICreateTypeInfo::SetDocString
ICreateTypeInfo::SetFuncAndParamNames
ICreateTypeInfo::SetFuncDocString
ICreateTypeInfo::SetFuncHelpContext
ICreateTypeInfo::SetGuid
ICreateTypeInfo::SetHelpContext
ICreateTypeInfo::SetImplTypeFlags
ICreateTypeInfo::SetMops
ICreateTypeInfo::SetTypeDescAlias
ICreateTypeInfo::SetTypeFlags
ICreateTypeInfo::SetVarDocString
ICreateTypeInfo::SetVarHelpContext
ICreateTypeInfo::SetVarName
ICreateTypeInfo::SetVersion
ICreateTypeLib Interface
ICreateTypeLib::CreateTypeInfo
ICreateTypeLib::SaveAllChanges
ICreateTypeLib::SetDocString
ICreateTypeLib::SetGuid
ICreateTypeLib::SetHelpContext
ICreateTypeLib::SetHelpFileName
ICreateTypeLib::SetLibFlags
ICreateTypeLib::SetLcid
ICreateTypeLib::SetName
ICreateTypeLib::SetVersion
CreateTypeLib

The type building interfaces include the following member functions:

Interface	Member name	Purpose
ICreateTypeInfo	AddFuncDesc	Adds a function description as a type description.
	AddImplType	Specifies an inherited interface.
	AddRefTypeInfo	When a type description is being created, this function adds a type description to the type information being referenced by the type description being created.
	AddVarDesc	Adds a data member description as a type description.
	DefineFuncAsDllEntry	Associates a DLL entry point with a function that has a specified index.
	LayOut	Assigns vtable offsets for virtual functions and instance offsets for per-instance data members.
	SetAlignment	Specifies data alignment for types of TKIND_RECORD.
	SetDocString	Sets brief documentation of the type description.
	SetFuncAndParamNames	Sets the function name and names of its parameters. If the function is a property function, only the function name is set.
	SetFuncDocString	Sets specified function's documentation string.
	SetFuncHelpContext	Sets specified function's Help context.
	SetGuid	Sets the globally unique ID for the type library.
	SetHelpContext	Sets the Help context of the type description.
	SetImplTypeFlags	Sets attributes for the type.
	SetMops	Set the opcode string for a type description.
	SetSchema	Reserved for future use.
	SetTypeDescAlias	Sets the type description for which this type description is an alias, assuming that TYPEKIND=TKIND_ALIAS.
	SetTypeFlags	Sets type flags of the type description being created.
	SetTypeIdlDesc	Reserved for future use.
	SetVarDocString	Sets specified variable's documentation string.
	SetVarHelpContext	Sets specified variable's Help context.
	SetVarName	Sets the variable's name.
	SetVersion	Sets version numbers of the type description.
ICreateTypeLib	CreateTypeInfo	Creates a new type description instance within the type library.
	SaveAllChanges	Saves the ICreateTypeLib instance.
	SetDocString	Sets the documentation string for the type library.
	SetHelpContext	Sets Help context for general information about the type library in the Help file.
	SetHelpFileName	Sets the Help filename.
	SetLcid	Sets locale code indicating the national language associated with the library.
	SetLibFlags	Sets library flags, such as LIBFLAG_FRESTRICTED.
	SetName	Sets the name of the type library.
	SetGuid	Sets the globally unique ID for the type library.
	SetVersion	Sets major and minor version numbers for the type library.
Library creation functions	CreateTypeLib	Gives access to a new object instance that supports the ICreateTypeLib interface.


Structures

This section includes the following topics:

LIBFLAGS
SYSKIND


LIBFLAGS

LIBFLAGS is defined as follows:

typedef enum tagLIBFLAGS {

LIBFLAG_FRESTRICTED = 0x01

} LIBFLAGS;

Value	Description
LIBFLAG_FRESTRICTED	The type library is restricted and should not be displayed to users.


SYSKIND

The SYSKIND identifies the target operating system platform.

typedef enum tagSYSKIND[

SYS_WIN16,

SYS_WIN32,

SYS_MAC

] SYSKIND;

Value	Description
SYS_WIN16	The target operating system for the type library is Win16. Data members are packed.
SYS_WIN32	The target operating system for the type library is Win32. Data members are naturally aligned (for example, 2-byte integers are aligned on even-byte boundaries; 4-byte integers are aligned on quad-word boundaries, and so forth).
SYS_MAC	The target operating system for the type library is Macintosh. All data members are aligned on even-byte boundaries.


ICreateTypeInfo::AddFuncDesc

HRESULT ICreateTypeInfo::AddFuncDesc(index, lpFuncDesc)
unsigned int index
FUNCDESC FAR* lpFuncDesc

Parameters

index

Specifies the index of the new FUNCDESC in the type information.

lpFuncDesc

Points to a FUNCDESC structure that describes the function. The bstrIDLInfo field should be set to NULL for future compatibility.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

AddFuncDesc is used to add a function description to the type description. The index specifies the order of the functions within the type information. The first function has an index of zero. If an index is specified that exceeds one less than the number of functions in the type information, then an error is returned. Calling this function does not pass ownership of the FUNCDESC structure to ICreateTypeInfo. Therefore, the caller must still deallocate the FUNCDESC structure.

The passed-in vtable field of the FUNCDESC is ignored. This attribute will be set when ICreateTypeInfo::LayOut is called. Also, the member ID fields within each FUNCDESC are ignored unless the TYPEKIND of the class is TKIND_DISPATCH (or the TYPEKIND is TKIND_INTERFACE and the ID is DISPID_VALUE).

Note that any HREFTYPE fields in the FUNCDESC structure must have been produced by the same instance of ITypeInfo for which AddFuncDesc is called.

All function descriptions with the same name (properties) must have the same DISPID.


ICreateTypeInfo::AddImplType

HRESULT ICreateTypeInfo::AddImplType(index, hreftype)
unsigned int index
HREFTYPE hreftype

Parameters

index

Index of the implementation class to be added; specifies the order of the type relative to the other type.

hreftype

Handle to the referenced type description obtained from AddRefType description.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

AddImplType is used only for specifying an inherited interface or an interface implemented by a component object class.


ICreateTypeInfo::AddRefTypeInfo

HRESULT ICreateTypeInfo::AddRefTypeInfo(lptinfo, lphreftype)
ITypeInfo FAR* lptinfo
HREFTYPE FAR* lphreftype

Parameters

lptinfo

Pointer to the type description to be referenced.

lphreftype

Output parameter which points to the handle which this type description associates with the referenced type information.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Adds a type description to those referenced by the type description being created. The second parameter returns a pointer to the handle of the added type information. If AddRefTypeInfo has previously been called for the same type information, the index that was returned by the previous call is returned in lphreftype.


ICreateTypeInfo::AddVarDesc

HRESULT ICreateTypeInfo::AddVarDesc(index, lpVarDesc)
unsigned int index
VARDESC FAR* lpVarDesc

Parameters

index

Specifies the index of the variable or data member to be added to the type description.

lpVarDesc

Points to the variable or data member description to be added.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Adds a variable or data member description to the type description. The index specifies the order of the variables. The first variable has an index of zero. ICreateTypeInfo::AddVarDesc returns an error if the specified index is greater than the number of variables currently in the type information. Calling this function does not pass ownership of the VARDESC structure to ICreateTypeInfo. Note that the Instance field of the VARDESC structure is ignored; this attribute is set when ICreateTypeInfo::LayOut is called. Also, the member ID fields within the VARDESCs are ignored unless the TYPEKIND of the class is TKIND_DISPATCH.

Note that any HREFTYPE fields in the VARDESC structure must have been produced by the same instance of ITypeInfo for which AddVarDesc is called.

AddVarDesc ignores the contents of the idldesc field of the ELEMDESC.


ICreateTypeInfo::DefineFuncAsDllEntry

HRESULT ICreateTypeInfo::DefineFuncAsDllEntry(index, szDllName, szProcName)
unsigned int index
OLECHAR FAR* szDllName
OLECHAR FAR* szProcName

Parameters

index

Index of the function.

szDllName

Name of the DLL containing the entry point.

szProcName

Name of the entry point or an ordinal (if the high word is zero).

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Associates a DLL entry point with the function that has the specified index. If the high word of szProcName is zero, then the low word must contain the ordinal of the entry point; otherwise, szProcName points to the zero-terminated name of the entry point.


ICreateTypeInfo::LayOut

HRESULT ICreateTypeInfo::LayOut()

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_UNDEFINEDTYPE	Bound to unrecognized type.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
TYPE_E_WRONGTYPEKIND	Type mismatch.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
TYPE_E_AMBIGUOUSNAME	More than one item exists with this name.
TYPE_E_SIZETOOBIG	The type information is too large to lay out.
TYPE_E_TYPEMISMATCH	Type mismatch.
Comments

LayOut assigns vtable offsets for virtual functions and instance offsets for per-instance data members. LayOut also assigns member ID numbers to the functions and variables unless the TYPEKIND of the class is TKIND_DISPATCH. LayOut should be called after all members of the type information are defined and before the type library is saved.

Use SaveAllChanges to save the type information after calling LayOut. Don't call other members of the ICreateTypeInfo interface after calling LayOut.

Note that different implementations of ICreateTypeInfo or other interfaces that create type information are free to assign any member ID numbers provided that all members, including inherited members, have unique IDs.


ICreateTypeInfo::SetAlignment

HRESULT ICreateTypeInfo::SetAlignment(cbAlignment)
unsigned short cbAlignment

Parameters

cbAlignment

Alignment method for the type. A value of 0 indicates alignment on the 64K boundary; 1 indicates no special alignment. For other values, n indicates alignment on byte n.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Specifies the data alignment for an item of TYPEKIND TKIND_RECORD.  The alignment is the minimum of the natural alignment (for example, byte on byte boundaries, word on word boundaries, and so on) and the alignment denoted by cbAlignment.


ICreateTypeInfo::SetDocString

HRESULT ICreateTypeInfo::SetDocString(szDoc)
OLECHAR FAR* szDoc

Parameters

szDoc

Pointer to the documentation string.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the documentation string displayed by type browsers. The documentation string is a brief description of the type description being created.


ICreateTypeInfo::SetFuncAndParamNames

HRESULT ICreateTypeInfo::SetFuncAndParamNames(index, rgszNames, cNames)
unsigned int index
OLECHAR FAR* FAR* rgszNames
unsigned int cNames

Parameters

index

Index of the function whose function name and parameter names are to be set.

rgszNames

Array of pointers to names. The first element is the function name; subsequent elements are names of parameters.

cNames

Number of elements in the rgszNames array.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Sets the name of a function and the names of its parameters to the names in the array of pointers rgszNames. You only need to use SetFuncAndParamNames once for each property, because all property accessor functions are identified by one name. For property functions, provide names for the named parameters only; the last parameter for put and putref accessor functions is unnamed.


ICreateTypeInfo::SetFuncDocString

HRESULT ICreateTypeInfo::SetFuncDocString(index, szDocString)
unsigned int index
OLECHAR FAR* szDocString

Parameters

index

Index of the function.

szDocString

Pointer to the documentation string.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Sets the documentation string for the function with the specified index. The documentation string is a brief description of the function for use by tools like type browsers. You only need to use SetFuncDocString once for each property, because all property accessor functions are identified by one name.


ICreateTypeInfo::SetFuncHelpContext

HRESULT ICreateTypeInfo::SetFuncHelpContext(index, dwHelpContext)
unsigned int index
unsigned long dwHelpContext

Parameters

index

Index of the function.

dwHelpContext

A Help context ID for the Help topic.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
E_INVALIDARG	One or more of the arguments is invalid.
Comments

Sets the Help context for the function with the specified index. You only need to use SetFuncHelpContext once for each property, because all property accessor functions are identified by one name.


ICreateTypeInfo::SetGuid

HRESULT ICreateTypeInfo::SetGuid(guid)
REFGUID guid

Parameters

guid

Globally unique ID to be associated with the type description.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
Comments

Sets the globally unique ID (GUID) associated with the type description.  For an interface, this is an interface ID; for a coclass, it is a Class ID. See Object Description Language for information on GUIDs.


ICreateTypeInfo::SetHelpContext

HRESULT ICreateTypeInfo::SetHelpContext(dwHelpContext)
unsigned long dwHelpContext

Parameters

dwHelpContext

Handle to the Help context.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
Comments

Sets the Help context of the type information.


ICreateTypeInfo::SetImplTypeFlags

HRESULT ICreateTypeInfo::SetImplTypeFlags(index, impltypeflags)
unsigned int index
int impltypeflags

Parameters

index

Index of the interface for which to set type flags.

impltypeflags

IMPLTYPE flags to set.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
Comments


ICreateTypeInfo::SetMops

HRESULT ICreateTypeInfo::SetMops(index, bstrMops)
unsigned int index
BSTR bstrMops

Parameters

index

Index of the member for which to set the opcode string. If index is −1,  sets the opcode string for the type description.

bstrMops

The marshaling opcode string.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
Comments

SetMops sets the marshaling opcode string associated with the type description or the function.


ICreateTypeInfo::SetTypeDescAlias

HRESULT ICreateTypeInfo::SetTypeDescAlias(lptDescAlias)
TYPEDESC FAR* lptDescAlias

Parameters

lptDescAlias

Pointer to a type description that describes the type for which this is an alias.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Call SetTypeDescAlias for a type description whose TYPEKIND is TKIND_ALIAS to set the type for which it is an alias.


ICreateTypeInfo::SetTypeFlags

HRESULT ICreateTypeInfo::SetTypeFlags(uTypeFlags)
unsigned int uTypeFlags

Parameter

uTypeFlags

Settings for the type flags.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Use SetTypeFlags to set the flags for the type description (as described in TYPEFLAGS, in Type Description Interfaces).


ICreateTypeInfo::SetVarDocString

HRESULT ICreateTypeInfo::SetVarDocString(index, szDocString)
unsigned int index
OLECHAR FAR* szDocString

Parameters

index

Index of the variable being documented.

szDocString

The documentation string to be set.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Sets the documentation string for the variable with the specified index.


ICreateTypeInfo::SetVarHelpContext

HRESULT ICreateTypeInfo::SetVarHelpContext(index, dwHelpContext)
unsigned int index
unsigned long dwHelpContext

Parameters

index

Index of the variable described by the type description.

dwHelpContext

Handle to the Help context for the Help topic on the variable.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Sets the Help context for the variable with the specified index.


ICreateTypeInfo::SetVarName

HRESULT	ICreateTypeInfo::SetVarName(index, szName)
unsigned int index
OLECHAR FAR* szName

Parameters

index

Index of the variable whose name is being set.

szName

Name for the variable.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_ELEMENTNOTFOUND	The element was not found.
Comments

Sets the name of a variable.


ICreateTypeInfo::SetVersion

HRESULT ICreateTypeInfo::SetVersion(wMajorVerNum, wMinorVerNum)
unsigned short wMajorVerNum
unsigned short wMinorVerNum

Parameters

wMajorVerNum

Major version number for the type.

wMinorVerNum

Minor version number for the type.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
E_ACCESSDENIED	Can't write to destination.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the major and minor version number of the type information.


ICreateTypeLib Interface


ICreateTypeLib::CreateTypeInfo

HRESULT	ICreateTypeLib::CreateTypeInfo(szName, tkind, lplpctinfo)
OLECHAR FAR* szName
TYPEKIND tkind
ICreateTypeInfo FAR* FAR* lplpctinfo

Parameters

szName

Name of the new type.

tkind

Kind of the type description to be created.

lplpctinfo

On return, contains a pointer to the type description.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
TYPE_E_NAMECONFLICT	The provided name is not unique.
TYPE_E_WRONGTYPEKIND	Type mismatch.
Comments

Use CreateTypeInfo to create a new type description instance within the library. An error is returned if the specified name already appears in the library.


ICreateTypeLib::SaveAllChanges

HRESULT ICreateTypeLib::SaveAllChanges()

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not write to the file.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Other returns	All FACILITY_STORAGE errors.
Comments

Saves the ICreateTypeLib instance following the layout of the type information.

Do not call any other ICreateTypeLib methods after calling SaveAllChanges.


ICreateTypeLib::SetDocString

HRESULT ICreateTypeLib::SetDocString(szDoc)
OLECHAR FAR* szDoc

Parameters

szDoc

A documentation string that briefly describes the type library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
Comments

Sets the documentation string associated with the library. This is a brief description of the library intended for use by type information browsing tools.


ICreateTypeLib::SetGuid

HRESULT ICreateTypeLib::SetGuid(guid)
REFGUID guid

Parameters

guid

The universal unique ID to be assigned to the library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the universal unique ID (UUID) associated with the type library. UUIDs are described in Object Description Language.


ICreateTypeLib::SetHelpContext

HRESULT ICreateTypeLib::SetHelpContext(dwHelpContext)
unsigned long dwHelpContext

Parameters

dwHelpContext

Help context to be assigned to the library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the Help context for retrieving general Help information for the type library. Note that calling SetHelpContext with a Help context of zero is equivalent to not calling it at all, because zero is used to indicate a NULL Help context.


ICreateTypeLib::SetHelpFileName

HRESULT ICreateTypeLib::SetHelpFileName(szFileName)
OLECHAR FAR* szFileName

Parameters

szFileName

The name of the Help file for the library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the name of the Help file. Each type library can reference a single Help file.

The GetDocumentation method of the created ITypeLib returns a fully qualified path for the Help file, which is formed by appending the name passed into szFileName to the registered Help directory for the type library. The Help directory is registered under:

\TYPELIB\<guid of library>\<Major.Minor version >\HELPDIR


ICreateTypeLib::SetLibFlags

HRESULT ICreateTypeLib::SetLibFlags(uLibFlags)
unsigned int uLibFlags

Parameters

uLibFlags

The flags to set for the library. Flags are as follows:

Flag	Description
LIBFLAG_FRESTRICTED	Indicates that the type library is restricted and should not be displayed to users.
Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

The TYPELIB_RESTRICTED flag means that users cannot program against the type library.


ICreateTypeLib::SetLcid

HRESULT ICreateTypeLib::SetLcid(lcid)
LCID lcid

Parameters

lcid

An LCID representing the locale ID for the type library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the binary Microsoft national language ID associated with the library. For more information on national language IDs, see Supporting Multiple National Languages in Exposing OLE Automation Objects, and refer to the Windows NT documentation on the NLS API.


ICreateTypeLib::SetName

HRESULT ICreateTypeLib::SetName(szName)
OLECHAR FAR* szName

Parameters

szName

Name to be assigned to the library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the name of the type library.


ICreateTypeLib::SetVersion

HRESULT ICreateTypeLib::SetVersion(wMajorVerNum, wMinorVerNum)
unsigned short wMajorVerNum
unsigned short wMinorVerNum

Parameters

wMajorVerNum

Major version number for the library.

wMinorVerNum

Minor version number for the library.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
TYPE_E_INVALIDSTATE	The type library's state is not valid for this operation.
Comments

Sets the major and minor version numbers of the type library.


CreateTypeLib

HRESULT CreateTypeLib(syskind, szFile, lplpctlib)
SYSKIND syskind
OLECHAR FAR* szFile
ICreateTypeLib FAR* FAR* lplpctlib

Parameters

syskind

Specifies the target operating system.

szFile

The name of the file to create.

lplpctlib

Gives access to a pointer to an instance supporting the ICreateTypeLib interface.

Return Value

The SCODE value of the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success.
STG_E_INSUFFICIENTMEMORY	Out of memory.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not create the file.
Other returns	All FACILITY_STORAGE errors.
Comments

CreateTypeLib sets its output parameter (lplpctlib) to point to a newly created object that supports the ICreateTypeLib interface.


DispTest

DispTest tests applications that expose objects by allowing you to program against them using Visual Basic language constructs. DispTest also lets you test an OLE object by means of an OLE container control.

DispTest provides two functions to access programmable objects:

•	CreateObject — creates a new instance of an object.

•	GetObject — gets the current active object.

In addition, DispTest provides an OLE container control you can use to test your implementation of OLE objects. DispTest uses the same language constructs provided in Microsoft Visual Basic version 3.0. For your convenience, a copy of the Help file is provided with DispTest and is accessible from the Program Manager.

This section includes the following topics:

CreateObject Function
GetObject Function
Using the OLE Container Control
OLE Container Control Reference

Note  DispTest is a 16-bit tool.  Testing 32-bit applications with DispTest uses the 16:32 interoperability feature of OLE Automation. Optionally, you can use a different 32-bit OLE Automation server or controller for your testing.


CreateObject Function

Creates an OLE Automation object.

Syntax

CreateObject(class)

Remarks

The class argument is a string indicating the name of the application used to create the object and the type of object. To specify an object's class, use the following syntax:

"appname.objecttype"

The class argument has these parts:

Part	Description
appname	The name of the application that provides the object.
objecttype	The type or class of object to create.
Each application that supports OLE Automation provides at least one type of object. For example, a word processing application may provide an Application object, a document object, and a toolbar object.

Note  To get a list of the OLE Automation objects that an application supports, consult your application's documentation.

Use this function to create an OLE Automation object and assign the object to an object variable. To do this, first dimension a variable of type object. Then use the Set statement to assign the object returned by CreateObect to the object variable. For example:

Dim MyObject As Object
Set MyObject = CreateObject("WordProc.Document")

When this code executes, the application creating the object is started, if it is not already running (WORDPROC.EXE in this example), and an object of the specified type is created. Unlike creating a linked or embedded object using the OLE control, the object's image is not displayed anywhere in DispTest, nor is the object's data maintained by DispTest.

Note  To create an object, the application providing the object must either be active or on the system's path.

Once an object is created, you reference it in DispTest code using the object variable you defined. In the previous example you access properties and methods of the new object using the object variable, MyObject. For example:

MyObject.Bold
MyObject.Insert "Hello, world."
MyObject.Print
MyObject.SaveAs "C:\WordProc\Docs\Test.Doc"


GetObject Function

Retrieves an OLE Automation object from a file.

Syntax

GetObject([filename][, class])

Remarks

The GetObject function has these arguments:

Part	Description
filename	The full path and name of the file that contains the object to retrieve. The filename is optional; if it is not specified, the class argument is required.
class	A string representing the class of the object to retrieve.
The class is optional, but must be present if filename is not specified. The class argument uses the following syntax:

"appname.objecttype"

The class argument has these parts:

Part	Description
appname	The name of the application that provides the object.
objecttype	The type or class of object to retrieve.
Use the GetObject function to access an OLE Automation object from a file and assign the object to an object variable. To do this, first dimension a variable of type object. Then use the Set statement to assign the object returned by GetObject to the object variable. For example:

Dim MyObject As Object
Set MyObject = GetObject("C:\WORDPROC\DOCS\OLETEST.DOC")

When this code executes, the application associated with the specified filename is started (WORDPROC.EXE in this example) and the object in the specified file is activated.

The previous example shows how to activate an entire file or object. However, some applications let you activate part of an object. To specify that you want to activate part of a file, add an exclamation point (!) to the end of the filename followed by a string that identifies the part of the file you want to activate. For information on how to create this string, see the documentation for the application that created the object.

For example, in many spreadsheet applications you specify the rows and columns of a range of cells using an R1C1 syntax. The following code could be used to activate a range of cells within a spreadsheet called REVENUE.SPD:

Set Sheet = GetObject("C:\ACCOUNTS\REVENUE.SPD!R1C1:R10C20")

If you do not specify the object's class, the OLE DLLs determine the application to invoke and the object to activate based on the filename you provide. Some files, however, may support more than one class of object. For example, a spreadsheet might support three different types of objects: an Application object, a worksheet object, and a toolbar object, all of which are part of the same file. To specify which object in a file you want to activate, use the optional class argument. For example:

Set Sheet = GetObject("C:\ACCOUNTS\REVENUE.SPD", "SPDSHEET.WORKSHEET")

Once an object is activated, you reference it in DispTest code using the object variable you defined. In the previous example, you access properties and methods of the new object using the object variable Sheet. For example:

Sheet.Row 	= 4
Sheet.Column	= 2
Sheet.Insert	= 66

If the filename argument is omitted, the GetObject function returns the currently active object of the specified type. If no object of that type is active, an error occurs.  For example:

Set MyObject = GetObject(,"MyApplication.MyObject")

This example returns the currently active object of type MyObject, created by MyApplication. Note the required comma before the class specification.


Using the OLE Container Control

You can use the OLE control to display an OLE object on a form. You can create the object either at design time by using standard OLE dialogs or at run time by setting the appropriate properties.

This section includes the following topics:

Creating Objects — Design Time vs. Run Time
Creating Objects at Design Time
Creating Objects at Run Time
Displaying the OLE Dialogs
Working with Files


Creating Objects  Design Time vs. Run Time
Creating Objects — Design Time vs. Run Time

There are advantages and disadvantages to creating objects at design time and to creating them at run time. You must decide which technique is best for your application.

It can take several seconds to create an object, regardless of when the object is created. If you create an object at run time, the only logical place to put this code may be in the Form_Load event of the form containing the OLE control. Remember that when you create an object in code, the system suspends all other processing until the object is created. Therefore, if you create an object in a Form_Load event, the form will not be displayed until the object is created.


Creating Objects at Design Time

Each time you draw an OLE control on a form, the Insert Object dialog box is displayed. You use this dialog box to create linked or embedded objects at design time.

When you create an object at design time, DispTest automatically sets the appropriate properties for the OLE control. You can view these settings in the Properties window when the OLE control is selected.

Determining How an Object Is Displayed

You use the SizeMode property to determine how an object's image is displayed in the OLE control. You can either clip the image at the OLE control's borders (SizeMode = 0 - Clip), stretch the image to fill the OLE control (SizeMode = 1 - Stretch), or resize the OLE control to fit the image (SizeMode = 2 - AutoSize).

Caution  When SizeMode = 2, the OLE control will change size at run time to accommodate the size of the object it contains.

Creating Linked Objects

When you create a linked object, the data displayed in the OLE control exists in one place — the linked file. Because the data exists in a file, other applications can view and modify the file. The OLE control maintains the object's link information, such as the name of the application that supplied the object, the name of the linked file, and an image of the linked data.

To create a linked object using the Insert Object dialog

1.	Draw an OLE control on the form.

The Insert Object dialog is displayed. You can also display this dialog by clicking the OLE control with the right mouse button and choosing the Insert Object command.

2.	In the Insert Object dialog, choose the Create from File option button.

3.	Choose the Browse button.

A Browse dialog is displayed.

4.	Use the Browse dialog to select the file you want to link.

5.	Click OK to return to the Insert Object dialog.

6.	In the Insert Object dialog, select the Link check box, and then click OK to create the object.

When you use a linked object, every user who runs your application must have access (a valid path) to the linked file. Otherwise, when your application is run, an image of the original data is displayed, but the user will not be able to modify the data, nor will the user see changes others may have made to the linked data.

If you allow the user to make changes to the linked data, you do not need to write any code to maintain these changes. The user can save the linked data using the application that created the object. For example, if you create an object linked to a Microsoft Excel spreadsheet, the user can activate Microsoft Excel at run time by double-clicking the object. When the spreadsheet is opened, the user makes any desired changes and saves the spreadsheet using the Microsoft Excel File Save command. When the user closes Microsoft Excel, the new data is displayed in the OLE control.

Linking to Data Within a File

When creating a linked object, you may not want to link an entire file. For example, you may want to link a range of cells in a spreadsheet or a paragraph in a word processor.

One way to do this is to use the SourceItem property to specify the data within the source document you want to link. The exact syntax used to set this property varies depending on the application supplying the object. For example, when linking to a Microsoft Excel spreadsheet, you specify SourceItem using an R1C1 syntax. This syntax is commonly used by spreadsheet applications to indicate the rows and columns in a range of cells. Here's how to specify the range A1:D5 in a Microsoft Excel spreadsheet using R1C1 syntax:

R1C1:R5C4

Alternately, you specify a range of cells using a named range.

An easier way to link to data within a file is to copy the desired data to the Clipboard and choose Paste Special from the OLE control's pop-up menu (click the OLE control with the right mouse button). Pop-up menus are explained later in this section.

Creating Embedded Objects

When you create an embedded object, you can either create an object from a file or create a new object. When you create an object from a file, a copy of the specified file's data is displayed in the OLE control. When you create a new object, the application that created the object is invoked and the user is allowed to enter any desired data. New embedded objects are usually created at run time to allow the user to enter data in the object.

Typically, you create embedded objects that display existing data at design time. This allows you to view the object's data as it will appear to the user. You can then move and size the OLE control and the other controls on the form and create your application's user interface accordingly.

You display existing data in an embedded object by creating the object using an existing file as a template. The OLE control then contains an image of the file along with a copy of the embedded file's data.

Because the OLE control contains the embedded data, an application that displays data using an embedded object will be larger than an application that displays the same data using a linked object.

To create an embedded object using an existing file

1.	Display the Insert Object dialog.

2.	In the Insert Object dialog, choose the Create from File option button.

3.	Choose the Browse button.

A Browse dialog is displayed.

4.	Use the Browse dialog to select the file you want to embed.

5.	Click OK to return to the Create from File dialog.

6.	In the Create from File dialog, click OK to create the object.

Data in an embedded object is not persistent. If the user modifies the object's data, you must write code to save the data if you want the changed data to appear the next time your application is run. Saving embedded data to a file is discussed in Working with Files.

Using the OLE Control's Pop-up Menus

At design time, you click the OLE control with the right mouse button to display a pop-up menu. The commands displayed on the pop-up menu depend on the state of the OLE control. If the control contains an embedded object, the pop-up menu shown below is displayed.

You use the Insert Object command to delete an existing object and create a new one. The Paste Special command allows you to paste an object from the Clipboard into the OLE control. The commands displayed below the separator bar are determined by the application that created the object. These commands reflect the verbs that the object supports. A verb is an action, such as play or edit, that the user can perform on an object.

When the OLE control is empty (that is, when it does not contain an object), the menu shown below is displayed.

In this figure, the Create Link and Create Embedded Object commands are grayed (disabled). The Paste Special command is grayed unless there is an OLE object on the Clipboard.

To activate the Create Link and Create Embedded Object commands, enter a valid class name in the Properties window. In addition, to activate the Create Link command, you must enter a value for the SourceDoc property. If the OleTypeAllowed property is set to a value other than 2 (Either), only the appropriate menu item is activated. When OleTypeAllowed is 0, Create Embedded Object is omitted from the menu. When OleTypeAllowed is 1, Create Link is omitted.

Tip  To choose from a list of valid class names, select the Class property in the Properties window and click the three dots (...) in the Settings box.

Creating Objects Using Paste Special

The Paste Special dialog shown below allows you to create linked and embedded objects from data that has been copied to the Clipboard. To display the Paste Special dialog at design time, click the OLE control with the right mouse button and select Paste Special from the pop-up menu.

To create an object using the Paste Special dialog

1.	Run the application containing the data you want to link or embed.

2.	Select the data you want to link or embed.

3.	From the Edit menu, choose Copy.

The data is copied to the Clipboard.

4.	In DispTest, right click the OLE control and choose the Paste Special command.

5.	Choose the Paste option to create an embedded object.

− or −

Choose the Paste Link option to create a linked object.

6.	Click OK to create the object.


Creating Objects at Run Time

To create objects at design time, you use the Insert Object and Paste Special dialogs. To create an OLE object at run time, however, you set properties in code.

Setting Properties

To create an OLE object at run time, set the following properties.

Property	Description
Class	Identifies the application that created the object.
OleTypeAllowed	Determines if the object you can create is linked, embedded, or either.
SourceDoc	Specifies the file to link to (for a linked object) or the file to use as a template (for an embedded object).
SourceItem	Specifies data within a file to link to (linked objects only).
Action	Specifies the operation to perform on an OLE control.
Class Property

The Class property determines the type of data that the object contains and the name of the application that supplied the object. For example, to create an object that contains data from a Microsoft Excel spreadsheet, you set the Class property to ExcelWorksheet.

You can get a list of the class names available to your application by highlighting the Class property in the Properties window and clicking the three dots (...) in the Settings box.

Important  When you create an object, the executable file associated with the object's class must be either active or on the system's path.

OleTypeAllowed Property

The OleTypeAllowed property determines the type of object you can create. You set the OleType property to OLE_LINKED, OLE_EMBEDDED, or OLE_EITHER.

SourceDoc and SourceItem Properties

To create a linked object or display existing data in an embedded object, you must also set the SourceDoc property.

The SourceDoc property specifies the name of the document you want to link to or the name of the file you want copied to an embedded object. When you set the SourceDoc property, you must include the path and the name of the file you want to link. For example:

Ole1.SourceDoc = "C:\REVENUES\QUARTER1.XLS"

Tip  To display a dialog at design time that allows you to browse your disk for files, highlight the SourceDoc property in the Properties window and click the three dots (...) in the Settings box.

For linked objects, you can optionally specify data within the source document to link. You do this using the SourceItem property.

Action Property

The Action property specifies the operation to perform on an OLE control. Setting this property serves the same function as a Visual Basic method — it performs an action on an object. For example, you can use the Action property to create, activate, or delete an object.

Creating a Linked Object at Run Time

Before creating a linked object, you must first specify the document you want to link to by setting the SourceDoc property. Optionally, you can specify the data within the document that you want to link by setting the SourceItem property. The document specified in the SourceDoc property must already exist.

The following code fragment creates a linked object at run time:

' Define the constant.

Const OLE_CREATE_LINK = 1

' Specify the application that will supply the object.

Ole1.Class = "ExcelWorksheet"

' Specify the file to link to.

Ole1.SourceDoc = "C:\EXCEL\TEST.XLS"

' Link to a range of cells within TEST.XLS.

Ole1.SourceItem = "R1C1:R3C3"

' Create the linked object (OLE_CREATE_LINK = 1).

Ole1.Action = OLE_CREATE_LINK

When you create a linked object, the data specified by the SourceDoc and SourceItem properties is displayed in the OLE control.

If the AutoActivate property is set to 2 (Double-Click), the default, double-clicking an object at run time automatically activates the object. Exactly how an object behaves when it is activated depends on the object's default verb.

Note  When AutoActivate is 2, the DblClick event does not occur when the user double-clicks an OLE control.

Creating an Embedded Object at Run Time

When creating an embedded object at run time, you can use a file template to create the object and display existing data, or you can create a new object and let the user enter new data.

When you create an embedded object from a file, the specified file's data is displayed in the OLE control. This is typically done in a front-end application in which you want to display, and provide access to, data in another application.

To create an embedded object from a file at run time

1.	Set the Class property to specify the object's type.

2.	Set the SourceDoc property to specify the file to use as a template.

3.	Set the Action property to OLE_CREATE_EMBED.

The following code fragment creates an embedded object using an existing file as a template for the object.

' Define the constant.

Const OLE_CREATE_EMBED = 0

' Specify the type of object.

Ole1.Class = "ExcelWorksheet"

' Specify the file to be embedded.

Ole1.SourceDoc = "Q1PROFIT.XLS"

' Specify data within the SourceDoc.

Ole1.SourceItem = "R1C1:R23C7"

' Create embedded object using the data specified in the

' SourceDoc and SourceItem properties (OLE_CREATE_EMBED = 0).

Ole1.Action = OLE_CREATE_EMBED

When you create an empty embedded object, the application providing the object is activated and displayed to the user. The user can then enter data into the application. To display the newly entered data, the user chooses the Update command on the File menu (this menu command should appear on the application providing the object, not in your DispTest application).

To create an empty embedded object at run time

1.	Set the Class property to specify the object's type.

2.	Set the Action property to OLE_CREATE_EMBED.

The following code fragment creates an empty embedded object.

' Specify the type of object.

Ole1.Class = "ExcelWorksheet"

' Create an embedded object (OLE_CREATE_EMBED = 0).

Ole1.Action = OLE_CREATE_EMBED

This technique is useful when creating a document-centered application in which you allow the user to embed different types of data in a single document.  For more details, see Displaying the OLE Dialogs.

Activating an Object

When you activate an object, you perform an action on the object using the object's default verb (or the verb you specify using the Verb property). You activate an object either programmatically or by setting the AutoActivate property.

At run time you activate an object in code by setting the Action property to 7. By activating an object in this way, you maintain control over when an object is activated. Use the AutoActivate property to allow users to activate an object at their convenience.

In-Place Activation

Some objects can be activated from within the OLE control. When such an object is activated, the user can edit the object (or perform some other action) from inside the boundaries of the OLE control. This feature is called in-place activation. If an object supports in-place activation, you set AutoActivate to 1 (GetFocus) to activate an object when the OLE control gets the focus.

You typically activate objects that don't support in-place activation when the user double-clicks the object. To do this automatically, set AutoActivate to 2. When AutoActivate is set to 2 (Double-Click) at run time, the object is activated (using its default verb) whenever the user double-clicks the OLE control or presses Enter when the object has the focus.

Displaying an Object's Verbs

When the AutoVerbMenu property is set to True, a pop-up menu showing all the object's available verbs is displayed when the user clicks the OLE control with the right mouse button.

Note  When the AutoVerbMenu property is set to True, Click events and MouseDown events do not occur when the user clicks the OLE control with the right mouse button.

The list of verbs an object supports may vary, depending on the state of the object. For example, it may make sense for an object to allow the user to play data, edit data, or both play or edit data. To update the list of verbs that an object supports, set Action = 17 (Fetch Verbs). Be sure to update the list of verbs before presenting the list of available verbs to the user.

When AutoVerbMenu is set to True, the verb list is updated automatically before the pop-up menu is displayed.


Displaying the OLE Dialogs

At run time, you use the Action property to display the Insert Object or Paste Special dialog to the user. The Insert Object dialog presents a list of available objects and creates an object based on user selection. The Paste Special dialog allows the user to paste an object from the Clipboard to an OLE control.

Using these dialogs, you allow the user to decide what type of object to create. You may do this, for example, when creating a document-centered application. In such an application, the user combines data from different applications to create a single document. This type of application is easy to visualize in terms of a word processor in which the user might enter some text and then embed a spreadsheet and a chart.

To create such an application in DispTest, you need to provide some sort of document into which the user can embed objects. For example, you could write a calendar application in which each month is maintained as a separate document. You allow the user to embed an object in the calendar by displaying the Insert Object or Paste Special dialog.

You display the Insert Object or Paste Special dialog at run time by setting the Action property. For example:

Sub InsObjectDlg_Click ()

' Display Insert Object dialog (OLE_INSERT_OBJ_DLG = 14).

Ole1.Action = OLE_INSERT_OBJ_DLG

' Check to make sure object was created (OLE_NONE = 3).

If Ole1.OleType = OLE_NONE Then

MsgBox "Object Not Created."

End If

End Sub

Sub PstSpecialDlg_Click ()

' Determine if Clipboard contents can be pasted into the

' OLE control.

If Ole1.PasteOK Then

' Display Paste Special dialog (OLE_PASTE_SPECIAL_DLG = 15)

Ole1.Action = OLE_PASTE_SPECIAL_DLG

' Check to make sure object was created.

If Ole1.OleType = OLE_NONE Then

MsgBox "Object Not Created."

End If

End If

End Sub

Once the dialog is displayed, you do not need to write more code to create the object. The user makes choices in the dialog and clicks OK to create an object. If the user cancels the dialog, an object is not created.

As shown in the previous examples, you may want to query the value of the OleType property to check the state of the OLE control.

If OleType is	Then
OLE_LINKED	The OLE control contains a linked object.
OLE_EMBEDDED	The OLE control contains an embedded object.
OLE_NONE	The OLE control does not contain an object.


Working with Files

Data associated with an OLE object is not persistent; that is, when a form containing an OLE control is closed, any data associated with that control is lost. To save the data from an OLE object to a file, you use the Action property. Once the data has been saved to a file, you can open the file and restore the OLE object.

Objects in the OLE control can be saved only to open, binary files.

To save the data from an OLE object

1.	Open a file in binary mode.

2.	Set the FileNumber property to the file number used in step 1.

3.	Set the Action property to OLE_SAVE_TO_FILE (11).

The SaveObject_Click event procedure illustrates these steps:

Sub SaveObject_Click ()

' Get file number.

FileNum = FreeFile

' Open file to be saved.

Open "FOOD.OLE" For Binary As #FileNum

' Set the file number.

Ole1.FileNumber = FileNum

' Save the file (OLE_SAVE_TO_FILE = 11).

Ole1.Action = OLE_SAVE_TO_FILE

' Close the file.

Close #FileNum

End Sub

Once an OLE object has been saved to a file, it can be opened and displayed in an OLE control.

Note  When you set Action = 11 (Save To File) or 12 (Read From File), the file position is located immediately following the object.

To read data from a file into an OLE control

1.	Open the file in binary mode.

2.	Set the FileNumber property to the file number used in step 1.

3.	Set the Action property to OLE_READ_FROM_FILE (12).

The OpenObject_Click event procedure illustrates these steps:

Sub OpenObject_Click ()

' Get file number.

FileNum = FreeFile

' Open the file.

Open "FOOD.OLE" For Binary As #FileNum

' Set the file number.

Ole1.FileNumber = FileNum

' Open the file (OLE_READ_FROM_FILE = 12).

Ole1.Action = OLE_READ_FROM_FILE

' Close the binary file.

Close #FileNum

End Sub


OLE Container Control Reference

This section lists and describes the properties, events, and methods of the OLE container control.

Action Property
AppRunning Property
AutoActivate Property
AutoVerbMenu Property
Class Property
Data Property
DataText Property
DisplayType Property
FileNumber Property
Format Property
HostName Property
lpOLEObject Property
Object Property
PasteOK Property
ObjectAcceptFormats Property
ObjectAcceptFormatsCount Property
ObjectGetFormats Property
ObjectGetFormatsCount Property
OleType Property
OleTypeAllowed Property
ObjectVerbs Property
ObjectVerbsCount Property
ObjectVerbFlags Property
SizeMode Property
SourceDoc Property
SourceItem Property
StringUpdateOptions Property
Verb Property
Updated Event

Properties
Action	Object
AppRunning	Parent
AutoActivate	PasteOK
AutoVerbMenu	Picture
BackColor	ObjectAcceptFormats
BorderStyle	ObjectAcceptFormatsCount
Class	ObjectGetFormats
Data	ObjectGetFormatsCount
DataText	OleType
DisplayType	OleTypeAllowed
DragI	ObjectVerbs
DragMode	ObjectVerbFlags
FileNumber	ObjectVerbsCount
Format	SizeMode
Height	SourceDoc
HelpContextID	SourceItem
HostName	Tag
hWnd	Top
Index	UpdateOptions
Left	Visible
lpOleObject	Width
Name
Events
Click	KeyDown
MouseMove	DblClick
KeyPress	MouseUp
DragDrop	KeyUp
Resize	DragOver
LostFocus	Updated
GotFocus	MouseDown
Methods
Drag	SetFocus
Move	ZOrder
Refresh


Action Property

Description

Determines an action to take; not available at design time; write-only at run time.

Usage

[form.]ole.Action = setting

Setting

By setting the Action property at run time, these operations can be performed:

Setting	Description
0	CreateEmbed creates an embedded object.  To use this action, you must first set the Class and OleTypeAllowed properties. OleTypeAllowed must be 1 (Embedded) or 2 (Either). The Class property determines the type of OLE object to create. To view a list of valid class names on your system, select the Class property in the Properties window and click the three dots (...) in the Settings box. When you create a new OLE object, the executable file associated with the class name (for example, EXCEL.EXE) must either be active or on the system's path.
1	CreateLink creates a linked OLE object from the contents of a file.  To use this action, you must first set the OleTypeAllowed and SourceDoc properties. OleTypeAllowed must be 0 (Linked) or 2 (Either). The SourceDoc property specifies the file from which the OLE object is created. You can also set the SourceItem property (for example a row/column range specification if the object is a Microsoft Excel worksheet). When an OLE object is created with this action, the OLE control simply displays an image of the SourceDoc. If the OLE object is saved, only the link references are saved, because the OLE control contains only a metafile image of the data and no source data.
	When creating a new OLE object, the executable file associated with the class name (for example, EXCEL.EXE) must either be active or on the system's path.
2	Reserved for future use.
3	Reserved for future use.
4	Copy copies the object to the clipboard. When an OLE object is copied to the Clipboard, all the data and link information associated with the object is placed on the Clipboard. You can copy both linked and embedded objects to the Clipboard.  You can use this action to support an Edit Copy command.
5	Paste copies data from the Clipboard to an OLE control. To use this action, set the OleTypeAllowed property, and then check the value of the PasteOK property. You cannot paste successfully unless PasteOK returns a value of True.  If the Paste was successful, the OleType property will be set to 0 (Link) or 1 (Embed). If the Paste was not successful, the OleType property will be set to 2 (None). You can use this action to support an Edit Paste command. See the PasteOK property topic for more information and an example.
6	Update retrieves the current data from the application that supplied the object and displays that data as a picture in the OLE control.
7	Activate opens an OLE object for an operation, such as editing. To use this action, first set the Verb property. The Verb property specifies the operation to perform when the OLE object is activated. If you set the AutoActivate property to 2 (Double-Click), the OLE control automatically activates the current object when the user double clicks the control.
8	Reserved for future use.
9	Close closes an OLE object and terminates the connection with the application that provided the object.  This action is equivalent to the user selecting Close from the object's Control-menu box.
10	Delete deletes the specified OLE object and frees the memory associated with it. This action allows the programmer to explicitly delete an OLE object. Objects are automatically deleted when a form is closed or when the object is updated to a new object.
11	SaveToFile saves an OLE object to a data file. To use this action, first set the FileNumber property to correspond to an open, binary file. If the OLE object is linked (OleType = 0, Linked), only the link information and an image of the data are saved to the specified file. The object's data is maintained by the application that created the object. If the OLE object is embedded (OleType = 1, Embedded), the object's data is maintained by the OLE control and can be saved by your DispTest application. See the FileNumber property topic for an example.
12	ReadFromFile loads an OLE object from a data file created using the SaveToFile action. To use this action, first set the FileNumber property to correspond to an open, binary file.  See the FileNumber property topic for an example.
13	Reserved for future use.
14	InsertObjDialog displays the Insert Object dialog.  At run time, you display this dialog to allow the user to create a linked or embedded object. Using this dialog, the user chooses the type of object (linked or embedded) and the application that is to provide the object.
15	PasteSpecialDlg displays the Paste Special dialog. At run time, you display this dialog to allow the user to paste an object from the clipboard. This dialog displays several options to the user, including the choice of pasting either a linked or embedded object.
16	Reserved for future use.
17	FetchVerbs updates the list of verbs an object supports.
18	SaveToOle1File saves the object in the Ole1 file format.
DataType

Integer (Enumerated)


AppRunning Property

Description

Indicates whether the application that created the object in the OLE control is currently activated and running. Not available at design time; read-only at run time.

Usage

[form.]ole.AppRunning

Remarks

The AppRunning property settings are:

Setting	Description
True	The application that produced the object in the OLE control is currently running.
False	The application that produced the object in the OLE control is not currently running.
Data Type

Integer (Boolean)


AutoActivate Property

Description

Allows the user to activate an object by double-clicking the OLE control, or by giving the OLE control the focus.

Usage

[form.]ole.AutoActivate = [setting]

Remarks

The AutoActivate property settings are:

Setting		Description
0	Manual	The OLE object is not automatically activated. You can activate an object programmatically by setting Action = 0 (create embed) or 1 (create link).
1	Focus	If the OLE control contains an object, the application that provided the object will be activated when the OLE control gets the focus.
2 (Default)	Double-Click	If the OLE control contains an object, the application that provided the object will be activated when the user double-clicks the OLE control or presses Enter when the control has the focus.
Remarks

You can determine whether the OLE control contains an object by querying the OleType property.

Some objects support in-place activation, which is the ability to be activated from within the OLE control. When such an object is activated, the user can edit the object (or perform some other action) from inside the boundaries of the OLE control. If an object supports in-place activation, you can set AutoActivate to 1 (GetFocus) to activate the object when the OLE control gets the focus.

Note  When AutoActivate is set to 2 (Double-Click), the double click event does not occur when the user double-clicks an OLE control.

Data Type

Integer (Enumerated)


AutoVerbMenu Property

Description

Determines if a pop-up menu containing the object's verbs is displayed when the user clicks the OLE control with the right mouse button.

Usage

[form.]ole.AutoVerbMenu[ = boolean ]

Setting

The AutoVerbMenu property settings are:

Setting	Description
True	(Default) When the user clicks the OLE control with the right mouse button, a pop-up menu is displayed showing the commands the object supports.
False	No pop-up menu is displayed.
Remarks

When this property is set to True, Click events and MouseDown events do not occur when the OLE control is clicked with the right mouse button.

Data Type

Integer (Boolean)


Class Property

Description

Determines the class name of an embedded OLE object.

Usage

[form.]ole.Class [ = classname ]

Remarks

A class name defines the type of an OLE object. The classname must be in the following form:

ApplicationName.ObjectType

ApplicationName specifies the name of the application supplying the object. ObjectType specifies the object's type (for example, worksheet, chart, macro sheet). For example, Microsoft Excel spreadsheets and charts have the class names "Excel.Worksheet" and "Excel.Chart," respectively.

When you create an object at design time using the Insert Object or Paste Special dialogs, the class of the new object is automatically entered in the Properties window.

To view a list of class names available on your system, select the Class property in the Properties window and click the three dots (...) in the Settings box.

This property is updated when an object is copied from the Clipboard. For example, if a Microsoft Excel chart is pasted from the Clipboard to an OLE object that previously contained a Microsoft Excel worksheet, the Class property changes from Excel.Worksheet to Excel.Graph. You can paste an object from the Clipboard by setting Action = 5 (Paste), or Action = 15 (Paste Special Dialog).

Note  In OLE 1, the syntax for the Class property was a string with a single component and no period, for example, ExcelWorksheet or ExcelChart.

Data Type

String


Data Property

Description

Determines a handle to a memory or graphical device interface (GDI) object containing data in a specified format; not available at design time.

Usage

[form.]ole.Data [ = data ]

Remarks

You send data to an application that created an object by setting this property. Before using the Data property, set the Format property to determine the type of data contained in the memory or GDI object.

You can get a list of acceptable formats for an OLE object using the ObjectAcceptFormats and ObjectGetFormats properties.

Setting this property to 0 frees the memory associated with the handle.

OLE Automation provides an easier and more reliable solution for sending data and commands to and from an object. If an object supports OLE Automation, you can access the object through the Object property or with the CreateObject and GetObject functions.

Data Type

Long


DataText Property

Description

Sends or retrieves a string from the specified OLE object; not available at design time.

Usage

[form.]ole.DataText [ = data ]

Remarks

To send a string to an object, first set the Format property to a format the object supports. Use the ObjectGetFormats and ObjectAcceptFormats properties to get a list of formats supported by an object.

When getting data from an object, the DataText property returns the string sent from the object, ending at the first null character.

The data string must be less than 64K.

OLE Automation provides an easier and more reliable solution for sending data and commands to and from an object. If an object supports OLE Automation, you can access the object through the Object property or with the CreateObject and GetObject functions.

Data Type

String

Example

The example sends data to the Microsoft Graph application, so you must have Graph to run the example. Create a form about half the size of the screen with an OLE control named Ole1 in the top four-fifths of the form and a command button named Command1 in the bottom one-fifth. When the Insert Object dialog is displayed, select Cancel. Start the DispTest application.

The DispTest application will transfer focus to the Microsoft Graph application with the Microsoft Graph default data showing. Choose Update from the Microsoft Graph File menu, then click the DispTest application. The default graph data appears in Ole1. Press the command button, and then click Microsoft Graph to see that the data from the DispTest application has been transmitted to Microsoft Graph. Choose Exit and Return from the Microsoft Graph File menu to see the data displayed in Ole1.

Sub Form_Load ()

Ole1.Format = "CF_TEXT"	' Set up link to Graph.

Ole1.Class = "MSGRAPH"

Ole1.Action = 0	' Show default graph in

End Sub	' Graph application.

Sub Command1_Click ()

Dim Msg, NL, TB	' Declare variables.

TB = Chr(9)	' Tab character.

NL = Chr(10)	' Newline character.

' Create data to replace default Graph data.

Msg = TB + "Drew" & TB & "Lorin" & TB & "Bob"

Msg = Msg + NL & "Eric" & TB & "1" & TB & "2" & TB & "3"

Msg = Msg + NL & "Ted" & TB & "11" & tb & "22" & TB & "33"

Msg = Msg + NL & "Arthur" & TB & "21" & TB & "32" & TB & "23"

' Send the data through the DataText property.

Ole1.DataText = Msg

End Sub


DisplayType Property

Description

Determines if an object displays its contents or an icon.

Usage

[form.]ole.DisplayType [ = setting ]

Remarks

The DisplayType property settings are:

Setting		Description
0 (Default)	Content	When the OLE control contains an object, the object's data is displayed in the control.
1	Icon	When the OLE control contains an object, the object's icon is displayed in the control. This property determines the default setting of the Icon checkbox in the Insert Object and Paste Special dialogs. When you display these dialogs either at run time (set Action to 14 or 15) or design time, the Icon checkbox is automatically selected if this property is set to 1 (Icon). Once an object is created, you cannot change its display type.
Data Type

Integer


FileNumber Property

Description

Determines the file number to be used when saving or loading an OLE object, or reflects the last file number used. Not available at design time.

Usage

[form.]ole.FileNumber [ = filenumber ]

Remarks

The filenumber must correspond to an open, binary file.

Use this property to specify the number of the file to be opened or saved when setting Action = 11 (Save) or Action = 12 (Read).

Data Type

Integer

Example

The example shows how to save and open the data associated with Ole1 in a file named MYOB.OLE. To run the example you need Microsoft Excel running or on your path. Create a form with an OLE control named Ole1. When the Insert Object dialog is displayed, press Cancel. Place a command button (Command1) on the form. Place a menu named File on the form with two menu items: mnuOpenObject and the other named mnuSaveObject.

Press F5, then press the command button to set the Class and Action properties. Microsoft Excel starts if it is not already running. Enter data in a spreadsheet, then choose Update from the Microsoft Excel File menu to show the data in the OLE control. Choose Save from the example form's File menu. This creates the file in which the OLE object is saved. You can end the example application, start it again and choose Open Object from its File menu to display the object independent of Microsoft Excel. Note that when you do so, the command button is disabled.

Sub Command1_Click ()

Ole1.Class = "Excel.Worksheet"	' Set class.

Ole1.Action = 0	' Create OLE object.

Show	' Show form with object,

Command1.Enabled = False	' then disable button...


Format Property

Description

Determines the format when setting and getting data from an application that created an object. Not available at design time.

Usage

[form.]ole.Format [ = format ]

Remarks

This property determines the format used with the Data and DataText properties.

Use the ObjectAcceptFormats, ObjectAcceptFormatsCount, ObjectGetFormats, and ObjectGetFormatsCount properties to get a list of the acceptable data formats for a specific class of OLE object.

Many applications that provide objects support only one or two formats. For example, Microsoft Draw accepts only the CF_METAFILEPICT format. CF_METAFILEPICT is a string literal and would be assigned as:

Ole1.Format = "CF_METAFILEPICT"

In many cases the list of formats an object can accept (ObjectAcceptFormats) is different from the list of formats that a object can provide (ObjectGetFormats).

Data Type

String


HostName Property

Description

Determines the friendly host name of your DispTest application.

Usage

End Sub

Sub mnuSaveObject_Click ()

Dim FileNum	' Declare variable.

FileNum = FreeFile	' Get a valid file number.

Open "MYOB.OLE" For Binary As FileNum ' Open file to be saved

Ole1.FileNumber = FileNum	' Set the file number.

Ole1.Action = 11	' Save the file.

Close #FileNum	' Close the file.

End Sub

Sub mnuOpenObject_Click ()

Dim FileNum	' Declare variable.

FileNum = FreeFile	' Get a valid file number.

Open "MYOB.OLE" For Binary As FileNum ' Open the file.

Ole1.FileNumber = FileNum	' Set the file number.

Ole1.Action = 12	' Get data from file.

Close #FileNum	' Close the binary file.

' Because you've opened the file,

' disable button that initiates the object

Command1.Enabled = False

End Sub

[form.]ole.HostName [ = name ]

Remarks

When editing an OLE object, the HostName may be displayed in the object's window title. Some applications that provide objects do not display the host name.

Data Type

String


lpOLEObject Property

Description

Specifies the address of the OLE object. Not available at design time; read-only at run time.

Usage

[form.]ole.lpOleObject

Remarks

Many function calls in the OLE 2 DLLs require the address of an object as an argument. Pass the value specified in the lpOleObject property when making API calls to the OLE 2 DLLs. The value is 0 if no object is currently displayed. If a call is made to an API that makes a callback to the OLE control, the result is unpredictable.

The address returned by this property is a far pointer to the IOleObject interface for the current object.

Data Type

Long


Object Property

Description

Represents the object in an OLE control. Not available at design time; read-only at run time.

Usage

[form.]ole.Object[.property | .method] [ = setting]

Remarks

Use this property to specify an OLE object when performing OLE Automation tasks.

You perform OLE Automation tasks on the object using properties and methods the object supports. For information on the properties and methods an object supports, see the documentation for the application that created the object.

Data Type

Long


PasteOK Property

Description

Specifies whether the contents of the Clipboard can be pasted into the OLE control. Not available at design time; read-only at run time.

Usage

[form.]ole.PasteOK

Remarks

When this property is True, the contents of the Clipboard can be pasted into the OLE control.

Use the OleTypeAllowed property to specify the type of object (linked or embedded) that can be pasted into the OLE control. Once an object has been successfully pasted into the OLE control, you can query the OleType property to determine the type of object that was created.

This property can be useful if your application supports an Edit Paste command. If PasteOK is False, then the Edit Paste command should be disabled; otherwise it can be enabled. Enable and disable menu commands by setting their Enabled property to True and False, respectively.

You paste an object into the OLE control by setting Action = 5 (paste).

To provide more flexibility to the user, display a Paste Special dialog (set Action = 15) when the user selects the Edit Paste command. When this dialog is displayed, an object is pasted to the Clipboard based on the user's selections in the dialog.

Data Type

Integer (Boolean)

Example

The example code cannot run by itself, but illustrates how to paste the contents of the Clipboard into the OLE control when the user chooses the appropriate menu item. To see how this works in an application, load the sample program OLE2DEMO.MAK, place a breakpoint in the mnuEditClick procedure, then press F5 and choose Paste from the Edit menu.

Sub mnuEditPaste_Click ()

' Check value of PasteOK.

If Ole1.PasteOK Then

Ole1.Action = 5	' Do the Paste if OK.

Else	' Otherwise disable Paste

mnuEditPaste.Enabled = False	' menu item and give

MsgBox "Cannot Paste"	' appropriate message.

End If

End Sub


ObjectAcceptFormats Property

Description

Specifies the list of formats an object can accept. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectAcceptFormats (index)

Remarks

ObjectAcceptFormats is a zero-based array of strings describing the valid formats that can be used in the Format property when exchanging data with an object using the Data and DataText properties. ObjectAcceptFormatsCount specifies the number of elements in the ObjectAcceptFormats array.

The list of acceptable formats is obtained from an object. If the OLE control does not contain an object, an error will occur when you attempt to access this property.

Data Type

String

Example

To run this example, place an OLE control and three list boxes on a form. When the Insert Object dialog is displayed, select an application in the New Object list box and click OK to create an object. Then paste the example code into the declarations section of the form.

Sub Form_Click ()

Dim I	' Declare variable.

' Update the list of available verbs

Ole1.Action = 17 	' Fetch verbs

' Fill the Verbs list box.

For I = 0 To Ole1.ObjectVerbsCount - 1

List1.AddItem Ole1.ObjectVerbs(I)

Next I

'Fill the Accept Formats list box.

For I = 0 To Ole1.ObjectAcceptFormatsCount - 1

List2.AddItem Ole1.ObjectAcceptFormats(I)

Next I

' Fill the Get Formats list box.

For I = 0 To Ole1.ObjectGetFormatsCount - 1

GetFmtsList3.AddItem Ole1.ObjectGetFormats(I)

Next I

End Sub


ObjectAcceptFormatsCount Property

Description

Specifies the number of formats that can be accepted by an object. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectAcceptFormatsCount

Remarks

Use this property to get the number of elements in the ObjectAcceptFormats property array.

Data Type

Integer


ObjectGetFormats Property

Description

Specifies the list of formats an object can return. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectGetFormats (index)

Remarks

The list is a zero-based string array. Elements of the array can be used to set the Format property when getting data from an object using the Data and DataText properties.

Data Type

String


ObjectGetFormatsCount Property

Description

Specifies the number of formats an object can provide. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectGetFormatsCount

Remarks

Use this property to determine the number of elements in the ObjectGetFormats property array.

Data Type

Integer


OleType Property

Description

Specifies the status of an object.

Usage

[form.]ole.OleType

Remarks

The OleType property settings are:

Setting	Description
0 - Linked	The OLE control contains a linked object. All the object's data is managed by the application that created it. When the object is saved (set Action = 11), only link information such as SourceDoc, SourceItem, and so on, is saved in the specified file by your DispTest application.
1 - Embedded	The OLE control contains an embedded object. All the OLE object's data is managed with the DispTest application. When the object is saved (set Action = 11), all data associated with the object is saved in the specified file.
3 - None	The OLE control does not contain an object. Use this property to determine if the OLE control contains an object, or to determine the type of object the OLE control contains.  Use the AppRunning property to determine if the application that created the object is running. When creating an object, use the OleTypeAllowed property to determine the type of object that can be created.
Data Type

Integer


OleTypeAllowed Property

Description

Determines the type of object that can be created.

Usage

[form.]ole.OleTypeAllowed [ = setting ]

Remarks

The OleTypeAllowed property settings are:

Setting	Description
0 - Linked	The OLE control can only contain a linked object.
1 - Embedded	The OLE control can only contain an embedded object.
2 (Default) - Either	The OLE control can contain either a linked or an embedded object.
When you allow the user to create an object by displaying the Insert Object dialog (set Action = 14) or the Paste Special dialog (set Action = 15), use this property to determine the type of object the user is allowed to create.

Use the OleType property to determine an object's type (linked, embedded, or none).

Data Type

Integer


ObjectVerbs Property

Description

Returns a list of verbs an object supports. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectVerbs (index)

Remarks

ObjectVerbs is a zero-based string array. Use this property along with the ObjectVerbsCount property to get the verbs supported by an object. These verbs are used by the Verb property to determine an action to perform when an OLE object is activated (set Action = 7).

The first verb in the ObjectVerbs array, ObjectVerbs(0), is the default verb. If the Verb property has not been set, this is the action performed when the object is activated.

The remaining verbs in the array are suitable for display in a menu. If it is appropriate to display the default verb in a menu (in most cases this is true), then the default verb will have two entries in the ObjectVerbs array.

Applications that display OLE objects typically include an Object command on the Edit menu. When the user selects Edit Object, a cascading menu displays the object's verbs. Use the ObjectVerbs, ObjectVerbsCount, and ObjectVerbFlags properties to create such a menu at run time.

The list of verbs an object supports may vary, depending on the state of the object. To update the list of verbs an object supports, set Action = 17 (Fetch Verbs). Be sure to update the list of verbs before presenting it to the user.

To automatically display the verbs in the ObjectVerbs array in a pop-up menu when the user clicks an object with the right mouse button, set the AutoVerbMenu property to True.

Note  Each object supports three special verbs which may or may not be listed in the ObjectVerbs property. These verbs are represented by the values 0,  − 1,  − 2, and  − 3. They are used by the Verb property to show, open, or hide an object.

Data Type

String


ObjectVerbsCount Property

Description

Specifies the number of verbs supported by an object. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectVerbsCount

Remarks

Use this property to determine the number of elements in the ObjectVerbs property array.

The list of verbs an object supports may vary, depending on the state of the object. To update the list of verbs an object supports, set Action = 17 (Fetch Verbs).

Data Type

Integer


ObjectVerbFlags Property

Description

Specifies the menu state for each verb in the ObjectVerbs array. Not available at design time; read-only at run time.

Usage

[form.]ole.ObjectVerbFlags (index)

Remarks

The first verb in the ObjectVerbs array, ObjectVerbs(0), is the default verb. The remaining verbs in this array are suitable for being displayed on a menu. The ObjectVerbFlags array contains information about the menu state (such as grayed, checked, and so on) for each verb in the ObjectVerbs array.

When displaying a menu containing an object's verbs, check the value of this property to see how the item should be displayed.

The following table describes the possible ObjectVerbFlags.

Constant	Value	Description
MF_CHECKED	&H0008	The menu item should be checked.
MF_DISABLED	&H0002	The menu item should be disabled (but not grayed).
MF_ENABLED	&H0000	The menu item should be enabled.
MF_GRAYED	&H0001	The menu item should be grayed.
MF_SEPARATOR	&H0800	The menu item is a separator bar.
Data Type

String


SizeMode Property

Description

Determines how the OLE control is sized or how its image is displayed when it contains an object.

Usage

[form.]ole.SizeMode [ = setting ]

Setting

The SizeMode property settings are:

Setting	Description
0 - Clip (Default)	The object is displayed in actual size. If the object is larger than the OLE control, its image is clipped by the control's borders.
1 - Stretch	The object's image is sized to fill the OLE control.
2 - Autosize	The OLE control is resized to display the entire object.
Data Type

Integer


SourceDoc Property

Description

Determines the filename to use when you create a linked or embedded OLE object.

Usage

[form.]ole.SourceDoc [ = filename ]

Remarks

When you create an embedded object (set Action = 0), the SourceDoc property specifies a filename to use as a template for the embedded object.

When you create a linked object (set Action = 1), the SourceDoc property specifies the file to be linked and the SourceItem property specifies data within the file to be linked.  The SourceItem property is concatenated to the SourceDoc property. The SourceDoc property then returns the entire path to the linked file along with any SourceItem information. For example:

"C:\WORK\QTR1\REVENUE.XLS!R1C1:R30C15"

When the SourceItem is concatenated to the SourceDoc property, the SourceItem property returns an empty string.

Data Type

String

Example

The example creates a linked OLE object when the user clicks the command button. First, use Microsoft Excel to create a spreadsheet named OLETEST.XLS. Then create a form with an OLE control named Ole1. When the Insert Object dialog is displayed, choose Cancel. Place a command button on the form and copy the following code into the form's declarations section, then press F5.

The file OLETEST.XLS must exist in the specified path.

Sub Command1_Click

' Set class.

Ole1.Class = "ExcelWorksheet"

' Specify source file.

Ole1.SourceDoc = "C:\EXCEL\OLETEST.XLS"

' Specify the data within the file to be linked.

Ole1.SourceItem = "R1C1:R5C5"

' Create a linked object using OleTest.xls.

Ole1.Action = 1

Ole1.Refresh	' Show the worksheet.

End Sub


SourceItem Property

Description

Specifies the data within the file to be linked when you create a linked object.

Usage

[form.]ole.SourceItem [ = stringexpression ]

Remarks

OleTypeAllowed must be set to 0 (Linked) or 2 (Either) when using this property. Use the SourceDoc property to specify the file to link.

Each object uses its own syntax to describe units of data. To set this property, specify a unit of data recognized by the object. For example, when you link to Microsoft Excel, you specify the SourceItem using a cell or cell-range reference such as "R1C1" or "R3C4:R9C22."

To determine the syntax to describe a unit of data for an object, see the documentation for the application that created the object.

Tip  You may be able to determine this syntax by creating a linked object at design time using the Paste Special command (click the OLE control with the right mouse button). Once the object is created, select the SourceDoc property in the Properties window and look at the string in the Settings box. For most objects, this string will contain a path to the linked file, followed by an exclamation mark (!) or a backslash (\), and the syntax for the linked data.

When a linked object is created, the SourceItem property is concatenated to the SourceDoc property. At run time, the SourceItem property returns an empty string ("") and the SourceDoc property returns the entire path to the linked file, followed by an exclamation mark (!) or a backslash (\), followed by the SourceItem. For example:

"C:\WORK\QTR1\REVENUE.XLS!R1C1:R30C15"


StringUpdateOptions Property

Description

Determines how an object is updated when linked data is modified. Not available at design time.

Usage

[form.]ole.UpdateOptions [ = option ]

Remarks

The UpdateOptions property settings are:

Setting	Description
0 - Automatic (Default)	The object is updated each time the linked data changes.
1 - Frozen	The object is updated whenever the user saves the linked document from within the application in which it was created.
2 - Manual	The object is updated only when the Action property is set to 6 (Update). This property is useful for linked objects where other users or applications can access and modify the linked data.
When an object's data is changed, the Updated event is invoked.

Data Type

Integer (Enumerated)


Verb Property

Description

Specifies an operation to perform when an OLE object is activated (Action = 7).

Usage

[form.]ole.Verb [ = verbnumber ]

Remarks

Each object can support its own set of verbs. For example, many objects support the verbs "edit" and "play". Use the ObjectVerbs and ObjectVerbsCount properties to access the list of verbs supported by an object. Set Verb = 1 to specify the first verb in the list, set Verb = 2 to specify the second verb in the list, and so on.

These standard verbs are supported by every object:

Value	Action
0	The default action for the object.
-1	Activates the object for editing. If the application that created the object supports in-place editing, the object is activated within the OLE control.
-2	Opens the object in a separate application window. If the application that created the object supports in-place editing, the object is activated in its own window.
-3	For embedded objects, this verb hides the application that created the object.
Note  These verbs may not be listed in the ObjectVerbs property array.

Set AutoActivate = 2 (Double-Click) to automatically activate an object when it is double-clicked by the user.

Set AutoVerbMenu = True to display a pop-up menu containing the object's verbs when the user clicks the object with the right mouse button.

Data Type

Integer


Updated Event

Description

Occurs when an object's data has been modified.

Syntax

Sub OLE_Updated (Code As Integer)

Remarks

Code indicates how the OLE object was updated. Its possible values are:

Value	Constant	Description
0	OLE_CHANGED	The object's data has changed.
1	OLE_SAVED	The object's data has been saved by the application that created the object.
2	OLE_CLOSED	The file containing the linked object's data has been closed by the application that created the object.
3	OLE_RENAMED	The file containing the linked object's data has been renamed by the application that created the object.
These constants are defined in the file CONSTANT.TXT.

The Updated event is useful for determining if an object's data has been changed since it was last saved. To do this, set a global variable in the Updated event indicating that the object needs to be saved. When you save the OLE object, reset the variable.


Questions and Answers

This section includes the following topics:

Questions about GUIDs
How OLE Automation Compares Strings

This section includes answers to many common questions asked by developers implementing OLE Automation objects and controllers.


Questions about GUIDs

Globally unique identifiers (GUIDs) appear in multiple places in a typical OLE Automation application. GUID errors make for pernicious bugs. This section is meant to save you time and effort.  It describes all the places GUIDs appear in a typical OLE Automation application and some common GUID bug characteristics. Finally, it offers some GUID management techniques.

This section includes the following topics:

The Basics
Where GUIDs live
GUID and Registry Troubleshooting
GUID Management Techniques


The Basics

GUID:	a globally unique identifier. Same as a UUID.

UUID:	a universally unique identifier. Same as a GUID.

CLSID:	a class identifier — an identifier (UUID/GUID) used to refer to a class.

Registry

The system registry is a central repository containing information about objects. GUIDs are used to index that information. You can view the registration information on your machine by running REGEDIT with the /V option:

regedit /v

Usually a name is connected to a GUID (that is, "hello.hello" maps to a GUID) and then the GUID is connected to all the other relevant aspects (that is, the GUID maps to "hello.exe").

A GUID/UUID/CLSID is created by the tool GUIDGEN.EXE. Running GUIDGEN.EXE produces a huge hex number which uniquely identifies your object, whether it be a class, an interface, a library, or some other kind of object.


Where GUIDs live

.REG files

When you create an application, you'll usually create one or more .REG files. Your .REG files contain the GUIDs for the classes that your application exposes. These GUIDs are added to the registry when you run REGEDIT.EXE to register your classes.

The system registry

The system registry contains the GUIDs for your classes in multiple places. This is where OLE and applications get information about your classes.

.ODL files

When you describe objects in an Object Description Language (ODL) file, you provide a GUID for each object. Compiling the .ODL file with MKTYPLIB.EXE places the GUIDs in a type library, which usually exists as a .TLB file. If you change a GUID in an .ODL file, make sure that you run MkTypLib again.

.TLB files

Type libraries describe your classes, and this information includes the GUIDs for the classes. You can browse .TLB files using the TIBROWSE sample application supplied with OLE.

.h files

Most application developers will declare CLSIDs for their classes in a header file using the DEFINE_GUID macro.


GUID and Registry Troubleshooting

"DispTest's GetObject can't seem to create an instance of my application."

DispTest uses OLE calls to find out where to get the .exe file that creates your application instance. To do this, DispTest does the following steps

1.	A call is generated to look up the GUID for your object. For the hello application, this means changing "hello.hello" into a GUID.

2.	A call is generated to find the "LocalServer" for the object. This is "hello.exe" for the hello application.

3.	A call is generated to launch the application.

There is a lot of room for error in this area. Ask yourself the following questions, and you'll probably find the root of the problem:

•	Did I remember to run the .REG file?

•	Are the entries in the registry correct? Do all the GUIDs match?

•	Can my application be launched? The executable for the application, listed in the LocalServer entry, should be on the path or it should be fully specified, as in "c:\hello\hello.exe."

"When I use DispTest's GetObject, the application launches and then the GetObject call fails."

Normally, a class factory is registered using CoRegisterClassObject when your application is started. Some applications only register their class factories when launched with the /Automation switch. If you inherited someone else's code, or copied a sample, you should find out whether or not you are testing for this switch. The /Automation option could appear in the .REG file, the registry, and in your development environment.

"GetTypeInfoOfGuid() is failing to get the type information from my type library."

This behavior can occur if the GUID in your code is not the same as the GUID in your .TLB file. When you call GetTypeInfoOfGuid, you provide a GUID. If this GUID doesn't match the GUID in your .TLB file, no type information will be returned. Check that the GUID in your code matches the GUID in your .TLB file. The GUID in your code is likely to be declared in a header file. You can check the GUID in your .TLB file by using TIBROWSE, which is provided with OLE.


GUID Management Techniques

The problem is not that managing GUIDs is difficult, but that they are pervasive, and their length prohibits a simple comparison. This section describes some GUID management techniques which will take you a few minutes, but could save you hours.

Recommendation

Keep a central list of all the GUIDs you have ever consumed. An easy way to do this is to run GUIDGEN.EXE several times, or use the -n option to generate the required number of GUIDs, and place the resulting strings in the first column of a spreadsheet. As you consume GUIDs, use the second column to enter a description of what the GUID was used for.

Benefits

•	Putting all the GUIDs in one location may prevent you from accidentally reusing a GUID. This often happens when you clone an application in order to create another one.

•	You can use the spreadsheet to compare GUIDs. You can check that a GUID is correct by copying it from the place it is being used (a .REG file, for example), pasting it into the spreadsheet, and then comparing the two cells with the "="operator.

•	You'll have a record of your GUID use in case of future problems, and you'll only need to look in one place to find out what the GUID is for your object.


How OLE Automation Compares Strings

This section describes how the OLE Automation implementations compare strings. Understanding these comparisons is useful when creating applications that support national languages that use accents and digraphs. The information in this section affects these components:

•	CreateStdDispatch (OLEAUT32.DLL on Win32 systems, OLE2DISP.DLL on Win16 systems)

•	DispGetIDsOfNames (OLEAUT32.DLL on Win32 systems, OLE2DISP.DLL on Win16 systems)

•	ITypeLib::FindName (OLEAUT32.DLL on Win32 systems, TYPELIB.DLL on Win16 systems)

•	ITypeLib::GetIDsOfNames (OLEAUT32.DLL on Win32 systems, TYPELIB.DLL on Win16 systems)

•	MkTypLib (MKTYPLIB.EXE)

When comparing strings, OLE Automation components use the following rules:

•	Comparisons are sensitive to locale based on the string's LCID. A string must have an LCID that is supported by the application or type library. Locales and LCIDs are described in the section "Supporting Multiple National Languages," in Chapter 2.

•	Accent characters are ignored. For example, the string "à" compares as the same as "a."

•	Case is ignored. For example, the string "A" compares as the same as "a."

•	Comparisons are sensitive to digraphs. For example, the string "Æ" is not the same as "AE."

•	For Japanese, Korean, and Chinese locales, ITypeLib::FindName and ITypeLib::GetIDsOfNames ignore width and kanatype.


Glossary

A

Application object

An object that identifies the application to the system. This object is specified by the appobj attribute in the type library. Typically, the Application object becomes active when the application starts.

C

Collection object

A grouping of exposed objects. You create collection objects when you want to address multiple occurrences of an object as a unit, such as when you want to draw a set of points.

E

Exposed object

See OLE Automation object.

I

Interface

One or more well-defined base classes providing member functions that, when implemented in an application, provide a specific service. Interfaces may include compiled support functions to simplify their implementation.

M

Methods

Member functions of an exposed object that perform some action on the object, such as saving it to disk.

O

OLE Automation controller

An application or scripting language that accesses OLE Automation objects. DispTest and Visual Basic are OLE Automation controllers.

OLE Automation object

An instance of a class defined within your application that is exposed for access by other applications or programming tools by means of OLE Automation interfaces.

Object Description Language (ODL)    The language used to describe an application's interface. ODL scripts are compiled into type libraries using the MkTypLib tool included with the OLE 2 SDK.

P

Programmable object

See OLE Automation object.

Property

A data member of an exposed object. Properties are set or returned by means of get and put accessor functions.

T

Type description

The information used to build the type information for one or more aspects of an application's interface. Type descriptions are written in Object Description Language (ODL). Note that this includes programmable and nonprogrammable interfaces.

Type information

Information that describes the interfaces of an application. Type information is created from type descriptions using OLE Automation tools, such as MkTypLib or the CreateDispTypeInfo function. Type information may be accessed through the ITypeInfo interface.

Type information element

A unit of information identified by one of these statements in a type description: typedef, enum, struct, module, interface, dispinterface, or coclass.

Type library

A file or component within another file that contains type information. Type libraries are created from type descriptions using MkTypLib, and may be accessed through the ITypeLib interface.

V

Value property

The property that defines the default behavior of an object when no other methods or properties are specified. You indicate the Value property by specifying the [default] attribute in ODL.


OLE Automation 94 Technical Notes
OLE Automation 1994 Technical Notes

This release of OLE Automation contains several enhancements and corrections that are not documented in the online Help file. The following changes will be included in the next version of the OLE 2 Programmer's Reference, Volume 2.

•	Returning Error Information

•	Retrieving Error Information

•	Errror Handling Interfaces

•	ICreateErrorInfo Interface

•	IErrorInfo Interface

•	ISupportErrorInfo Interface

•	Error Handling Functions

•	Registering Interfaces

•	New ODL Attributes

•	Type Library Loading Function

•	LoadTypeLibFromResource

•	ArrayManipulation Functions

•	SafeArrayPtrOfIndex

•	DEFINE_GUID macros

•	Additions to ITypeInfo Structures and Enumerations

•	Active Object Registration

•	National Language Support

•	VT_UI1 Data Type


Error Handling

When an OLE Automation client application calls an OLE vtable interface that returns an HRESULT, it is now possible for an OLE Automation client to get the same rich (contextual) information that is returned from IDispatch::Invoke.

Objects that are invoked through vtables need to use the OLE Automation error handling interfaces to define and return error information. The interfaces and functions include the following:

•	ICreateErrorInfo interface — Sets error information.

•	IErrorInfo interface — Returns information from an error object.

•	ISupportErrorInfo interface — Identifies this object as supporting the ErrorInfo interface.

•	Error handling functions — Create, retrieve, set and clear the error object.

The member functions of each interface are listed in the following table.

Category	Member name	Purpose
IErrorInfo	GetDescription	Returns a textual description of the error.
	GetGUID	Returns the GUID for the interface that defined the error.
	GetHelpContext	Returns the Help context ID for the error.
	GetHelpFile	Returns the path of the Help file that describes the error.
	GetSource	Returns the ProgID for the class or application that returned the error.
ICreateErrorInfo	SetDescription	Sets a textual description of the error.
	SetGUID	Sets the GUID for the interface that defined the error.
	SetHelpContext	Sets the Help context ID for the error.
	SetHelpFile	Sets the path of the Help file that describes the error.
	SetSource	Sets the ProgID for the class or application that returned the error.
ISupportErrorInfo	InterfaceSupportsErrorInfo	Indicates whether an interface supports the IErrorInfo interface.
Error handling functions	CreateErrorInfo	Creates a generic error object.
	GetErrorInfo	Retrieves and clears the current error object.
	SetErrorInfo	Sets the current error object.


Returning Error Information

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 that is associated with the current thread of execution.

To return error information

1.	Implement the ISupportErrorInfo interface.

2.	Call the CreateErrorInfo function to create an instance of a generic error object.

3.	Use the ICreateErrorInfo methods to set its contents.

4.	Call the SetErrorInfo function to associate the error object with the current logical thread.


Retrieving Error Information

To retrieve error information, an object

1.	Checks whether the returned SCODE represents an error that the object is prepared to handle.

2.	Calls QueryInterface on the object (ISupportErrorInfo::InterfaceSupportsErrorInfo) to verify that the object that returned the error code supports the ErrorInfo interface. If ErrorInfo is not supported, additional information about the error is not available.

3.	Calls the GetErrorInfo function to get a pointer to the error object.

4.	Uses the IErrorInfo methods to retrieve information from the error object.

If the object cannot  handle the error, but needs to propagate the error information further down the call chain, it should pass the SCODE to its caller. Because the GetErrorInfo function clears the error information, the function should be called only by the object that handles the error.


ICreateErrorInfo Interface

Implemented by	Used by	Header filename
OLEAUT32.DLL OLE2DISP.DLL	Applications that return rich error information	OLEAUTO.H  DISPATCH.H
The functions included in this section are:

ICreateErrorInfo::SetDescription
ICreateErrorInfo::SetGUID
ICreateErrorInfo::SetHelpContext
ICreateErrorInfo::SetHelpFile
ICreateErrorInfo::SetSource


ICreateErrorInfo::SetDescription

HRESULT ICreateErrorInfo::SetDescription(szDescription)
LPOLESTR * szDescription

Sets the textual description of the error.

Parameters

szDescription

A brief, zero-terminated string that describes the error.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

The text should be supplied in the language specified by the LCID that was passed to the method that raised the error. Refer to the section on lcid parameters for more information.


ICreateErrorInfo::SetGUID

HRESULT ICreateErrorInfo::SetGUID(rguid)
REFGUID rguid

Sets the GUID of interface that defined the error.

Parameters

rguid

The GUID of the interface that defined the error, or GUID_NULL, if the error was defined by the operating system.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

ICreateErrorInfo::SetGUID sets the GUID of the interface that defined the error. If the error was defined by the system, ICreateErrorInfo::SetGUID should set GUID_NULL.

This GUID does not necessarily represent the source of the error. The source is the class or application that raised the error.  Using the GUID, applications can handle errors in an interface independent of the class that implements the interface.


ICreateErrorInfo::SetHelpContext

HRESULT ICreateErrorInfo::SetHelpContext(dwHelpContext)
DWORD dwHelpContext

Sets the Help context ID for the error.

Parameters

dwHelpContext

The Help context ID for the error.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

ICreateErrorInfo::SetHelpContext sets the Help context ID for the error.  To establish the Help file to which it applies, use ICreateErrorInfo::SetHelpFile.


ICreateErrorInfo::SetHelpFile

HRESULT ICreateErrorInfo::SetHelpFile(szHelpFile)
LPOLESTR szHelpFile

Sets the path of the Help file that describes the error.

Parameters

szHelpFile

The fully qualified path of the Help file that describes the error.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

ICreateErrorInfo::SetHelpFile sets the fully qualified path of the Help file that describes the current error. Use ICreateErrorInfo::SetHelpContext to set the Help context ID for the error within the Help file.


ICreateErrorInfo::SetSource

HRESULT ICreateErrorInfo::SetSource(szSource)
LPSTR szSource

Sets the language-dependent ProgID for the class or application that raised the error.

Parameters

szSource

A programmatic ID, in the form progname.objectname.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

Use ICreateErrorInfo::SetSource to identify the class or application that is the source of the error.  The language for the returned ProgID depends on the LCID that was passed into the method at invocation.


IErrorInfo Interface

Implemented by	Used by	Header filename
OLEAUT32.DLL OLE2DISP.DLL	Applications that receive rich information	OLEAUTO.H DISPATCH.H
Functions included in this section are:

IErrorInfo::GetDescription
IErrorInfo::GetGUID
IErrorInfo::GetHelpContext
IErrorInfo::GetHelpFile
IErrorInfo::GetSource


IErrorInfo::GetDescription

HRESULT IErrorInfo::GetDescription(pbstrDescription)
BSTR * pbstrDescription

Returns a textual description of the error.

Parameters

pbstrDescription

Pointer to where to return a brief string that describes the error.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

The text is returned in the language specified by the LCID that was passed to IDispatch::Invoke for the method that encountered the error.


IErrorInfo::GetGUID

HRESULT IErrorInfo::GetGUID(pguid)
GUID * pguid

Returns the GUID of interface that defined the error.

Parameters

pguid

Pointer to a GUID, or GUID_NULL, if the error was defined by the operating system.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

IErrorInfo::GetGUID returns the GUID of the interface that defined the error. If the error was defined by the system, IError::GetGUID returns GUID_NULL.

Note that this GUID does not necessarily represent the source of the error, however; the source is the class or application that raised the error.  Using the GUID, your application can handle errors in an interface independent of the class that implements the interface.


IErrorInfo::GetHelpContext

HRESULT IErrorInfo::GetHelpContext(pdwHelpContext)
DWORD * pdwHelpContext

Returns the Help context ID for the error.

Parameters

pdwHelpContext

Pointer to the Help context ID for the error.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

IErrorInfo::GetHelpContext returns the Help context ID for the error.  To find the Help file to which it applies, use IErrorInfo::GetHelpFile.


IErrorInfo::GetHelpFile

HRESULT IErrorInfo::GetHelpFile(pbstrHelpFile)
BSTR * pbstrHelpFile

Returns the path of the Help file that describes the error.

Parameters

pbstrHelpFile

Pointer to a string containing the fully qualified path of the Help file.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

IErrorInfo::GetHelpFile returns the fully qualified path of the Help file that describes the current error. Use IError::GetHelpContext to find the Help context ID for the error within the Help file.


IErrorInfo::GetSource

HRESULT IErrorInfo::GetSource(pbstrSource)
BSTR * pbstrSource

Returns the language-dependent ProgID for the class or application that raised the error.

Parameters

pbstrSource

Pointer to a string containing a ProgID.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

Use IErrorInfo::GetSource to determine the class or application that is the source of the error.  The language for the returned ProgID depends on the LCID that was passed into the method at invocation.


ISupportErrorInfo Interface

Implemented by	Used by	Header filename
Applications that return error information	Applications that retrieve error information	OLEAUTO.H
The function included in this interface is:

ISupportErrorInfo::InterfaceSupportsErrorInfo


ISupportErrorInfo::InterfaceSupportsErrorInfo

HRESULT ISupportErrorInfo::InterfaceSupportsErrorInfo(riid)
REFIID riid

Indicates whether an interface supports the IErrorInfo interface.

Parameters

riid

Pointer to an interface ID.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Interface supports IErrorInfo.
S_FALSE	Interface does not support IErrorInfo.
Comments

Objects that support IErrorInfo must implement this interface.

Objects that receive an error SCODE should call QueryInterface to get a pointer to the ISupportErrorInfo interface, then call InterfaceSupportsErrorInfo with the riid of the interface that returned the SCODE. If InterfaceSupportsErrorInfo returns S_FALSE, then the error object does not represent an error returned from the caller, but from somewhere further up the call chain.  In this case, the error object can be considered incorrect and should be discarded.

If InterfaceSupportsErrorInfo returns S_OK, use GetErrorInfo to get a pointer to the error object.


Error Handling Functions

Implemented by	Used by	Header filename
OLEAUT32.DLL OLE2DISP.DLL	Applications that receive rich error information	OLEAUTO.H DISPATCH.H
The functions included in this interface are:

CreateErrorInfo
GetErrorInfo
SetErrorInfo


CreateErrorInfo

HRESULT CreateErrorInfo(ppcerrinfo)
ICreateErrorInfo * *  ppcerrinfo

Creates an instance of a generic error object.

Parameters

ppcerrinfo

Pointer to a system-implemented generic error object.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
E_OUTOFMEMORY	Could not create the error object.
Comments

This function returns a pointer to a generic error object, which can later be passed to ICreateErrorInfo or SetErrorInfo to set the error information for the current logical thread.  The generic error object implements both ICreateErrorInfo and IErrorInfo.


GetErrorInfo

HRESULT GetErrorInfo(DWORD dwReserved,
IErrorInfo * *  pperrinfo)

Gets the error information pointer set by the previous call to SetErrorInfo in the current logical thread.

Parameters

pperrinfo

Pointer to a pointer to an error object.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
S_FALSE	There was no error object to return.
Comments

This function returns a pointer to the most recently set IErrorInfo pointer in the current logical thread, and removes the error object from the thread; in effect, it clears the error object.  Use this function only in code that will handle the error.  Code that needs to propagate the error should simply pass along the error SCODE without retrieving the error object.


SetErrorInfo

HRESULT SetErrorInfo(DWORD dwReserved,
IErrorInfo * *  pperrinfo)

Sets the error information pointer for the current thread of execution.

Parameters

perrinfo

Pointer to an error object.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success
Comments

This function releases the existing error information pointer and sets the pointer to perrinfo. Use this function after creating an error object to associate the object with the current thread of execution.


Registering Interfaces

By default, OLE Automation registers dispinterfaces that appear in the ODL file.  It also registers remote OLE Automation-compatible interfaces that are not registered elsewhere under the ProxyStubClsid.

Example

The ProxyStubClsid in the following example refers to the proxy/stub implementation of IDispatch.

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A} = _DHello

HKEY_CLASSES_ROOT\Interface\{D3CE6D46-F1AF-1068-9FBB-08002B32372A}
\ProxyStubClsid = {00020420-0000-0000-C000-000000000046}


New ODL Attributes

Type information is described using the Object Description Language (ODL). Scripts written in ODL are compiled with the MkTypLib tool to create type libraries. New attributes for this release are described here.

Nonextensible Objects

Compile-time checking is provided to generate "member-not-found" and "parameter type mismatch" for nonextensible objects. The TypeInfo for an interface can be set as nonextensible through the nonextensible ODL attribute. If no attribute is specified, the interface is assumed to be extensible.

Hidden

The hidden attribute is used to indicate that a given member or interface should not be displayed in a browser, but that the member should still exist, and the macro programmer should be able to bind to it.

[hidden]

dispinterface Document

{

  [hidden] void PrintOut

  void Print

}

Control

The control attribute indicates that the item represents a control from which a container site will derive additional type libraries or component classes. This attribute allows you to mark type libraries that describe controls so that they will not be displayed in type browsers intended for nonvisual objects. The TypeInfo of the container should describe the members of the controls and the TypeInfo of the control members should be used.

 [control]

CoClass ListBox

{

  [Default] DListBox

}

Source (on a Property or Method)

The source attribute now also indicates that a property or method returns an object that is a source of events. The property or method must return a VARIANT or object. By using the source attribute for controls, you can attach code to these pieces of the whole object using IConnectionPointContainer.

OLE Automation Compatibility

The oleautomation attribute indicates that an interface is compatible with OLE Automation. The parameters and return types specified for its members must be OLE Automation-compatible, as listed in the following table.

Type	Description
int	Signed integer, whose size is system-dependent.
boolean	Data item that can have the values True or False. The size may be system-dependent.
char	8-bit signed data item.
double	64-bit IEEE floating-point number.
float	32-bit IEEE floating-point number.
long	32-bit signed integer.
short	16-bit signed integer.
BSTR	Length-prefixed string.
typedef enum myenum	Signed integer, whose size is system-dependent.
CURRENCY	8-byte fixed-point number.
DATE	64-bit floating-point fractional number of days since December 30, 1899.
IDispatch *	Pointer to IDispatch interface (VT_DISPATCH).
IUnknown *	Pointer to interface that does not derive from IDispatch (VT_UNKNOWN). (Any OLE interface can be represented by its IUnknown interface.)
Typename *	Where Typename is a coclass, dispinterface, or another OLE Automation-compatible interface.
The following are compatibility rules:

•	A parameter is compatible with OLE Automation if its type is an OLE Automation- compatible type, a pointer to an OLE Automation-compatible type, or a SafeArray of an OLE Automation-compatible type.

•	A return type is compatible with OLE Automation if its type is either an HRESULT or is an OLE Automation-compatible type.

•	A member is compatible with OLE Automation if its return type and all of its parameters are compatible with OLE Automation.

•	An interface is compatible with OLE Automation if it derives from IDispatch, if it has the oleautomation or dual attribute, and if all of its vtable entries are OLE Automation compatible.

•	Every dispinterface is OLE Automation compatible.

Dual Interfaces

The dual attribute creates an interface that is both a Dispatch interface and a Component Object Model interface. The first seven entries of the vtable for a dual interface are the seven members of IDispatch, and the remaining entries are OLE Component Object Model entries for direct access to members of the interface. All the parameters and return types specified for members of a dual interface must be OLE Automation-compatible types.

Dual interfaces provide for both the speed of direct vtable binding and the flexibility of IDispatch binding. For this reason, dual interfaces are recommended whenever possible.

Raising Exceptions

Members of interfaces that need to raise exceptions should return an HRESULT and specify a retval parameter for the actual return value. The retval parameter is always the last parameter in the list.

The following example creates a dual interface, which supports both vtable and IDispatch binding to its members.

[dual] interface Goodbye : IDispatch

{

// A property that is a string

[propget] HRESULT GoodbyeMessage ()
[out, retval] BSTR * pbstrRetVal);

[propput] HRESULT GoodbyeMessage ([in] BSTR rhs);

//A method that takes a long integer and returns a string

HRESULT SayGoodbye([in] long NextTime,

   [out, retval] BSTR *pbstrRetVal);

}

TypeInfo and Dual Interfaces

Dual interfaces have two different TypeInfos for the same interface. The TKIND_INTERFACE TypeInfo describes the interface as a standard OLE Component Object Model interface. The TKIND_DISPATCH TypeInfo describes the interface as a standard Dispatch interface. The lcid and retval parameters and HRESULT return types are removed, and the return type of the member is specified to be the same type as the retval parameter.

By default, the TypeKind for a dual interface is TKIND_DISPATCH. Tools that bind to interfaces should check the type flags for TYPEFLAG_FDUAL. If this flag is set, the TKIND_INTERFACE TypeInfo is available through a call to ITypeInfo::GetRefTypeOfImplType.

Using the lcid Attribute

When ITypeInfo::Invoke calls an object's vtable, it passes the LCID of the TypeInfo into parameters tagged with the lcid attribute, and gets the return value from the retval parameter.

If the member returns an error scode, it uses the Error Interfaces to get rich error information and fill in the ExceptInfo structure that it returns, and returns DISP_E_EXCEPTION.

The following example defines an interface named IMyInt, which has a pair of accessor functions for the MyMessage property and a method that returns a string.

interface IMyInt : IUnknown

{

//A property that is a string

[propget] HRESULT MyMessage([in, lcid] LCID lcid,

[out, retval] BSTR *pbstrRetVal);

[propput] HRESULT MyMessage([in] BSTR rhs, [in, lcid] DWORD lcid);

//A method returning a string

HRESULT SayMessage([in] long NumTimes,

   [in, lcid] DWORD lcid,

   [out, retval] BSTR *pbstrRetVal);

}

The members of this interface return error information and function return values through the HRESULT values and retval parameters, respectively. Tools that access the members can return the HRESULT, or can expose the retval parameter as the return value and handle the HRESULT transparently.

New ODL Attribute Descriptions

MkTypeLib accepts the following new attributes.

Attribute	Allowed on	Comments
control	Typelib and coclass	Indicates that the item represents a control from which a container site will derive additional typelibs or coclasses. This attribute allows you to mark type libraries that describe controls so that they will not be displayed in type browsers intended for nonvisual objects.  Representation: TYPEFLAG_FCONTROL, LIBFLAG_FCONTROL
dual	Interface	Identifies an interface that can be bound with IDispatch and directly through the vtable. The interface must be compatible with OLE Automation and derive from IDispatch. Not allowed on dispinterfaces. Specifying dual on an interface implies that the interface is compatible with OLE Automation, and therefore causes both the TYPEFLAG_FDUAL and TYPEFLAG_FOLEAUTOMATION flags to be set. Representation: LIBFLAG FDUAL, FUNCFLAG, TYPEFLAG_FDUAL and TYPEFLAG_FOLEAUTOMATION
hidden	Property, method, coclass, dispinterface, and typelib	Indicates that the item exists but should not be displayed in a user-oriented browser. This attribute allows you to remove members from your interface (by shielding them from further use) while maintaining compatibility with existing code. Representation: VARFLAG_FHIDDEN, FUNCFLAG_FHIDDEN, TYPEFLAG_FHIDDEN, LIBFLAG FHIDDEN
lcid	parameter	Indicates that the parameter is the locale ID. At most one parameter may have this attribute.  The parameter must have the in attribute and can't have the out attribute, and its type must be DWORD. The lcid attribute is not allowed on Dispatch interfaces.  By convention, the lcid parameter is the last parameter that does not have the retval attribute.  ITypeInfo::Invoke passes the LCID of the TypeInfo into the lcid parameter. Parameters with this attibute are not displayed in browsers.
licensed	Coclass	Indicates that the class is licensed. Representation: TYPEFLAG_FLICENSED
nonextensible	Dispinterface, interface	Indicates that the IDispatch implementation includes only the properties and methods listed in the interface or dispinterface description. The dispinterface or interface must have the oleautomation attribute. By default, OLE Automation assumes that interfaces may add members at runtime; that is, it assumes they are extensible.  Representation: TYPEFLAG_FNONEXTENSIBLE
oleautomation	Interface	Indicates that the interface is compatible with OLE Automation. Not allowed on dispinterfaces. Representation: TYPEFLAG_FOLEAUTOMATION
retval	Parameters of interface members that describe methods or get properties	Indicates that the parameter is the return value of the member.  This attribute may be used only on the last parameter of the member.  The parameter must have the out attribute and must be a pointer type. Parameters with this attibute are not displayed in user-oriented browsers.  Representation: IDLFLAG_FRETVAL
source	Property or method	Indicates that the property or method returns an object that is a source of events. The property or method must return a VARIANT or object.    Indicates that the object implements IConnectionPointContainer. Representation:  VARFLAG_SOURCE, FUNCFLAG_SOURCE


Type Loading Function

Multiple type libraries can now be stored in an executable (.EXE) file or DLL. These libraries can be loaded using LoadTypeLibFromResource.


LoadTypeLibFromResource

HRESULT LoadTypeLibFromResource(szFileName, szResName, lResID, lplptlib)
OLECHAR FAR* szFileName
OLECHAR FAR* szResName
long  lResID
ITypeLib FAR* FAR* lplptlib

Loads a type library from a designated position in a DLL or .EXE file.

Parameters

szFileName

Contains the name of a DLL or .EXE file that contains a type library in one of its resource areas.

szResName

Identifies the resource area that contains the type library.

lResID

Identifies the resource ID that contains the type library.

lplptlib

On return, contains a pointer to a pointer to the loaded type library.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success.
E_OUTOFMEMORY	Out of memory.
E_INVALIDARG	One or more of the arguments is invalid.
TYPE_E_IOERROR	The function could not read the file.
TYPE_E_INVDATAREAD	The function could not read from the file.
TYPE_E_UNSUPFORMAT	The type library has an old format.
TYPE_E_CANTLOADLIBRARY	The type library or DLL could not be loaded.
Other returns	All FACILITY_STORAGE errors may also be returned.
Comments

The DLL or .EXE file may contain more than one type library.  Use LoadTypeLibFromResource to load a type library stored at positions other than lResID = 1.  Use LoadTypeLib or LoadRegTypeLib to load the type library that is stored at the standard position, that is, lResID = 1.

LoadTypeLibFromResource does not register the type library.  After successfully loading the library, you must call RegisterTypeLibrary to register the library.

If szFileName specifies only a filename, with no path, LoadTypeLibFromResource will search for the file according to the Path variable.

If the type library is already loaded, LoadTypeLibFromResource increments the type library's reference count and returns a pointer to the type library.


Array Manipulation Functions

The SafeArrayPtrOfIndex function has been added to calculate the address of any given element in a safe array.


SafeArrayPtrOfIndex

HRESULT SafeArrayPtrOfIndex(psa, rgIndices, ppvData)
SAFEARRAY FAR* psa
long FAR* rgIndices
void HUGEP* FAR* ppvData

Returns a pointer to an array element.

Parameters

psa

Pointer to an array descriptor created by SafeArrayCreate.

rgIndices

An array of index values that identify an element of the array.  All indexes for the element must be specified.

ppvData

On return, pointer to the element identified by the values in rgIndices.

Comments

The array should be locked before SafeArrayPtrOfIndex is called.  Failing to lock the array may cause undpredictable results.

Return Value

The SCODE obtained from the returned HRESULT is as follows:

SCODE	Meaning
S_OK	Success.
E_INVALIDARG	The argument psa was not a valid safe array descriptor.
DISP_E_BADINDEX	The specified index was invalid.


DEFINE_GUID Macros

MkTypLib creates a type library file (extension .TLB) based on source text input contained in the file specified by ODLfile. It can optionally produce a header file (extension .H), which is a stripped version of the input file suitable for inclusion in C or C++ programs that want to access the types defined in the input file. When it generates the header file, MkTypLib includes DEFINE_GUID macros for each element (interface, dispinterface, and so forth) defined in the type library.


ITypeInfo Structures and Enumerations

The following new elements have been added for the new ODL attributes.

TYPEFLAGS

VARFLAGS

FUNCFLAGS


TYPEFLAGS

The TYPEFLAGS enumeration is defined as follows:

typedef enum tagTYPEFLAGS {

TYPEFLAG_FAPPOBJECT = 0x01

    , TYPEFLAG_FCANCREATE = 0x02

    , TYPEFLAG_FLICENSED = 0x04

    , TYPEFLAG_FPREDECLID = 0x08

    , TYPEFLAG_FHIDDEN = 0x10

    , TYPEFLAG_FCONTROL = 0x20

    , TYPEFLAG_FDUAL = 0x40

    , TYPEFLAG_FNONEXTENSIBLE = 0x80

    , TYPEFLAG_FOLEAUTOMATION = 0x100

} TYPEFLAGS;

Value	Description
TYPEFLAG_FAPPOBJECT	A type description that describes an Application object.
TYPEFLAG_FCANCREATE	Instances of the type can be created by ITypeInfo::CreateInstance.
TYPEFLAG_FLICENSED	The type is licensed.
TYPEFLAG_FPREDECLID	The type is predefined. The client application should automatically create a single instance of the object that has this attribute. The name of the variable that points to the object is the same as the class name of the object.
TYPEFLAG_FHIDDEN	The type should not be displayed to browsers.
TYPEFLAG_FCONTROL	The type is a control from which other types will be derived, and should not be displayed to users.
TYPEFLAG_FDUAL	The types in the interface derive from IDispatch and are fully compatible with OLE Automation.  Not allowed on Dispatch interfaces.
TYPEFLAG_FNONEXTENSIBLE	The interface can't add members at run time.
TYPEFLAG_FOLEAUTOMATION	The types used in the interface are fully compatible with OLE, and may be displayed in an object browser.  Setting [dual] on an interface sets this flag in addition to TYPEFLAG_FDUAL.  Not allowed on Dispatch interfaces.


VARFLAGS

The VARFLAGS enumeration is defined as follows:

typedef enum tagVARFLAGS {

      VARFLAG_FREADONLY= 1

      , VARFLAG_FSOURCE= 0x2

      , VARFLAG_FBINDABLE= 0x4

      , VARFLAG_FREQUESTEDIT= 0x8

      , VARFLAG_FDISPLAYBIND= 0x10

      , VARFLAG_FDEFAULTBIND= 0x20

      , VARFLAG_FHIDDEN= 0x40

} VARFLAGS;

Value	Description
VARFLAG_READONLY	Assignment to the variable should not be allowed.
VARFLAG_FSOURCE	The variable returns an object that is a source of events.
VARFLAG_FBINDABLE	The variable supports data binding.
VARFLAG_FDISPLAYBIND	The variable is displayed to the user as bindable; that is, VARFLAG_FBINDABLE must also be set.
VARFLAG_FDEFAULTBIND	The variable is the single property that best represents the object.  Only one variable in a dispinterface may have this attibute.
VARFLAG_FHIDDEN	The variable should not be displayed to the user in a browser, though it exists and is bindable.


FUNCFLAGS

The FUNCFLAGS enumeration is defined as follows:

typedef enum tagFUNCFLAGS {

FUNCFLAG_FRESTRICTED= 1

      , FUNCFLAG_FSOURCE= 0x2

      , FUNCFLAG_FBINDABLE= 0x4

      , FUNCFLAG_FREQUESTEDIT= 0x8

      , FUNCFLAG_FDISPLAYBIND= 0x10

      , FUNCFLAG_FDEFAULTBIND= 0x20

      , FUNCFLAG_FHIDDEN= 0x40

} FUNCFLAGS;

Value	Description
FUNCFLAG_FRESTRICTED	The function should not be accessible from macro languages. This flag is intended for system-level functions or functions that you do not want type browsers to display.
FUNCFLAG_FSOURCE	The function returns an object that is a source of events.
FUNCFLAG_FBINDABLE	The function that supports data binding.
FUNCFLAG_FDISPLAYBIND	The function that is displayed to the user as bindable; that is, FUNC_FBINDABLE must also be set.
FUNCFLAG_FDEFAULTBIND	The function that best represents the object.  Only one function in a dispinterface may have this attibute.
FUNCFLAG_FHIDDEN	The function should not be displayed to the user, though it exists and is bindable.


Active Object Registration

A new flag has been added to the RegisterActiveObject function to specify weak and strong registration.


RegisterActiveObject

HRESULT RegisterActiveObject (punk, rclsid, dwFlags, pdwRegister)
IUnknown FAR* punk
REFCLSID rclsid
unsigned long dwFlags
unsigned long FAR* pdwRegister

Registers an object (punk) as the active object for the class indicated by rclsid.

Parameters

punk

Pointer to the IUnknown interface of the active object.

rclsid

Pointer to the class ID of the active object.

dwFlags

Flags controlling registration of an object. Possible values are
ACTIVEOBJECT_STRONG  and ACTIVEOBJECT_WEAK .

pdwRegister

Pointer to a handle used when initializing and revoking an active object. This handle is created by RegisterActiveObject and passed to RevokeActiveObject when you want to end an object's status as active.

Return Value

The SCODE obtained from the returned HRESULT is one of the following:

SCODE	Meaning
S_OK	Success
Other returns	Failure
Comments

The RegisterActiveObject function registers the object to which punk points as the active object for the class indicated by rclsid. Registration causes the object to be listed in OLE's running object table, a globally accessible lookup table that keeps track of the objects that are currently running on the workstation. (See the OLE 2 Programmer's Reference, Volume 1 for more information on the running object table.) The dwFlags parameter specifies the strength or weakness of the registration, which affects the way the object is shut down.

In general, OLE Automation objects should behave in the following manner:

•	If the object is visible, it should shut down only in response to an explicit user command (such as the Exit command from a File menu), or to the equivalent command from the OLE Automation controller (invoking the Quit or Exit method on the Application object).

•	If the object is not visible, it should shut down only when the last external connection to it disappears.

Strong registration performs an AddRef on the object, thereby incrementing the reference count of the object (and its associated stub) in the running object table. A strongly registered object must be explicitly revoked from the table with RevokeActiveObject. Strong registration (ACTIVEOBJECT_STRONG) is the default.

Weak registration keeps a pointer to the object in the running object table, but does not increment the reference count. Consequently, when the last external connection to a weakly registered object disappears, OLE releases the object's stub and the object itself is no longer available.

To ensure the desired behavior, you must not only consider OLE's default actions,  but you need to consider the following:

•	Even though your code creates an invisible object, the object may become visible at some later time. Once the object is visible, it should remain visible and active until it receives an explicit command to shut down, which may occur after references from your code disappear.

•	Other OLE Automation controllers may be using the object. If so, your code should not force the object to shut down.

To avoid possible conflicts, OLE Automation objects should always register with ACTIVEOBJECT_WEAK, and call CoLockObjectExternal when necessary to guarantee that the object remains active. CoLockObjectExternal adds a strong lock, thereby preventing the object's reference count from reaching zero. For detailed information on this function, refer to the OLE 2 Programmer's Reference, Volume 1.

Most commonly, objects need to call CoLockObjectExternal when they become visible, so that they remain active until the user requests a shut down. Ensure that your code contains the following steps:

1.	When the object becomes visible, make the following call to add a lock on behalf of the user:

CoLockObjectExternal(punk, TRUE, TRUE)

The lock should remain in effect until the user explicitly requests shutdown, such as with a Quit or Exit command.

2.	When the user requests shutdown, call CoLockObjectExternal again to free the lock, as follows:

CoLockObjectExternal(punk, FALSE, TRUE)

3.	Call RevokeActiveObject to make the object inactive.

4.	To end all connections from remote processes, call CoDisconnectObject as follows:

CoDisconnectObject(punk, 0)

This function is described in the OLE 2 Programmer's Reference, Volume 1.


National Language Support

The following LCTYPE constants have been added to OLE Automation for 16-bit systems. All values are variable length ANSI strings, and are null-terminated. The values in the brackets indicate a maximum number of characters allowed for the string (including the null termination). If no maximum is indicated, then the string may be of variable length. When you request any of this information by using GetLocaleInfo, the API returns the appropriate string depending on what the current system ANSI code page is.

Constant name	Description
LOCALE_IFIRSTWEEKOFYEAR	Specifies which week of the year is considered first. [2] "0" Week containing 1/1 is the first  week of that year. "1" First full week following 1/1 is the  first week of that year. "2" First week containing at least 4  days is the first week of that year.
LOCALE_IFIRSTDAYOFWEEK	Specifies which day is considered first in a week. [2] "0" SDAYNAME1 "1" SDAYNAME2 "2" SDAYNAME3 "3" SDAYNAME4 "4" SDAYNAME5 "5" SDAYNAME6 "6" SDAYNAME7
LOCALE_IDEFAULTANSICODEPAGE	ANSI code page associated with this locale [6]
LOCALE_INEGNUMBER	Negative number mode. [2] "0" (1.1) "1" -1.1 "2" -1.1 "3" 1.1 "4" 1.1
LOCALE_STIMEFORMAT	Time formatting string. [80]
LOCALE_ITIMEMARKPOSN	Whether the time marker string (AM|PM designator) preceeds or succeeds the time string.  The registry value is name iTimePrefix for previous Far East version compatability. "0" Suffix (that is, 9:15 AM)  "1" Prefix (that is, AM 9:15)
LOCALE_ICALENDARTYPE	Specifies which type of calendar is currently being used. [2] "1" Gregorian (as in United States) "2" Gregorian (always English strings) "3" Era: Year of the Emperor (Japan) "4" Era: Year of the Republic of China "5" Tangun Era (Korea)
LOCALE_IOPTIONALCALENDAR	Specifies which additional calendar types are valid and available for this LCID.  This can be a null-separated list of all valid optional calendars. [2] "0" No additional types valid "1" Gregorian (localized)  "2" Gregorian (always English strings) "3" Era: Year of the Emperor (Japan)  "4" Era: Year of the Republic of China  "5" Tangun Era (Korea)
LOCALE_SMONTHNAME13	Native name for 13th month, if exists.
LOCALE_SABBREVMONTHNAME13	Native abbreviated name for 13th month, if exists.
In this version, every value has a maximum length (in bytes, and including the null-terminator) shown in brackets, at the end of its format description.


New Data Type

OLE Automation supports the VT_UI1 data type (VARTYPE). This data type is useful for creating arrays of binary data. You can create a VT_ARRAY | VT_UI1 and use it as a binary byte buffer in your code. This guarantees that no ANSI to Unicode translation will be performed on a binary buffer on 32-bit systems.