Вы находитесь на странице: 1из 770

The COM Core Technology Specification

Version 1.0
April 10, 1998
Note: This document is meant to specify and accompany software that is still in
development. Some of the information in this documentation may be inaccurate or m
ay not be an accurate representation of the actual functionality of the software.
When this document and the functionality of the software conflict, The actual f
unctionality of the software represents the correct functionality. Microsoft as
sumes no responsibility for any damages that might occur either directly or indi
rectly from these discrepencies or inaccuracies. Microsoft may have trademarks,
copyrights, patents or pending patent applications, or other intellectual proper
ty rights covering subject matter in this document. The furnishing of this docum
ent does not give you a license to these trademarks, copyrights, patents, or oth
er intellectual property rights.
This document contains the specification to the COM core technologies, an archit
ecture and supporting infrastructure for building, using, and evolving component
software in a robust manner. This specification contains the standard APIs supp
orted by the COM core technology along with the network protocol used by the Com
ponent Object Model (COM) in support of distributed computing.
The COM Core Technology Specification

Copyright (c) 1992-98 Microsoft Corporation.


Microsoft does not make any representation or warranty regarding the Specificati
on or any product or item developed based on the Specification. Microsoft discl
aims all express and implied warranties, including but not limited to the implie
d warranties of merchantability, fitness for a particular purpose and freedom fr
om infringement. Without limiting the generality of the foregoing, Microsoft do
es not make any warranty of any kind that any item developed based on the Specif
ication, or any portion of it, will not infringe any copyright, patent, trade se
cret or other intellectual property right of any person or entity in any country
. It is your responsibility to seek licenses for such intellectual property rig
hts where appropriate. Microsoft shall not be liable for any damages arising ou
t of or in connection with the use of the Specification, including liability for
lost profit, business interruption, or any other damages whatsoever. Some stat
es do not allow the exclusion or limitation of liability for consequential or in
cidental damages; the above limitation may not apply to you.
Table of Contents
Part 0: Information Regarding This Specification 12
Part I: COM Core Technology Introduction 16
1. Introduction and Terminology 17
1.1 The Component Object Model 17
1.2 MS-RPC 23
1.3 Registry 23
1.4 Security Support Provider Interface 23
1.5 Windows NT Distributed Security Provider 24
2. The Motivations for COM 25
2.1 Challenges Facing The Software Industry 25
2.2 The Solution: Component Software 27
2.3 The Component Software Solution: COM 28
3. Component Object Model Technical Overview 36
3.1 Objects and Interfaces 36
3.2 Clients, Servers, and Object implementors 46
3.3 Memory Management Rules 49
3.4 The COM Client/Server Model 50
3.5 Object Reusability 59
3.6 Connectable Objects and Events 60
3.7 Error Codes and Error Handling 61
3.8 Enumerators and Enumerator Interfaces 61
3.9 Persistent Storage 62
3.10 Persistent, Intelligent Names: Monikers 68
3.11 Uniform Data Transfer 71
3.12 Type Libraries 73
3.13 Automation 74
Part II: Component Object Model Programming Interface 75
4. Objects And Interfaces 76
4.1 Interfaces 76
4.2 Globally Unique Identifiers 81
4.3 The IUnknown Interface 82
4.4 Providing Class Information 84
4.5 Connectable Objects and Events 85
4.6 Designing and Implementing Objects 85
4.7 Objects And Interfaces API Descriptions 89
4.8 Objects and Interfaces Enumeration Description 94
5. The COM Library 95
5.1 COM Application Responsibilities 95
5.2 Library Initialization / Uninitialization 95
5.3 Memory Management 95
5.4 COM Library Interface Descriptions 96
5.5 COM Library API Descriptions 120
5.6 COM Library Enumeration Definitions 137
6. COM Threading Models 139
6.1 Choosing the Threading Model 140
6.2 Single-threaded Apartments 140
6.3 Multi-threaded Apartments 142
6.4 Single-/Multi-threaded Communication 143
6.5 In-process server Threading Issues 143
6.6 Process and Thread Related Interface Descriptions 144
6.7 Process and Thread Related API Descriptions 149
6.8 Process and Thread Related Structure Descriptions 152
6.9 Process and Thread Related Enumeration Descriptions 153
7. COM Clients 156
7.1 Getting a Pointer to an Object 156
7.2 Initializing the Object 159
7.3 Managing the Object 160
7.4 Releasing the Object 161
7.5 Server Management 161
7.6 Client Related Interface Descriptions 162
7.7 Client Related API Descriptions 169
7.8 COM Client Related Structure Definitions 181
7.9 COM Client Related Enumeration Definitions 181
8. COM Servers 184
8.1 COM Server Responsibilities 184
8.2 Implementing IClassFactory 184
8.3 Licensing and IClassFactory2 184
8.4 Registering COM Servers 185
8.5 Self-Registration 187
8.6 Out-of-process Server Implementation Helpers 187
8.7 Object Handlers 189
8.8 Object Reusability 189
8.9 Emulating Other Servers 193
8.10 COM Server Related Interface Descriptions 194
8.11 COM Server Related API Descriptions 208
8.12 COM Server Related Structure Definitions 217
8.13 COM Server Related Enumeration Definitions 218
9. Interface Remoting and Marshaling 220
9.1 How Interface Remoting Works 220
9.2 Architecture of Custom Object Marshaling 221
9.3 Architecture of Standard Interface / Object Marshaling 223
9.4 Architecture of Handler Marshaling 226
9.5 Standards for Marshaled Data Packets 227
9.6 Creating an Initial Connection Between Processes 227
9.7 Interface Remoting and Marshaling Interface Descriptions 227
9.8 Interface Remoting and Marshaling API Descriptions 250
9.9 Interface Remoting and Marshaling Enumeration Definitions 260
10. Security in COM 263
10.1 Launch Security 263
10.2 Call Security 263
10.3 Security Related Interface Descriptions 264
10.4 Security Related API Descriptions 272
10.5 Security Related Structure Definitions 282
10.6 Security Related Enumeration Descriptions 284
11. Error Handling 287
11.1 Returning Error Information 287
11.2 Retrieving Error Information 287
11.3 Error Handling API Descriptions 287
12. Enumerators and Enumerator Interfaces 300
12.1 IEnum::Next 300
12.2 IEnum::Skip 301
12.3 IEnum::Reset 301
12.4 IEnum::Clone 301
13. Connectable Objects 303
13.1 Connectable Objects Interface Descriptions 303
13.2 Connectable Objects Structure Descriptions 312
14. Persistent Storage 313
14.1 Interfaces 313
14.2 API Functions 313
14.3 Access Modes 313
14.4 Storage Object Naming Conventions 315
14.5 Persistent Property Sets 315
14.6 Persistent Storage Interface Descriptions 324
14.7 Persistent Storage API Descriptions 432
14.8 Persistent Storage Structure Descriptions 460
14.9 Persistent Storage Enumeration Descriptions 463
15. Persistent Intelligent Names: Monikers 473
15.1 Overview 473
15.2 Moniker Interface Descriptions 474
15.3 Moniker API Descriptions 536
15.4 Moniker Structure Definitions 550
15.5 Moniker Enumeration Definitions 553
16. Uniform Data Transfer 556
16.1 Data Transfer Interfaces 556
16.2 Data Formats and Transfer Media 556
16.3 Data Notification 558
16.4 Uniform Data Transfer Interface Descriptions 558
16.5 Uniform Data Transfer API Descriptions 581
16.6 Uniform Data Transfer Structure Descriptions 583
16.7 Uniform Data Transfer Enumeration Descriptions 588
17. Type Libraries 595
17.1 Overview of Type Description Interfaces 595
17.2 Type Library Interface Descriptions 597
17.3 Type Library API Descriptions 633
18. Automation 640
18.1 Overview of the IDispatch Interface 640
18.2 Registering the Object 641
18.3 National Language Support Functions 641
18.4 Automation Interface Descriptions 652
18.5 Automation API Descriptions 689
18.6 Automation Related Structure Definitions 737
19. Support for Remote Debugging 740
19.1 Implementation 740
Part III: Component Object Model Protocols and Services 750
20. Interface Definition Language 751
20.1 Object RPC IDL Extensions 751
20.2 Mapping from ORPC IDL to DCE RPC IDL. 756
21. Component Object Model Protocol 757
21.1 Purpose 757
21.2 Overall Operation 757
21.3 Data Types and Structures 761
21.4 IRemUnknown interface 770
21.5 The OXID Resolver 773
21.6 Wrapping DCE RPC calls to interoperate with ORPC 779
Part IV: Other COM Core Technologies 782
22. MSRPC 783
23. Registry 784
23.1 Structure of the Registry 784
23.2 Registry Storage Space 784
23.3 Predefined Keys 784
23.4 Opening, Creating, and Closing Keys 785
23.5 Writing and Deleting Registry Data 785
23.6 Retrieving Data from the Registry 785
23.7 Registry Files 786
23.8 The Registry API Descriptions 786
23.9 RegCloseKey 786
23.10 RegConnectRegistry 787
23.11 RegCreateKeyEx 787
23.12 RegDeleteKey 789
23.13 RegDeleteValue 790
23.14 RegEnumKeyEx 791
23.15 RegEnumValue 792
23.16 RegFlushKey 794
23.17 RegGetKeySecurity 795
23.18 RegLoadKey 796
23.19 RegNotifyChangeKeyValue 797
23.20 RegOpenKeyEx 798
23.21 RegQueryInfoKey 800
23.22 RegQueryMultipleValues 801
23.23 RegQueryValueEx 803
23.24 RegReplaceKey 805
23.25 RegRestoreKey 806
23.26 RegSaveKey 807
23.27 RegSetKeySecurity 808
23.28 RegSetValueEx 808
23.29 RegUnLoadKey 810
23.30 Registry Structures 811
23.31 VALENT 811
24. Security Support Provider Iinterface 813
24.1 Security Package Capabilities 814
24.2 Initializing the Security Provider 814
24.3 Loading the Security Provider DLL 815
24.4 Provider Initialization 815
24.5 Security Function Table 817
24.6 Establishing an Authenticated Connection 819
24.7 Secure Message Exchange 825
24.8 Datatype Descriptions 835
Appendix A: Header Files and Libraries 869
Appendix B: Bibliography 878
Appendix C: Specification Revision History 879
Appendix D: Glossary 880
Appendix E: Index 890
Part 0: Information Regarding This Specification
This version of the COM core technology specification has been produced to facil
itate licensees of the technology in understanding exactly what technology is in
cluded and how the technology works. To facilitate a better understanding of the
scope of the COM Core Technology a comprehensive list of the public library fun
ctions and interfaces for which support is provided is given below. This specifi
cation includes full documentation on each library function and interface listed
below.
COM Library Functions
BindMoniker
CLSIDFromProgID
CLSIDFromString
CoAddRefServerProcess
CoCopyProxy
CoCreateFreeThreadedMarshaler
CoCreateGuid
CoCreateInstance
CoCreateInstanceEx
CoDisconnectObject
CoDosDateTimeToFileTime
CoFileTimeNow
CoFileTimeToDosDateTime
CoFreeAllLibraries
CoFreeLibrary
CoFreeUnusedLibraries
CoGetCallContext
CoGetClassObject
CoGetCurrentProcess
CoGetInstanceFromFile
CoGetInstanceFromIStorage
CoGetInterfaceAndReleaseStream
CoGetMalloc
CoGetMarshalSizeMax
CoGetPSClsid
CoGetStandardMarshal
CoGetTreatAsClass
CoImpersonateClient
CoInitialize
CoInitializeEx
CoInitializeSecurity
CoIsHandlerConnected
CoLoadLibrary
CoLockObjectExternal
CoMarshalInterface
CoMarshalInterThreadInterfaceInStream
CoQueryAuthenticationServices
CoQueryClientBlanket
CoQueryProxyBlanket
CoRegisterClassObject
CoRegisterMallocSpy
CoRegisterMessageFilter
CoRegisterPSClsid
CoReleaseMarshalData
CoReleaseServerProcess
CoResumeClassObjects
CoRevertToSelf
CoRevokeClassObject
CoRevokeMallocSpy
CoSetProxyBlanket
CoSuspendClassObjects
CoTaskMemAlloc
CoTaskMemFree
CoTaskMemRealloc
CoTreatAsClass
CoUninitialize
CoUnmarshalInterface
CreateAntiMoniker
CreateBindCtx
CreateClassMoniker
CreateGenericComposite
CreateILockBytesOnHGlobal
CreateItemMoniker
CreatePointerMoniker
CreateStreamOnHGlobal
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer
FreePropVariantArray
GetClassFile
GetConvertStg
GetHGlobalFromILockBytes
GetHGlobalFromStream
GetRunningObjectTable
IIDFromString
IsEqualGUID
IsEqualCLSID
IsEqualIID
MkParseDisplayName
MonikerCommonPrefixWith
MonikerRelativePathTo
ProgIDFromCLSID
PropStgNameToFmtId
PropVariantClear
PropVariantCopy
ReadClassStg
ReadClassStm
ReadFmtUserTypeStg
ReleaseStgMedium
SetConvertStg
StgCreateDocfile
StgCreateDocfileOnILockBytes
StgCreatePropSetStg
StgCreatePropStg
StgIsStorageFile
StgIsStorageILockBytes
StgOpenPropStg
StgOpenStorage
StgOpenStorageOnILockBytes
StgSetTimes
StringFromCLSID
StringFromGUID2
StringFromIID
WriteClassStg
WriteClassStm
WriteFmtUserTypeStg
COM Defined Interfaces
IAdviseSink
IBindCtx
ICatInformation
ICatRegister
IClassActivator
IClassFactory
IClassFactory2
IClientSecurity
IConnectionPoint
IConnectionPointContainer
IDataAdviseHolder
IDataObject
IEnumXXXX
IEnumConnectionPoints
IEnumConnections
IEnumFORMATETC
IEnumMoniker
IEnumSTATDATA
IEnumSTATPROPSETSTG
IEnumSTATPROPSTG
IEnumSTATSTG
IEnumString
IEnumUnknown
IErrorLog
IExternalConnection
ILockBytes
IMalloc
IMallocSpy
IMarshal
IMessageFilter
IMoniker
IMultiQI
IOleItemContainer
IOXIDResolver
IParseDisplayName
IPersist
IPersistFile
IPersistMemory
IPersistMoniker
IPersistPropertyBag
IPersistStorage
IPersistStream
IPersistStreamInit
IRemoteActivation
IPropertyBag
IPropertySetStorage
IPropertyStorage
IProvideClassInfo
IProvideClassInfo2
IPSFactoryBuffer
IRemUnknown
IRootStorage
IROTData
IRpcChannelBuffer
IRpcProxyBuffer
IRpcStubBuffer
IRunnableObject
IRunningObjectTable
IServerSecurity
IStdMarshalInfo
IStorage
IStream
IUnknown
Type Information Library Functions
CreateTypeLib
LHashValOfName
LHashValOfNameSys
LoadRegTypeLib
LoadTypeLibEx
QueryPathOfRegTypeLib
RegisterTypeLib
UnRegisterTypeLib
Type Information Interfaces
ICreateTypeInfo
ICreateTypeInfo2
ICreateTypeLib
ICreateTypeLib2
ITypeComp
ITypeInfo
ITypeInfo2
ITypeLib
ITypeLib2
Automation Library Functions
BstrFromVector
CreateDispTypeInfo
CreateErrorInfo
CreateStdDispatch
DispGetIDsOfNames
DispGetParam
DispInvoke
DosDateTimeToVariantTime
GetActiveObject
GetAltMonthNames
GetErrorInfo
RegisterActiveObject
RevokeActiveObject
SafeArrayAccessData
SafeArrayAllocData
SafeArrayAllocDescriptor
SafeArrayCopy
SafeArrayCopyData
SafeArrayCreate
SafeArrayCreateVector
SafeArrayDestroy
SafeArrayDestroyData
SafeArrayDestroyDescriptor
SafeArrayGetDim
SafeArrayGetElement
SafeArrayGetElemsize
SafeArrayGetLBound
SafeArrayGetUBound
SafeArrayLock
SafeArrayPtrOfIndex
SafeArrayPutElement
SafeArrayRedim
SafeArrayUnaccessData
SafeArrayUnlock
SetErrorInfo
SysAllocString
SysAllocStringByteLen
SysAllocStringLen
SysFreeString
SysReAllocString
SysReAllocStringLen
SysStringByteLen
SysStringLen
SystemTimeToVariantTime
VarDateFromUdate
VariantChangeType
VariantChangeType
VariantChangeTypeEx
VariantChangeTypeEx
VariantClear
VariantCopy
VariantCopyInd
VariantInit
VariantTimeToDosDateTime
VariantTimeToSystemTime
VarNumFromParseNum
VarParseNumFromStr
VarUdateFromDate
VectorFromBstr
National Language Library Functions
CompareString
LCMapString
GetLocaleInfo
GetStringType
GetSystemDefaultLangID
GetSystemDefaultLCID
GetUserDefaultLangID
GetUserDefaultLCID
Automation Interfaces
ICreateErrorInfo
IDispatch
IErrorInfo
ISupportErrorInfo
Registry Library Functions
RegCloseKey
RegConnectRegistry
RegCreateKeyEx
RegDeleteKey
RegDeleteValue
RegEnumKeyEx
RegEnumValue
RegFlushKey
RegGetKeySecurity
RegLoadKey
RegNotifyChangeKeyValue
RegOpenKeyEx
RegQueryInfoKey
RegQueryMultipleValues
RegQueryValueEx
RegReplaceKey
RegRestoreKey
RegSaveKey
RegSetKeySecurity
RegSetValueEx
RegUnLoadKey
Debugging Support
DllDebugObjectRPCHook
DebugORPCClientGetBufferSize
DebugORPCClientFillBuffer
DebugORPCClientNotify
DebugORPCServerGetBufferSize
DebugORPCServerFillBuffer
DebugORPCServerNotify
IOrpcDebugNotify
Part I: COM Core Technology Introduction
Part I is an overview and introduction to the COM Core Technologies. Chapter 1 p
rovides high level terminology definitions for the various technologies that mak
e up the COM Core. Chapter 2 explains at a high level the motivations of COM and
the problems the core technology addresses and describes the major benefits and
advantages of the COM Core Technology. Only a few code frangments or examples a
re provided in this specification. An ample number of samples will be provided i
n an Interoperability Testing Guide.
1. Introduction and Terminology
This chapter provides an overview of the technologies that make up the COM Core
Technologies. Each of these technologies is specified in detail in Part II of th
is document.
The COM Core Technologies comprise the following:
The Component Object Model (COM). The underlying distributed object model for al
l COMcomponents. This includes:
Distributed capabilities, commonly referred to as DCOM.
The Service Control Manager. a part of the COM library responsible for locating
class implementations implemented as libraries, local processes, or remote serve
rs.
Security. A rich and pluggable infrastructure for building secure distributed ap
plications. Facilities are provided for authentication, authorization, and priva
cy.
Structured Storage. Provides a rich, transaction based, hierarchical file format
that enables COM applications to create files that can be shared across applica
tions and platforms.
Monikers. Allows references to objects to be stored persistently. Provides for p
ersistent, intelligent names.
Automation. Allows objects to expose functionality to high-level programming la
nguages and scripting environments.
MS-RPC. An implementation of the Distributed Computing Environment (DCE) Remote
Procedure Call (RPC) specification, upon which COM is based.
The Registry. Provides a database of COM components and their configuration info
rmation.
The Security Support Provider Interface (SSPI). A standard for pluggable securit
y providers.
The Windows NT Distributed Security Provider. An SSPI security provider which su
pports the Windows NT Distributed Security model (also called the NTLM SSP).
Each of these sub-technologies is described below. Lists of relevant interfaces
and APIs are provided for each.
1.1 The Component Object Model
The Component Object Model (COM) is an object-based, distributed programming mod
el designed to promote software interoperability. COM to allows two or more appl
ications or components to easily cooperate with one another, even if they were wri
tten by different vendors at different times, in different programming languages
, or if they are running on different machines running different operating syste
ms. COM is based on the Distributed Computing Environment's (DCE) Remote Proced
ure Call (RPC). Microsoft's implementation of DCE RPC is called MS-RPC and is d
escribed in detail later in this document.
Figure 1-1: Once COM connects client and object, the client and object communica
te directly without added overhead.
To support its interoperability features, COM defines and implements mechanisms
that allow applications to connect to each other as component objects. A compon
ent object is a collection of related function (or intelligence) and the functio
n s associated state. In other words, COM, like a traditional system service API,
provides the operations through which a client of some service can connect to mu
ltiple providers of that service in a polymorphic fashion. But once a connection
is established, COM drops out of the picture. COM serves to connect a client an
d an object, but once that connection is established, the client and object comm
unicate directly without having to suffer overhead of being forced through a cen
tral piece of API code as illustrated in Figure 1-1.
COM is not a prescribed way to structure an application; rather, it is a set of
technologies for building robust groups of services in both systems and applicat
ions such that the services and the clients of those services can evolve over ti
me. In this way, COM is a technology that makes the programming, use, and uncoor
dinated/independent evolution of binary objects possible.
This is a fundamental strength of COM: COM solves the deployment problem, the vers
ioning/evolution problem where it is necessary that the functionality of objects
can incrementally evolve or change without the need to simultaneously and in lo
ckstep evolve or change all existing clients of the object. Objects/services can
easily continue to support the interfaces through which they communicated with
older clients as well as provide new and better interfaces through which they co
mmunicate with newer clients.
To solve the versioning problems as well as providing connection services withou
t undue overhead, the Component Object Model builds a foundation that:
Enables the creation and use of reusable components by making them component obje
cts.
Defines strict rules for interoperability between applications and objects. This
means that on a given operating system the interface between a user of an objec
t and the object itself, the calling interface is defined. In addition, since th
e distributed operation of COM is based on DCE RPC, the remote interface is also
well defined and independent of languages or operating systems.
Provides secure distributed capabilities.
COM is designed to allow clients to transparently communicate with objects regar
dless of where those objects are running, be it the same process, the same machi
ne, or a different machine. What this means is that there is a single programmin
g model for all types of objects for not only clients of those objects but also
for the servers of those objects.
At the core the Component Object Model is a specification (hence Model ) for how ob
jects and their clients interact through interfaces. As a specification it defin
es a number of other standards for interoperability:
The fundamental process of interface negotiation.
A reference counting mechanism through which objects (and their resources) are m
anaged even when connected to multiple clients.
Rules for memory allocation and responsibility for those allocations when exchan
ged between independently developed components.
Consistent and rich error reporting facilities.
In addition to being a specification, COM is also an implementation contained in
what is called the COM Library. The implementation is provided through a library
(such as a DLL on Microsoft Windows) that includes:
A small number of fundamental API functions that facilitate the creation of COM
applications, both clients and servers. For clients, COM supplies basic object c
reation functions; for servers the facilities to expose their objects.
Implementation locator services through which COM determines from a class identi
fier which server implements that class and where that server is located. This i
ncludes support for a level of indirection, usually a system registry, between t
he identity of an object class and the packaging of the implementation such that
clients are independent of the packaging which can change in the future.
Transparent remote procedure calls when an object is running in a local or remot
e server.
A standard mechanism to allow an application to control how memory is allocated
within its process.
A rich and robust security model which allows for multiple, pluggable, security
providers.
COM provides security along several crucial dimensions. First, COM uses standard
operating system permissions to determine whether a client (running in a partic
ular user s security context) has the right to start the code associated with a pa
rticular class of object. Second, with respect to persistent objects (class code
along with data stored in a persistent store such as file system or database),
COM uses operating system or application permissions to determine if a particula
r client can load the object at all. COM's security model supports multiple simu
ltaneous security providers. Security providers are plugged in to the system usi
ng the Security Service Provider Interface (SSPI). Finally, COM s security archite
cture is based on the design of the DCE RPC security architecture, an industry-s
tandard communications mechanism that includes fully authenticated sessions. COM
provides cross-process and cross-network object servers with standard security
information about the client or clients that are using it so that a server can u
se security in more sophisticated fashion than that of simple OS permissions on
code execution and read/write access to persistent data.
In general, only one vendor needs to, or should, implement a COM Library for any
particular operating system.
1.1.1 COM Infrastructure
COM provides more than just the fundamental object creation and management facil
ities: it also builds an infrastructure of other core system components.
The combination of the foundation and the infrastructure COM components reveals
a system that describes how to create and communicate with objects, how to store
them, how to label to them, and how to exchange data with them. These four aspe
cts of COM form the core of information management. Furthermore, the infrastruct
ure components not only build on the foundation, but monikers and uniform data t
ransfer also build on storage. The result is a system that is not only very rich
, but also deep, which means that work done in an application to implement lower
level features is leveraged to build higher level features.
From a technology perspective, the interfaces and APIs that make up COM can be b
roken down into sub-technologies. Each of these sub-technologies is briefly desc
ribed below.
1.1.2 ORPC
Object RPC, or ORPC, refers to the layer of code which separates the component o
bject model from the base RPC runtime. ORPC can be broken into the following ma
jor components: Interface Marshaling, Message Filter/Call Control, and Proxy/Stu
b management. Interface marshaling, as its name implies is concerned with how C
OM marshals object interface pointers. The MessageFilter/Call control component
deals with reentrancy concerns and call categories. Proxy/Stub component ensur
es that for each remoted interface, correct proxy and stubs are generated which
marshal/unmarshal all parameters.
1.1.3 Service Control Manager
The Service Control Manager (SCM) is the component of the COM Library responsibl
e for locating class implementations and running them. The SCM ensures that when
a client request is made, the appropriate server is connected and ready to rece
ive the request. The SCM keeps a database of class information based on the syst
em registry that the client caches locally through the COM library.
The SCM uses DCE RPC interfaces to communicate with SCM implementations on other
machines.
1.1.4 Security
Using the network for distributing an application is challenging not only becaus
e of the physical limitations of bandwidth and latency. It also raises new issue
s related to security between and among clients and components. Since many opera
tions are now physically accessible by anyone with access to the network, access
to these operations has to be restricted at a higher level.
Without security support from the distributed development platform, each applica
tion would be forced to implement its own security mechanisms. A typical mechani
sm would involve passing some kind of username and password (or a public key) usua
lly encrypted to some kind of logon method. The application would validate these c
redentials against a user database or directory and return some dynamic identif
ier for use in future method calls. On each subsequent call to a secure method,
the clients would have to pass this security identifier. Each application would
have to store and manage a list of usernames and passwords, protect the user dir
ectory against unauthorized access, and manage changes to passwords, as well as
dealing with the security hazard of sending passwords over the network.
A distributed platform must thus provide a security framework to safely distingu
ish different clients or different groups of clients so that the system or the a
pplication has a way of knowing who is trying to perform an operation on a compo
nent. COM uses an extensible security framework (SSPI) that supports multiple id
entification and authentication mechanisms, from traditional trusted-domain secu
rity models to non-centrally managed, massively scaling public-key security mech
anisms. A central part of the security framework is a user directory, which stor
es the necessary information to validate a user's credentials (user name, passwo
rd, public key). Most COM implementations on non-Windows€NT platforms provide a si
milar or identical extensibility mechanism to use whatever kind of security pro
viders is available on that platform. Most UNIX-implementations of COM will incl
ude a Windows€NT-compatible security provider.
1.1.5 Error Handling
COM interface member functions and COM Library API functions use a specific conv
ention for error codes in order to pass back to the caller both a useful return
value and along with an indication of status or error information. For example,
it is highly useful for a function to be capable of returning a Boolean result (
true or false) as well as indicate failure or success. Returning true and false
means that the function executed successfully, and true or false is the answer w
hereas an error code indicates the function failed completely.
In addition, for those programming languages that support exception handling, CO
M provides mechanisms where the client can obtain very rich exception informatio
n.
1.1.6 Uniform Data Transfer
COM provides a standard mechanism for transferring structured data between compo
nents. This mechanism is the data object, which is simply any COM object that im
plements the IDataObject interface. Some data objects, such as a piece of text c
opied to the clipboard, have IDataObject as their sole interface.
By exchanging pointers to a data object, providers and consumers of data can man
age data transfers in a uniform manner, regardless of the format of the data, th
e type of medium used to transfer the data, or the target device on which it is
to be rendered.
1.1.7 Structured Storage
Traditional file systems face challenges when they try to efficiently store mult
iple kinds of objects in one document. COM provides a solution: a file system wi
thin a file. COM structured storage defines how to treat a single file entity as
a structured collection of two types of objects, storages and streams, that act
like directories and files. This scheme is called structured storage. The purpo
se of structured storage is to reduce the performance penalties and overhead ass
ociated with storing separate objects in a flat file
A storage object is analogous to a file system directory. Just as a directory ca
n contain other directories and files, a storage object can contain other storag
e objects and stream objects. Also like a directory, a storage object tracks the
locations and sizes of the storage objects and stream objects nested beneath it
.
A stream object is analogous to the traditional notion of a file. Like a file, a
stream contains data stored as a consecutive sequence of bytes.
Although an application or component developer can implement the structured stor
age objects, COM provides a standard implementation called Compound Files. The C
OM Core Technologies includes a reference implementation of Compound Files. This
implementation has the following features:
File-system and platform independence. Since the Compound Files implementation r
uns on top of existing flat file systems, compound files stored on FAT, NTFS, Ma
cintosh, or any other file system can be opened by applications using any one of
the others.
Browsability. The separate objects in a compound file are saved in a standard fo
rmat and can be accessed using standard interfaces and APIs. Therefore any brows
er utility using these interfaces and APIs can list the objects in the file, eve
n though the data within a given object may be in a proprietary format.
Access to certain internal data. Since the Compound Files implementation provide
s standard ways of writing certain types of data (document summary information p
roperties, for example) applications can read this data using the structured sto
rage interfaces and APIs.
1.1.7.1 Persistent Property Sets
Persistent Property Sets directly addresses the need to attach structured inform
ation to objects which have been stored via structured storage. This information
could include other objects, files (structured, compound, etc.), directories, d
ocument properties, and summary catalogs
Structured Storage defines both a standard serialized format, and a set of inter
faces and functions that allow you to create and manipulate persistent property
sets. The reference implementation of Structured Storage includes full support f
or these interfaces.
Persistent properties are stored as sets, and one or more sets may be associated
with a file system entity. These persistent property sets are intended to be us
ed to store data that is suited to being represented as a collection of fine-gra
ined values. They are not intended to be used as a large database. They can be u
sed, for example, to store summary information about an object on the system, wh
ich can then be accessed by any other object that understands how to interpret t
hat property set.
1.1.8 Persistent Objects
COM objects can save their internal state when asked to do so by a client. COM d
efines standards through which clients can request objects to be initialized, lo
aded, and saved to and from a data store (for example an HTML <OBJECT> tag, stru
ctured storage, or memory). It is the client's responsibility to manage the plac
e where the object's persistent data is stored, but not the format of the data.
COM objects that adhere to these standards are called persistent objects.
1.1.9 Monikers
Monikers are best described as "Persistent, Intelligent Names". To set the conte
xt for why Persistent, Intelligent Names are an important technology in COM, think
for a moment about a standard, mundane file name. That file name refers to some
collection of data that happens to be stored on disk somewhere. The file name d
escribes the somewhere. In that sense, the file name is really a name for a part
icular object of sorts where the object is defined by the data in the file.
The limitation is that a file name by itself is unintelligent; all the intellige
nce about what that filename means and how it gets used, as well as how it is st
ored persistently if necessary, is contained in whatever application is the clie
nt of that file name. The file name is nothing more than some piece of data in t
hat client. This means that the client must have specific code to handle file na
mes. This normally isn t seen as much of a problem most applications can deal with f
iles and have been doing so for a long time.
Now introduce some sort of name that describes a query in a database. The introd
uce others that describe a file and a specific range of data within that file, s
uch as a range of spreadsheet cells or a paragraph is a document. Introduce yet
more than identify a piece of code on the system somewhere that can execute some
interesting operation. In a world where clients have to know what a name means
in order to use it, those clients end up having to write specific code for each
type of name causing that application to grow monolithically in size and complex
ity. This is one of the problems that COM was created to solve.
In COM, therefore, the intelligence of how to work with a particular name is enc
apsulated inside the name itself, where the name becomes an object that implemen
ts name-related interfaces. These objects are called monikers. A moniker impleme
ntation provides an abstraction to some underlying connection (or binding ) mechani
sm. Each different moniker class (with a different CLSID) has its own semantics
as to what sort of object or operation it can refer to, which is entirely up to
the moniker itself. A section below describes some typical types of monikers. Wh
ile a moniker class itself defines the operations necessary to locate some gener
al type of object or perform some general type of action, each individual monike
r object (each instantiation) maintains its own name data that identifies some o
ther particular object or operation. The moniker class defines the functionality
; a moniker object maintains the parameters.
With monikers, clients always work with names through an interface, rather than
directly manipulating the strings (or whatever) themselves. This means that when
ever a client wishes to perform any operation with a name, it calls some code to
do it instead of doing the work itself. This level of indirection means that th
e moniker can transparently provide a whole host of services, and that the clien
t can seamlessly interoperate over time with various different moniker implement
ations which implement these services in different ways.
Monikers provide the perfect model for allowing applications to integrate with n
ew name services. For example, a logical next step to the integration of COM Co
re Technologies with DCE RPC would be to design and implement a DCE CDS moniker
class.
1.1.10 Connectable Objects
The COM technology known as Connectable Objects (also called connection points ) su
pports a generic ability for any object, called in this context a connectable obje
ct, to express these capabilities:
The existence of outgoing interfaces, such as event sets.
The ability to enumerate the outgoing interfaces.
The ability to connect and disconnect sinks to the object for those outgoing inter
faces.
The ability to enumerate the connections that exist to a particular outgoing int
erface.
1.1.11 Component Categories
Being able to group COM classes based on their capabilities is extremely useful.
Building lists of available classes for a particular task which the user may ch
oose from, an object which automatically chooses to aggregate in the closest/sma
llest/most-efficient class from those available, and type browsers for COM devel
opment tools are just some examples of using such categorization information.
1.1.12 Licensing
In a component software industry, vendors need a fine level of control over the
licensing of their components. The COM Licensing protocol allows vendors to supp
ort machine, application, and user level licensing. This protocol makes it possi
ble for vendors, for example, to ship a component that can be used in a developm
ent environment without royalty but only with a royalty on the end-users machine
. Or the vendor may choose to make the component available to software developer
s at a relatively large fee, but allow it to be freely redistributable as part o
f the developers application.
1.1.13 Type Libraries
Type libraries are COM's "interface repository". A type library is a database th
at contains details about interfaces. COM uses type libraries for data driven cr
oss process and cross-machine marshaling. COM development tools use type librari
es to provide graphical object browsers and code generators.
The contents of type libraries (type information) are exposed to programmers thr
ough interfaces. The actual binary file format is opaque.
1.1.14 Automation
Automation allows COM objects to expose functionality to high-level programming
environments such as visual development tools. Scripting languages such as JScri
pt and VBScript can make use of COM components that support automation.
Automation adds the following capabilities to COM:
Late bound, dynamic method invocation.
Type unsafe method invocation. This is useful to "typeless" programming environm
ents such as VBScript and REXX.
National language independent programming. This allows programmers to use their
chosen language (spoken, not programming) in their source code.
1.2 MS-RPC
MS-RPC is an implementation of the Open Software Foundation's (OSF) Distributed
Computing Environment (DCE) Remote Procedure Call (RPC) system.
The design and technology behind DCE RPC is just one part of a complete environm
ent for distributed computing defined by the OSF.
In selecting the RPC standard, the OSF cited the following rationale:
The three most important properties of a remote procedure call are simplicity, t
ransparency, and performance.
The selected RPC model adheres to the local procedure model as closely as possib
le. This requirement minimizes the amount of time developers spend learning the
new environment.
The selected RPC model permits interoperability; its core protocol is well defin
ed and cannot be modified by the user.
The selected RPC model allows applications to remain independent of the transpor
t and protocol on which they run, while supporting a variety of transports and p
rotocols.
The selected RPC model can be easily integrated with other components of the DCE
.
The OSF-DCE remote procedure call standards define not only the overall approach
, but the language and the specific protocols to use for communications between
computers as well, down to the format of data as it is transmitted over the netw
ork[CAE RPC]. The Component Object Model adds to this definition by specifying a
n object oriented, programming language independent programming model.
Microsoft's implementation of DCE RPC is compatible with the OSF standard with t
he exception of some minor differences. Client or server applications written us
ing Microsoft RPC will interoperate with any DCE RPC client or server whose run-
time libraries run over a supported protocol.
MS-RPC includes the following major components:
MIDL compiler
Run-time libraries and header files
Transport interface modules
Name service provider
Endpoint supply service
MS-RPC includes the following features beyond those defined by OSF:
Support for marshaling of interface pointers
Support for custom marshaling (i.e. application defined mechanisms for marshalin
g which are more efficient than standard NDR marshaling)
Support for additional transport protocols such as Appletalk and local RPC.
1.3 Registry
The registry is a database that COM components use to store and retrieve configu
ration data. The registry stores data in opaque binary files. To manipulate regi
stry data, a component must use the registry API functions. The COM Core Techno
logy runtime uses the registry to map COM class identifiers to the implementatio
n of that class, among other things.
1.4 Security Support Provider Interface
The Security Support Provider Interface (SSPI) is a common interface to security
support providers. Security support providers export a set of functions that ca
n be used to gain a certain level of security support. This interface is support
ed by MS-RPC.
The Security Support Provider Interface provides a common interface from a trans
port provider, such as Microsoft RPC, and security providers, such as Windows NT
Distributed Security (NTLM).
The functions defined fall into the following major categories:
Credential management. A credential is data used by a principal to establish the
identity of the principal, such as a password, or a Kerberos ticket.
Context management. A context contains information such as a session key, durati
on of the session, and so on.
Message support. Message support functions provide integrity services based on a
security context.
Package management. Support for differing security models (such as Kerberos and
Microsoft LAN Manager) is supported through security packages providing the nece
ssary mapping between this API and the actual security model.
1.5 Windows NT Distributed Security Provider
The Windows NT Distributed Security Provider is the primary security provider us
ed by Windows NT. It is also called the NTLM security provider. It is a Security
Support Provider Interface (SSPI) "package".
COM provides a developer with a well integrated set of technologies that can be
used for building applications and components at all levels right from desktop a
pplications, ActiveX controls for the internet, right up to distributed enterpri
se class applications that integrate with Microsoft Transaction Server. COM and
Distributed COM are pieces of infrastructure that underlies all of the COM techn
ologies and ties them together. DCOM is a key piece of infrastructure that makes
it easier to build distributed applications. This section provides information
on how software vendors and corporations have benefited from using COM technolog
ies particularly DCOM when building software solutions.
2. The Motivations for COM
2.1 Challenges Facing The Software Industry
Constant innovation in computing hardware and software have brought a multitude
of powerful and sophisticated applications to users desktops and across their net
works. Yet with such sophistication have come commensurate problems for applicat
ion developers, software vendors, and users:
Today s applications are large and complex they are time-consuming to develop, diffi
cult and costly to maintain, and risky to extend with additional functionality.
Applications are monolithic they come prepackaged with a wide range of features bu
t most features cannot be removed, upgraded independently, or replaced with alte
rnatives.
Applications are not easily integrated data and functionality of one application a
re not readily available to other applications, even if the applications are wri
tten in the same programming language and running on the same machine.
Operating systems have a related set of problems. They are not sufficiently modu
lar, and it is difficult to override, upgrade, or replace OS-provided services i
n a clean and flexible fashion.
Programming models are inconsistent for no good reason. Even when applications h
ave a facility for cooperating, their services are provided to other application
s in a different fashion from the services provided by the operating system or t
he network. Moreover, programming models vary widely depending on whether the se
rvice is coming from a provider in the same address space as the client program
(via dynamic linking), from a separate process on the same machine, from the ope
rating system, or from a provider running on a separate machine (or set of coope
rating machines) across the network.
In addition, a result of the trends of hardware down-sizing and increasing softw
are complexity is the need for a new style of distributed, client/server, modula
r and componentized computing. This style calls for:
A generic set of facilities for finding and using service providers (whether pro
vided by the operating system or by applications, or a combination of both), for
negotiating capabilities with service providers, and for extending and evolving
service providers in a fashion that does not inadvertently break the consumers
of earlier versions of those services.
Use of object-oriented concepts in system and application service architectures
to better match the new generation of object-oriented development tools, to mana
ge increasing software complexity through increased modularity, to re-use existi
ng solutions, and to facilitate new designs of more self-sufficient software com
ponents.
Client/server computing to take advantage of, and communicate between, increasin
gly powerful desktop devices, network servers, and legacy systems.
Distributed computing to provide a single system image to users and applications
and to permit use of services in a networked environment regardless of location
, machine architecture, or implementation environment.
As an illustration of the issues at hand, consider the problem of creating a sys
tem service API (Application Programming Interface) that works with multiple pro
viders of some service in a polymorphic fashion. That is, a client of the service
can transparently use any particular provider of the service without any special
knowledge of which specific provider or implementation is in use. In traditional
systems, there is a central piece of code conceptually, the service manager is a s
ort of object manager, although traditional systems usually involve function-call
programming models with system-provided handles used as the means for object selec
tion that every application calls to access meta-operations such as selecting an o
bject and connecting to it. But once applications have used those object manager o
perations and are connected to a service provider, the object manager only gets in
the way and forces unnecessary overhead upon all applications as shown in Figur
e 2-1.
Figure 2-1: Traditional system service APIs require all applications to communic
ate
through a central manager with corresponding overhead.
In addition to the overhead of the system-provided layer, another significant pr
oblem with traditional service models is that it is impossible for the provider
to express new, enhanced, or unique capabilities to potential consumers in a sta
ndard fashion. A well-designed traditional service architecture may provide the
notion of different levels of service. (Microsoft s Open Database Connectivity (OD
BC) API is an example of such an API.) Applications can count on the minimum lev
el of service, and can determine at run-time if the provider supports higher lev
els of service in certain pre-defined quanta, but the providers are restricted t
o providing the levels of services defined at the outset by the API; they cannot
readily provide a new capability and then evangelize consumers to access it che
aply and in a fashion that fits within the standard model. To take the ODBC exam
ple, the vendor of a database provider intent on doing more than the current ODB
C standard permits must convince Microsoft to revise the ODBC standard in a way
that exposes that vendor s extra capabilities. Thus, traditional service architect
ures cannot be readily extended or supplemented in a decentralized fashion.
Traditional service architectures also tend to be limited in their ability to ro
bustly evolve as services are revised and versioned. The problem with versioning
is one of representing capabilities (what a piece of code can do) and identity
(what a piece of code is) in an interrelated, fuzzy way. A later version of some
piece of code, such as Code version 2 indicates that it is like Code version 1 but
different in some way. The problem with traditional versioning in this manner is
that it s difficult for code to indicate exactly how it differs from a previous v
ersion and worse yet, for clients of that code to react appropriately to new ver
sions or to not react at all if they expect only the previous version. The version
ing problem can be reasonably managed in a traditional system when (i)€there is on
ly a single provider of a certain kind of service, (ii)€the version number of the
service is checked by the consumer when it binds to the service, (iii)€the service
is extended only in an upward-compatible manner i.e., features can only be added
and never removed (a significant restriction as software evolves over a long per
iod of time) so that a version N provider will work with consumers of versions 1 t
hrough N-1 as well, and (iv)€references to a running instance of the service are n
ot freely passed around by consumers to other consumers, all of which may expect
or require different versions. But these kind of restrictions are obviously una
cceptable in a multi-vendor, distributed, modular system with polymorphic servic
e providers.
These problems of service management, extensibility, and versioning have fed the
problems stated earlier. Application complexity continues to increase as it bec
omes more and more difficult to extend functionality. Monolithic applications ar
e popular because it is safer and easier to collect all interdependent services
and the code that uses those services into one package. Interoperability between
applications suffers accordingly, where monolithic applications are loathe to a
llow independent agents to access their functionality and thus build a dependenc
e upon a certain behavior of the application. Because end users demand interoper
ability, however, application are compelled to attempt interoperability, but thi
s leads directly back to the problem of application complexity, completing a cir
cle of problems that limit the progress of software development.
2.2 The Solution: Component Software
Object-oriented programming has long been advanced as a solution to the problems
at hand. However, while object-oriented programming is powerful, it has yet to
reach its full potential because no standard framework exists through which soft
ware objects created by different vendors can interact with one another within t
he same address space, much less across address spaces, and across network and m
achine architecture boundaries. The major result of the object-oriented programm
ing revolution has been the production of islands of objects that can t talk to one
another across the sea of application boundaries in a meaningful way.
The solution is a system in which application developers create reusable softwar
e components. A component is a reusable piece of software in binary form that ca
n be plugged into other components from other vendors with relatively little eff
ort. For example, a component might be a spelling checker sold by one vendor tha
t can be plugged into several different word processing applications from multip
le vendors. It might be a math engine optimized for computing fractals. Or it mi
ght be a specialized transaction monitor that can control the interaction of a n
umber of other components (including service providers beyond traditional databa
se servers). Software components must adhere to a binary external standard, but
their internal implementation is completely unconstrained. They can be built usi
ng procedural languages as well as object-oriented languages and frameworks, alt
hough the latter provide many advantages in the component software world.
Software component objects are much like integrated circuit (IC) components, and
component software is the integrated circuit of tomorrow. The software industry
today is very much where the hardware industry was 20 years ago. At that time,
vendors learned how to shrink transistors and put them into a package so that no
one ever had to figure out how to build a particular discrete function an NAND ga
te for example ever again. Such functions were made into an integrated circuit, a
neat package that designers could conveniently buy and design around. As the har
dware functions got more complex, the ICs were integrated to make a board of chi
ps to provide more complex functionality and increased capability. As integrated
circuits got smaller yet provided more functionality, boards of chips became ju
st bigger chips. So hardware technology now uses chips to build even bigger chip
s.
The software industry is at a point now where software developers have been busy
building the software equivalent of discrete transistors software routines for a lo
ng time.
The Component Object Model enables software suppliers to package their functions
into reusable software components in a fashion similar to the integrated circui
t. What COM and its objects do is bring software into the world where an applica
tion developer no longer has to write a sorting algorithm, for example. A sortin
g algorithm can be packaged as a binary object and shipped into a marketplace of
component objects. The developer who need a sorting algorithm just uses any sor
ting object of the required type without worrying about how the sort is implemen
ted. The developer of the sorting object can avoid the hassles and intellectual
property concerns of source-code licensing, and devote total energy to providing
the best possible binary version of the sorting algorithm. Moreover, the develo
per can take advantage of COM s ability to provide easy extensibility and innovati
on beyond standard services as well as robust support for versioning of componen
ts, so that a new component works perfectly with software clients expecting to u
se a previous version.
As with hardware developers and the integrated circuit, applications developers
now do not have to worry about how to build that function; they can simply purch
ase that function. The situation is much the same as when you buy an integrated
circuit today: You don t buy the sources to the IC and rebuild the IC yourself. CO
M allows you to simply buy the software component, just as you would buy an inte
grated circuit. The component is compatible with anything you plug it into.
By enabling the development of component software, COM provides a much more prod
uctive way to design, build, sell, use, and reuse software. Component software h
as significant implications for software vendors, users, and corporations:
Application developers are enabled to build and distribute applications more eas
ily than ever before. Component objects provide both scalability from single pro
cesses to enterprise networks and modularity for code reuse. In addition, develo
pers can attain higher productivity because they can learn one object system for
many platforms.
Vendors are provided with a single model for interacting with other applications
and the distributed computing environment. While component software can readily
be added to existing applications without fundamental rewriting, it also provid
es the opportunity to modularize applications and to incrementally replace syste
m capabilities where appropriate. The advent of component software will help cre
ate more diverse market segments and niches for small, medium, and large vendors
.
End-users will see a much greater range of software choices, coupled with better
productivity. Users will have access to hundreds of objects across client and s
erver platforms objects that were previously developed by independent software ven
dors (ISVs) and corporations. In addition, as users see the possibilities of com
ponent software, demand is likely to increase for specialized components they ca
n purchase at a local software retail outlet and plug into applications.
Corporations benefit from lower costs for corporate computing, helping IS depart
ments work more efficiently, and enabling corporate computer users to be more pr
oductive. IS developers will spend less time developing general purpose software
components and more time developing glue components to create business-specific s
olutions. Existing applications do not need to be rewritten to take advantage of
a component architecture. Instead, corporate developers can create object-based
wrappers that encapsulate the legacy application and make its operations and data
available as an object to other software components in the network.
2.3 The Component Software Solution: COM
The Component Object Model provides a means to address problems of application c
omplexity and evolution of functionality over time. It is a widely available, po
werful mechanism for customers to adopt and adapt to a new style multi-vendor di
stributed computing, while minimizing new software investment.. COM is an open s
tandard, fully and completely publicly documented from the lowest levels of its
protocols to the highest. As a robust, efficient and workable component architec
ture it has been proven in the marketplace as the foundation of diverse and seve
ral application areas including compound documents, programming widgets, 3D engi
neering graphics, stock market data transfer, high performance transaction proce
ssing, and so on.
The Component Object Model is an object-based programming model designed to prom
ote software interoperability; that is, to allow two or more applications or comp
onents to easily cooperate with one another, even if they were written by differe
nt vendors at different times, in different programming languages, or if they ar
e running on different machines running different operating systems. To support
its interoperability features, COM defines and implements mechanisms that allow
applications to connect to each other as software objects. A software object is
a collection of related function (or intelligence) and the function s (or intelli
gence s) associated state.
Figure 2-2: Once COM connects client and object, the client and object communica
te directly without added overhead.
In other words, COM, like a traditional system service API, provides the operati
ons through which a client of some service can connect to multiple providers of
that service in a polymorphic fashion. But once a connection is established, COM
drops out of the picture. COM serves to connect a client and an object, but onc
e that connection is established, the client and object communicate directly wit
hout having to suffer overhead of being forced through a central piece of API co
de as illustrated in Figure 2-2.
COM is not a prescribed way to structure an application; rather, it is a set of
technologies for building robust groups of services in both systems and applicat
ions such that the services and the clients of those services can evolve over ti
me. In this way, COM is a technology that makes the programming, use, and uncoor
dinated/independent evolution of binary objects possible. COM is not a technolog
y designed primarily for making programming necessarily easy; indeed, some of th
e difficult requirements that COM accepts and meets necessarily involve some deg
ree of complexity. However, COM provides a ready base for extensions oriented to
wards increased ease-of-use, as well as a great basis for powerful, easy develop
ment environments, language-specific improvements to provide better language int
egration, and pre-packaged functionality within the context of application frame
works.
This is a fundamental strength of COM over other proposed object models: COM sol
ves the deployment problem, the versioning/evolution problem where it is necessary
that the functionality of objects can incrementally evolve or change without th
e need to simultaneously and in lockstep evolve or change all existing the clien
ts of the object. Objects/services can easily continue to support the interfaces
through which they communicated with older clients as well as provide new and b
etter interfaces through which they communicate with newer clients.
To solve the versioning problems as well providing connection services without u
ndue overhead, the Component Object Model builds a foundation that:
Enables the creation and use of reusable components by making them component obje
cts.
Defines a binary standard for interoperability.
Is a true system object model.
Provides distributed capabilities.
The following sections describe each of these points in more detail.
2.3.1 Reusable Component Objects
Object-oriented programming allows programmers to build flexible and powerful so
ftware objects that can easily be reused by other programmers. Why is this? What
is it about objects that are so flexible and powerful?
The definition of an object is a piece of software that contains the functions t
hat represent what the object can do (its intelligence) and associated state inf
ormation for those functions (data). An object is, in other words, some data str
ucture and some functions to manipulate that structure.
An important principle of object-oriented programming is encapsulation, where th
e exact implementation of those functions and the exact format and layout of the
data is only of concern to the object itself. This information is hidden from t
he clients of an object. Those clients are interested only in an object s behavior
and not the object s internals. For instance, consider an object that represents
a stack: a user of the stack cares only that the object supports push and pop operat
ions, not whether the stack is implemented with an array or a linked list. Put a
nother way, a client of an object is interested only in the contract the promised be
havior that the object supports, not the implementation it uses to fulfill that co
ntract.
COM goes as far as to formalize the notion of a contract between object and clie
nt. Such a contract is the basis for interoperability, and for interoperability
to work on a large scale requires a strong standard.
2.3.2 Binary and Wire-Level Standards for Interoperability
The Component Object Model defines a completely standardized mechanism for creat
ing objects and for clients and objects to communicate. Unlike traditional objec
t-oriented programming environments, these mechanisms are independent of the app
lications that use object services and of the programming languages used to crea
te the objects. The mechanisms also support object invocations across the networ
k. COM therefore defines a binary interoperability standard rather than a langua
ge-based interoperability standard on any given operating system and hardware pl
atform. In the domain of network computing, COM defines a standard architecture-
independent wire format and protocol for interaction between objects on heteroge
neous platforms.
2.3.2.1 Why Is Providing a Binary and Network Standard Important?
By providing a binary and network standard, COM enables interoperability among a
pplications that different programmers from different companies write. For examp
le, a word processor application from one vendor can connect to a spreadsheet ob
ject from another vendor and import cell data from that spreadsheet into a table
in the document. The spreadsheet object in turn may have a hot link to data provi
ded by a data object residing on a mainframe. As long as the objects support a p
redefined standard interface for data exchange, the word processor, spreadsheet,
and mainframe database don t have to know anything about each other s implementatio
n. The word processor need only know how to connect to the spreadsheet; the spre
adsheet need only know how to expose its services to anyone who wishes to connec
t. The same goes for the network contract between the spreadsheet and the mainfr
ame database. All that either side of a connection needs to know are the standar
d mechanisms of the Component Object Model.
Without a binary and network standard for communication and a standard set of co
mmunication interfaces, programmers face the daunting task of writing a large nu
mber of procedures, each of which is specialized for communicating with a differ
ent type of object or client, or perhaps recompiling their code depending on the
other components or network services with which they need to interact. With a b
inary and network standard, objects and their clients need no special code and n
o recompilation for interoperability. But these standards must be efficient for
use in both a single address space and a distributed environment; if the mechani
sm used for object interaction is not extremely efficient, especially in the cas
e of local (same machine) servers and components within a single address space,
mass-market software developers pressured by size and performance requirements s
imply will not use it.
Finally, object communication must be programming language-independent since pro
grammers cannot and should not be forced to use a particular language to interac
t with the system and other applications. An illustrative problem is that every
C++ vendor says, We ve got class libraries and you can use our class libraries. But
the interfaces published for that one vendor s C++ object usually differs from the
interfaces publishes for another vendor s C++ object. To allow application develo
pers to use the objects capabilities, each vendor has to ship the source code for
the class library for the objects so that application developers can rebuild th
at code for the vendor s compiler they re using. By providing a binary standard to w
hich objects conform, vendors do not have to send source code to provide compati
bility, nor to users have to restrict the language they use to get access to the
objects capabilities. COM objects are compatible by nature.
2.3.2.2 COM s Standards Enable Object Interoperability
With COM, applications interact with each other and with the system through coll
ections of function calls also known as methods or member functions or requests call
ed interfaces. An interface in the COM sense is a strongly typed contract between
software components to provide a relatively small but useful set of semantically
related operations. An interface is an articulation of an expected behavior and
expected responsibilities, and the semantic relation of interfaces gives progra
mmers and designers a concrete entity to use when referring to the contract. Alt
hough not a strict requirement of the model, interfaces should be factored in su
ch fashion that they can be re-used in a variety of contexts. For example, a sim
ple interface for generically reading and writing streams of data can be re-used
by many different types of objects and clients.
The use of such interfaces in COM provides four major benefits:
The ability for functionality in applications (clients or servers of objects) to
evolve over time: This is accomplished through a request called QueryInterface
that all COM objects support (or else they are not COM objects). QueryInterface
allows an object to make more interfaces (that is, new groups of functions) avai
lable to new clients while at the same time retaining complete binary compatibil
ity with existing client code. In other words, revising an object by adding new,
even unrelated functionality will not require any recompilation on the part of
any existing clients. Because COM allows objects to have multiple interfaces, an
object can express any number of versions simultaneously, each of which may be in
simultaneous use by clients of different vintage. And when its clients pass aro
und a reference to the object, an occurrence that in principle cannot be known and
therefore guarded against by the object, they actually pass a reference to a part
icular interface on the object, thus extending the chain of backward compatibili
ty. The use of immutable interfaces and multiple interfaces per object solves th
e problem of versioning.
Very fast and simple object interaction for same-process objects: Once a client
establishes a connection to an object, calls to that object s services (interface
functions) are simply indirect functions calls through two memory pointers. As a
result, the performance overhead of interacting with an in-process COM object (
an object that is in the same address space) as the calling code is negligible onl
y a handful of processor instructions slower than a standard direct function cal
l and no slower than a compile-time bound C++ single-inheritance object invocati
on.
Location transparency : The binary standard allows COM to intercept a interface cal
l to an object and make instead a remote procedure call (RPC) to the real instance
of the object that is running in another process or on another machine. A key p
oint is that the caller makes this call exactly as it would for an object in the
same process. Its binary and network standards enables COM to perform inter-pro
cess and cross-network function calls transparently. While there is, of course,
a great deal more overhead in making a remote procedure call, no special code is
necessary in the client to differentiate an in-process object from out-of-proce
ss objects. All objects are available to clients in a uniform, transparent fashi
on.
This is all well and good. But in the real world, it is sometimes necessary for
performance reasons that special considerations be taken into account when desig
ning systems for network operation that need not be considered when only local o
peration is used. What is needed is not pure local / remote transparency, but loc
al / remote transparency, unless you need to care. COM provides this capability.
An object implementor can if he wishes support custom marshaling which allows hi
s objects to take special action when they are used from across the network, dif
ferent action if he would like than is used in the local case. The key point is
that this is done completely transparently to the client. Taken as a whole, this
architecture allows one to design client / object interfaces at their natural a
nd easy semantic level without regard to network performance issues, then at a l
ater address network performance issues without disrupting the established desig
n.
Programming language independence: Because COM is a binary standard, objects can
be implemented in a number of different programming languages and used from cli
ents that are written using completely different programming languages. Any prog
ramming language that can create structures of pointers and explicitly or implic
itly call functions through pointers languages such as C, C++, Java, COBOL, Pascal
, Ada, Smalltalk, and even BASIC programming environments can create and use COM o
bjects immediately. Other languages can easily be enhanced to support this requi
rement.
In sum, only with a binary standard can an object model provide the type of stru
cture necessary for full interoperability, evolution, and re-use between any app
lication or component supplied by any vendor on a single machine architecture. O
nly with an architecture-independent network wire protocol standard can an objec
t model provide full interoperability, evolution, and re-use between any applica
tion or component supplied by any vendor in a network of heterogeneous computers
. With its binary and networking standards, COM opens the doors for a revolution
in software innovation without a revolution in networking, hardware, or program
ming and programming tools.
2.3.3 A True System Object Model
To be a true system model, an object architecture must allow a distributed, evol
ving system to support millions of objects without risk of erroneous connections
of objects and other problems related to strong typing or definition. COM is su
ch an architecture. In addition to being an object-based service architecture, C
OM is a true system object model because it:
Uses globally unique identifiers to identify object classes and the interfaces tho
se objects may support.
Provides methods for code reusability without the problems of traditional langua
ge-style implementation inheritance.
Has a single programming model for in-process, cross-process, and cross-network
interaction of software components.
Encapsulates the life-cycle of objects via reference counting.
Provides a flexible foundation for security at the object level.
The following sections elaborate on each of these aspects of COM.
2.3.3.1 Globally Unique Identifiers
Distributed object systems have potentially millions of interfaces and software
components that need to be uniquely identified. Any system that uses human-reada
ble names for finding and binding to modules, objects, classes, or requests is a
t risk because the probability of a collision between human-readable names is ne
arly 100% in a complex system. The result of name-based identification will inev
itably be the accidental connection of two or more software components that were
not designed to interact with each other, and a resulting error or crash even tho
ugh the components and system had no bugs and worked as designed.
By contrast, COM uses globally unique identifiers (GUIDs) 128-bit integers that ar
e virtually guaranteed to be unique in the world across space and time to identify
every interface and every object class and type. These globally unique identifi
ers are the same as UUIDs (Universally Unique IDs) as defined by DCE. Human-read
able names are assigned only for convenience and are locally scoped. This helps
insure that COM components do not accidentally connect to an object or via an in
terface or method, even in networks with millions of objects.
2.3.3.2 Code Reusability and Implementation Inheritance
Implementation inheritance the ability of one component to subclass or inherit some of
its functionality from another component while over-riding other functions is a ver
y useful technology for building applications. But more and more experts are con
cluding that it creates serious problems in a loosely coupled, decentralized, ev
olving object system. The problem is technically known as the lack of type-safet
y in the specialization interface and is well-documented in the research literat
ure.
The general problem with traditional implementation inheritance is that the contr
act or interface between objects in an implementation hierarchy is not clearly de
fined; indeed, it is implicit and ambiguous. When the parent or child component
changes its implementation, the behavior of related components may become undefi
ned. This tight coupling of implementations is not a problem when the implementa
tion hierarchy is under the control of a defined group of programmers who can, i
f necessary, make updates to all components simultaneously. But it is precisely
this ability to control and change a set of related components simultaneously th
at differentiates an application, even a complex application, from a true distri
buted object system. So while traditional implementation inheritance can be a ve
ry good thing for building applications and components, it is inappropriate in a
system object model.
Today, COM provides two mechanisms for code reuse called containment/delegation
and aggregation. In the first and more common mechanism, one object (the outer obj
ect) simply becomes the client of another, internally using the second object (t
he inner object) as a provider of services that the outer object finds useful in i
ts own implementation. For example, the outer object may implement only stub fun
ctions that merely pass through calls to the inner object, only transforming obj
ect reference parameters from the inner object to itself in order to maintain fu
ll encapsulation. This is really no different than an application calling functi
ons in an operating system to achieve the same ends other objects simply extend th
e functionality of the system. Viewed externally, clients of the outer object on
ly ever see the outer object the inner contained object is completely hidden encapsula
ted from view. And since the outer object is itself a client of the inner object,
it always uses that inner object through a clearly defined contracts: the inner
object s interfaces. By implementing those interfaces, the inner object signs the
contract promising that it will not change its behavior unexpectedly.
With aggregation, the second and more rare reuse mechanism, COM objects take adv
antage of the fact that they can support multiple interfaces. An aggregated obje
ct is essentially a composite object in which the outer object exposes an interf
ace from the inner object directly to clients as if it were part of the outer ob
ject. Again, clients of the outer object are impervious to this fact, but intern
ally, the outer object need not implement the exposed interface at all. The oute
r object has determined that the implementation of the inner object s interface is
exactly what it wants to provide itself, and can reuse that implementation acco
rdingly. But the outer object is still a client of the inner object and there is
still a clear contract between the inner object and any client. Aggregation is
really nothing more than a special case of containment/delegation to prevent the
outer object from having to implement an interface that does nothing more than
delegate every function to the same interface in the inner object. Aggregation i
s really a performance convenience more than the primary method of reuse in COM.
Both these reuse mechanisms allow objects to exploit existing implementation whi
le avoiding the problems of traditional implementation inheritance. However, the
y lack a powerful, if dangerous, capability of traditional implementation inheri
tance: the ability of a child object to hook calls that a parent object might make
on itself and override entirely or supplement partially the parent s behavior. Th
is feature of implementation inheritance is definitely useful, but it is also th
e key area where imprecision of interface and implicit coupling of implementatio
n (as opposed to interface) creeps in to traditional implementation inheritance
mechanisms. A future challenge for COM is to define a set of conventions that co
mponents can use to provide this hooking feature of implementation inheritance whi
le maintaining the strictness of contract between objects and the full encapsula
tion required by a true system object model, even those in parent/child relationsh
ips.
2.3.3.3 Single Programming Model
A problem related to implementation inheritance is the issue of a single program
ming model for in-process objects and out-of-process/cross-network objects. In t
he former case, class library technology (or application frameworks) permits onl
y the use of features or objects that are in a single address. Such technology i
s far from permitting use of code outside the process space let alone code runni
ng on another machine altogether. In other words, a programmer can t subclass a re
mote object to reuse its implementation. Similarly, features like public data it
ems in classes that can be freely manipulated by other objects within a single a
ddress space don t work across process or network boundaries. In contrast, COM has
a single interface-based binding model and has been carefully designed to minim
ize differences between the in-process and out-of-process programming model. Any
client can work with any object anywhere else on the machine or network, and be
cause the object reusability mechanisms of containment and aggregation maintain
a client/server relationship between objects, reusability is also possible acros
s process and network boundaries.
2.3.3.4 Life-cycle Encapsulation
In traditional object systems, the life-cycle of objects the issues surrounding th
e creation and deletion of objects is handled implicitly by the language (or the l
anguage runtime) or explicitly by application programmers. In other words, an ob
ject-based application, there is always someone (a programmer or team of program
mers) or something (for example, the startup and shutdown code of a language run
time) that has complete knowledge when objects must be created and when they sho
uld be deleted.
But in an evolving, decentralized system made up of objects, it is no longer tru
e that someone or something always knows how to deal with object life-cycle. Objec
t creation is still relatively easy; assuming the client has the right security
privileges, an object is created whenever a client requests that it be created.
But object deletion is another matter entirely. How is it possible to know a prior
i when an object is no longer needed and should be deleted? Even when the origin
al client is done with the object, it can t simply shut the object down since it i
s likely to have passed a reference to the object to some other client in the sy
stem, and how can it know if/when that client is done with the object? or if that
second client has passed a reference to a third client of the object, and so on.
At first, it may seem that there are other ways of dealing with this problem. In
the case of cross-process and cross-network object usage, it might be possible
to rely on the underlying communication channel to inform the system when all co
nnections to an object have disappeared. The object can then be safely deleted.
There are two drawbacks to this approach, however, one of which is fatal. The fi
rst and less significant drawback is that it simply pushes the problem out to th
e next level of software. The object system will need to rely on a connection-or
iented communications model that is capable of tracking object connections and t
aking action when they disappear. That might, however, be an acceptable trade-of
f.
But the second drawback is flatly unacceptable: this approach requires a major d
ifference between the cross-process/cross-network programming model, where the c
ommunication system can provide the hook necessary for life-cycle management, an
d the single-process programming model where objects are directly connected toge
ther without any intervening communications channel. In the latter case, object
life-cycle issues must be handled in some other fashion. This lack of location t
ransparency would mean a difference in the programming model for single-process
and cross-process objects. It would also force clients to make a once-for-all co
mpile-time decision about whether objects were going to run in-process or out-of
-process instead of allowing that decision to be made by users of the binary com
ponent on a flexible, ad hoc basis. Finally, it would eliminate the powerful pos
sibility of composite objects or aggregates made up of both in-process and out-o
f-process objects.
Could the issue simply be ignored? In other words, could we simply ignore garbag
e collection (deletion of unused objects) and allow the operating system to clea
n up unneeded resources when the process was eventually torn down? That non- solut
ion might be tempting in a system with just a few objects, or in a system (like a
laptop computer) that comes up and down frequently. It is totally unacceptable,
however, in the case of an environment where a single process might be made up
of potentially thousands of objects or in a large server machine that must never
stop. In either case, lack of life-cycle management is essentially an embrace o
f an inherently unstable system due to memory leaks from objects that never die.
There is only one solution to this set of problems, the solution embraced by COM
: clients must tell an object when they are using it and when they are done, and
objects must delete themselves when they are no longer needed. This approach, b
ased on reference counting by all objects, is summarized by the phrase life-cycle
encapsulation since objects are truly encapsulated and self-reliant if and only
if they are responsible, with the appropriate help of their clients acting singl
y and not collectively, for deleting themselves.
Reference counting is admittedly complex for the new COM programmer; arguably, i
t is the most difficult aspect of the COM programming model to understand and to
get right when building complex peer-to-peer COM applications. When viewed in l
ight of the non-alternatives, however, its inevitability for a true system objec
t model with full location transparency is apparent. Moreover, reference countin
g is precisely the kind of mechanical programming task that can be automated to
a large degree or even entirely by well-designed programming tools and applicati
on frameworks. Tools and frameworks focused on building COM components exist tod
ay and will proliferate increasingly over the next few years. Moreover, the COM
model itself may evolve to provide support for optionally delegating life-cycle
management to the system. Perhaps most importantly, reference counting in partic
ular and native COM programming in general involves the kind of mind-shift for p
rogrammers as in GUI event-driven programming just a few short years ago that seems
difficult at first, but becomes increasingly easy, then second-nature, then almo
st trivial as experience grows.
2.3.3.5 Security
For a distributed object system to be useful in the real world it must provide a
means for secure access to objects and the data they encapsulate. The issues su
rrounding system object models are complex for corporate customers and ISVs maki
ng planning decisions in this area, but COM meets the challenges, and is a solid
foundation for an enterprise-wide computing environment.
COM provides security along several crucial dimensions. First, COM uses standard
operating system permissions to determine whether a client (running in a partic
ular user s security context) has the right to start the code associated with a pa
rticular class of object. Second, with respect to persistent objects (class code
along with data stored in a persistent store such as file system or database),
COM uses operating system or application permissions to determine if a particula
r client can load the object at all, and if so whether they have read-only or re
ad-write access, etc. Finally, because its security architecture is based the de
sign of the DCE RPC security architecture, an industry-standard communications m
echanism that includes fully authenticated sessions, COM provides cross-process
and cross-network object servers with standard security information about the cl
ient or clients that are using it so that a server can use security in more soph
isticated fashion than that of simple OS permissions on code execution and read/
write access to persistent data.
2.3.4 Distributed Capabilities
COM supports distributed objects; that is, it allows application developers to s
plit a single application into a number of different component objects, each of
which can run on a different computer. Since COM provides network transparency,
these applications do not appear to be located on different machines. The entire
network appears to be one large computer with enormous processing power and cap
acity.
Many single-process object models and programming languages exist today and a fe
w distributed object systems are available. However, none provides an identical,
transparent programming model for small, in-process objects, medium out-of-proc
ess objects on the same machine, and potentially huge objects running on another
machine on the network. The Component Object Model provides just such a transpa
rent model, where a client uses an object in the same process in precisely the s
ame manner as it would use one on a machine thousands of miles away. COM explici
tly bars certain kinds of features such as direct access to object data, properties,
or variables that might be convenient in the case of in-process objects but would
make it impossible for an out-of-process object to provide the same set of serv
ices. This is called location transparency.

3. Component Object Model Technical Overview


Chapter 2 introduced some important challenges and problems in computing today a
nd the Component Object Model as a solution to these problems. This chapter will
describe COM in a more technical light but not going as far as describing indiv
idual interface functions or COM API functions or interfaces. Instead, this chap
ter will refer to later chapters that cover various topics in complete detail in
cluding the specifications for functions and interfaces themselves.
This chapter covers the following topics that are then treated in complete detai
l in the indicated chapters:
Objects and Interfaces: A comparison of interfaces to C++ classes, the IUnknown
interface (including the QueryInterface function and reference counting), the st
ructure of an instantiated interface and the benefits of that structure, and how
clients of objects deal with interfaces. Chapter 4 covers the underlying interf
aces and API functions themselves.
COM Applications: The responsibilities of all applications making use of COM, wh
ich includes rules for memory management. How applications meet these responsibi
lities is covered in Chapter 5.
COM Clients and Servers: The roles and responsibilities of each specific type of
application, the use of class identifiers, and the COM Library s role in providin
g communication. Chapter 7 and 8 treat COM Clients and Servers separately. How C
OM achieves location transparency is described in Chapter 9.
Connectable Objects: A brief overview of the connection point interfaces and sem
antics. The actual functional specification of connectable objects is in Chapte
r 13.
Persistent Storage: A detailed look at what persistent storage is, what benefits
it holds for applications including incremental access and transactioning suppo
rt, leaving the APIs and interface specifications to Chapter 14.
Persistent, Intelligent Names: Why it is important to assign names to individual
object instantiations (as opposed to a class identifier for an object class) an
d the mechanisms for such naming including moniker objects. The interfaces a mon
iker implements as well as other support functions are described in Chapter 15.
Uniform Data Transfer: The separation of transfer protocols from data exchange,
improvements to data format descriptions, the expansion of available exchange me
diums (over global memory), and data change notification mechanisms. New data st
ructures and interfaces specified to support data transfer is given in Chapter 1
6.
Type Libraries: Type libraries and the related interfaces are described in chapt
er 17.
Automation: The IDispatch interface and it s related support infrastructure is des
cribed in chapter 18.
3.1 Objects and Interfaces
What is an object? An object is an instantiation of some class. At a generic lev
el, a class is the definition of a set of related data and capabilities grouped to
gether for some distinguishable common purpose. The purpose is generally to prov
ide some service to things outside the object, namely clients that want to make us
e of those services.
A object that conforms to COM is a special manifestation of this definition of o
bject. A COM object appears in memory much like a C++ object. Unlike C++ objects
, however, a client never has direct access to the COM object in its entirety. I
nstead, clients always access the object through clearly defined contracts: the
interfaces that the object supports, and only those interfaces.
What exactly is an interface? As mentioned earlier, an interface is a strongly-t
yped group of semantically-related functions, also called interface member functi
ons. The name of an interface is always prefixed with an I by convention, as in IUn
known. (The real identity of an interface is given by its GUID; names are a prog
ramming convenience, and the COM system itself uses the GUIDs exclusively when o
perating on interfaces.) In addition, while the interface has a specific name (o
r type) and names of member functions, it defines only how one would use that in
terface and what behavior is expected from an object through that interface. Int
erfaces do not define any implementation. For example, a hypothetical interface
called IStack that had member functions of Push and Pop would only define the pa
rameters and return types for those functions and what they are expected to do f
rom a client perspective; the object is free to implement the interface as it se
es fit, using an array, linked list, or whatever other programming methods it de
sires.
When an object implements an interface that object implements each member function
of the interface and provides pointers to those functions to COM. COM then make
s those functions available to any client who asks. This terminology is used in
this document to refer to the object as the important element in the discussion.
An equivalent term is an interface on an object which means the object implements
the interface but the main subject of discussion is the interface instead of th
e object.
3.1.1 Attributes of Interfaces
Given that an interface is a contractual way for an object to expose its service
s, there are four very important points to understand:
An interface is not a class: An interface is not a class in the normal definitio
n of class. A class can be instantiated to form an object. An interface cannot be
instantiated by itself because it carries no implementation. An object must impl
ement that interface and that object must be instantiated for there to be an int
erface. Furthermore, different object classes may implement an interface differe
ntly yet be used interchangeably in binary form, so long as the behavior conform
s to the interface definition (such as two objects that implement IStack where o
ne uses an array and the other a linked list).
An interface is not an object: An interface is just a related group of functions
and is the binary standard through which clients and objects communicate. The o
bject can be implemented in any language with any internal state representation,
so long as it can provide pointers to interface member functions.
Interfaces are strongly typed: Every interface has its own interface identifier
(a GUID) thereby eliminating any chance of collision that would occur with human
-readable names. Programmers must consciously assign an identifier to each inter
face and must consciously support that interface and/or the interfaces defined b
y others: confusion and conflict among interfaces cannot happen by accident, lea
ding to much improved robustness.
Interfaces are immutable: Interfaces are never versioned, thus avoiding versioni
ng problems. A new version of an interface, created by adding or removing functi
ons or changing semantics, is an entirely new interface and is assigned a new un
ique identifier. Therefore a new interface does not conflict with an old interfa
ce even if all that changed is the semantics. Objects can, of course, support mu
ltiple interfaces simultaneous; and they can have a single internal implementati
on of the common capabilities exposed through two or more similar interfaces, su
ch as versions (progressive revisions) of an interface. This approach of immutable
interfaces and multiple interfaces per object avoids versioning problems.
Two additional points help to further reinforce the second point about the relat
ionship of an object and its interfaces:
Clients only interact with pointers to interfaces: When a client has access to a
n object, it has nothing more than a pointer through which it can access the fun
ctions in the interface, called simply an interface pointer. The pointer is opaq
ue, meaning that it hides all aspects of internal implementation. You cannot see
any details about the object such as its state information, as opposed to C++ o
bject pointers through which a client may directly access the object s data. In CO
M, the client can only call functions of the interface to which it has a pointer
. But instead of being a restriction, this is what allows COM to provide the eff
icient binary standard that enables location transparency.
Objects can implement multiple interfaces: A object class can and typically does imp
lement more than one interface. That is, the class has more than one set of serv
ices to provide from each object. For example, a class might support the ability
to exchange data with clients as well as the ability to save its persistent sta
te information (the data it would need to reload to return to its current state)
into a file at the client s request. Each of these abilities is expressed through
a different interface, so the object must implement two interfaces.
Note that just because a class supports one interface, there is no general requi
rement that it supports any other. Interfaces are meant to be small contracts th
at are independent of one another. There are no contractual units smaller than i
nterfaces; if you write a class that implements an interface, your class must im
plement all the functions defined by that interface (the implementation doesn t al
ways have to do anything). Also note that an object may be attempting to conform
to a higher specification than COM. These specifications can define required in
terfaces on objects, but those interfaces themselves do not depend on the presen
ce of the others. It is instead the clients of those objects that depend on the
presence of all the interfaces.
The encapsulation of functionality into objects accessed through interfaces make
s COM an open, extensible system. It is open in the sense that anyone can provid
e an implementation of a defined interface and anyone can develop an application
that uses such interfaces, such as a compound document application. It is exten
sible in the sense that new or extended interfaces can be defined without changi
ng existing applications and those applications that understand the new interfac
es can exploit them while continuing to interoperate with older applications thr
ough the old interfaces.
3.1.2 Object Pictures
It is convenient to adopt a standard pictorial representation for objects and th
eir interfaces. The adopted convention is to draw each interface on an object as
a plug-in jack. These interfaces are generally drawn out the left or right side o
f a box representing the object as a whole as illustrated in Figure 3-1. If desi
red, the names of the interfaces are positioned next to the interface jack itsel
f.
Figure 3-1: A typical picture of an object that supports three interfaces A, B,
and C.
The side from which interfaces extend is usually determined by the position of a
client in the same picture, if applicable. If there is no client in the picture
then the convention is for interfaces to extend to the left as done in Figure 3
-1. With a client in the picture, the interfaces extend towards the client, and
the client is understood to have a pointer to one or more of the interfaces on t
hat object as illustrated in Figure 3-2.
Figure 3-2: Interfaces extend towards the clients connected to them.
In some circumstances a client may itself implement a small object to provide an
other object with functions to call on various events or to expose services itse
lf. In such cases the client is also an object implementor and the object is als
o a client. Illustrations for such are similar to that in Figure 3-3.
Figure 3-3: Two applications may connect to each other s objects, in which
case they extend their interfaces towards each other.
Some objects may be acting as an intermediate between other clients in which cas
e it is reasonable to draw the object with interfaces out both sides with client
s on both sides. This is, however, a less frequent case than illustrating an obj
ects connected to one client.
There is one interface that demands a little special attention: IUnknown. This i
s the base interface of all other interfaces in COM that all objects must suppor
t. Usually by implementing any interface at all an object also implements a set
of IUnknown functions that are contained within that implemented interface. In s
ome cases, however, an object will implement IUnknown by itself, in which case t
hat interface is extended from the top of the object as shown in Figure 3-4.
Figure 3-4: The IUnknown interface extends from the
top of objects by convention.
In order to use an interface on a object, a client needs to know what it would w
ant to do with that interface that s what makes it a client of an interface rather t
han just a client of the object. In the plug-in jack concept, a client has to have
the right kind of plug to fit into the interface jack in order to do anything w
ith the object through the interface. This is like having a stereo system which
has a number of different jacks for inputs and outputs, such as a 1/4 stereo jack
for headphones, a coax input for an external CD player, and standard RCA connec
tors for speaker output. Only headphones, CD players, and speakers that have the
matching plugs are able to plug into the stereo object and make use of its serv
ices. Objects and interfaces in COM work the same way.
3.1.3 Objects with Multiple Interfaces and QueryInterface
In COM, an object can support multiple interfaces, that is, provide pointers to
more than one grouping of functions. Multiple interfaces is a fundamental innova
tion of COM as the ability for such avoids versioning problems (interfaces are i
mmutable as described earlier) and any strong association between an interface a
nd an object class. Multiple interfaces is a great improvement over systems in w
hich each object only has one massive interface, and that interface is a collect
ion of everything the object does. Therefore the identity of the object is stron
gly tied to the exact interface, which introduces the versioning problems once a
gain. Multiple interfaces is the cleanest way around the issue altogether.
The existence of multiple interfaces does, however, bring up a very important qu
estion. When a client initially gains access to an object, by whatever means, th
at client is given one and only one interface pointer in return. How, then, does
a client access the other interfaces on that same object?
The answer is a member function called QueryInterface that is present in all COM
interfaces and can be called on any interface polymorphically. QueryInterface i
s the basis for a process called interface negotiation whereby the client asks t
he object what services it is capable of providing. The question is asked by cal
ling QueryInterface and passing to that function the unique identifier of the in
terface representing the services of interest.
Here s how it works: when a client initially gains access to an object, that clien
t will receive at minimum an IUnknown interface pointer (the most fundamental in
terface) through which it can only control the lifetime of the object tell the obj
ect when it is done using the object and invoke QueryInterface. The client is prog
rammed to ask each object it manages to perform some operations, but the IUnknow
n interface has no functions for those operations. Instead, those operations are
expressed through other interfaces. The client is thus programmed to negotiate
with objects for those interfaces. Specifically, the client will ask each object b
y calling QueryInterface for an interface through which the client may invoke the
desired operations.
Now since the object implements QueryInterface, it has the ability to accept or
reject the request. If the object accepts the client s request, QueryInterface ret
urns a new pointer to the requested interface to the client. Through that interf
ace pointer the client thus has access to the functions in that interface. If, o
n the other hand, the object rejects the client s request, QueryInterface returns
a null pointer an error and the client has no pointer through which to call the desi
red functions. An illustration of both success and error cases is shown in Figur
e 3-5 where the client initially has a pointer to interface A and asks for inter
faces B and C. While the object supports interface B, it does not support interf
ace C.
Figure 3-5: Interface negotiation means that a client must ask an object for an
interface
pointer that is the only way a client can invoke functions of that interface.
A key point is that when an object rejects a call to QueryInterface, it is impos
sible for the client to ask the object to perform the operations expressed throu
gh the requested interface. A client must have an interface pointer to invoke fu
nctions in that interface, period. If the object refuses to provide one, a clien
t must be prepared to do without, simply failing whatever it had intended to do
with that object. Had the object supported that interface, the client might have
done something useful with it. Compare this with other object-oriented systems
where you cannot know whether or not a function will work until you call that fu
nction, and even then, handling of failure is uncertain. QueryInterface provides
a reliable and consistent way to know before attempting to call a function.
3.1.3.1 Robustly Evolving Functionality Over Time
Recall that an important feature of COM is the ability for functionality to evol
ve over time. This is not just important for COM, but important for all applicat
ions. QueryInterface is the cornerstone of that feature as it allows a client to
ask an object do you support functionality X? It allows the client to implement c
ode that will use this functionality if and only if an object supports it. In th
is manner, the client easily maintains compatibility with objects written before
and after the X functionality was available, and does so in a robust manner. An o
ld object can reliably answer the question do you support X with a no whereas a new
object can reliably answer yes. Because the question is asked by calling QueryInte
rface and therefore on a contract-by-contract basis instead of an individual fun
ction-by-function basis, COM is very efficient in this operation.
To illustrate the QueryInterface cornerstone, imagine a client that wishes to di
splay the contents of a number of text files, and it knows that for each file fo
rmat (ASCII, RTF, Unicode, etc.) there is some object class associated with that
format. Besides a basic interface like IUnknown, which we ll call interface A, th
ere are two others that the client wishes to use to achieve its ends: interface
B allows a client to tell an object to load some information from a file (or to
save it), and interface C allows a client to request a graphical rendering of wh
atever data the object loaded from a file and maintains internally.
With these interfaces, the client is then programmed to process each file as fol
lows:
Find the object class associated with the file format.
Instantiate an object of that class obtaining a pointer to a basic interface A i
n return.
Check if the object supports loading data from a file by calling interface A s Que
ryInterface function requesting a pointer to interface B. If successful, ask the
object to load the file through interface B.
Check if the object supports graphical rendering of its data by calling interfac
e A or B s Querynterface function (doesn t matter which interface, because queries ar
e uniform on the object) requesting a pointer to interface C. If successful, ask
the object for a graphic of the file contents that the client then displays on
the screen.
If an object class exists for every file format in the client s file list, and all
those objects implement interfaces A, B, and C, then the client will be able to
display all the contents of all the files. But in an imperfect world, let s say t
hat the object class for the ASCII text formats does not support interface C, th
at is, the object can load data from a file and save it to another file if neces
sary, but can t supply graphics. When the client code, written as described above,
encounters this object, the QueryInterface for interface C fails, and the clien
t cannot display the file contents. Oh well...
Now the programmers of the object class for ASCII realizes that they are losing
market share because they don t support graphics, and so they update the object cl
ass such that it now supports interface C. This new object is installed on the m
achine alone with the client application, but nothing else changes in the entire
system. The client code remains exactly the same. What now happens the next tim
e someone runs the client?
The answer is that the client immediately begins to use interface C on the updat
ed object. Where before the object failed QueryInterface when asked for interfac
e C, it now succeeds. Because it succeeds, the client can now display the conten
ts of the file that it previously could not.
Here is the raw power of QueryInterface: a client can be written to take advanta
ge of as much functionality as it would ideally like to use on every object it m
anages. When the client encounters an object that lacks the ideal support, the c
lient can use as much functionality as is available on that given object. When t
he object it later updated to support new interfaces, the same exact code in the
client, without any recompilation, redeployment, or changes whatsoever, automat
ically begins to take advantage of those additional interfaces. This is true com
ponent software. This is true evolution of components independently of one anoth
er and retaining full compatibility.
Note that this process also works in the other direction. Imagine that since the
client application above was shipped, all the objects for rendering text into g
raphics were each upgraded to support a new interface D through which a client m
ight ask the object to spell-check the text. Each object is upgraded independent
ly of the client, but since the client never queries for interface D, the object
s all continue to work perfectly with just interfaces B and C. In this case the
objects support more functionality than the client, but still retain full compat
ibility requiring absolutely no changes to the client. The client, at a later da
te, might then implement code to use interface D as well as code for yet a newer
interface E (that supports, say, language translation). That client begins to i
mmediately use interface D in all existing objects that support it, without requ
iring any changes to those objects whatsoever.
This process continues, back and forth, ad infinitum, and applies not only to ne
w interfaces with new functionality but also to improvements of existing interfa
ces. Improved interface are, for all practical purposes, a brand-new interface b
ecause any change to any interface requires a new interface identifier. A new id
entifier isolates an improved interface from its predecessor as much as it isola
tes unrelated interfaces from each other. There is no concept of version because t
he interfaces are totally different in identity.
So up to this point there has been this problem of versioning, presented at the
beginning of this chapter, that made independent evolution of clients and object
s practically impossible. But now, for all time, QueryInterface solves that prob
lem and removes the barriers to rapid software innovation without the growing pa
ins.
Interfaces are strongly typed semantic contracts between client and object and tha
t an object in COM is any structure that exposes its functionality through the i
nterface mechanism. In addition, Chapter 2 noted how interfaces follow a binary
standard and how such a standard enables clients and objects to interoperate reg
ardless of the programming languages used to implement them. While the type of a
n interface is by colloquial convention referred to with a name starting with an
I (for interface), this name is only of significance in source-level programming
tools. Each interface itself the immutable contract, that is as a functional group i
s referred to at runtime with a globally-unique interface identifier, an IID that
allows a client to ask an object if it supports the semantics of the interface w
ithout unnecessary overhead and without versioning problems. Clients ask questio
ns using a QueryInterface function that all objects support through the base int
erface, IUnknown.
Furthermore, clients always deal with objects through interface pointers and nev
er directly access the object itself. Therefore an interface is not an object, a
nd an object can, in fact, have more than one interface if it has more than one
group of functionality it supports.
Let s now turn to how interfaces manifest themselves and how they work.
3.1.4 Interfaces and C++ Classes
As just reiterated, an interface is not an object, nor is it an object class. Gi
ven an interface definition by itself, that is, the type definition for an inter
face name that begins with I, you cannot create an object of that type. This is on
e reason why the prefix I is used instead of the common C++ convention of using a C
to prefix an object class, such as CMyClass. While you can instantiate an object
of a C++ class, you cannot instantiate an object of an interface type.
In C++ applications, interfaces are, in fact, defined as abstract base classes.
That is, the interface is a C++ class that contains nothing but pure virtual mem
ber functions. This means that the interface carries no implementation and only
prescribes the function signatures for some other class to implement C++ compilers
will generate compile-time errors for code that attempts to instantiate an abst
ract base class. C++ applications implement COM objects by inheriting these func
tion signatures from one or more interfaces, overriding each interface function,
and providing an implementation of each function. This is how a C++ COM applica
tion implements interfaces on an object.
Implementing objects and interfaces in other languages is similar in nature, dep
ending on the language. In C, for example, an interface is a structure containin
g a pointer to a table of function pointers, one for each method in the interfac
e. It is very straightforward to use or to implement a COM object in C, or indee
d in any programming language which supports the notion of function pointers. No
special tools or language enhancements are required (though of course such thin
gs may be desirable).
The abstract-base class comparison exposes an attribute of the contract concept of
interfaces: if you want to implement any single function in an interface, you m
ust provide some implementation for every function in that interface. The implem
entation might be nothing more than a single return statement when the object ha
s nothing to do in that interface function. In most cases there is some meaningf
ul implementation in each function, but the number of lines of code varies great
ly (one line to hundreds, potentially).
A particular object will provide implementations for the functions in every inte
rface that it supports. Objects which have the same set of interfaces and the sa
me implementations for each are often said (loosely) to be instances of the same
class because they generally implement those interfaces in a certain way. Howev
er, all access to the instances of the class by clients will only be through int
erfaces; clients know nothing about an object other than it supports certain int
erfaces. As a result, classes play a much less significant role in COM than they
do in other object oriented systems.
COM uses the word interface in a sense different from that typically used in objec
t-oriented programming using C++. In the C++ context, interface describes all the
functions that a class supports and that clients of an object can call to intera
ct with it. A COM interface refers to a pre-defined group of related functions t
hat a COM class implements, but does not necessarily represent all the functions
that the class supports. This separation of an object s functionality into groups
is what enables COM and COM applications to avoid the problems inherent with ve
rsioning traditional all-inclusive interfaces.
3.1.5 Interfaces and Inheritance
COM separates class hierarchy (or indeed any other implementation technology) fr
om interface hierarchy and both of those from any implementation hierarchy. The
refore, interface inheritance is only applied to reuse the definition of the con
tract associated with the base interface. There is no selective inheritance in C
OM: if one interface inherits from another, it includes all the functions that t
he other interface defines, for the same reason than an object must implement al
l interface functions it inherits.
Inheritance is used sparingly in the COM interfaces. Most of the pre-defined int
erfaces inherit directly from IUnknown (to receive the fundamental functions lik
e QueryInterface), rather than inheriting from another interface to add more fun
ctionality. Because COM interfaces are inherited from IUnknown, they tend to be
small and distinct from one another. This keeps functionality in separate groups
that can be independently updated from the other interfaces, and can be recombi
ned with other interfaces in semantically useful ways.
In addition, interfaces only use single inheritance, never multiple inheritance,
to obtain functions from a base interface. Providing otherwise would significan
tly complicate the interface method call sequence, which is just an indirect fun
ction call, and, further, the utility of multiple inheritance is subsumed within
the capabilities provided by QueryInterface.
3.1.6 Interface Definitions: IDL
When a designer creates an interface, that designer usually defines it using an
Interface Description Language (IDL). From this definition an IDL compiler can g
enerate header files for programming languages such that applications can use th
at interface, create proxy and stub objects to provide for remote procedure call
s, and output necessary to enable RPC calls across a network.
IDL is simply a tool (one of possibly many) for the convenience of the interface
designer and is not central to COM s interoperability. It really just saves the d
esigner from manually creating many header files for each programming environmen
t and from creating proxy and stub objects by hand, which would not likely be a
fun task.
Chapter 20 describes the COM Interface Description Language in detail. In addit
ion, Chapter 17 covers Type Libraries which are the machine readable form of IDL
, used by tools and other components at runtime.
3.1.7 Basic Operations: The IUnknown Interface
All objects in COM, through any interface, allow clients access to two basic ope
rations:
Navigating between multiple interfaces on an object through the QueryInterface f
unction.
Controlling the object s lifetime through a reference counting mechanism handled w
ith functions called AddRef and Release.
Both of these operations as well as the three functions (and only these three) m
ake up the IUnknown interface from which all other interfaces inherit. That is,
all interfaces are polymorphic with IUnknown so they all contain QueryInterface,
AddRef, and Release functions.
3.1.7.1 Navigating Multiple Interfaces: the QueryInterface Function
As described above, QueryInterface is the mechanism by which a client, having ob
tained one interface pointer on a particular object, can request additional poin
ters to other interfaces on that same object. An input parameter to QueryInterfa
ce is the interface identifier (IID) of the interface being requested. If the ob
ject supports this interface, it returns that interface on itself through an acc
ompanying output parameter typed as a generic void; if not, the object returns a
n error.
In effect, what QueryInterface accomplishes is a switch between contracts on the
object. A given interface embodies the interaction that a certain contract requ
ires. Interfaces are groups of functions because contracts in practice invariabl
y require more than one supporting function. QueryInterface separates the reques
t Do you support a given contract? from the high-performance use of that contract
once negotiations have been successful. Thus, the (minimal) cost of the contract
negotiation is amortized over the subsequent use of the contract.
Conversely, QueryInterface provides a robust and reliable way for a component to
indicate that in fact does not support a given contract. That is, if using Quer
yInterface one asks an old object whether it supports a new interface (one, say, tha
t was invented after the old object has been shipped), then the old object will
reliably and robustly answer no; the technology which supports this is the algorit
hm by which IIDs are allocated. While this may seem like a small point, it is ex
cruciatingly important to the overall architecture of the system, and this capab
ility to robustly inquire of old things about new functionality is, surprisingly
, a feature not present in most other object architectures.
The strengths and benefits of the QueryInterface mechanism need not be reiterate
d here further, but there is one pressing issue: how does a client obtain its fi
rst interface pointer to an object? That question is of central interest to COM
applications but has no one answer. There are, in fact, four methods through whi
ch a client obtains its first interface pointer to a given object:
Call a COM Library API function that creates an object of a pre-determined type th
at is, the function will only return a pointer to one specific interface for a s
pecific object class.
Call a COM Library API function that can create an object based on a class ident
ifier and that returns any type interface pointer requested.
Call a member function of some interface that creates another object (or connect
s to an existing one) and returns an interface pointer on that separate object.
Implement an object with an interface through which other objects pass their int
erface pointer to the client directly. This is the case where the client is an o
bject implementor and passes a pointer to its object to another object to establ
ish a bi-directional connection.
3.1.7.2 Reference Counting: Controlling Object Life-cycle
Just like an application must free memory it allocated once that memory is no lo
nger in use, a client of an object is responsible for freeing the object when th
at object is no longer needed. In an object-oriented system the client can only
do this by giving the object an instruction to free itself.
However, the difficulty lies in having the object know when it is safe to free i
tself. COM objects, which are dynamically allocated, must allow the client to de
cide when the object is no longer in use, especially for local or remote objects
that may be in use by multiple clients at the same time the object must wait unti
l all clients are finished with it before freeing itself.
COM specifies a reference counting mechanism to provide this control. Each objec
t maintains a 32-bit reference count that tracks how many clients are connected
to it, that is, how many pointers exist to any of its interfaces in any client.
The use of a 32-bit counter (more than four billions clients) means that there s v
irtually no chance of overloading the count.
The two IUnknown functions of AddRef and Release that all objects must implement
control the count: AddRef increments the count and Release decrements it. When
the reference count is decremented to zero, Release is allowed to free the objec
t because no one else is using it anywhere. Most objects have only one implement
ation of these functions (along with QueryInterface) that are shared between all
interfaces, though this is just a common implementation approach. Architectural
ly, from a client s perspective, reference counting is strictly and clearly a per-
interface notion.
Whenever a client calls a function that returns a new interface pointer to it, s
uch as QueryInterface, the function being called is responsible for incrementing
the reference count through the returned pointer. For example, when a client fi
rst creates an object it receives back an interface pointer to an object that, f
rom the client s point of view, has a reference count of one. If the client calls
QueryInterface once for another interface pointer, the reference count is two. T
he client must then call Release through both pointers (in any order) to decreme
nt the reference count to zero before the object as a whole can free itself.
In general, every copy of any pointer to any interface requires a reference coun
t on it. Chapter 4, however, identifies some important optimizations that can be
made to eliminate extra unnecessary overhead with reference counting and identi
fies the specific cases in which calling AddRef is absolutely necessary.
3.1.8 How an Interface Works
An instantiation of an interface implementation (because the defined interfaces
themselves cannot be instantiated without implementation) is simply pointer to a
n array of pointers to functions. Any code that has access to that array a pointer
through which it can access the array can call the functions in that interface. I
n reality, a pointer to an interface is actually a pointer to a pointer to the t
able of function pointers. This is an inconvenient way to speak about interfaces
, so the term interface pointer is used instead to refer to this multiple indirect
ion. Conceptually, then, an interface pointer can be viewed simply as a pointer
to a function table in which you can call those functions by dereferencing them
by means of the interface pointer as shown in Figure 3-6.
Figure 3-6: An interface pointer is a pointer to a pointer to an array of pointe
rs
to the functions in the interface.
Since these function tables are inconvenient to draw they are represented with t
he plug-in jack or bubbles and push-pins diagram first shown earlier in Chapter 3 to
mean exactly the same thing:
Objects with multiple interfaces are merely capable of providing more than one f
unction table. Function tables can be created manually in a C application or alm
ost automatically with C++ (and other object oriented languages that support COM
). Chapter 4 describes exactly how this is accomplished along with how the imple
mentation of the interface functions know exactly which object is being used at
any given time.
With appropriate compiler support (which is inherent in C and C++), a client can
call an interface function through the name of the function and not its positio
n in the array. The names of functions and the fact that an interface is a type
allows the compiler to check the types of parameters and return values of each i
nterface function call. In contrast, such type-checking is not available even in
C or C++ if a client used a position-based calling scheme.
3.1.9 Interfaces Enable Interoperability
COM is designed around the use of interfaces because interfaces enable interoper
ability. There are three properties of interfaces that provide this: polymorphis
m, encapsulation, and transparent remoting.
3.1.9.1 Polymorphism
Polymorphism means the ability to assume many forms, and in object-oriented prog
ramming it describes the ability to have a single statement invoke different fun
ctions at different times. All COM interfaces are polymorphic; when you call a f
unction using an interface pointer, you don t specify which implementation is invo
ked. A call to pInterface->SomeFunction can cause different code to run dependin
g on what kind of object is the implementor of the interface pointed by pInterfa
ce while the semantics of the function are always the same, the implementation det
ails can vary.
Because the interface standard is a binary standard, clients that know how to us
e a given interface can interact with any object that supports that interface no
matter how the object implements that contract. This allows interoperability as
you can write an application that can cooperate with other applications without
you knowing who or what they are beforehand.
3.1.9.2 Encapsulation
Other advantages of COM arise from its enforcement of encapsulation. If you have
implemented an interface, you can change or update the implementation without a
ffecting any of the clients of your class. Similarly, you are immune to changes
that others make in their implementations of their interfaces; if they improve t
heir implementation, you can benefit from it without recompiling your code.
This separation of contract and implementation can also allow you to take advant
age of the different implementations underlying an interface, even though the in
terface remains the same. Different implementations of the same interface are in
terchangeable, so you can choose from multiple implementations depending on the
situation.
Interfaces provides extensibility; a class can support new functionality by impl
ementing additional interfaces without interfering with any of its existing clie
nts. Code using an object s ISomeInterface is unaffected if the class is revised t
o in addition support IAnotherInterface.
3.1.9.3 Transparent Remoting
COM interfaces allow one application to interact with others anywhere on the net
work just as if they were on the same machine. This expands the range of an obje
ct s interoperability: your application can use any object that supports a given c
ontract, no matter how the object implements that contract, and no matter what m
achine the object resides on.
Before COM, class code such as C++ class libraries ran in same process, either l
inked into the executable or as a dynamic-link library. Now class code can run i
n a separate process, on the same machine or on a different machine, and your ap
plication can use it with no special code. COM can intercept calls to interfaces
through the function table and generate remote procedure calls instead.
3.2 Clients, Servers, and Object implementors
The interaction between objects and the users of those objects in COM is based o
n a client/server model. This chapter has already been using the term client to re
fer to some piece of code that is using the services of an object. Because an ob
ject supplies services, the implementor of that object is usually called the serv
er, the one who serves those capabilities. A client/server architecture in any co
mputing environment leads to greater robustness: if a server process crashes or
is otherwise disconnected from a client, the client can handle that problem grac
efully and even restart the server if necessary. As robustness is a primary goal
in COM, then a client/server model naturally fits.
However, there is more to COM than just clients and servers. There are also obje
ct implementors, or some program structure that implements an object of some kin
d with one or more interfaces on that object. Sometimes a client wishes to provi
de a mechanism for an object to call back to the client when specific events occ
ur. In such cases, COM specifies that the client itself implements an object and
hands that object s first interface pointer to the other object outside the clien
t. In that sense, both sides are clients, both sides are servers in some way. Si
nce this can lead to confusion, the term server is applied in a much more specific
fashion leading to the following definitions that apply in all of COM:
Object A unit of functionality that implements one or more interfaces to expose
that functionality. For convenience, the word is used both to refer to an objec
t class as well as an individual instantiation of a class. Note that an object c
lass does not need a class identifier in the COM sense such that other applicati
ons can instantiate objects of that class the class used to implement the object i
nternally has no bearing on the externally visible COM class identifier.
Object Implementor Any piece of code, such as an application, that has impl
emented an object with any interfaces for any reason. The object is simply a mea
ns to expose functions outside the particular application such that outside agen
ts can call those functions. Use of object by itself implies an object found in so
me object implementor unless stated otherwise.
Client There are two definitions of this word. The general definition is any pi
ece of code that is using the services of some object, wherever that object migh
t be implemented. A client of this sort is also called an object user. The second
definition is the active agent (an application) that drives the flow of operatio
n between itself an other objects and uses specific COM implementation locator ser
vices to instantiate or create objects through servers of various object classes
.
Server A piece of code that structures an object class in a specific fashion an
d assigns that class a COM class identifier. This enables a client to pass the c
lass identifier to COM and ask for an object of that class. COM is able to load
and run the server code, ask the sever to create an object of the class, and con
nect that new object to the client. A server is specifically the necessary struc
ture around an object that serves the object to the rest of the system and assoc
iates the class identifier: a server is not the object itself. The word server is
used in discussions to emphasize the serving agent more than the object. The phr
ase server object is used specifically to identify an object that is implemented i
n a server when the context is appropriate.
Putting all of these pieces together, imagine a client application that initiall
y uses COM services to create an object of a particular class. COM will run the
server associated with that class and have it create an object, returning an int
erface pointer to the client. With that interface pointer the client can query f
or any other interface on the object. If a client wants to be notified of events
that happen in the object in the server, such as a data change, the client itse
lf will implement an event sink object and pass the interface pointer to that sink
to the server s object through an interface function call. The server holds onto
that interface pointer and thus itself becomes a client of the sink object. When
the server object detects an appropriate event, it calls the sink object s interf
ace function for that even. The overall configuration created in this scenario i
s much like that shown earlier in Figure 3-3. There are two primary modules of c
ode (the original client and the server) who both implement objects and who both
act in some aspects as clients to establish the configuration.
When both sides in a configuration implement objects then the definition of clien
t is usually the second one meaning the active agent who drives the flow of opera
tion between all objects, even when there is more than one piece of code that is
acting like a client of the first definition. This specification endeavors to p
rovide enough context to make it clear what code is responsible for what service
s and operations.
3.2.1 Server Flavors: In-Process and Out-Of-Process
As defined in the last section, a server in general is some piece of code that str
uctures some object in such a way that COM implementor locator services can run th
at code and have it create objects. The section below entitled The COM Library exp
ands on the specific responsibilities of COM in this sense.
Any specific server can be implemented in one of a number of flavors depending o
n the structure of the code module and its relationship to the client process th
at will be using it. A server is either in-process which means it s code executes in
the same process space as the client, or out-of-process which means it runs in an
other process on the same machine or in another process on a remote machine. The
se three types of servers are called in-process, local, and remote as defined below:
In-Process Server A server that can be loaded into the client s process spac
e and serves in-process objects. Under Microsoft Windows, these are implemented as
dynamic link libraries or DLLs. This specification uses DLL as a generic term to
describe any piece of code that can be loaded in this fashion which will, of cou
rse, differ between operating systems.
Local Server A server that runs in a separate process on the same machine as
the client and serves local objects. This type of server is another complete appli
cation of its own thus defining the separate process. This specification uses th
e terms EXE or executable to describe an application that runs in its own process as
opposed to a DLL which must be loaded into an existing process.
Remote Server A server that runs on a separate machine and therefore always ru
ns in another process as well to serve remote objects. Remote servers may be imp
lemented in either DLLs or EXEs; if a remote server is implemented in a DLL, a s
urrogate process will be created for it on the remote machine.
Note that the same words in-process, local, and remote are used in this specification
as a qualifier for the word object where emphasis is on the object more than the s
erver.
Object implementors choose the type of server based on the requirements of imple
mentation and deployment. COM is designed to handle all situations from those th
at require the deployment of many small, lightweight in-process objects (like co
ntrols, but conceivably even smaller) up to those that require deployment of a h
uge central corporate database server. Furthermore, COM does so in a transparent
fashion, with what is called location transparency, the topic of the next secti
on.
3.2.2 Location Transparency
COM is designed to allow clients to transparently communicate with objects regar
dless of where those objects are running, be it the same process, the same machi
ne, or a different machine. What this means is that there is a single programmin
g model for all types of objects for not only clients of those objects but also
for the servers of those objects.
From a client s point of view, all objects are access through interface pointers.
A pointer must be in-process, and in fact, any call to an interface function alw
ays reaches some piece of in-process code first. If the object is in-process, th
e call reaches it directly, with no intervening system-infrastructure code. If t
he object is out-of-process, then the call first reaches what is called a proxy ob
ject provided by COM itself which generates the appropriate remote procedure cal
l to the other process or the other machine.
From a server s point of view, all calls to an object s interface functions are made
through a pointer to that interface. Again, a pointer only has context in a sin
gle process, and so the caller must always be some piece of in-process code. If
the object is in-process, the caller is the client itself. Otherwise, the caller
is a stub object provided by COM that picks up the remote procedure call from the
proxy in the client process and turns it into an interface call to the server obj
ect.
As far as both clients and servers know, they always communicate directly with s
ome other in-process code as illustrated in Figure 3-7.
The bottom line is that dealing with in-process or remote objects is transparent
and identical to dealing with in-process objects. This location transparency ha
s a number of key benefits:
A common solution to problems that are independent of the distance between clien
t and server: For example, connection, function invocation, interface negotiatio
n, feature evolution, and so forth.
Programmers leverage their learning: New services are simply exposed through new
interfaces, and once programmers learn how to deal with interfaces, they alread
y know how to deal with new services that will be created in the future. This is
a great improvement over environments where each service is exposed in a comple
tely different fashion.
Systems implementation is centralized: The implementors of COM can focus on maki
ng the central process of providing this transparency as efficient and powerful
as possible such that every piece of code that uses COM benefits immensely.
Interface designers focus on design: In designing a suite of interfaces, the des
igners can spend their time in the essence of the design the contracts between the
parties without having to think about the underlying communication mechanisms for
any interoperability scenario. COM provides those mechanisms for free and trans
parently.
Figure 3-7: Clients always call in-process code; objects are always called by in
-process
code. COM provides the underlying transparent RPC.
The clear separation of interface from implementation provided by location trans
parency for some situations gets in the way when performance is of critical conc
ern. When designing an interface while focusing on making it natural and functio
nal from the client s point of view, one is sometimes lead to design decisions tha
t are in tension with allowing for efficient implementation of that interface ac
ross a network. What is needed is not pure location transparency, but location tr
ansparency, unless you need to care. COM provides this capability. An object impl
ementor can if he wishes support custom marshaling which allows his objects to t
ake special action when they are used from across the network, different action
if he would like than is used in the local case. The key point is that this is d
one completely transparently to the client. Taken as a whole, this architecture
allows one to design client / object interfaces at their natural and easy semant
ic level without regard to network performance issues, then at a later address n
etwork performance issues without disrupting the established design.
Also note again that COM is not a specification for how applications are structu
red: it is a specification for how applications interoperate. For this reason, C
OM is not concerned with the internal structure of an application that is the job
of programming languages and development environments. Conversely, programming e
nvironments have no set standards for working with objects outside of the immedi
ate application. C++, for example, works extremely well with objects inside an a
pplication, but has no support for working with objects outside the application.
Generally all other programming languages are the same in this regard. Therefor
e COM, through language-independent interfaces, picks up where programming langu
ages leave off to provide the network-wide interoperability.
3.3 Memory Management Rules
In COM there are many interface member functions and APIs which are called by co
de written by one programming organization and implemented by code written by an
other. Many of the parameters and return values of these functions are of types
that can be passed around by value; however, sometimes there arises the need to
pass data structures for which this is not the case, and for which it is therefo
re necessary that the caller and the callee agree as to the allocation and de-al
location policy. This could in theory be decided and documented on an individual
function by function basis, but it is much more reasonable to adopt a universal
convention for dealing with these parameters. Also, having a clear convention i
s important technically in order that the COM remote procedure call implementati
on can correctly manage memory.
Memory management of pointers to interfaces is always provided by member functio
ns in the interface in question. For all the COM interfaces these are the AddRef
and Release functions found in the IUnknown interface, from which again all oth
er COM interfaces derive (as described earlier in this chapter). This section re
lates only to non-by-value parameters which are not pointers to interfaces but a
re instead more mundane things like strings, pointers to structures, etc.
The COM Library provides an implementation of a memory allocator (see CoGetMallo
c and CoTaskMemAlloc). Whenever ownership of an allocated chunk of memory is pas
sed through a COM interface or between a client and the COM library, this alloca
tor must be used to allocate the memory.
Each parameter to and the return value of a function can be classified into one
of three groups: an in parameter, an out parameter (which includes return values
), or an in-out parameter. In each class of parameter, the responsibility for al
locating and freeing non-by-value parameters is the following:
in parameter Allocated and freed by the caller.
out parameter Allocated by the callee; freed by the caller.
in-out parameter Initially allocated by the caller, then freed and re-all
ocated by the callee if necessary. As with out parameters, the caller is respons
ible for freeing the final returned value.
In the latter two cases there is one piece of code that allocates the memory and
a different piece of code that frees it. In order for this to be successful, th
e two pieces of code must of course have knowledge of which memory allocator is
being used. Again, it is often the case that the two pieces of code are written
by independent development organizations. To make this work, we require that the
COM allocator be used.
Further, the treatment of out and in-out parameters in failure conditions needs
special attention. If a function returns a status code which is a failure code,
then in general the caller has no way to clean up the out or in-out parameters.
This leads to a few additional rules:
out parameter In error returns, out parameters must be always reliably set to
a value which will be cleaned up without any action on the caller s part. Further,
it is the case that all out pointer parameters (usually passed in a pointer-to-
pointer parameter, but which can also be passed as a member of a caller-allocate
callee-fill structure) must explicitly be set to NULL. The most straightforward
way to ensure this is (in part) to set these values to NULL on function entry.
(On success returns, the semantics of the function of course determine t
he legal return values.)
in-out parameter In error returns, all in-out parameters must either be l
eft alone by the callee (and thus remaining at the value to which it was initial
ized by the caller; if the caller didn t initialize it, then it s an out parameter,
not an in-out parameter) or be explicitly set as in the out parameter error retu
rn case.
The specific COM APIs and interfaces that apply to memory management are discuss
ed further below.
Remember that these memory management conventions for COM applications apply onl
y across public interfaces and APIs there is no requirement at all that memory all
ocation strictly internal to a COM application need be done using these mechanis
ms.
3.4 The COM Client/Server Model
Chapter 1 mentioned how COM supports a model of client/server interaction betwee
n a user of an object s services, the client, and the implementor of that object a
nd its services, the server. To be more precise, the client is any piece of code
(not necessarily an application) that somehow obtains a pointer through which i
t can access the services of an object and then invokes those services when nece
ssary. The server is some piece of code that implements the object and structure
s in such a way that the COM Library can match that implementation to a class id
entifier, or CLSID. The involvement of a class identifier is what differentiates
a server from a more general object implementor.
The COM Library uses the CLSID to provide implementation locator services to clien
ts. A client need only tell COM the CLSID it wants and the type of server in-proce
ss, local, or remote that it allows COM to load or launch. COM, in turn, locates t
he implementation of that class and establishes a connection between it and the
client. This relationship between client, COM, and server is illustrated in Figu
re 3-8 on the next page.
Chapter 1 also introduced the idea of Location transparency, where clients and s
ervers never need to know how far apart they actually are, that is, whether they
are in the same process, different processes, or different machines.
This section now takes a closer look at the mechanisms in COM that make this tra
nsparency work as well as the responsibilities of client and server applications
.
Figure 3-8: Clients locate and access objects through implementation locator
services in COM. COM then connects the client to the object in a server.
3.4.1 COM Objects and Class Identifiers
A COM class is a particular implementation of certain interfaces; the implementa
tion consists of machine code that is executed whenever you interact with an ins
tance of the COM class. COM is designed to allow a class to be used by different
applications, including applications written without knowledge of that particul
ar class s existence. Therefore class code exists either in a dynamic linked libra
ry (DLL) or in another application (EXE). COM specifies a mechanism by which the
class code can be used by many different applications.
A COM object is an object that is identified by a unique 128-bit CLSID that asso
ciates an object class with a particular DLL or EXE in the file system. A CLSID
is a GUID itself (like an interface identifier), so no other class, no matter wh
at vendor writes it, has a duplicate CLSID. Servers implementors generally obtai
n CLSIDs through the CoCreateGUID function in COM, or through a COM-enabled tool
that internally calls this function.
The use of unique CLSIDs avoids the possibility of name collisions among classes
because CLSIDs are in no way connected to the names used in the underlying impl
ementation. So, for example, two different vendors can write classes which they
call StackClass, but each will have a unique CLSID and therefore avoid any possibi
lity of a collision.
Further, no central authoritative and bureaucratic body is needed to allocate or
assign CLSIDs. Thus, server implementors across the world can independently dev
elop and deploy their software without fear of accidental collision with softwar
e written by others.
On its host system, COM maintains a registration database (or registry ) of all the
CLSIDs for the servers installed on the system, that is, a mapping between each
CLSID and the location of the DLL or EXE that houses the server for that CLSID.
COM consults this database whenever a client wants to create an instance of a C
OM class and use its services. That client, however, only needs to know the CLSI
D which keeps it independent of the specific location of the DLL or EXE on the p
articular machine.
If a requested CLSID is not found in the local registration database, various ot
her administratively-controlled algorithms are available by which the implementa
tion is attempted to be located on the network to which the local machine may be
attached; these are explained in more detail below.
Given a CLSID, COM invokes a part of itself called the Service Control Manager (
SCM) which is the system element that locates the code for that CLSID. The code
may exist as a DLL or EXE on the same machine or on another machine: the SCM iso
lates most of COM, as well as all applications, from the specific actions necess
ary to locate code. We ll return a discussion of the SCM in a moment after examini
ng the roles of the client and server applications.
3.4.2 COM Clients
Whatever application passes a CLSID to COM and asks for an instantiated object i
n return is a COM Client. Of course, since this client uses COM, it is also a CO
M application that must perform the required steps described above and in subseq
uent chapters.
Regardless of the type of server in use (in-process, local, or remote), a COM Cl
ient always asks COM to instantiate objects in exactly the same manner. The simp
lest method for creating one object is to call the COM function CoCreateInstance
. This creates one object of the given CLSID and returns an interface pointer of
whatever type the client requests. Alternately, the client can obtain an interf
ace pointer to what is called the class factory object for a CLSID by calling CoGe
tClassObject. This class factory supports an interface called IClassFactory thro
ugh which the client asks that factory to manufacture an object of its class. At
that point the client has interface pointers for two separate objects, the clas
s factory and an object of that class, that each have their own reference counts
. It s an important distinction that is illustrated in Figure 3-9 and clarified fu
rther in Chapter 7.
Figure 3-9: A COM Client creates objects through a class factory.
The CoCreateInstance function internally calls CoGetClassObject itself. It s just
a more convenient function for clients that want to create one object.
The bottom line is that a COM Client, in addition to its responsibilities as a C
OM application, is responsible to use COM to obtain a class factory, ask that fa
ctory to create an object, initialize the object, and to call that object s (and t
he class factory s) Release function when the client is finished with it. These st
eps are the bulk of Chapter 7 which also explains some features of COM that allo
w clients to manage when servers are loaded and unloaded to optimize performance
.
3.4.3 COM Servers
There are two basic kinds of object servers:
Dynamic Link Library (DLL) Based: The server is implemented in a module that can
be loaded into, and will execute within, a client s address space. (The term DLL
is used in this specification to describe any shared library mechanism that is
present on a given COM platform.)
EXE Based: The server is implemented as a stand-alone executable module.
Since COM allows for distributed objects, it also allows for the two basic kinds
of servers to implemented on a remote machine. To allow client applications to
activate remote objects, COM defines the Service Control Manager (SCM) whose rol
e is described below under The COM Library.
As a client is responsible for using a class factory and for server management,
a server is responsible for implementing the class factory, implementing the cla
ss of objects that the factory manufactures, exposing the class factory to COM,
and providing for unloading the server under the right conditions. A diagram ill
ustrating what exists inside a server module (EXE or DLL) is shown in Figure 3-1
0.
Figure 3-10: The general structure of a COM server.
How a server accomplishes these requirements depends on whether the server is im
plemented as a DLL or EXE, but is independent of whether the server is on the sa
me machine as the client or on a remote machine. That is, remote servers are the
same as local servers but have been registered to be visible to remote clients.
Chapter 8 goes into all the necessary details about these implementations as we
ll as how the server publishes its existence to COM in the registration database
.
A special kind of server is called an custom object handler that works in conjunct
ion with a local server to provide a partial in-process implementation of an obj
ect class. Since in-process code is normally much faster to load, in-process cal
ls are extremely fast, and certain resources can be shared only within a single
process space, handlers can help improve performance of general object operatio
ns as well as the quality of operations such as printing. An object handler is a
rchitecturally similar to an in-process server but with more specialized semanti
cs for its use. While the client can control the loading of handlers, it doesn t h
ave to do any special work whatsoever to work with them. The existence of a hand
ler changes nothing for clients.
3.4.4 The COM Library and Service Control Manager
As described in Chapter 1, the COM Library itself is the implementation of the s
tandard API functions defined in COM along with support for communicating betwee
n objects and clients. The COM Library is then the underlying plumbing that makes
everything work transparently through RPC as shown in Figure 3-11 (this the same
figure as Figure 3-7, repeated here for convenience). Whenever COM determines t
hat it has to establish communication between a client and a local or remote ser
ver, it creates proxy objects that act as in-process objects to the client. These
proxies then talk to stub objects that are in the same process as the server and c
an call the server directly. The stubs pick up RPC calls from the proxies, turn
them into function calls to the real object, then pass the return values back to
the proxy via RPC which in turn returns them to the client. The underlying remo
te procedure call mechanism is based on the standard DCE remote procedure call m
echanism.
Figure 3-11: COM provides transparent access to local and remote servers
through proxy and stub objects.
3.4.5 Architecture for Distributed Objects
The COM architecture for object distribution is similar to the remoting architec
ture. When a client wants to connect to a server object, the name of the server
is stored in the system registry. With distributed objects, the server can imple
mented as an in-process DLL, a local executable, or as executable or DLL running
remotely. A component called the Service Control Manager (SCM) is responsible f
or locating the server and running it. The next section, The Service Control Mana
ger , explains the role of the SCM in greater depth and Chapter 21 contains the sp
ecification for it s interfaces.
Making a call to an interface method in a remote object involves the cooperation
of several components. The interface proxy is a piece of interface-specific cod
e 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
recreated and understood in the receiving process. The interface stub, also a pi
ece 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 on to the server. It also packages reply information to send b
ack to the client.
The actual transmitting of the data across the network is handled by the RPC run
time library and the channel, part of the COM library. The channel works transpa
rently with different channel types and supports both single and multi-threaded
applications.
The flow of communication between the components involved in interface remoting
is shown in Figure 3-12. 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 chan
nel is part of the COM library. The channel sends the buffer containing the mars
haled parameters to the RPC runtime library who transmits it across the process
boundary. The RPC runtime and the COM libraries exist on both sides of the proce
ss.
Figure 3-12. Components of COM s distributed architecture.
3.4.6 The Service Control Manager
The Service Control Manager ensures that when a client request is made, the appr
opriate server is connected and ready to receive the request. The SCM keeps a da
tabase of class information based on the system registry that the client caches
locally through the COM library. This is the basis for COM s implementation locato
r services as shown in Figure 3-13.
When a client makes a request to create an object of a CLSID, the COM Library co
ntacts the local SCM (the one on the same machine) and requests that the appropr
iate server be located or launched, and a class factory returned to the COM Libr
ary. After that, the COM Library, or the client, can ask the class factory to cr
eate an object.
The actions taken by the local SCM depend on the type of object server that is r
egistered for the CLSID:
In-Process The SCM returns the file path of the DLL containing the object s
erver implementation. The COM library then loads the DLL and asks it for its cla
ss factory interface pointer.
Local The SCM starts the local executable which registers a class factory on s
tartup. That pointer is then available to COM.
Remote The local SCM contacts the SCM running on the appropriate remote machine
and forwards the request to the remote SCM. The remote SCM launches the server
which registers a class factory like the local server with COM on that remote ma
chine. The remote SCM then maintains a connection to that class factory and retu
rns an RPC connection to the local SCM which corresponds to that remote class fa
ctory. The local SCM then returns that connection to COM which creates a class f
actory proxy which will internally forward requests to the remote SCM via the RP
C connection and thus on to the remote server.
Figure 3-13: COM delegates responsibility of loading and launching servers to th
e SCM.
Note that if the remote SCM determines that the remote server is actually an in-
process server, it launches a surrogate server that then loads that in-process ser
ver. The surrogate does nothing more than pass all requests on through to the lo
aded DLL.
3.4.7 Security
Using the network for distributing an application is challenging not only becaus
e of the physical limitations of bandwidth and latency. It also raises new issue
s related to security between and among clients and components. Since many opera
tions are now physically accessible by anyone with access to the network, access
to these operations has to be restricted at a higher level.
Without security support from the distributed development platform, each applica
tion would be forced to implement its own security mechanisms. A typical mechani
sm would involve passing some kind of username and password (or a public key) usua
lly encrypted to some kind of logon method. The application would validate these c
redentials against a user database or directory and return some dynamic identif
ier for use in future method calls. On each subsequent call to a secure method,
the clients would have to pass this security identifier. Each application would
have to store and manage a list of usernames and passwords, protect the user dir
ectory against unauthorized access, and manage changes to passwords, as well as
dealing with the security hazard of sending passwords over the network.
A distributed platform must thus provide a security framework to safely distingu
ish different clients or different groups of clients so that the system or the a
pplication has a way of knowing who is trying to perform an operation on a compo
nent. COM uses an extensible security framework (SSPI) that supports multiple id
entification and authentication mechanisms, from traditional trusted-domain secu
rity models to non-centrally managed, massively scaling public-key security mech
anisms. A central part of the security framework is a user directory, which stor
es the necessary information to validate a user's credentials (user name, passwo
rd, public key). Most COM implementations on non-Windows€NT platforms provide a si
milar or identical extensibility mechanism to use whatever kind of security pro
viders is available on that platform. Most UNIX-implementations of COM will incl
ude a Windows€NT-compatible security provider.
3.4.7.1 Security by Configuration
DCOM can make distributed applications secure without any security-specific codi
ng or design in either the client or the component. Just as the COM programming
model hides a component's location, it also hides the security requirements of a
component. The same (existing or off-the-shelf) binary code that works in a sin
gle-machine environment, where security may be of no concern, can be used in a d
istributed environment in a secure fashion.
COM achieves this security transparency by letting developers and administrators
configure the security settings for each component. COM stores Access Control L
ists for components. These lists simply indicate which users or groups of users
have the right to access a component of a certain class. These lists can easily
be configured using a COM configuration tool or programmatically.
Whenever a client calls a method or creates an instance of a component, COM obta
ins the client's current username associated with the current process (actually
the current thread of executionCOM then passes the username to the machine or pr
ocess where the component is running. COM on the component's machine then valida
tes the username again using whatever authentication mechanism is configured and
checks the access control list for the component. If the client's username is n
ot included in this list (either directly or indirectly as a member of a group o
f users), COM simply rejects the call before the component is ever involved. Thi
s default security mechanism is completely transparent to both the client and th
e component and is highly optimized.
Figure 3-14 - Security by Configuration
3.4.7.2 Programmatic Control Over Security
For some applications, a single component-wide access control list is not suffic
ient. Some methods in a component may be accessible only to certain users.
Example: An accounting business component may have a method for registering new
transactions and another method for retrieving existing transactions. Only membe
rs of the accounting department (user group "Accounting") should be able to add
new transactions, while only members of upper management (user group "Upper Mana
gement") should be able to view the transactions.
As indicated in the previous section, applications can always implement their ow
n security by managing their own user database and security credentials. However
, working from a standardized security framework provides many benefits to end u
sers. Without a security framework , users have to remember and manage logon cre
dentials for each application they are using. Developers have to be aware of sec
urity in each and every component of their applications.
COM simplifies customizing security to the needs of specific components and appl
ications, providing extreme flexibility that allows it to be extended to support
any security standard supported by the operating system. See the following sect
ion for details.
How can an application use COM security to implement the selective security requ
ired in the examples above? When a method call comes in, the component asks COM
to impersonate the client. After this, the called thread can perform only those
operations on secured objects, that the client is permitted to perform. The comp
onent can then try to access a secured object, such as a registry key, that has
an Access Control List on it. If this access fails, the client was not contained
in the ACL, and the component rejects the method call. By choosing different re
gistry keys according to the method that is being called, the component can prov
ide selective security in a very easy, yet flexible and efficient way.
Figure 3-15 - Per interface security using registry keys
Components can also simply obtain the authenticated username of the client and u
se it to look up permissions or policies in their own database. This strategy em
ploys the authentication mechanism of the SSPI. The application does not have to
worry about storing passwords or other sensitive information.
COM provides even more flexibility. Components can require different levels of e
ncryption and different levels of authentication, while clients can prevent comp
onents from using their credentials when impersonating.
3.4.7.3 Security on the Internet
There are two basic challenges facing applications designed to work over the Int
ernet.
The number of users can be orders of magnitude higher than in even the largest c
ompany.
End users want to use the same key or password for all of the applications they
are using, even if they are run by different companies. The application or the s
ecurity framework on the provider side cannot store the private key of the user.
How can COM's flexible security architecture help applications to deal with thes
e problems? COM uses the SSPI which supports multiple security providers, includ
ing:
Windows€NT NTLM authentication protocol, which is used by Windows€NT 4.0 and previou
s versions of Windows€NT.
The Kerberos Version 5 authentication protocol, which replaces NTLM (in Windows
NT 5.0) as the primary security protocol for access to resources within or acros
s Windows€NT domains.
Distributed password authentication (DPA), the shared secret authentication prot
ocol used by some of the largest Internet membership organizations, such as MSN a
nd CompuServe.
Secure channel security services, which implement the SSL/PCT protocols in Windo
ws€NT 4.0. The next generation of Windows€NT security has enhanced support for publi
c-key protocols that support SSL 3.0 client authentication.
A DCE-compliant security provider, available as a third-party add-on to Windows€NT
.
All of these providers work over standard Internet protocols and have different
advantages and disadvantages. The NTLM security provider and the Kerberos-based
provider replacing it in Windows€NT 5.0 are private key based protocols. Commercia
l implementations of NTLM security providers are available for all major Unix pl
atforms (such as AT&T's "Advanced Server for Unix Systems").
A Kerberos-based security provider allows even more advanced security concepts,
such as control over what components can do while impersonating clients.
A wide range of fundamentally different security providers (private key, public-
key) can be used by COM-based distributed applications without requiring any cha
nge to even advanced, security sensitive applications. The Windows€NT security fra
mework makes writing scalable and secure applications easy, without sacrificing
flexibility and performance.
3.5 Object Reusability
An important goal of any object model is that component authors can reuse and ex
tend objects provided by others as pieces of their own component implementations
. Implementation inheritance is one way this can be achieved: to reuse code in t
he process of building a new object, you inherit implementation from it and over
ride methods in the tradition of C++ and other languages. However, as a result o
f many years experience, many people believe traditional language-style implemen
tation inheritance technology as the basis for object reuse is simply not robust
enough for large, evolving systems composed of software components. (See page 3
2 for more information.) For this reason COM introduces other reusability mechan
isms.
3.5.1 COM Reusability Mechanisms
The key point to building reusable components is black-box reuse which means the
piece of code attempting to reuse another component knows nothing, and does not
need to know anything, about the internal structure or implementation of the co
mponent being used. In other words, the code attempting to reuse a component dep
ends upon the behavior of the component and not the exact implementation.
To achieve black-box reusability, COM supports two mechanisms through which one
object may reuse another. For convenience, the object being reused is called the
inner object and the object making use of that inner object is the outer object.
Containment/Delegation: the outer object behaves like an object client to the in
ner object. The outer object contains the inner object and when the outer object w
ishes to use the services of the inner object the outer object simply delegates
implementation to the inner object s interfaces. In other words, the outer object
uses the inner s services to implement itself. It is not necessary that the outer
and inner objects support the same interfaces; in fact, the outer object may use
an inner object s interface to help implement parts of a different interface on t
he outer object especially when the complexity of the interfaces differs greatly
.
Aggregation: the outer object wishes to expose interfaces from the inner object
as if they were implemented on the outer object itself. This is useful when the
outer object would always delegate every call to one of its interfaces to the sa
me interface of the inner object. Aggregation is a convenience to allow the oute
r object to avoid extra implementation overhead in such cases.
These two mechanisms are illustrated in Figures 3-16 and 3-17. The important par
t to both these mechanisms is how the outer object appears to its clients. As fa
r as the clients are concerned, both objects implement interfaces A, B, and C. F
urthermore, the client treats the outer object as a black box, and thus does not
care, nor does it need to care, about the internal structure of the outer objec
t the client only cares about behavior.
Containment is simple to implement for an outer object: during its creation, the
outer object creates whatever inner objects it needs to use as any other client
would. This is nothing new the process is like a C++ object that itself contains
a C++ string object that it uses to perform certain string functions even if the
outer object is not considered a string object in its own right.
Figure 3-16: Containment of an inner object and delegation to its interfaces.
Aggregation is almost as simple to implement, the primary difference being the i
mplementation of the three IUnknown functions: QueryInterface, AddRef, and Relea
se. The catch is that from the client s perspective, any IUnknown function on the
outer object must affect the outer object. That is, AddRef and Release affect th
e outer object and QueryInterface exposes all the interfaces available on the ou
ter object. However, if the outer object simply exposes an inner object s interfac
e as it s own, that inner object s IUnknown members called through that interface wi
ll behave differently than those IUnknown members on the outer object s interfaces
, a sheer violation of the rules and properties governing IUnknown.
The solution is for the outer object to somehow pass the inner object some IUnkn
own pointer to which the inner object can re-route (that is, delegate) IUnknown
calls in its own interfaces, and yet there must be a method through which the ou
ter object can access the inner object s IUnknown functions that only affect the i
nner object. COM provides specific support for this solution as described in Cha
pter 8.
Figure 3-17: Aggregation of an inner object where the outer object exposes one o
r
more of the inner object s interfaces as it s own.
3.6 Connectable Objects and Events
In the preceding discussions of interfaces it was implied that, from the object s
perspective, the interfaces were incoming . Incoming, in the context of a client-obj
ect relationship, implies that the object listens to what the client has to say.
In other words, incoming interfaces and their member functions receive input fro
m the outside. COM also defines mechanisms where objects can support outgoing int
erfaces. Outgoing interfaces allow objects to have two-way conversations, so t
o speak, with clients. When an object supports one or more outgoing interfaces,
it is said to be connectable. One of the most obvious uses for outgoing interf
aces is for event notification. This section describes Connectable Objects.
A connectable object (also called a source) can have as many outgoing interface
s as it likes. Each interface is composed of distinct member functions, with ea
ch function representing a single event, notification, or request. Events and
notifications are equivalent concepts (and interchangeable terms), as they are b
oth used to tell the client that something interesting happened in the object.
Events and notifications differ from a request in that the object expects respon
se from the client. A request, on the other hand, is how an object asks the cli
ent a question and expects a response.
In all of these cases, there must be some client that listens to what the object
has to say and uses that information wisely. It is the client, therefore, that
actually implements these interfaces on objects called sinks. From the sink s pe
rspective, the interfaces are incoming, meaning that the sink listens through th
em. A connectable object plays the role of a client as far as the sink is conce
rned; thus, the sink is what the object s client uses to listen to that object.
An object doesn t necessarily have a one-to-one relationship with a sink. In fact,
a single instance of an object usually supports any number of connections to si
nks in any number of separate clients. This is called multicasting. In additio
n, any sink can be connected to any number of objects.
Chapter 13 covers the Connectable Object interfaces (IConnectionPoint and IConne
ctionPointContainer) in complete detail.
3.7 Error Codes and Error Handling
COM interface member functions and COM Library API functions use a specific conv
ention for error codes in order to pass back to the caller both a useful return
value and along with an indication of status or error information. For example,
it is highly useful for a function to be capable of returning a Boolean result (
true or false) as well as indicate failure or success returning true and false mea
ns that the function executed successfully, and true or false is the answer wher
eas an error code indicates the function failed completely.
But before we get into error handling in COM, we ll first take a small digression.
Many readers might here be wondering about exceptions. How do exceptions relate
to interfaces? In short, it is strictly illegal to throw an exception across an
interface invocation; all such cross-interface exceptions which are thrown are
in fact bugs in the offending interface implementation.
Why have such a policy? It is well-understood that, quite apart from COM per se,
the exceptions that may be legally thrown from a function implementation in the
public interface of an encapsulated module must necessarily from part of the co
ntract of that function implementation. Thus, a thrown exception across such a b
oundary is merely an alternative mechanism by which values may be returned from
the function. In COM, we instead make use of the simpler, ubiquitous, already-ex
isting return-value mechanism for returning information from a function as our e
rror reporting mechanism: simply returning HRESULTs, which are the topic of this
section.
This all being said, it would be absolutely perfectly reasonable for the impleme
ntor of a tool for using or implementing COM interfaces to within the body of co
de managed by his tool turn errors returned from invoked COM interfaces into loc
al exceptions and, conversely, to turn internally generated exceptions into erro
r-returns across an interface boundary. The interfaces described in the chapter
on error handling allow environments to do this in a standard way. This is yet a
nother example of the clear architectural difference that needs to be made betwe
en the rules and design of the underlying COM system architecture and the capabi
lities and design freedom afforded to tools that support that architecture.
3.8 Enumerators and Enumerator Interfaces
A frequent programming task is that of iterating through a sequence of items. Th
e COM interfaces are no exception: there are places in several interfaces descri
bed in this specification where a client of some object needs to iterate through
a sequence of items controlled by the object. COM supports such enumeration thr
ough the use of enumerator objects. Enumerators cleanly separate the caller s desire
to loop over a set of objects from the callee s knowledge of how to accomplish th
at function.
Enumerators are just a concept; there is no actual interface called IEnumerator
or IEnum or the like. This is due to the fact that the function signatures in an
enumerator interface must include the type of the things that the enumerator en
umerates. As a consequence, separate interfaces exist for each kind of thing tha
t can be enumerated. However, the difference in the type being enumerated is the
only difference between each of these interfaces; they are all used in fundamen
tally the same way. In other words, they are generic over the element type. This d
ocument describes the semantics of enumerators using a generic interface IEnum w
hich is specified in Chapter 12.
3.9 Persistent Storage
As mentioned in Chapter 1, the enhanced COM services define a number of storage-
related interfaces, collectively called Persistent Storage or Structured Storage
. By definition of the term interface, these interfaces carry no implementation.
They describe a way to create a file system within a file, and they provide some
extremely powerful features for applications including incremental access, trans
actioning, and a sharable medium that can be used for data exchange or for stori
ng the persistent data of objects that know how to read and write such data them
selves. The following sections deal with the structure of storage and the other
features.
3.9.1 A File System Within A File
Years ago, before there were disk operating systems, applications had to write per
sistent data directly to a disk drive (or drum) by sending commands directly to
the hardware disk controller. Those applications were responsible for managing t
he absolute location of the data on the disk, making sure that it was not overwr
iting data that was already there. This was not too much of a problem seeing as
how most disks were under complete control of a single application that took ove
r the entire computer.
The advent of computer systems that could run more than one application brought
about problems where all the applications had to make sure they did not write ov
er each other s data on the disk. It therefore became beneficial that each adopted
a standard of marking the disk sectors that were used and which ones were free.
In time, these standards became the disk operating system which provided a file sy
stem. Now, instead of dealing directly with absolute disk sectors and so forth, a
pplications simply told the file system to write blocks of data to the disk. Fur
thermore, the file system allowed applications to create a hierarchy of informat
ion using directories which could contain not only files but other sub-directori
es which in turn contained more files, more sub-directories, etc.
The file system provided a single level of indirection between applications and
the disk, and the result was that every application saw a file as a single conti
guous stream of bytes on the disk. Underneath, however, the file system was stor
ing the file in dis-contiguous sectors according to some algorithm that optimize
d read and write time for each file. The indirection provided from the file syst
em freed applications from having to care about the absolute position of data on
a storage device.
Today, virtually all system APIs for file input and output provide applications
with some way to write information into a flat file that applications see as a s
ingle stream of bytes that can grow as large as necessary until the disk is full
. For a long time these APIs have been sufficient for applications to store thei
r persistent information. Applications have made some incredible innovations in
how they deal with a single stream of information to provide features like incre
mental fast saves.
However, a major feature of COM is interoperability, the basis for integration b
etween applications. This integration brings with it the need to have multiple a
pplications write information to the same file on the underlying file system. Th
is is exactly the same problem that the computer industry faced years ago when m
ultiple applications began to share the same disk drive. The solution then was t
o create a file system to provide a level of indirection between an application f
ile and the underlying disk sectors.
Thus, the solution for the integration problem today is another level of indirec
tion: a file system within a file. Instead of requiring that a large contiguous
sequence of bytes on the disk be manipulated through a single file handle with a
single seek pointer, COM defines how to treat a single file system entity as a
structured collection of two types of objects storages and streams that act like dir
ectories and files, respectively.
3.9.2 Storage and Stream Objects
Within COM s Persistent Storage definition there are two types of storage elements
: storage objects and stream objects. These are objects generally implemented by
the COM library itself; applications rarely, if ever, need to implement these s
torage elements themselves. These objects, like all others in COM, implement int
erfaces: IStream for stream objects, IStorage for storage objects as detailed in
Chapter 14.
A stream object is the conceptual equivalent of a single disk file as we underst
and disk files today. Streams are the basic file-system component in which data
lives, and each stream in itself has access rights and a single seek pointer. Th
rough its IStream interface stream can be told to read, write, seek, and perform
a few other operations on its underlying data. Streams are named by using a tex
t string and can contain any internal structure you desire because they are simp
ly a flat stream of bytes. In addition, the functions in the IStream interface m
ap nearly one-to-one with standard file-handle based functions such as those in
the ANSI C run-time library.
A storage object is the conceptual equivalent of a directory. Each storage, like
a directory, can contain any number of sub-storages (sub-directories) and any n
umber of streams (files). Furthermore, each storage has its own access rights. T
he IStorage interface describes the capabilities of a storage object such as enu
merate elements (dir), move, copy, rename, create, destroy, and so forth. A stor
age object itself cannot store application-defined data except that it implicitl
y stores the names of the elements (storages and streams) contained within it.
Storage and stream objects, when implemented by COM as a standard on a system, a
re sharable between processes. This is a key feature that enables objects runnin
g in-process or out-of-process to have equal incremental access to their on-disk
storage. Since COM is loaded into each process separately, it must use some ope
rating-system supported shared memory mechanisms to communicate between processe
s about opened elements and their access modes.
3.9.3 Application Design with Structured Storage
COM s structured storage built out of storage and stream objects makes it much eas
ier to design applications that by their nature produce structured information.
For example, consider a diary program that allows a user to make entries for any d
ay of any month of any year. Entries are made in the form of some kind of object
that itself manages some information. Users wanting to write some text into the
diary would store a text object; if they wanted to save a scan of a newspaper c
lip they could use a bitmap objects, and so forth.
Figure 3-18: A flat-file structure for a diary application. This
sort of structure is difficult to manage.
Without a powerful means to structure information of this kind, the diary applic
ation might be forced to manage some hideous file structure with an overabundanc
e of file position cross-reference pointers as shown in Figure 3-18.
There are many problems in trying to put structured information into a flat file
. First, there is the sheer tedium of managing all the cross-reference pointers
in all the different structures of the file. Whenever a piece of information gro
ws or moves in the file, every cross-reference offset referring to that informat
ion must be updated as well. Therefore even a small change in the size of one of
the text objects or an addition of a day or month might precipitate changes thr
oughout the rest of the file to update seek offsets. While not only tedious to m
anage, the application will have to spend enormous amounts of time moving inform
ation around in the file to make space for data that expands. That, or the appli
cation can move the newly enlarged data to the end of the file and patch a few s
eek offsets, but that introduces the whole problem of garbage collection, that i
s, managing the free space created in the middle of the file to minimize waste a
s well as overall file size.
The problems are compounded even further with objects that are capable of readin
g and writing their own information to storage. In the example here, the diary a
pplication would prefer to give each objects in it text, bitmap, drawing, table, e
tc. its own piece of the file in which the object can write whatever the it wants,
however much it wants. The only practical way to do this with a single flat fil
e is for the diary application to ask each object for a memory copy of what the
object would like to store, and then the diary would write that information into
a place in its own file. This is really the only way in which the diary could m
anage the location of all the information. Now while this works reasonably well
for small data, consider an object that wants to store a 10MB bitmap scan of a t
rue-color photograph exchanging that much data through memory is horribly ineffici
ent. Furthermore, if the end user wants to later make changes to that bitmap, th
e diary would have to load the bitmap in entirety from its file and pass it back
to the object. This is again extraordinarily inefficient.
COM s Persistent Storage technology solves these problems through the extra level
of indirection of a file system within a file. With COM, the diary application c
an create a structured hierarchy where the root file itself has sub-storages for
each year in the diary. Each year sub-storage has a sub-storage for each month,
and each month has a sub-storage for each day. Each day then would have yet ano
ther sub-storage or perhaps just a stream for each piece of information that the
user stores in that day. This configuration is illustrated in Figure 3-19.
Figure 3-19: A structured storage scheme for a diary application. Every object t
hat has
some content is given its own storage or stream element for its own exclusive us
e.
This structure solves the problem of expanding information in one of the objects
: the object itself expands the streams in its control and the COM implementatio
n of storage figures out where to store all the information in the stream. The d
iary application doesn t have to lift a finger. Furthermore, the COM implementatio
n automatically manages unused space in the entire file, again, relieving the di
ary application of a great burden.
In this sort of storage scheme, the objects that manage the content in the diary
always have direct incremental access to their piece of storage. That is, when
the object needs to store its data, it writes it directly into the diary file wi
thout having to involve the diary application itself. The object can, if it want
s to, write incremental changes to that storage, thus leading to much better per
formance than the flat file scheme could possibly provide. If the end user wante
d to make changes to that information later on, the object can then incrementall
y read as little information as necessary instead of requiring the diary to read
all the information into memory first. Incremental access, a feature that has t
raditionally been very hard to implement in applications, is now the default mod
e of operation. All of this leads to much better performance.
3.9.4 Naming Elements
Every storage and stream object in a structured file has a specific character na
me to identify it. These names are used to tell IStorage functions what element
in that storage to open, destroy, move, copy, rename, etc. Depending on which co
mponent, client or object, actually defines and stores these names, different co
nventions and restrictions apply.
Names of root storage objects are in fact names of files in the underlying file
system. Thus, they obey the conventions and restrictions that it imposes. String
s passed to storage-related functions which name files are passed on un-interpre
ted and unchanged to the file system.
Names of elements contained within storage objects are managed by the implementa
tion of the particular storage object in question. All implementations of storag
e objects must at the least support element names that are 32 characters in leng
th; some implementations may if they wish choose to support longer names. Names
are stored case-preserving, but are compared case-insensitive. As a result, appl
ications which define element names must choose names which will work in either
situation.
The names of elements inside an storage object must conform to certain conventio
ns:
The two specific names . and .. are reserved for future use.
Element names cannot contain any of the four characters \ , / , : , or ! .
In addition, the name space in a storage element is partitioned in to different
areas of ownership. Different pieces of code have the right to create elements i
n each area of the name space.
The set of element names beginning with characters other than \0x01 through \0x1F (t
hat is, decimal 1 through decimal 31) are for use by the object whose data is st
ored in the IStorage. Conversely, the object must not use element names beginnin
g with these characters.
Element names beginning with a \0x01 and \0x02 are for the exclusive use of COM.
Element names beginning with a \0x03 are for the exclusive use of the client which
is managing the object. The client can use this space as a place to persistentl
y store any information it wishes to associate with the object along with the re
st of the storage for that object.
Element names beginning with a \0x04 are for the exclusive use of the COM structur
ed storage implementation itself. They will be useful, for example, should that
implementation support other interfaces in addition to IStorage, and these inter
face need persistent state.
Element names beginning with \0x05 and \0x06 are for the exclusive use of COM.
All other names beginning with \0x07 through \0x1F are reserved for future definitio
n and use by the system.
In general, an element s name is not considered useful to an end-user. Therefore,
if a client wants to store specific user-readable names of objects, it usually u
ses some other mechanism. For example, the client may write its own stream under
one of its own storage elements that has the names of all the other objects wit
hin that same storage element. Another method would be for the client to store a
stream named \0x03Name in each object s storage that would contain that object s name
. Since the stream name itself begins with \0x03 the client owns that stream even
through the objects controls much of the rest of that storage element.
3.9.5 Direct Access vs. Transacted Access
Storage and stream elements support two fundamentally different modes of access:
direct mode and transacted mode. Changes made while in direct mode are immediat
ely and permanently made to the affected storage object. In transacted mode, cha
nges are buffered so that they may be saved ( committed ) or reverted when modificat
ions are complete.
If an outermost level IStorage is used in transacted mode, then when it commits,
a robust two-phase commit operation is used to publish those changes to the und
erlying file on the file system. That is, great pains are taken are taken so as
not to loose the user s data should an untimely crash occurs.
The need for transacted mode is best explained by an illustrative scenario. Imag
ine that a user has created a spreadsheet which contains a sound clip object, an
d that the sound clip is an object that uses the new persistent storage faciliti
es provided in COM. Suppose the user opens the spreadsheet, opens the sound clip
, makes some editing changes, then closes the sound clip at which point the chan
ges are updated in the spreadsheet storage set aside for the sound clip. Now, at
this instant, the user has a choice: save the spreadsheet or close the spreadsh
eet without saving. Either way, the next time the user opens the spreadsheet, th
e sound clip had better be in the appropriate state. This implies that at the in
stant before the save vs. close decision was made, both the old and the new vers
ions of the sound clip had to exist. Further, since large objects are precisely
the ones that are expensive in time and space to copy, the new version should ex
ist as a set of differences from the old.
The central issue is whose responsibility it is to keep track of the two version
s. The client (the spreadsheet in this example) had the old version to begin wit
h, so the question really boils down to how and when does the object (sound clip
) communicate the new version to the spreadsheet. Applications today are in gene
ral already designed to keep edits separate from the persistent copy of an objec
t until such time as the user does a save or update. Update time is thus the ear
liest time at which the transfer should occur. The latest is immediately before
the client saves itself. The most appropriate time seems to be one of these two
extremes; no intermediate time has any discernible advantage.
COM specifies that this communication happens at the earlier time. When asked to
update edits back to the client, an object using the new persistence support wi
ll write any changes to its storage) exactly as if it were doing a save to its o
wn storage completely outside the client. It is the responsibility of the client
to keep these changes separate from the old version until it does a save (commi
t) or close (revert). Transacted mode on IStorage makes dealing with this requir
ement easy and efficient.
The transaction on each storage is nested in the transaction of its parent stora
ge. Think of the act of committing a transaction on an IStorage instance as publi
shing changes one more level outwards. Inner objects publish changes to the trans
action of the next object outwards; outermost objects publish changes permanentl
y into the file system.
Let s examine for a moment the implications of using instead the second option, wh
ere the object keeps all editing changes to itself until it is known that the us
er wants to commit the client (save the file). This may happen many minutes afte
r the contained object was edited. COM must therefore allow for the possibility
that in the interim time period the user closed the server used to edit the obje
ct, since such servers may consume significant system resources. To implement th
is second option, the server must presumably keep the changes to the old version
around in a set of temporary files (remember, these are potentially big objects
). At the client s commit time, every server would have to be restarted and asked
to incorporate any changes back onto its persistent storage. This could be very
time consuming, and could significantly slow the save operation. It would also c
ause reliability concern in the user s mind: what if for some reason (such as memo
ry resources) a server cannot be restarted? Further, even when the client is clo
sed without saving, servers have to be awakened to clean up their temporary file
s. Finally, if a object is edited a second time before the client is committed,
in this option its the client can only provide the old, original storage, not th
e storage that has the first edits. Thus, the server would have to recognize on
startup that some edits to this object were lying around in the system. This is
an awkward burden to place on servers: it amounts to requiring that they all sup
port the ability to do incremental auto-save with automatic recovery from crashe
s. In short, this approach would significantly and unacceptably complicate the r
esponsibilities of the object implementors.
To that end, it makes the most sense that the standard COM implementation of the
storage system support transactioning through IStorage and possibly IStream.
3.9.6 Browsing Elements
By its nature, COM s structured storage separates applications from the exact layo
ut of information within a given file. Every element of information in that file
is access using functions and interfaces implemented by COM. Because this imple
mentation is central, a file generated by some application using this structure
can be browsed by some other piece of code, such as a system shell. In other wor
ds, any piece of code in the system can use COM to browse the entire hierarchy o
f elements within any structured file simply by navigating with the IStorage int
erface functions which provide directory-like services. If that piece of code al
so knows the format and the meaning of a specific stream that has a certain name
, it could also open that stream and make use of the information in it, without
having to run the application that wrote the file.
This is a powerful enabling technology for operating system shells that want to
provide rich query tools to help end users look for information on their machine
or even on a network. To make it really happen requires standards for certain s
tream names and the format of those streams such that the system shell can open
the stream and execute queries against that information. For example, consider w
hat is possible if all applications created a stream called Summary Information un
derneath the root storage element of the file. In this stream the application wo
uld write information such as the author of the document, the create/modify/last
saved time-stamps, title, subject, keywords, comments, a thumbnail sketch of th
e first page, etc. Using this information the system shell could find any docume
nts that a certain user write before a certain date or those that contained subj
ect matter matched against a few keywords. Once those documents are found, the s
hell can then extract the title of the document along with the thumbnail sketch
and give the user a very engaging display of the search results.
This all being said, in the general the actual utility of this capability is per
haps significantly less than what one might first imagine. Suppose, for example,
that I have a structured storage that contains some word processing document wh
ose semantics and persistent representation I am unaware of, but which contains
some number of contained objects, perhaps the figures in the document, that I ca
n identify by their being stored and tagged in contained sub-storages. One might
naively think that it would be reasonable to be able to walk in and browse the
figures from some system-provided generic browsing utility. This would indeed wo
rk from a technical point of view; however, it is unlikely to be useable from a
user interface perspective. The document may contain hundreds of figures, for ex
ample, that the user created and thinks about not with a name, not with a number
, but only in the relationship of a particular figure to the rest of the documen
t s information. With what user interface could one reasonably present this list o
f objects to the user other than as some add-hoc and arbitrarily-ordered sequenc
e? There is, for example, no name associated with each object that one could use
to leverage a file-system directory-browsing user interface design. In general,
the content of a document can only be reasonably be presented to a human being
using a tool that understands the semantics of the document content, and thus ca
n show all of the information therein in its appropriate context.
3.9.7 Persistent Objects
Because COM allows an object to read and write itself to storage, there must be
a way through which the client tells objects to do so. The way is, of course, ad
ditional interfaces that form a storage contract between the client and objects.
When a client wants to tell and object to deal with storage, it queries the obj
ect for one of the persistence-related interfaces, as suits the context. The int
erfaces that objects can implement, in any combination, are described below:
IPersistStorage Object can read and write its persistent state to a storage obje
ct. The client provides the object with an IStorage pointer through this interfa
ce. This is the only IPersist* interface that includes semantics for incremental
access.
IPersistStream Object can read and write its persistent state to a stream objec
t. The client provides the object with an IStream pointer through this interface
.
IPersistFile Object can read and write its persistent state to a file on the
underlying system directly. This interface does not involve IStorage or IStream
unless the underlying file is itself access through these interfaces, but the IP
ersistFile itself has no semantics relating to such structures. The client simpl
y provides the object with a filename and orders to save or load; the object doe
s whatever is necessary to fulfill the request.
These interfaces and the rules governing them are described in Chapter 14.
3.10 Persistent, Intelligent Names: Monikers
To set the context for why Persistent, Intelligent Names are an important technolo
gy in COM, think for a moment about a standard, mundane file name. That file nam
e refers to some collection of data that happens to be stored on disk somewhere.
The file name describes the somewhere. In that sense, the file name is really a
name for a particular object of sorts where the object is defined by the data in
the file.
The limitation is that a file name by itself is unintelligent; all the intellige
nce about what that filename means and how it gets used, as well as how it is st
ored persistently if necessary, is contained in whatever application is the clie
nt of that file name. The file name is nothing more than some piece of data in t
hat client. This means that the client must have specific code to handle file na
mes. This normally isn t seen as much of a problem most applications can deal with f
iles and have been doing so for a long time.
Now introduce some sort of name that describes a query in a database. The introd
uce others that describe a file and a specific range of data within that file, s
uch as a range of spreadsheet cells or a paragraph is a document. Introduce yet
more than identify a piece of code on the system somewhere that can execute some
interesting operation. In a world where clients have to know what a name means
in order to use it, those clients end up having to write specific code for each
type of name causing that application to grow monolithically in size and complex
ity. This is one of the problems that COM was created to solve.
In COM, therefore, the intelligence of how to work with a particular name is enc
apsulated inside the name itself, where the name becomes an object that implemen
ts name-related interfaces. These objects are called monikers. A moniker impleme
ntation provides an abstraction to some underlying connection (or binding ) mechani
sm. Each different moniker class (with a different CLSID) has its own semantics
as to what sort of object or operation it can refer to, which is entirely up to
the moniker itself. A section below describes some typical types of monikers. Wh
ile a moniker class itself defines the operations necessary to locate some gener
al type of object or perform some general type of action, each individual monike
r object (each instantiation) maintains its own name data that identifies some o
ther particular object or operation. The moniker class defines the functionality
; a moniker object maintains the parameters.
With monikers, clients always work with names through an interface, rather than
directly manipulating the strings (or whatever) themselves. This means that when
ever a client wishes to perform any operation with a name, it calls some code to
do it instead of doing the work itself. This level of indirection means that th
e moniker can transparently provide a whole host of services, and that the clien
t can seamlessly interoperate over time with various different moniker implement
ations which implement these services in different ways.
3.10.1 Moniker Objects
A moniker is simply an object that supports the IMoniker interface. IMoniker int
erface includes the IPersistStream interface; thus, monikers can be saved to and l
oaded from streams. The persistent form of a moniker includes the data comprisin
g its name and the CLSID of its implementation which is used during the loading
process. This allows new kinds of monikers to be created transparently to client
s.
The most basic operation in the IMoniker interface is that of binding to the obj
ect to which it points. The binding function in IMoniker takes as a parameter th
e interface identifier by which the client wishes to talk to the bound object, r
uns whatever algorithm is necessary in order to locate the object, then returns
a pointer of that interface type to the client. The client can also ask to bind
to the object s storage (for example, the IStorage containing the object) if desir
ed, instead of to the running object through a slightly different IMoniker funct
ion. As binding may be an expensive and time-consuming process, a client can con
trol how long it is willing to wait for the binding to complete. Binding also ta
kes place inside a specific bind context that is given to the moniker. Such a cont
ext enables the binding process overall to be more efficient by avoiding repeate
d connections to the same object.
A moniker also supports an operation called reduction through which it re-writes i
tself into another equivalent moniker that will bind to the same object, but doe
s so in a more efficient way. This capability is useful to enable the constructi
on of user-defined macros or aliases as new kinds of moniker classes (such that
when reduced, the moniker to which the macro evaluates is returned) and to enabl
e construction of a kind of moniker which tracks data as it moves about (such th
at when reduced, the new moniker contains a reference to the new location). Chap
ter 15 will expand on the reduction concept.
Each moniker class can store arbitrary data its persistent representation, and c
an run arbitrary code at binding time. The client therefore only knows each moni
ker by the presence of a persistent representation and whatever label the client
wishes to assign to each moniker. For example, a spreadsheet as a client may ke
ep, from the user s perspective, a list of links to other spreadsheets where, in fac
t, each link was an arbitrary label for a moniker (regardless of whether the mon
iker is loaded or persistently on disk at the moment) where the moniker manages
the real identity of the linked data. When the spreadsheet wants to resolve a li
nk for the user, it only has to ask the moniker to bind to the object. After the
binding is complete, the spreadsheet then has an interface pointer for the link
ed object and can talk to it directly the moniker falls out of the picture as its
job is complete.
The label assigned to a moniker by a client does not have to be arbitrary. Monik
ers support the ability to produce a display name for whatever object they represe
nt that is suitable to show to an end user. A moniker that maintains a file name
(such that it can find an application to load that file) would probably just us
e the file name directly as the display name. Other monikers for things such as
a query may want to provide a display name that is a little more readable than s
ome query languages.
3.10.2 Types of Monikers
As some of the examples above has hinted, monikers can have many types, or class
es, depending on the information they contain and the type of objects they can r
efer to. A moniker class is really defined by the information it persistently ma
intains and the binding operation is uses on that information. COM specifies six
moniker classes: generic composite, class, file, item, anti, and ponter.
The generic composite moniker is special in two ways. First, its persistent data
is completely composed of the persistent data of other monikers, that is, a com
posite moniker is a collection of other monikers. Second, binding a composite mo
niker simply tells the composite to bind each moniker it contains in sequence. S
ince the composite s behavior and persistent state is defined by other monikers, i
t is a standard type of moniker that works identically on any host system; the c
omposite is generic because it has no knowledge of its pieces except that they a
re monikers. Chapter 15 describes the generic composite in more detail.
So what other types of monikers can go in a composite? Virtually any other type
(including other composite monikers!). However, other types of monikers are not
so generic and have more dependency on the underlying operating system or the sc
enarios in which such a moniker is used.
The other five other monikers defined by COM class, file, item, anti, pointer have b
een used to help implement linked objects in OLE. A file moniker, for example, mai
ntains a file name as its persistent data and its binding process is one of loca
ting an application that can load that file, launching the application, and retr
ieving from it an IPersistFile interface through which the file moniker can ask
the application to load the file. Item monikers are used to describe smaller por
tions of a file that might have been loaded with a file moniker, such as a speci
fic sheet of a three-dimensional spreadsheet or a range of cells in that sheet.
To link to a specific cell range in a specific sheet of a specific file, the singl
e moniker used to describe the link is a generic composite that is composed with
a file moniker and two item monikers as illustrated in Figure 3-20. Each monike
r in the composite is one step in the path to the final source of the link.
Figure 3-20: A composite moniker that is composed with a file moniker and two it
em monikers
to describe the source of a link which is a cell range in a specific sheet of a
spreadsheet file.
More complete descriptions of the class, file, item, anti, and pointer monikers
are provided in Chapter 15 as examples of how monikers can be used. But monikers
can represent virtually any type of information and operation, and are not limi
ted to this basic set of COM defined monikers.
3.10.3 Connections and Reconnections
How does a client come by a moniker in the first place? In other words, how does
a client establish a connection to some object and obtain a moniker that descri
bes that connection? The answer depends on the scenario involved but is generall
y one of two ways. First, the source of the object may have created a moniker an
d made it available for consumption through a data transfer mechanism such (in t
he workstation case) as a clipboard or perhaps a drag & drop operation. Second,
the client may have enough knowledge about a particular moniker class that it ca
n synthesize a moniker for some object using other known information such that t
he client can forget about that specific information itself and thereafter deal
only with monikers. So regardless of how a client obtains a moniker, it can simp
ly ask the moniker to bind to establish a connection to the object referred to b
y the moniker.
Binding a moniker does not always mean that the moniker must run the object itse
lf. The object might already be running within some appropriate scope (such as t
he current desktop) by the time the client wants to bind the moniker to it. Ther
efore the moniker need only connect to that running object.
COM supports this scenario through two mechanisms. The first is the Running Obje
ct Table in which objects register themselves and their monikers when they becom
e running. This table is available to all monikers as they attempt to bind if a mo
niker sees that a matching moniker in the table, it can quickly connect to the a
lready running object.
3.11 Uniform Data Transfer
Just as COM provides interfaces for dealing with storage and object naming, it a
lso provides interfaces for exchanging data between applications. So built on to
p of both COM and the Persistent Storage technology is Uniform Data Transfer, wh
ich provides the functionality to represent all data transfers through a single
implementation of a data object. Data objects implement an interface called IDat
aObject which encompasses the standard operations of get/set data and query/enum
erate formats as well as functions through which a client of a data object can e
stablish a notification loop to detect data changes in the object. In addition,
this technology enables use of richer descriptions of data formats and the use o
f virtually any storage medium as the transfer medium.
3.11.1 Isolation of Transfer Protocols
The Uniform in the name of this technology arose from the fact that the IDataObjec
t interface separates all the common exchange operations from what is called a t
ransfer protocol. Existing protocols include facilities such as a clipboard or a dr
ag & drop feature as well as OLE compound documents. Uniform Data Transfer is a g
eneric service with applications throught COM technologies. With Uniform Data Tr
ansfer, all protocols are concerned only with exchanging a pointer to an IDataOb
ject interface. The source of the data the server need only implement one data objec
t which is usable in any exchange protocol and that s it. The consumer the client need
only implement one piece of code to request data from a data object once it rec
eives an IDataObject pointer from any protocol. Once the pointer exchange has oc
curred, both sides deal with data exchange in a uniform fashion, through IDataOb
ject.
This uniformity not only reduces the code necessary to source or consume data, b
ut also greatly simplifies the code needed to work with the protocol itself. Bef
ore COM was first implemented in OLE 2, each transfer protocol available on Micr
osoft Windows had its own set of functions that tightly bound the protocol to th
e act of requesting data, and so programmers had to implement specific code to h
andle each different protocol and exchange procedure. Now that the exchange func
tionality is separated from the protocol, dealing with each protocol requires on
ly a minimum amount of code which is absolutely necessary for the semantics of t
hat protocol.
3.11.2 Data Formats and Transfer Mediums
Before Uniform Data Transfer, virtually all standard protocols for data transfer
were quite weak at describing the data being transferred and usually required t
he exchange to occur through global memory. This was especially true on Microsof
t Windows: the format was described by a single 16-bit clipboard format and the me
dium was always global memory.
The problem with the clipboard format is that it can only describe the structure o
f the data, that is, identify the layout of the bits. For example, the format CF
_TEXT describes ASCII text. CF_BITMAP describes a device-dependent bitmap of so
many colors and such and such dimensions, but was incapable of describing the ac
tual device it depends upon. Furthermore, none of these formats gave any indicat
ion of what was actually in the data such as the amount of detail whether a bitmap
or metafile contained the full image or just a thumbnail sketch.
The problem with always using global memory as a transfer medium is apparent whe
n large amounts of data are exchanged. Unless you have a machine with an obnoxio
us amount of memory, an exchange of, say, a 20MB scanned true-color bitmap throu
gh global memory is going to cause considerable swapping to virtual memory on th
e disk. Restricting exchanges to global memory means that no application can cho
ose to exchange data on disk when it will usually reside on disk even when being
manipulated and will usually use virtual memory on disk anyway. It would be muc
h more efficient to allow the source of that data to indicate that the exchange
happens on disk in the first place instead of forcing 20MB of data through a vir
tual-memory bottleneck to just have it end up on disk once again.
Further, latency of the data transfer is sometimes an issue, particularly in net
work situations. One often needs or wants to start processing the beginning of a
large set of data before the end the data set has even reached the destination
machine. To accomplish this, some abstraction on the medium by which the data is
transferred is needed.
To solve these problems, COM defines two new data structures: FORMATETC and STGM
EDIUM. FORMATETC is a better clipboard format, for the structure not only contai
ns a clipboard format but also contains a device description, a detail descripti
on (full content, thumbnail sketch, iconic, and as printed ), and a flag indicating
what storage device is used for a particular rendering. Two FORMATETC structure
s that differ only by storage medium are, for all intents and purposes, two diff
erent formats. STGMEDIUM is then the better global memory handle which contains
a flag indicating the medium as well as a pointer or handle or whatever is neces
sary to access that actual medium and get at the data. Two STGMEDIUM structures
may indicate different mediums and have different references to data, but those
mediums can easily contain the exact same data.
So FORMATETC is what a consumer (client) uses to indicate the type of data it wa
nts from a data source (object) and is used by the source to describe what forma
ts it can provide. FORMATETC can describe virtually any data, including other ob
jects such a monikers. A client can ask a data object for an enumeration of its
formats by requesting the data object s IEnumFORMATETC interface. Instead of an ob
ject blandly stating that it has text and a bitmap it can say it has A device-indep
endent string of text that is stored in global memory and a thumbnail sketch bitma
p rendered for a 100dpi dot-matrix printer which is stored in an IStorage object
. This ability to tightly describe data will, in time, result in higher quality p
rinter and screen output as well as more efficiency in data browsing where a thu
mbnail sketch is much faster to retrieve and display than a full detail renderin
g.
STGMEDIUM means that data sources and consumers can now choose to use the most e
fficient exchange medium on a per-rendering basis. If the data is so big that it
should be kept on disk, the data source can indicate a disk-based medium in it s
preferred format, only using global memory as a backup if that s all the consumer
understands. This has the benefit of using the best medium for exchanges as the
default, thereby improving overall performance of data exchange between applicat
ions if some data is already on disk, it does not even have to be loaded in order
to send it to a consumer who doesn t even have to load it upon receipt. At worst,
COM s data exchange mechanisms would be as good as anything available today where
all transfers restricted to global memory. At best, data exchanges can be effect
ively instantaneous even for large data.
Note that two potential storage mediums that can be used in data exchange are st
orage objects and stream objects. Therefore Uniform Data Transfer as a technolog
y itself builds upon the Persistent Storage technology as well as the basic COM
foundation. Again, this enables each piece of code in an application to be lever
aged elsewhere.
3.11.3 Data Selection
A data object can vary to a number of degrees as to what exact data it can excha
nge through the IDataObject interface. Some data objects, such as those represen
ting the clipboard or those used in a drag & drop operation, statically represen
t a specific selection of data in the source, such as a range of cells in a spre
adsheet, a certain portion of a bitmap, or a certain amount of text. For the lif
e of such static data objects, the data underneath them does not change.
Other types of data objects, however, may support the ability to dynamically cha
nge their data set. This ability, however, is not represented through the IDataO
bject interface itself. In other words, the data object has to implement some ot
her interface to support dynamic data selection. An example of such objects are
those that support COM for Real-Time Market Data (WOSA/XRT) specification. COM f
or Real-Time Market Data uses a data object and the IDataObject interface for ex
change of data, but use the IDispatch interface from Automation to allow consume
rs of the data to dynamically instruct the data object to change its working set
. In other words, the Automation technology (built on COM but not part of COM it
self) allows the consumer to identify the specific market issues and the informa
tion on those issues (high, low, volume, etc.) that it wants to obtain from the
data object. In response, the data object internally determines where to retriev
e that data and how to watch for changes in it. The data object then notifies th
e consumer of changes in the data through COM s Notification mechanism.
3.11.4 Notification
Consumers of data from an external source might be interested in knowing when da
ta in that source changes. This requires some mechanism through which a data obj
ect itself asynchronously notifies a client connected to it of just such an even
t at which point a client can remember to ask for an updated copy of the data wh
en it later needs such an update.
COM handles notifications of this kind through an object called an advise sink w
hich implements an interface called IAdviseSink. This sink is a body that absorb
s asynchronous notifications from a data source. The advise sink object itself,
and the IAdviseSink interface is implemented by the consumer of data which then
hands an IAdviseSink pointer to the data object in question. When the data objec
t detects a change, it then calls a function in IAdviseSink to notify the consum
er as illustrated in Figure 3-21.
Figure 3-21: A consumer of data implements an object with the IAdviseSink interf
ace
through which data objects notify that consumer of data changes.
This is the most frequent situation where a client of one object, in this case t
he consumer, will itself implement an object to which the data object acts as a
client itself. Notice that there are no circular reference counts here: the cons
umer object and the advise sink have different COM object identities, and thus s
eparate reference counts. When the data object needs to notify the consumer, it
simply calls the appropriate member function of IAdviseSink.
So IAdviseSink is more of a central collection of notifications of interest to a
number of other interfaces and scenarios outside of IDataObject and data exchan
ge. It contains, for example, a function for the event of a view change, that is,
when a particular view of data changes without a change in the underlying data.
In addition, it contains functions for knowing when an object has saved itself,
closed, or been renamed. All of these other notifications are of particular use
in compound document scenarios and are used in OLE, but not COM proper. Chapter
16 will describe these functions but the mechanisms by which they are called are
not part of COM and are not covered in this specification. Interested readers s
hould refer to the OLE 2 Specifications from Microsoft.
Finally, data objects can establish notifications with multiple advise sinks. CO
M provides some assistance for data objects to manage an arbitrary number of IAd
viseSink pointers through which the data object can pass each pointer to COM and
then tell COM when to send notifications. COM in turn notifies all the advise s
inks it maintains on behalf of the data object.
3.12 Type Libraries
Type libraries are streams (typically stored in files or as resources attached t
o executables) that include information about types exposed by an COM component.
A type library is a binary representation of the interface definition language
(IDL) and can contain any of the following:
Information about data types, such as aliases, enumerations, structures, or unio
ns.
Descriptions of one or more objects, such as a module, interface, IDispatch base
d interface (dispinterface), or component object class (coclass). Each of these
descriptions is commonly referred to as a typeinfo.
References to type descriptions from other type libraries.
Type libraries are mapped together via the Registry. In this manner, type librar
ies are COM s interface repository.
By including the type library with a product, the information about the objects
in the library can be made available to the users of the applications and progra
mming tools. In addition COM provides a marshaling engine that can marshal any C
OM interface described in a type library. See Chapter 9, Interface Remoting, for
details on how type libraries can be used for marshaling.
Type libraries can be shipped in any of the following forms:
A stand-alone binary file. Type library files typically have the extension .tlb.
A resource attached to a binary executable (e.g. a DLL or EXE). On the Win32 pla
tform this resource should have the type TypeLib and an integer identifier. It m
ust be declared in the resource (.rc) file as follows:
1 typelib mylib1.tlb
2 typelib mylib2.tlb
There can be multiple type library resources attached to a binary. Developers sh
ould use the resource compiler to add the type library file to their own DLL. A
DLL with one or more type library resources typically has the file extension .ol
b (object library).
Object browsers, compilers, and similar tools access type libraries through the
interfaces ITypeLib, ITypeLib2, ITypeInfo, ITypeInfo2 and ITypeComp. Type librar
y generation tools (such as the MIDL compiler) can be created using the interfac
es ICreateTypeLib, ICreateTypeLib2, ICreateTypeInfo and ICreateTypeInfo2.
3.13 Automation
Automation (formerly called OLE Automation) is a technology that allows software
components to expose their unique features to scripting tools and other applica
tions. Using Automation, you can:
Create applications and programming tools that expose objects.
Create and manipulate objects exposed in one application from another applicatio
n.
Create tools that access and manipulate objects. These tools can include embedde
d macro languages, external programming tools, object browsers, and compilers.
COM objects that expose their features via Automation do so by implementing the
IDispatch interface. Automation is covered in depth in chapter 18.
Part II: Component Object Model Programming Interface
Part II contains the programming interface to COM, the suite of interfaces and A
PIs by which Component Object Model software is implemented and used. Each chapt
er is structured with technical overview material up front, followed by a sectio
n with full specifications for each library function and interface.
4. Objects And Interfaces
This chapter describes in detail the heart of COM: the notion of interfaces and
their relationships to the objects on which they are implemented. More specifica
lly, this chapter covers what an interface is (technically), interface calling c
onventions, object and interface identity, the fundamental interface called IUnk
nown, and COM s error reporting mechanism. In addition, this chapter describes how
an object implements one or more interfaces as well as a special type of object
called the enumerator which comes up in various contexts in COM.
As described in Part I, the COM Library provides the fundamental implementation
locator services to clients and provides all the necessary glue to help clients
communicate transparently with object regardless of where those objects execute:
in-process, out-of-process, or on a different machine entirely. All servers exp
ose their object s services through interfaces, and COM provides implementations o
f the proxy and stub objects that make communication possible between processes and
machines where RPC is necessary.
However, as we ll see in this chapter and those that follow, the COM Library also
provides fundamental API functions for both clients and servers or, in general,
any piece of code that uses COM, application or not. These API functions will be
described in the context of where other applications or DLLs use them. A COM im
plementor reading this document will find the specifications for each function o
ffset clearly from the rest of the text. These functions are implemented in the
COM Library to standardize the parts of this specification that applications sho
uld not have to implement nor would want to implement. Through the services of t
he COM Library, all clients can make use of all objects in all servers, and all
servers can expose their objects to all clients. Only by having a standard is th
is possible, and the COM Library enforces that standard by doing most of the har
d work.
Not all the COM Library functions are truly fundamental. Some are just convenien
t wrappers to common sequences of other calls, sometimes called helper functions.
Others exist simply to maintain global lists for the sake of all applications. O
thers just provide a solid implementation of functions that could be implemented
in every application, but would be tedious and wasteful to do so.
4.1 Interfaces
An interface, in the COM definition, is a contract between the user, or client,
of some object and the object itself. It is a promise on the part of the object
to provide a certain level of service, of functionality, to that client. Chapter
s 1 and 2 have already explained why interfaces are important COM and the whole
idea of an object model. This chapter will now fill out the definition of an int
erface on the technical side.
4.1.1 The Interface Binary Standard
Technically speaking, an interface is some data structure that sits between the
client s code and the object s implementation through which the client requests the
object s services. The interface in this sense is nothing more than a set of membe
r functions that the client can call to access that object implementation. Those
member functions are exposed outside the object implementor application such th
at clients, local or remote, can call those functions.
The client maintains a pointer to the interface which is, in actuality, a pointe
r to a pointer to an array of pointers to the object s implementations of the inte
rface member functions. That s a lot of pointers; to clarify matters, the structur
e is illustrated in Figure 4-1.
Figure 4-1: The interface structure: a client has a pointer to an interface whic
h is
a pointer to a pointer to an array (table) of pointers to the object s implementat
ion.
By convention the pointer to the interface function table is called the pVtbl po
inter. The table itself is generally referred to with the name vtbl for virtual f
unction table.
On a given implementation platform, a given method in a given interface (a parti
cular IID, that is) has a fixed calling convention; this is decoupled from the i
mplementation of the interface. In principle, this decision can be made on a met
hod by method basis, though in practice on a given platform virtually all method
s in all interfaces use the same calling convention
In contrast, just for note, COM API functions (not interface members) use the st
andard host system-call calling convention, which on both Microsoft Win16 and Wi
n32 is the __far __pascal sequence.
Finally, and quite significantly, all strings passed through all COM interfaces
(and, at least on Microsoft platforms, all COM APIs) are Unicode strings. There
simply is no other reasonable way to get interoperable objects in the face of (i
) location transparency, and (ii) a high-efficiency object architecture that doe
sn t in all cases intervene system-provided code between client and server. Furthe
r, this burden is in practice not large.
When calling member functions, the caller must include an argument which is the
pointer to the object instance itself. This is automatically provided in C++ com
pilers and completely hidden from the caller. The Microsoft Object Mapping speci
fies that this pointer is pushed very last, immediately before the return addres
s. The location of this pointer is the reason that the pIInterface pointer appea
rs at the beginning of the argument list of the equivalent C function prototype:
it means that the layout in the stack of the parameters to the C function proto
type is exactly that expected by the member function implemented in C++, and so
no re-ordering is required.
Usually the pointer to the interface itself is the pointer to the entire object
structure (state variables, or whatever) and that structure immediately follows
the pVtbl pointer memory as shown in Figure 4-2.
Figure 4-2: Convention places object data following the pointer
to the interface function table.
Since the pVtbl is received as the this pointer in the interface function, the i
mplementor of that function knows which object is being called an object is, after
all, some structure and functions to manipulate that structure, and the interfa
ce definition here supplies both.
In any case, this vtbl structure is called a binary standard because on the binary
level, the structure is completely determined by the particular interface being
used and the platform on which it is being invoked. It is independent of the pr
ogramming language or tool used to create it. In other words, a program can be w
ritten in C to generate this structure to match what C++ does automatically. For
more details, see the section C vs. C++ below. You could even create this structu
re in assembly if so inclined. Since compilers for other languages eventually re
duce source code to assembly (as is the compiler itself) it is really a matter f
or compiler vendors to support this structure for languages such as Pascal, COBO
L, Smalltalk, etc. Thus COM clients, objects, and servers can be written in any
languages with appropriate compiler support.
Note that it is technically legal for the binary calling conventions for a given
interface to vary according the particular implementation platform in question,
though this flexibility should be exercised by COM system implementors only wit
h very careful attention to source portability issues. It is the case, for examp
le, that on the Macintosh, the pVtbl pointer does not point to the first functio
n in the vtbl, but rather to a dummy pointer slot (which is ignored) immediately
before the first function; all the function pointers are thus offset by an inde
x of one in the vtbl.
An interface implementor is free to use the memory before and beyond the as-speci
fied-by-the-standard vtbl for whatever purpose he may wish; others cannot assume
anything about such memory.
4.1.2 Interface Definition and Identity
Every interface has a name that serves as the programmatic compile-time type in
code that uses that interface (either as a client or as an object implementor).
The convention is to name each interface with a capital I followed by some descrip
tive label that indicates what functionality the interface encompasses. For exam
ple, IUnknown is the label of the interface that represents the functionality of
an object when all else about that object is unknown.
These programmatic types are defined in header files provided by the designer of
the interface through use of the Interface Description Language (IDL, see next
section). For C++, an interface is defined as an abstract base, that is, a struc
ture containing nothing but pure virtual member functions. This specification uses
C++ notation to express the declaration of an interface. For example, the IUnkn
own interface is declared as:
interface IUnknown
{
virtual HRESULT QueryInterface(IID& iid, void** ppv) =0;
virtual ULONG AddRef(void) =0;
virtual ULONG Release(void) =0;
};
where virtual and =0 describe the attribute of a pure virtual function and where the
nterface keyword is defined as:
#define interface struct
The programmatic name and definition of an interface defines a type such that an
application can declare a pointer to an interface using standard C++ syntax as
in IUnknown *.
In addition, this specification as a notation makes some use of the C++ referenc
e mechanism in parameter passing, for example:
QueryInterface(const IID& iid, void**ppv);
Usually const <type>& is written as REF<type> as in REFIID for convenience. As you m
ight expect, this example would appear in a C version of the interface as a para
meter of type:
const IID * const
Input parameters passed by reference will themselves be const, as shown here. In
-out or out- parameters will not.
The use of the interface keyword is more a documentation technique than any requ
irement for implementation. An interface, as a binary standard, is definable in
any programming language as shown in the previous section. This specification s us
e of C++ syntax is just a convenience. Also, for ease of reading, this specifica
tion generally omits parameter types in code fragments such as this but does doc
ument those parameters and types fully with each member function. Types do, of c
ourse, appear in header files with interfaces.
It is very important to note that the programmatic name for an interface is only
a compile-time type used in application source code. Each interface must also h
ave a run-time identifier. This identifier enables a caller to query (via QueryI
nterface) an object for a desired interface. Interface identifiers are GUIDs, th
at is, globally-unique 16 byte values, of type IID. The person who defines the i
nterface allocates and assigns the IID as with any other GUID, and he informs ot
hers of his choice at the same time he informs them of the interface member func
tions, semantics, etc. Use of a GUID for this purpose guarantees that the IID wi
ll be unique in all programs, on all machines, for all time, the run-time identi
fier for a given interface will in fact have the same 16 byte value.
Programmers who define interfaces convey the interface identifier to implementor
s or clients of that interface along with the other information about the interf
ace (in the form of header files, accompanying semantic documentation, etc.). To
make application source code independent of the representation of particular in
terface identifiers, it is standard practice that the header file defines a cons
tant for each IID where the symbol is the name of the interface prefixed with IID
_ such that the name can be derived algorithmically. For example, the interface I
Unknown has an identifier called IID_IUnknown.
For brevity in this specification, this definition will not be repeated with eac
h interface, though of course it is present in the COM implementation.
4.1.3 Defining Interfaces: IDL
The Interface Description Language (IDL) is based on the Open Software Foundatio
n (OSF) Distributed Computing Environment (DCE) specification for describing int
erfaces, operations, and attributes to define remote procedure calls. COM extend
s the IDL to support distributed objects.
A designer can define a new custom interface by writing an interface definition
file. The interface definition file uses the IDL to describe data types and memb
er functions of an interface. The interface definition file contains the informa
tion that defines the actual contract between the client application and server
object. The interface contract specifies three things:
Language binding defines the programming model exposed to the application program
using a particular programming language.
Application binary interface specifies how consumers and providers of the interfac
e interoperate on a particular target platform.
Network interface defines how client applications access remote server objects via
the network.
After completing the interface definition file, the programmer runs the IDL comp
iler to generate the interface header and the source code necessary to build the
interface proxy and interface stub that the interface definition file describes
. The interface header file is made available so client applications can use the
interface. The interface proxy and interface stub are used to construct the pro
xy and stub DLLs. The DLL containing the interface proxy must be distributed wit
h all client applications that use the new interface. The DLL containing the int
erface stub must be distributed with all server objects that provide the new int
erface.
It is important to note that the IDL is a tool that makes the job of defining in
terfaces easier for the programmer, and is one of possibly many such tools. It i
s not the key to COM interoperability. COM compliance does not require that the
IDL compiler be used. However, as IDL is broadly understood and used, it provide
s a convenient means by which interface specifications can be conveyed to other
programmers.
4.1.4 C vs. C++ vs. ...
This specification documents COM interfaces using C++ syntax as a notation but (
again) does not mean COM requires that programmers use C++, or any other particu
lar language. COM is based on a binary interoperability standard, rather than a
language interoperability standard. Any language supporting structure or record type
s containing double-indirected access to a table of function pointers is suitabl
e.
However, this is not to say all languages are created equal. It is certainly tru
e that since the binary vtbl standard is exactly what most C++ compilers generat
e on PC and many RISC platforms, C++ is a convenient language to use over a lang
uage such as C.
That being said, COM can declare interface declarations for both C++ and C (and
for other languages if the COM implementor desires). The C++ definition of an in
terface, which in general is of the form:
interface ISomeInterface
{
virtual RET_T MemberFunction(ARG1_T arg1, ARG2_T arg2 /*, etc */);
[Other member functions]
...
};
then the corresponding C declaration of that interface looks like
typedef struct ISomeInterface
{
ISomeInterfaceVtbl * pVtbl;
} ISomeInterface;
typedef struct ISomeInterfaceVtbl ISomeInterfaceVtbl;
struct ISomeInterfaceVtbl
{
RET_T (*MemberFunction)(ISomeInterface * this, ARG1_T arg1,
ARG2_T arg2 /*, etc */);
[Other member functions]
} ;
This example also illustrates the algorithm for determining the signature of C f
orm of an interface function given the corresponding C++ form of the interface f
unction:
Use the same argument list as that of the member function, but add an initial pa
rameter which is the pointer to the interface. This initial parameter is a point
er to a C type of the same name as the interface.
Define a structure type which is a table of function pointers corresponding to t
he vtbl layout of the interface. The name of this structure type should be the
name of the interface followed by Vtbl. Members in this structure have the same na
mes as the member functions of the interface.
The C form of interfaces, when instantiated, generates exactly the same binary s
tructure as a C++ interface does when some C++ class inherits the function signa
tures (but no implementation) from an interface and overrides each virtual funct
ion.
These structures show why C++ is more convenient for the object implementor beca
use C++ will automatically generate the vtbl and the object structure pointing t
o it in the course of instantiating an object. A C object implementor must defin
e and object structure with the pVtbl field first, explicitly allocate both obje
ct structure and interface Vtbl structure, explicitly fill in the fields of the
Vtbl structure, and explicitly point the pVtbl field in the object structure to
the Vtbl structure. Filling the Vtbl structure need only occur once in an applic
ation which then simplifies later object allocations. In any case, once the C pr
ogram has done this explicit work the binary structure is indistinguishable from
what C++ would generate.
On the client side of the picture there is also a small difference between using
C and C++. Suppose the client application has a pointer to an ISomeInterface on
some object in the variable psome. If the client is compiled using C++, then th
e following line of code would call a member function in the interface:
psome->MemberFunction(arg1, arg2, /* other parameters */);
A C++ compiler, upon noting that the type of psome is an ISomeInterface * will k
now to actually perform the double indirection through the hidden pVtbl pointer
and will remember to push the psome pointer itself on the stack so the implement
ation of MemberFunction knows which object to work with. This is, in fact, what
C++ compilers do for any member function call; C++ programmers just never see it
.
What C++ actually does is be expressed in C as follows:
psome->lpVtbl->MemberFunction(psome, arg1, arg2, /* other parameters */);
This is, in fact, how a client written in C would make the same call. These two
lines of code show why C++ is more convenient there is simply less typing and ther
efore fewer chances to make mistakes. The resulting source code is somewhat clea
ner as well. The key point to remember, however, is that how the client calls an
interface member depends solely on the language used to implement the client an
d is completely unrelated to the language used to implement the object. The code
shown above to call an interface function is the code necessary to work with th
e interface binary standard and not the object itself.
4.1.5 Remoting Magic Through Vtbls
The double indirection of the vtbl structure has an additional, indeed enormous,
benefit: the pointers in the table of function pointers do not need to point di
rectly to the real implementation in the real object. This is the heart of Locat
ion Transparency.
It is true that in the in-process server case, where the object is loaded direct
ly into the client process, the function pointers in the table are, in fact, the
actual pointers to the actual implementation. So a function call from the clien
t to an interface member directly transfers execution control to the interface m
ember function.
However, this cannot possibly work for local, let alone remote, object, because
pointers to memory are absolutely not sharable between processes. What must stil
l happen to achieve transparency is that the client continues to call interface
member functions as if it were calling the actual implementation. In other words
, the client uniformly transfers control to some object s member function by makin
g the call.
Figure 4-3: A client always calls interface members in some in-process object. I
f
the actual object is local or remote, the call is made to a proxy object which t
hen
makes a remote procedure call to the actual object.
So what member function actually executes? The answer is that the interface memb
er called is implemented by a proxy object that is always an in-process object t
hat acts on behalf of the object being called. This proxy object knows that the
actual object is running in a local or remote server and so it must somehow make
a remote procedure call, through a standard RPC mechanism, to that object as sh
own in Figure 4-3.
The proxy object packages up the function parameters in some data packets and ge
nerates an RPC call to the local or remote object. That packet is picked up by a
stub object in the server s process, on the local or a remote machine, which unpa
cks the parameters and makes the call to the real implementation of the member f
unction. When that function returns, the stub packages up any out-parameters and
the return value, sends it back to the proxy, which unpacks them and returns th
em to the original client. For exact details on how the proxy-stub and RPC mecha
nisms work, see Chapter 9.
The bottom line is that client and server always talk to each other as if everyt
hing was in-process. All calls from the client and all calls to the server do at
some point, in fact, happen in-process. But because the vtbl structure allows s
ome agent, like COM, to intercept all function calls and all returns from functi
ons, that agent can redirect those calls to an RPC call as necessary. All of thi
s is completely transparent to the client and server, hence Location Transparenc
y.
4.2 Globally Unique Identifiers
As mentioned earlier in this document, the GUID, from which are also obtained CL
SID, IIDs, and any other needed unique identifier, is a 128-bit, or 16-byte, val
ue. The term GUID as used in this specification is completely synonymous and int
erchangeable with the term UUID as used by the DCE RPC architecture; they are inde
ed one and the same notion. In binary terms, a GUID is a data structure defined
as follows, where DWORD is 32-bits, WORD is 16-bits, and BYTE is 8-bits:
typedef struct GUID {
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} GUID;
This structure provides applications with some way of addressing the parts of a
GUID for debugging purposes, if necessary. This information is also needed when
GUIDs are transmitted between machines of different byte orders.
For the most part, applications never manipulate GUIDs directly they are almost al
ways manipulated either as a constant, such as with interface identifiers, or as
a variable of which the absolute value is unimportant. For example, a client mi
ght enumerate all object classes registered on the system and display a list of
those classes to an end user. That user selects a class from the list which the
client then maps to an absolute CLSID value. The client does not care what that
value is it simply knows that it uniquely identifies the object that the user sele
cted.
The GUID design allows for coexistence of several different allocation technolog
ies, but the one by far most commonly used incorporates a 48-bit machine unique
identifier together with the current UTC time and some persistent backing store
to guard against retrograde clock motion. It is in theory capable of allocating
GUIDs at a rate of 10,000,000 per second per machine for the next 3240 years, en
ough for most purposes.
For further information regarding GUID allocation technologies, see pp585-592 of
[CAE RPC].
4.3 The IUnknown Interface
This specification has already mentioned the IUnknown interface many times. It i
s the fundamental interface in COM that contains basic operations of not only al
l objects, but all interfaces as well: reference counting and QueryInterface. Al
l interfaces in COM are polymorphic with IUnknown, that is, if you look at the f
irst three functions in any interface you see QueryInterface, AddRef, and Releas
e. In other words, IUnknown is base interface from which all other interfaces in
herit.
Any single object usually only requires a single implementation of the IUnknown
member functions. This means that by virtue of implementing any interface on an
object you completely implement the IUnknown functions. You do not generally nee
d to explicitly inherit from nor implement IUnknown as its own interface: when q
ueried for it, simply typecast another interface pointer into an IUnknown* which
is entirely legal with polymorphism.
In some specific situations, more notably in creating an object that supports ag
gregation, you may need to implement one set of IUnknown functions for all inter
faces as well as a stand-alone IUnknown interface. The reasons and techniques fo
r this are described in the Object Reusability section of Chapter 8.
In any case, any object implementor will implement IUnknown functions, and we ar
e now in a position to look at them in their precise terms.
4.3.1 Reference Counting
Objects accessed through interfaces use a reference counting mechanism to ensure
that the lifetime of the object includes the lifetime of references to it. This
mechanism is adopted so that independent components can obtain and release acce
ss to a single object, and not have to coordinate with each other over the lifet
ime management. In a sense, the object provides this management, so long as the
client components conform to the rules. Within a single component that is comple
tely under the control of a single development organization, clearly that organi
zation can adopt whatever strategy it chooses. The following rules are about how
to manage and communicate interface instances between components, and are a rea
sonable starting point for a policy within a component.
Note that the reference counting paradigm applies only to pointers to interfaces
; pointers to data are not referenced counted.
It is important to be very clear on exactly when it is necessary to call AddRef
and Release through an interface pointer. By its nature, pointer management is a
cooperative effort between separate pieces of code, which must all therefore co
operate in order that the overall management of the pointer be correct. The foll
owing discussion should hopefully clarify the rules as to when AddRef and Releas
e need to be called in order that this may happen. Some special reference counti
ng rules apply to objects which are aggregated; see the discussion of aggregatio
n in Chapter 8.
The conceptual model is the following: interface pointers are thought of as livi
ng in pointer variables, which for the present discussion will include variables
in memory locations and in internal processor registers, and will include both
programmer- and compiler-generated variables. In short, it includes all internal
computation state that holds an interface pointer. Assignment to or initializat
ion of a pointer variable involves creating a new copy of an already existing po
inter: where there was one copy of the pointer in some variable (the value used
in the assignment/initialization), there is now two. An assignment to a pointer
variable destroys the pointer copy presently in the variable, as does the destru
ction of the variable itself (that is, the scope in which the variable is found,
such as the stack frame, is destroyed).
Rule 1: AddRef must be called for every new copy of an interface pointer, and Re
lease called every destruction of an interface pointer except where subsequent r
ules explicitly permit otherwise.
This is the default case. In short, unless special knowledge permits otherwise,
the worst case must be assumed. The exceptions to Rule 1 all involve knowledge o
f the relationships of the lifetimes of two or more copies of an interface point
er. In general, they fall into two categories.
Category 1. Nested lifetimes
Category 2. Staggered overlapping lifetimes
In Category 1 situations, the AddRef A2 and the Release R2 can be omitted, while
in Category 2, A2 and R1 can be eliminated.
Rule 2: Special knowledge on the part of a piece of code of the relationships of
the beginnings and the endings of the lifetimes of two or more copies of an int
erface pointer can allow AddRef/Release pairs to be omitted.
The following rules call out specific common cases of Rule 2. The first two of t
hese rules are particularly important, as they are especially common.
Rule 2a: In-parameters to functions. The copy of an interface pointer which is p
assed as an actual parameter to a function has a lifetime which is nested in tha
t of the pointer used to initialize the value. The actual parameter therefore ne
ed not be separately reference counted.
Rule 2b: Out-parameters from functions, including return values. This is a Categ
ory 2 situation. In order to set the out parameter, the function itself by Rule
1 must have a stable copy of the interface pointer. On exit, the responsibility
for releasing the pointer is transferred from the callee to the caller. The out-
parameter thus need not be separately reference counted.
Rule 2c: Local variables. A function implementation clearly has omniscient knowl
edge of the lifetimes of each of the pointer variables allocated on the stack fr
ame. It can therefore use this knowledge to omit redundant AddRef/Release pairs.
Rule 2d: Backpointers. Some data structures are of the nature of containing two
components, A and B, each with a pointer to the other. If the lifetime of one co
mponent (A) is known to contain the lifetime of the other (B), then the pointer
from the second component back to the first (from B to A) need not be reference
counted. Often, avoiding the cycle that would otherwise be created is important
in maintaining the appropriate deallocation behavior. However, such non-referenc
e counted pointers should be used with extreme caution.In particular, as the rem
oting infrastructure cannot know about the semantic relationship in use here, su
ch backpointers cannot be remote references. In almost all cases, an alternative
design of having the backpointer refer a second friend object of the first rather
than the object itself (thus avoiding the circularity) is a superiour design. T
he following figure illustrates this concept.
The following rules call out common non-exceptions to Rule 1.
Rule 1a: In-Out-parameters to functions. The caller must AddRef the actual param
eter, since it will be Released by the callee when the out-value is stored on to
p of it.
Rule 1b: Fetching a global variable. The local copy of the interface pointer fet
ched from an existing copy of the pointer in a global variable must be independe
ntly reference counted since called functions might destroy the copy in the glob
al while the local copy is still alive.
Rule 1c: New pointers synthesized out of thin air. A function which synthesizes an
interface pointer using special internal knowledge rather than obtaining it fro
m some other source must do an initial AddRef on the newly synthesized pointer.
Important examples of such routines include instance creation routines, implemen
tations of IUnknown::QueryInterface, etc.
Rule 1d: Returning a copy of an internally stored pointer. Once the pointer has
been returned, the callee has no idea how its lifetime relates to that of the in
ternally stored copy of the pointer. Thus, the callee must call AddRef on the po
inter copy before returning it.
Finally, when implementing or using reference counted objects, a technique somet
imes termed artificial reference counts sometimes proves useful. Suppose you re writ
ing the code in method Foo in some interface IInterface. If in the implementatio
n of Foo you invoke functions which have even the remotest chance of decrementin
g your reference count, then such function may cause you to release before it re
turns to Foo. The subsequent code in Foo will crash.
A robust way to protect yourself from this is to insert an AddRef at the beginni
ng of Foo which is paired with a Release just before Foo returns:
void IInterface::Foo(void) {
this->AddRef();
/*
* Body of Foo, as before, except short-circuit returns
* need to be changed.
*/
this->Release();
return;
}
These artificial reference counts guarantee object stability while processing is d
one.
4.4 Providing Class Information
It is often useful for a client of an object to examine the object s type informat
ion. Given the object s CLSID, a client can locate the object s type library using r
egistry entries, and then can scan the type library for the coclass entry in the
library matching the CLSID.
However, not all objects have a CLSID, although they still need to provide type
information. In addition, it is convenient for a client to have a way to simply
ask an object for its type information instead of going through all the tedium t
o extract the same information from registry entries.
This capability is important when dealing with outgoing interfaces on connectabl
e objects. See Using IProvideClassInfo in the Connectable Objects chapter for mo
re information on how connectable objects provide this capability.
In these cases, a client can query the object for any of the IProvideClassInfo[x
] interfaces. If these interfaces exist, the client calls IProvideClassInfo[x]::
GetClassInfo to get the type information for the interface.
By implementing IProvideClassInfo[x], an object specifies that it can provide ty
pe information for its entire class, that is, what it would describe in its cocl
ass section of its type library, if it has one. The GetClassInfo method returns
an ITypeInfo pointer corresponding to the object s coclass information. Through th
is ITypeInfo pointer, the client can examine all the object s incoming and outgoin
g interface definitions.
The object can also provide IProvideClassInfo2. The IProvideClassInfo2 interface
is a simple extension to IProvideClassInfo that makes it quick and easy to retr
ieve an object s outgoing interface identifiers for its default event set. IProvid
eClassInfo2 is derived from IProvideClassInfo.
4.5 Connectable Objects and Events
The COM technology known as Connectable Objects (also called connection points ) su
pports a generic ability for any object, called in this context a connectable obje
ct, to express these capabilities:
The existence of outgoing interfaces, such as event sets
The ability to enumerate the IIDs of the outgoing interfaces
The ability to connect and disconnect sinks to the object for those outgoing IIDs
The ability to enumerate the connections that exist to a particular outgoing int
erface.
Support for these capabilities involves four interfaces: IConnectionPointContai
ner, IEnumConnectionPoints, IConnectionPoint, and IEnumConnections. A connectabl
e object implements IConnectionPointContainer to indicate existence of outgoing i
nterfaces. Through this interface a client can enumerate connection points for
each outgoing IID (via an enumerator with IEnumConnectionPoints) and can obtain
an IConnectionPoint interface to a connection point for each IID. Through a con
nection point a client starts or terminates an advisory loop with the connectabl
e object and the client s own sink. The connection point can also enumerate the c
onnections it knows about through an enumerator with IEnumConnections. See Chapt
er 13 for a complete specification of the connection point interfaces.
4.6 Designing and Implementing Objects
Objects can come in all shapes and sizes and applications will implement objects
for various purposes with or without assigning the class a CLSID. COM servers i
mplement objects for the sake of serving them to clients. In some cases, such as
data change notification, a client itself will implement a classless object to
essentially provide callback functions for the server object.
In all cases there is only one requirement for all objects: implement at least t
he IUnknown interface. An object is not a COM object unless it implements at lea
st one interface which at minimum is IUnknown. Not all objects even need a uniqu
e identifier, that is, a CLSID. In fact, only those objects that wish to allow C
OM to locate and launch their implementations really need a CLSID. All other obj
ects do not.
IUnknown implemented by itself can be useful for objects that simply represent t
he existence of some resource and control that resource s lifetime without providi
ng any other means of manipulating that resource. By and large, however, most in
teresting objects will want to provide more services, that is, additional interf
aces through which to manipulate the object. This all depends on the purpose of
the object and the context in which clients (or whatever other agents) use it. T
he object may wish to provide some data exchange capabilities by implementing ID
ataObject, or may wish to indicate the contract through which it can serialize i
t s information by implementing one of the IPersist flavors of interfaces. If the
object is a moniker, it will implement an interface called IMoniker that we ll see
in Chapter 15. Objects that are used specifically for handling remote procedure
calls implement a number of specialized interfaces themselves as we ll see in Cha
pter 9.
The bottom line is that you decide what functionality the object should have and
implement the interface that represents that functionality. In some cases there
are no standard interfaces that contain the desired functionality in which case
you will want to design a custom interface. You may need to provide for remotin
g that interface as described in Chapter 9.
The following chapters that discuss COM clients and servers use as an example an
object class designed to render ASCII text information from text stored in file
s. This object class is called TextRender and it has a CLSID of {12345678-ABCD-123
4-5678-9ABCDEF00000} defined as the symbol CLSID_TextRender in some include file
. Note again that an object class does not have to have an associated CLSID. Thi
s example has one so we can use it to demonstrate COM clients and servers in Cha
pters 7 and 8.
The TextRender object can read and write text to and from a file, and so impleme
nts the IPersistFile interface to support those operations. An object can be ini
tialized (see Chapter 7, Initializing the Object ) with the contents of a file thro
ugh IPersistFile::Load. The object class also supports rendering the text data i
nto straight text as well as graphically as metafiles and bitmaps. Rendering cap
abilities are handled through the IDataObject interface, and IDataObject::SetDat
a when given text forms a second initializing function. The operation of TextRen
der objects is illustrated in Figure 4-4:
Figure 4-4: An object with IDataObject and IPersistFile Interfaces.
The Object Reusability section of Chapter 8 will show how we might implement this
object when another object that provides some the desired functionality is avail
able for reuse. But for now, we want to see how to implement this object on its
own.
4.6.1 Implementing Interfaces: Multiple Inheritance
There are two different strategies for implementing interfaces on an object: mul
tiple inheritance and interface containment. Which method works best for you dep
ends first of all on your language of choice (languages that don t have an inherit
ance notion cannot support multiple inheritance, obviously) but if you are imple
menting an object in C++, which is a common occurrence, your choice depends on t
he object design itself.
Multiple inheritance works best for most objects. Declaring an object in this ma
nner might appear as follows:
class CTextRender : public IDataObject, public IPersistFile {
private:
ULONG m_cRef; //Reference Count
char * m_pszText; //Pointer to allocated t
ext
ULONG m_cchText; //Number of characters i
n m_pszText
//Other internal member functions here
public:
[Constructor, Destructor]
/*
* We must override all interface member functions we
* inherit to create an instantiatable class.
*/
//IUnknown members shared between IDataObject and IPersistFile
HRESULT QueryInterface(REFIID iid, void ** ppv);
ULONG AddRef(void);
ULONG Release(void);
//IDataObject Members overrides
HRESULT GetData(FORAMTETC *pFE, STGMEDIUM *pSTM);
[Other members]
...
//IPersistFile Member overrides
HRESULT Load(char * pszFile, DWORD grfMode);
[Other members]
...
};
This object class inherits from the interfaces it wishes to implement, declares
whatever variables are necessary for maintaining the object state, and overrides
all the member functions of all inherited interfaces, remembering to include th
e IUnknown members that are present in all other interfaces. The implementation
of the single QueryInterface function of this object would use typecasts to retu
rn pointers to different vtbl pointers:
HRESULT CTextRender::QueryInterface(REFIID iid, void ** ppv) {
*ppv=NULL;
//This code assumes an overloaded == operator for GUIDs exists
if (IID_IUnknown==iid)
*ppv=(void *)(IUnknown *)this;
if (IID_IPersitFile==iid)
*ppv=(void *)(IPersistFile *)this;
if (IID_IDataObject==iid)
*ppv=(void *)(IDataObject *)this;
if (NULL==*ppv)
return E_NOINTERFACE; //iid not supported.
// Any call to anyone s AddRef is our own, so we can just call that direct
ly
AddRef();
return NOERROR;
}
This technique has the advantage that all the implementation of all interfaces i
s gathered together in the same object and all functions have quick and drect ac
cess to all the other members of this object. In addition, there only needs to b
e one implementation of the IUnknown members. However, when we deal with aggrega
tion in Chapter 8 we will see how an object might need a separate implementation
of IUnknown by itself.
4.6.2 Implementing Interfaces: Interface Containment
There are at times reasons why you may not want to use multiple inheritance for
an object implementation. First, you may not be using C++. That aside, you may w
ant to individually track reference counts on each interface separate from the o
verall object for debugging or for resource management purposes reference counting
is from a client perspective an interface-specific operation. This can uncover
problems in a client you might also be developing, exposing situations where the
client is calling AddRef through one interface but matching it with a Release c
all through a different interface. The third reason that you would use a differe
nt method of implementation is when you have two interfaces with the same member
function names with possibly identical function signatures or when you want to
avoid function overloading. For example, if you wanted to implement IPersistFile
, IPersistStorage, and IPersistStream on an object, you would have to write over
loaded functions for the Load and Save members of each which might get confusing
. Worse, if two interface designers should happen to define interfaces that have
like-named methods with like parameter lists but incompatible semantics, such o
verloading isn t even possible: two separate functions need to be implemented, but
C++ unifies the two method definitions. Note that as in general interfaces may
be defined by independent parties that do not communicate with each other, such
situations are inevitable.
The other implementation method is to use interface implementations which are sepa
rate C++ objects that each inherit from and implement one interface. The real ob
ject itself singly inherits from IUnknown and maintains (or contains) pointers t
o each interface implementation that it creates on initialization. This keeps al
l the interfaces separate and distinct. An example of code that uses the contain
ment policy follows:
class CImpIPersistFile : public IPersistFile {
private:
ULONG m_cRef; //Interface reference co
unt for debugging
//"Backpointer" to the actual object.
class CTextRender * m_pObj;
public:
[Constructor, Destructor]
//IUnknown members for IPersistFile
HRESULT QueryInterface(REFIID iid, void ** ppv);
ULONG AddRef(void);
ULONG Release(void);
//IPersistFile Member overrides
HRESULT Load(char * pszFile, DWORD grfMode);
[Other members]
...
}
class CImpIDataObject : public IDataObject
private:
ULONG m_cRef; //Interface reference co
unt for debugging
//"Backpointer" to the actual object.
class CTextRender * m_pObj;
public:
[Constructor, Destructor]
//IUnknown members for IDataObject
HRESULT QueryInterface(REFIID iid, void ** ppv);
ULONG AddRef(void);
ULONG Release(void);
//IPersistFile Member overrides
HRESULT GetData(FORMATETC *pFE,STGMEDIUM *pSTM);
[Other members]
...
}

class CTextRender : public IUnknown


{
friend class CImpIDataObject;
friend class CImpIPersistFile;
private:
ULONG m_cRef; //Reference Count
char * m_pszText; //Pointer to allocated t
ext
ULONG m_cchText; //Number of characters i
n m_pszText
//Contained interface implementations
CImpIPersistFile * m_pImpIPersistFile;
CImpIDataObject * m_pImpIDataObject;
//Other internal member functions here
public:
[Constructor, Destructor]
HRESULT QueryInterface(REFIID iid, void ** ppv);
ULONG AddRef(void);
ULONG Release(void);
};
In this technique, each interface implementation must maintain a backpointer to
the real object in order to access that object s variables (normally this is passe
d in the interface implementation constructor). This may require a friend relati
onship (in C++) between the object classes; alternatively, these friend classes
can be implemented as nested classes in CTextRender.
Notice that the IUnknown member functions of each interface implementation do no
t need to do anything more than delegate directly to the IUnknown functions impl
emented on the CTextRender object. The implementation of QueryInterface on the m
ain object would appear as follows:
HRESULT CTextRender::QueryInterface(REFIID iid, void ** ppv)
{
*ppv=NULL;
//This code assumes an overloaded == operator for GUIDs exists
if (IID_IUnknown==iid)
*ppv=(void *)(IUnknown *)this;
if (IID_IPersitFile==iid)
*ppv=(void *)(IPersistFile *)m_pImpIPersistFile;
if (IID_IDataObject==iid)
*ppv=(void *)(IDataObject *)m_pImpIDataObject;
if (NULL==*ppv)
return E_NOINTERFACE; //iid not supported.
//Call AddRef through the returned interface
((IUnknown *)ppv)->AddRef();
return NOERROR;
}
This sort of delegation structure makes it very easy to redirect each interface s
IUnknown members to some other IUnknown, which is necessary in supporting aggreg
ation as explained in Chapter 8. But overall the implementation is not much diff
erent than multiple inheritance and both methods work equally well. Containment
of interface implementation is more easily translatable into C where classes sim
ply become equivalent structures, if for any reason such readability is desirabl
e (such as making the source code more comprehensible to C programmers who do no
t know C++ and do not understand multiple inheritance). In the end it really all
depends upon your preferences and has no significant impact on performance nor
development.
4.7 Objects And Interfaces API Descriptions
4.7.1 IUnknown
The IUnknown interface lets clients get pointers to other interfaces on a given
object through the QueryInterface method, and manage the existence of the object
through the IUnknown::AddRef and IUnknown::Release methods. All other COM inter
faces are inherited, directly or indirectly, from IUnknown. Therefore, the three
methods in IUnknown are the first entries in the VTable for every interface.
4.7.1.1 When to Implement
You must implement IUnknown as part of every interface. If you are using C++ mul
tiple inheritance to implement multiple interfaces, the various interfaces can s
hare one implementation of IUnknown. If you are using nested classes to implemen
t multiple interfaces, you must implement IUnknown once for each interface you i
mplement.
4.7.1.2 When to Use
Use IUnknown methods to switch between interfaces on an object, add references,
and release objects.
Methods in Vtable Order
Iunknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
4.7.1.3 IUnknown::AddRef
The IUnknown::AddRef method increments the reference count for an interface on a
n object. It should be called for every new copy of a pointer to an interface on
a given object.
ULONG AddRef(void);
Return Value
Returns an integer from 1 to n, the value of the new reference count. This infor
mation is meant to be used for diagnostic/testing purposes only, because, in cer
tain situations, the value may be unstable.
Remarks
Objects use a reference counting mechanism to ensure that the lifetime of the ob
ject includes the lifetime of references to it. You use IUnknown::AddRef to stab
ilize a copy of an interface pointer. It can also be called when the life of a c
loned pointer must extend beyond the lifetime of the original pointer. The clone
d pointer must be released by calling IUnknown::Release.
Objects must be able to maintain (231)-1 outstanding pointer references. Therefo
re, the internal reference counter that IUnknown::AddRef maintains must be a 32-
bit unsigned integer.
4.7.1.3.1 Notes to Callers
Call this function for every new copy of an interface pointer that you make. For
example, if you are passing a copy of a pointer back from a function, you must
call IUnknown::AddRef on that pointer. You must also call IUnknown::AddRef on a
pointer before passing it as an in-out parameter to a function; the function wil
l call IUnknown::Release before copying the out-value on top of it.
See Also
IUnknown::Release
4.7.1.4 IUnknown::QueryInterface
Returns a pointer to a specified interface on an object to which a client curren
tly holds an interface pointer. This function must call IUnknown::AddRef on the
pointer it returns.
HRESULT QueryInterface(
REFIID iid, //Identifier of the requested interface
void ** ppvObject //Indirect pointer to the object
);
Parameters
iid
[in] Identifier of the interface being requested.
ppvObject
[out] Indirectly points to the interface specified in iid. If the object does no
t support the interface specified in iid, *ppvObject is set to NULL.
Return Value
S_OK if the interface is supported, E_NOINTERFACE if not.
Remarks
The QueryInterface method gives a client access to other interfaces on an object
.
For any one object, a specific query for the IUnknown interface on any of the ob
ject s interfaces must always return the same pointer value. This allows a client
to determine whether two pointers point to the same component by calling QueryIn
terface on both and comparing the results. It is specifically not the case that
queries for interfaces (even the same interface through the same pointer) must r
eturn the same pointer value.
There are four requirements for implementations of QueryInterface (In these case
s, must succeed means must succeed barring catastrophic failure. ):
The set of interfaces accessible on an object through IUnknown::QueryInterface m
ust be static, not dynamic. This means that if a call to QueryInterface for a po
inter to a specified interface succeeds the first time, it must succeed again, a
nd if it fails the first time, it must fail on all subsequent queries.
It must be symmetric if a client holds a pointer to an interface on an object, a
nd queries for that interface, the call must succeed.
It must be reflexive if a client holding a pointer to one interface queries succ
essfully for another, a query through the obtained pointer for the first interfa
ce must succeed.
It must be transitive if a client holding a pointer to one interface queries suc
cessfully for a second, and through that pointer queries successfully for a thir
d interface, a query for the first interface through the pointer for the third i
nterface must succeed.
4.7.1.5 IUnknown::Release
Decrements the reference count for the calling interface on a object. If the ref
erence count on the object falls to 0, the object is freed from memory.
ULONG Release(void);
Return Value
Returns the resulting value of the reference count, which is used for diagnostic
/testing purposes only. If you need to know that resources have been freed, use
an interface with higher-level semantics.
Remarks
If IUnknown::AddRef has been called on this object s interface n times and this is
the n+1th call to IUnknown::Release, the implementation of IUnknown::AddRef mus
t cause the interface pointer to free itself. When the released pointer is the o
nly existing reference to an object (whether the object supports single or multi
ple interfaces), the implementation must free the object.
Note
Aggregation of objects restricts the ability to recover interface pointers.
4.7.1.5.1 Notes to Callers
Call this function when you no longer need to use an interface pointer. If you a
re writing a function that takes an in-out parameter, call IUnknown::Release on
the pointer you are passing in before copying the out-value on top of it.
See Also
IUnknown::AddRef
4.7.2 IProvideClassInfo
The IProvideClassInfo interface provides a single method for accessing the type
information for an object s coclass entry in its type library.
4.7.2.1 When to Implement
Implement this interface on any object that can provide type information for its
entire class, that is, the coclass entry in the type library.
4.7.2.2 When to Use
Use this interface to access the coclass type information for an object.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IProvideClassInfo Methods Description
GetClassInfo Returns the ITypeInfo interface for the object s coclass type info
rmation.
4.7.2.3 IProvideClassInfo::GetClassInfo
Returns a pointer to the ITypeInfo interface for the object s type information. Th
e type information for an object corresponds to the object s coclass entry in a ty
pe library.
HRESULT GetClassInfo(
ITypeInfo** ppTI //Indirect pointer to object s type information
);
Parameters
ppTI
[out] Indirect pointer to object s type information. The caller is responsible for
calling ITypeInfo::Release on the returned pointer if this method returns succe
ssfully.
Return Values
This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED,
as well as the following:
S_OK
The type information was successfully returned.
E_POINTER
The address in ppTI is not valid. For example, it may be NULL.
Remarks
4.7.2.3.1 Notes to Callers
The caller is responsible for calling ITypeInfo::Release when the returned inter
face pointer is no longer needed.
4.7.2.3.2 Notes to Implementers
This method must call ITypeInfo::AddRef before returning. If the object loads th
e type information from a type library, the type library itself will call AddRef
in creating the pointer.
Because the caller cannot specify a locale identifier (LCID) when calling this m
ethod, this method must assume the neutral language, that is, LANGID_NEUTRAL, an
d use this value to determine what locale-specific type information to return.
This method must be implemented; E_NOTIMPL is not an acceptable return value.
4.7.3 IProvideClassInfo2
The IProvideClassInfo2 interface is a simple extension to IProvideClassInfo for
the purpose of making it quick and easy to retrieve an object s outgoing interface
IID for its default event set. The mechanism, the added GetGUID method, is exte
nsible for other types of GUIDs as well.
4.7.3.1 When to Implement
An object implements this interface to provide type information for its outgoing
interfaces.
4.7.3.2 When to Use
Call the method in this interface to obtain type information on an object s outgoi
ng interfaces.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IProvideClassInfo Method Description
GetClassInfo Returns the ITypeInfo interface for the object s coclass type info
rmation.
IProvideClassInfo2 Method Description
GetGUID Returns the GUID for the object s outgoing IID for its default event set.

4.7.3.3 IProvideClassInfo2::GetGUID
Returns a GUID corresponding to the specified dwGuidKind. The dwGuidKind paramet
er has several values defined. See GUIDKIND. Additional flags can be defined at
a later time and will be recognized by an IProvideClassInfo2 implementation.
HRESULT GetGUID(
DWORD dwGuidKind, //Desired GUID
GUID * pGUID //Pointer to the desired GUID
);
Parameters
dwGuidKind
[in] Specifies the GUID desired on return. This parameter takes a value from the
GUIDKIND enumeration.
pGUID
[out] Pointer to the caller s variable in which to store the GUID associated with
dwGuidKind.
Return Values
S_OK
The GUID was successfully returned in *pGUID.
E_POINTER
The address in pGUID is not valid (such as NULL).
E_UNEXPECTED
An unknown error occurred.
E_INVALIDARG
The dwGuidKind value does not correspond to a supported GUID kind.
Remarks
E_NOTIMPL is not a valid return code since it would be pointless to implement th
is interface without implementing this method.
E_INVALIDARG is not valid when dwGuidKind is GUIDKIND_DEFAULT_SOURCE_DISP_IID.
See Also
GUIDKIND
4.8 Objects and Interfaces Enumeration Description
4.8.1 GUIDKIND
The GUIDKIND enumeration values are flags used to specify the kind of informatio
n requested from an object in the IProvideClassInfo2.
typedef enum tagGUIDKIND
{
GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1,
} GUIDKIND;
Elements
GUIDKIND_DEFAULT_SOURCE_DISP_IID
The interface identifier (IID) of the object s outgoing dispinterface, labeled [so
urce, default]. The outgoing interface in question must be derived from IDispatc
h.
See Also
IProvideClassInfo2
5. The COM Library
5.1 COM Application Responsibilities
All applications, that is, running programs that define a task or a process be t
hey client or servers, have specific responsibilities. This chapter examines the
roles and responsibilities of all COM applications and the necessary COM librar
y support functions for those responsibilities.
In short, any application that makes use of COM, client or server, has three spe
cific responsibilities to insure proper operation with other components:
On application startup, initialize the COM Library.
On application shutdown, uninitialize the COM Library to allow it to free resour
ces and perform any cleanup operations as necessary.
Each of these responsibilities requires support from the COM Library itself as d
etailed in the following sections. For convenience, initialization and uninitial
ization are described together. Additional COM Library functions related to init
ialization and memory management are also given in this chapter.
5.2 Library Initialization / Uninitialization
To use basic COM services, all COM threads of execution in clients and out-of-pr
ocess servers must call either the CoInitialize or the CoInitializeEx function b
efore calling any other COM function except memory allocation calls. CoInitializ
eEx replaces the other function, adding a parameter that allows you to specify t
he threading model of the thread either apartment-threaded or free-threaded. A c
all to CoInitialize simply sets the threading model to apartment-threaded. For i
nformation on threading in clients and servers, refer to Processes and Threads.
In-process servers do not call the initialization functions, because they are be
ing loaded into a process that has already done so. As a result, in-process serv
ers must set their threading model in the registry under the InprocServer32 key.
For detailed information on threading issues in in-process servers, refer to In
-Process Server Threading Issues.
It is also important to uninitialize the library. For each call to CoInitialize
or CoInitializeEx, there must be a corresponding call to CoUninitialize.
5.3 Memory Management
As was articulated earlier in this specification, when ownership of allocated me
mory is passed through an interface, COM requires that the memory be allocated w
ith a specific task allocator. Most general purpose access to the task allocator i
s provided through the IMalloc interface instance returned from CoGetMalloc. Sim
ple shortcut allocation and freeing APIs are also provided in the form of CoTask
MemAlloc and CoTaskMemFree.
5.3.1 Memory Allocation Example
An object may need to pass memory between it and the client at some point in the
object s lifetime this applies to in-process as well as out-of-process servers. Whe
n such a situation arises the object must use the task allocator as described in
Chapter 3. That is, the object must allocate memory whose ownership is transfer
red from one party to another through an interface function by using the local t
ask allocator.
CoGetMalloc provides a convenient way for objects to allocate working memory as
well. For example, when the TextRender object (see Chapter 4, Designing and Imple
menting Objects ) under consideration in this document loads text from a file in t
he function IPersistFile::Load (that is, CTextRender::Load) it will want to make
a memory copy of that text. It would use the task allocator for this purpose as
illustrated in the following code (unnecessary details of opening files and rea
ding data are omitted for simplicity):
//Implementation of IPersistFile::Load
HRESULT CTextRender::Load(char *pszFile, DWORD grfMode) {
int hFile;
DWORD cch;
IMalloc * pIMalloc;
HRESULT hr;
/*
* Open the file and seek to the end to set the
* cch variable to the length of the file.
*/
hr=CoGetMalloc(MEMCTX_TASK, &pIMalloc);
if (FAILED(hr))
//Close file and return failure
psz=pIMalloc->Alloc(cch);
pIMalloc->Release();
if (NULL==psz)
//Close file and return failure
//Read text into psz buffer and close file
//Save memory pointer and return success
m_pszText=psz;
return NOERROR;
}
If an object will make many allocations throughout it s lifetime, it makes sense t
o call CoGetMalloc once when the object is created, store the IMalloc pointer in
the object (m_pIMalloc or such), and call IMalloc::Release when the object is d
estroyed. Alternatively, the APIs CoTaskMemAlloc and its friends may be used.
5.4 COM Library Interface Descriptions
5.4.1 IMalloc
Allocates, frees, and manages memory.
5.4.1.1.1 When to Implement
In general, you should not implement IMalloc, instead using the COMOLE implement
ation, which is guaranteed to be thread-safe in managing task memory. You get a
pointer to the COMOLE task allocator object s IMalloc through a call to the CoGetM
alloc function.
5.4.1.1.2 When to Use
Call the methods of IMalloc to allocate and manage memory. The COMOLE libraries
and object handlers also call the IMalloc methods to manage memory. Object handl
ers should call CoGetMalloc to get a pointer to the IMalloc implementation on th
e task allocator object, and use the implementation of those methods to manage t
ask memory.
The IMalloc methods Alloc, Free, and Realloc are similar to the C library functi
ons malloc, free, and realloc. For debugging, refer to the functions CoRegisterM
allocSpy and CoRevokeMallocSpy.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IMalloc Methods Description
Alloc Allocates a block of memory.
Realloc Changes the size of a previously allocated block of memory.
Free Frees a previously allocated block of memory.
GetSize Returns the size in bytes of a previously allocated block of memory.
DidAlloc Determines if this instance of IMalloc was used to allocate the
specified block of memory.
HeapMinimize Minimizes the heap by releasing unused memory to the operating s
ystem.
See Also
CoGetMalloc, IMallocSpy, CoRegisterMallocSpy, CoRevokeMallocSpy
5.4.1.2 IMalloc::Alloc
Allocates a block of memory.
void * Alloc(
ULONG cb //Size of the requested memory block in bytes
);
Parameter
cb
[in] Size , in bytes, of the memory block to be allocated.
Return Values
If successful, Alloc returns a pointer to the allocated memory block.
NULL
If insufficient memory is available, Alloc returns NULL.
Remarks
The IMalloc::Alloc method allocates a memory block in essentially the same way t
hat the C Library malloc function does.
The initial contents of the returned memory block are undefined there is no guar
antee that the block has been initialized, so you should initialize it in your c
ode. The allocated block may be larger than cb bytes because of the space requir
ed for alignment and for maintenance information.
If cb is zero, IMalloc::Alloc allocates a zero-length item and returns a valid p
ointer to that item. If there is insufficient memory available, IMalloc::Alloc r
eturns NULL.
Note
Applications should always check the return value from this method, even when re
questing small amounts of memory, because there is no guarantee the memory will
be allocated.
See Also
IMalloc::Free, IMalloc::Realloc, CoTaskMemAlloc
5.4.1.3 IMalloc::DidAlloc
Determines if this allocator was used to allocate the specified block of memory.
int DidAlloc(
void *pv //Pointer to the memory block
);
Parameter
pv
[in] Pointer to the memory block; can be a NULL pointer, in which case, -1 is re
turned.
Return Values
1
The memory block was allocated by this IMalloc instance.
0
The memory block was not allocated by this IMalloc instance.
-1
DidAlloc is unable to determine whether or not it allocated the memory block.
Remarks
Calling IMalloc::DidAlloc is useful if a application is using multiple allocatio
ns, and needs to know whether a previously allocated block of memory was allocat
ed by a particular allocation.
See Also
IMalloc::Alloc, IMalloc::HeapMinimize, IMalloc::Realloc

5.4.1.4 IMalloc::Free
Frees a previously allocated block of memory.
void Free(
void * pv //Pointer to the memory block to be freed
);
Parameter
pv
[in] Pointer to the memory block to be freed.
Remarks
IMalloc:Free frees a block of memory previously allocated through a call to IMal
loc::Alloc or IMalloc::Realloc. The number of bytes freed equals the number of b
ytes that were allocated. After the call, the memory block pointed to by pv is i
nvalid and can no longer be used.
Note
The pv parameter can be NULL. If so, this method has no effect.
See Also
IMalloc::Alloc, IMalloc::Realloc, CoTaskMemFree

5.4.1.5 IMalloc::GetSize
Returns the size (in bytes) of a memory block previously allocated with IMalloc:
:Alloc or IMalloc::Realloc.
ULONG GetSize(
void *pv //Pointer to the memory block for which the size is requested
);
Parameter
pv
[in] Pointer to the memory block for which the size is requested.
Return Value
The size of the allocated memory block in bytes or, if pv is a NULL pointer, -1.
Remarks
To get the size in bytes of a memory block, the block must have been previously
allocated with IMalloc::Alloc or IMalloc::Realloc. The size returned is the actu
al size of the allocation, which may be greater than the size requested when the
allocation was made.
See Also
IMalloc::Alloc, IMalloc::Realloc

5.4.1.6 IMalloc::HeapMinimize
Minimizes the heap as much as possible by releasing unused memory to the operati
ng system, coalescing adjacent free blocks and committing free pages.
void HeapMinimize();
Remarks
Calling IMalloc::HeapMinimize is useful when an application has been running for
some time and the heap may be fragmented.
See Also
IMalloc::Alloc, IMalloc::Free, IMalloc::Realloc

5.4.1.7 IMalloc::Realloc
Changes the size of a previously allocated memory block.
void *Realloc(
void *pv, //Pointer to memory block to be reallocated
ULONG cb //Size of the memory block in bytes
);
Parameters
pv
[in] Pointer to the memory block to be reallocated. The pointer can have a NULL
value, as discussed in the following Remarks section.
cb
[in] Size of the memory block (in bytes) to be reallocated. It can be zero, as d
iscussed in the following remarks.
Return Values
Reallocated memory block
Memory block successfully reallocated.
NULL
Insufficient memory or cb is zero and pv is not NULL.
Remarks
IMalloc::Realloc reallocates a block of memory, but does guarantee that the cont
ents of the returned memory block are initialized. Therefore, the caller is resp
onsible for intializing it in code, subsequent to the reallocation. The allocate
d block may be larger than cb bytes because of the space required for alignment
and for maintenance information.
The pv argument points to the beginning of the memory block. If pv is NULL, IMal
loc::Realloc allocates a new memory block in the same way that IMalloc::Alloc do
es. If pv is not NULL, it should be a pointer returned by a prior call to IMallo
c::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 dif
ferent memory location, the pointer returned by IMalloc::Realloc is not guarante
ed 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 argume
nt is not NULL, or if there is not enough memory available to expand the block t
o 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 al
igned for storage of any type of object. To get a pointer to a type other than v
oid, use a type cast on the return value.
See Also
IMalloc::Alloc, IMalloc::Free5.4.2 IMallocSpy
The IMallocSpy interface is a debugging interface that allows application develo
pers to monitor (spy on) memory allocation, detect memory leaks and simulate mem
ory failure in calls to IMalloc methods.
Caution
The IMallocSpy interface is intended to be used only to debug application code u
nder development. Do not ship this interface to retail customers of your applica
tion, because it causes severe performance degradation and could conflict with u
ser-installed software to produce unpredictable results.
When to Implement
Implement this interface to debug memory allocation during application developme
nt.
When to Use
When an implementation of IMallocSpy is registered with CoRegisterMallocSpy, COM
calls the pair of IMallocSpy methods around the corresponding IMalloc method. Y
ou would not make direct calls to IMallocSpy methods. The COM SDK contains a sam
ple implementation of IMallocSpy. The call to the pre-method through the return
from the corresponding post-method is guaranteed to be thread-safe in multi-thre
aded operations.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IMallocSpy Methods Description
PreAlloc Called before invoking IMalloc::Alloc, and may extend or modify
the allocation to store debug information.
PostAlloc Called after invoking IMalloc::Alloc.
PreFree Called before invoking IMalloc::Free.
PostFree Called after invoking IMalloc::Free.
PreRealloc Called before invoking IMalloc::Realloc.
PostRealloc Called after invoking IMalloc::Realloc.
PreGetSize Called before invoking IMalloc::GetSize.
PostGetSize Called after invoking IMalloc::GetSize.
PreDidAlloc Called before invoking IMalloc::DidAlloc.
PostDidAlloc Called after invoking IMalloc::DidAlloc.
PreHeapMinimize Called before invoking IMalloc::DidAlloc.
PostHeapMinimize Called after invoking IMalloc::HeapMinimize.
See Also
IMalloc, CoGetMalloc, CoRegisterMallocSpy

IMallocSpy::PreAlloc
Called just prior to invoking IMalloc::Alloc.
ULONG PreAlloc(
ULONG cbRequest //Byte count passed to IMalloc::Alloc
);
Parameter
cbRequest
[in] Number of bytes specified in the allocation request the caller is passing t
o IMalloc::Alloc. byte count actually passed to IMalloc::Alloc, which should be
greater than or equal to the value of cbRequest.
Remarks
The IMallocSpy::PreAlloc implementation may extend and/or modify the allocation
to store debug-specific information with the allocation.
PreAlloc can force memory allocation failure by returning 0, allowing testing to
ensure that the application handles allocation failure gracefully in all cases.
In this case, PostAlloc is not called and Alloc returns NULL. Forcing allocatio
n failure is effective only if cbRequest is not equal to 0. If PreAlloc is forci
ng failure by returning NULL, PostAlloc is not called. However, if IMalloc::Allo
c encounters a real memory failure and returns NULL, PostAlloc is called.
The call to PreAlloc through the return from PostAlloc is guaranteed to be threa
d safe.
See Also
IMalloc::Alloc, IMallocSpy::PostAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy

IMallocSpy::PostAlloc
Called just after invoking IMalloc::Alloc, taking as input a pointer to the IMal
loc::Alloc caller's allocation, and returning a pointer to the actual allocation
.
void * PostAlloc(
void * pActual //Pointer to the allocation actually done by
//IMalloc::Alloc
);
Parameter
pActual
[in] Pointer to the allocation done by IMalloc::Alloc.
Return Value
A pointer to the beginning of the memory block actually allocated. This pointer
is also returned to the caller of IMalloc::Alloc. If debug information is writte
n at the front of the caller's allocation, this should be a forward offset from
pActual. The value is the same as pActual if debug information is appended or if
no debug information is attached.
Remarks
When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy
, COM calls IMallocSpy::PostAlloc after any call to IMalloc::Alloc. It takes as
input a pointer to the allocation done by the call to IMalloc::Alloc, and return
s a pointer to the beginning of the total allocation, which could include a forw
ard offset from the other value if IMallocSpy::Prealloc was implemented to attac
h debug information to the allocation in this way. If not, the same pointer is r
eturned, and also becomes the return value to the caller of IMalloc::Alloc.
See Also
IMalloc::Alloc, IMallocSpy::PreAlloc, CoRegisterMallocSpy, CoRevokeMallocSpy
IMallocSpy::PreDidAlloc
Called by COM just prior to invoking IMalloc::DidAlloc.
void * PreDidAlloc(
void * pRequest,
//Pointer the caller is passing to IMalloc::DidAlloc
BOOL fSpyed //Whether pRequest was allocated while this spy was
//active
);
Parameters
pRequest
[in] Pointer the caller is passing to IMalloc::DidAlloc.
fSpyed
[in] TRUE if the allocation was done while this spy was active.
Return Value
The pointer for which allocation status is determined. This pointer is passed to
PostDidAlloc as the fActual parameter.
Remarks
When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy
, COM calls this method immediately before any call to IMalloc::DidAlloc. This m
ethod is included for completeness and consistency it is not anticipated that de
velopers will implement significant functionality in this method.
See Also
IMalloc::DidAlloc, IMallocSpy::PostDidAlloc, CoRegisterMallocSpy, CoRevokeMalloc
Spy

IMallocSpy::PostDidAlloc
Called just after invoking IMalloc::DidAlloc.
int PostDidAlloc(
void * pRequest, //Original pointer passed to IMalloc::DidAlloc
BOOL fSpyed, //Whether the allocation was done while this spy
//was active
int fActual //Whether pRequest was actual value used in
//IMalloc call
);
Parameters
pRequest
[in] Pointer specified in the original call to IMalloc::DidAlloc.
fSpyed
[in] TRUE if the allocation was done while this spy was active.
fActual
[in] Actual value returned by IMalloc::DidAlloc.
Return Value
The value returned to the caller of IMalloc::DidAlloc.
Remarks
When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy
, COM calls this method immediately after any call to IMalloc::DidAlloc. This me
thod is included for completeness and consistency it is not anticipated that dev
elopers will implement significant functionality in this method.
For convenience, pRequest, the original pointer passed in the call to IMalloc::D
idAlloc, is passed to PostDidAlloc. In addition, the parameter fActual is a bool
ean that indicates whether this value was actually passed to IMalloc::DidAlloc.
If not, it would indicate that IMallocSpy::PreDidAlloc was implemented to alter
this pointer for some debugging purpose.
The fSpyed parameter is a boolean that indicates whether the allocation was done
while the current spy object was active.
See Also
IMalloc::DidAlloc, IMallocSpy::PreDidAlloc, CoRegisterMallocSpy, CoRevokeMallocS
py

IMallocSpy::PreFree
Called just before invoking IMalloc::Free to ensure that the pointer passed to I
Malloc::Free points to the beginning of the actual allocation.
void * PreFree(
void * pRequest, //Pointer is passing to IMalloc::Free
BOOL fSpyed //TRUE if this memory was allocated while the
//spy was active
);
Parameters
pRequest
[in] Pointer to the block of memory that the caller is passing to IMalloc::Free.
fSpyed
[in] TRUE if the pRequest parameter of IMallocSpy::PreFree was allocated while t
he spy was installed. This value is also passed to IMallocSpy::PostFree.
Return Value
The actual pointer to pass to IMalloc::Free.
Remarks
If IMallocSpy::PreAlloc modified the original allocation request passed to IMall
oc::Alloc (or IMalloc::Realloc), IMallocSpy::PreFree must supply a pointer to th
e actual allocation, which COM will pass to IMalloc::Free. For example, if the P
reAlloc/PostAlloc pair attached a header used to store debug information to the
beginning of the caller's allocation, PreFree must return a pointer to the begin
ning of this header, so all of the block that was allocated can be freed.
See Also
IMalloc::Free, IMallocSpy::PostFree, CoRegisterMallocSpy, CoRevokeMallocSpy

IMallocSpy::PostFree
Called just after invoking IMalloc::Free.
void PostFree(
BOOL fSpyed //Whether the memory block to be freed was allocated
//while the spy is active
);
Parameter
fSpyed
[in] TRUE if the memory block to be freed was allocated while the current spy wa
s active, otherwise FALSE.
Remarks
When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy
, COM calls this method immediately after any call to IMalloc::Free. This method
is included for completeness and consistency it is not anticipated that develop
ers will implement significant functionality in this method. On return, the fSpy
ed parameter simply indicates whether the memory was freed while the current spy
was active.
See Also
IMalloc::Free, IMallocSpy::PreFree, CoRegisterMallocSpy, CoRevokeMallocSpy

IMallocSpy::PreGetSize
Called by COM just prior to any call to IMalloc::GetSize.
void * PreGetSize(
void * pRequest,
//Pointer the caller is passing to IMalloc::GetSize
BOOL fSpyed //TRUE if allocation was done while this spy was
//active
);
Parameters
pRequest
[in] Pointer the caller is passing to IMalloc::GetSize.
fSpyed
[in] TRUE if the allocation was done while the spy was active.
Return Value
Pointer to the actual allocation for which the size is to be determined.
Remarks
The PreGetSize method receives as its pRequest parameter the pointer the caller
is passing to IMalloc::GetSize. It must then return a pointer to the actual allo
cation, which may have altered pRequest in the implementation of either the PreA
lloc or PreRealloc methods of IMallocSpy. The pointer to the true allocation is
then passed to IMalloc::GetSize as its pv parameter.
IMalloc::GetSize then returns the size determined, and COM passes this value to
IMallocSpy::PostGetSize in cbActual.
Note
The size determined by IMalloc::GetSize is the value returned by the Win32 funct
ion HeapSize. On Windows NT, this is the size originally requested. On Windows 9
5, memory allocations are done on eight-byte boundaries. For example, a memory a
llocation request of 27 bytes on Windows NT would return an allocation of 32 byt
es and GetSize would return 27. On Windows 95, the same request would return an
allocation of 28 bytes and GetSize would return 28. Implementers of IMallocSpy::
PostGetSize cannot assume, for example, that if cbActual is sizeof(debug_header)
, that the value is the actual size of the user's allocation.
See Also
IMalloc::GetSize, IMallocSpy::PostGetSize, CoRegisterMallocSpy, CoRevokeMallocSp
y

IMallocSpy::PostGetSize
Called just after invoking IMalloc::GetSize.
ULONG PostGetSize(
ULONG cbActual, //Actual size of the allocation
BOOL fSpyed //Whether the allocation was done while a spy was
//active
);
Parameters
cbActual
[in] Actual number of bytes in the allocation, as returned by IMalloc::GetSize.
fSpyed
[in] TRUE if the allocation was done while a spy was active.
Return Values
The same value returned by IMalloc::GetSize, which is the size of the allocated
memory block in bytes.
Remarks
The size determined by IMalloc::GetSize is the value returned by the Win32 funct
ion HeapSize. On Windows NT, this is the size originally requested. On Windows 9
5, memory allocations are done on eight-byte boundaries. For example, a memory a
llocation request of 27 bytes on Windows NT would return an allocation of 32 byt
es and GetSize would return 27. On Windows 95, the same request would return an
allocation of 28 bytes and GetSize would return 28. Implementers of IMallocSpy::
PostGetSize cannot assume, for example, that if cbActual is sizeof(debug_header)
, that the value is the actual size of the user's allocation.
See Also
IMalloc::GetSize, IMallocSpy::PreGetSize, CoRegisterMallocSpy, CoRevokeMallocSpy

IMallocSpy::PreHeapMinimize
Called just prior to invoking IMalloc::HeapMinimize.
void PreHeapMinimize(void);
Remarks
This method is included for completeness; it is not anticipated that developers
will implement significant functionality in this method.
See Also
IMalloc::HeapMinimize, IMallocSpy::PostHeapMinimize, CoRegisterMallocSpy, CoRevo
keMallocSpy
IMallocSpy::PostHeapMinimize
Called just after invoking IMalloc::HeapMinimize.
void PostHeapMinimize(void);
Remarks
When a spy object implementing IMallocSpy is registered with CoRegisterMallocSpy
, COM calls this method immediately after any call to IMalloc::Free. This method
is included for completeness and consistency it is not anticipated that develop
ers will implement significant functionality in this method.
See Also
IMalloc::HeapMinimize, IMallocSpy::PreHeapMinimize, CoRegisterMallocSpy, CoRevok
eMallocSpy

IMallocSpy::PreRealloc
Called just before invoking IMalloc::Alloc.
ULONG PreRealloc(
void * pRequest, //Pointer the caller is passing to
//IMalloc::Realloc
ULONG cbRequest, //Byte count the caller is passing to
//IMalloc::Realloc
void ** ppNewRequest,
//Address of output variable that receives a
//pointer to the requested memory block to be
//reallocated
BOOL fSpyed //Whether the original allocation was "spyed"
);
Parameters
pRequest
[in] Pointer specified in the original call to IMalloc::Realloc, indicating the
the memory block to be reallocated.
cbRequest
[in] Memory block's byte count as specified in the original call to IMalloc::Rea
lloc.
ppNewRequest
[out] Address of pointer variable that receives a pointer to the actual memory b
lock to be reallocated. This may be different from the pointer in pRequest if th
e implementation of IMallocSpy::PreRealloc extends or modifies the reallocation.
This is an out pointer and should always be stored by PreRealloc.
fSpyed
[in] TRUE if the original allocation was done while the spy was active.
Return Value
The actual byte count to be passed to IMalloc::Realloc.
Remarks
The IMallocSpy::PreRealloc implementation may extend and/or modify the allocatio
n to store debug-specific information with the allocation. Thus, the ppNewReques
t parameter may differ from pRequest, a pointer to the request specified in the
original call to IMalloc::Realloc.
PreRealloc can force memory allocation failure by returning 0, allowing testing
to ensure that the application handles allocation failure gracefully in all case
s. In this case, PostRealloc is not called and Realloc returns NULL. However, if
IMalloc::Realloc encounters a real memory failure and returns NULL, PostRealloc
is called. Forcing allocation failure is effective only if cbRequest is not equ
al to 0.
See Also
IMalloc::Realloc, IMallocSpy::PostRealloc, CoRegisterMallocSpy, CoRevokeMallocSp
y

IMallocSpy::PostRealloc
Called after invoking IMalloc::Realloc.
void * PostRealloc(
void * pActual, //Pointer returned by IMalloc::Realloc
BOOL fSpyed //Whether the original allocation was "spyed"
);
Parameters
pActual
[in] Pointer to the memory block reallocated by IMalloc::Realloc.
fSpyed
[in] If TRUE, the original memory allocation was done while the spy was active.
Return Values
A pointer to the beginning of the memory block actually allocated. This pointer
is also returned to the caller of IMalloc::Realloc. If debug information is writ
ten at the front of the caller's allocation, it should be a forward offset from
pActual. The value should be the same as pActual if debug information is appende
d or if no debug information is attached.
See Also
IMalloc::Realloc, IMallocSpy::PreRealloc, CoRegisterMallocSpy, CoRevokeMallocSpy
5.4.3 IOleContainer
The IOleContainer interface is used to enumerate objects in a compound document
or lock a container in the running state. Container and object applications both
implement this interface.
When to Implement
Applications that support links and links to embedded objects implement this int
erface to provide object enumeration, name parsing, and silent updates of link s
ources. Simple, nonlinking containers do not need to implement IOleContainer if
it is useful mainly to support links to embedded objects.
When to Use
Call IOleContainer to enumerate the objects in a compound document or to lock a
container so that silent updates of link sources can be carried out safely.
Many applications inherit the functions of IOleContainer by implementing IOleIte
mContainer, which is used to bind item monikers.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IParseDisplayName Method Description
ParseDisplayName Parses object's display name to form moniker.
IOleContainer Methods Description
EnumObjects Enumerates objects in a container.
LockContainer Keeps container running until explicitly released.
See Also
IOleItemContainer, IParseDisplayName
IOleContainer::EnumObjects
Enumerates objects in the current container.
HRESULT EnumObjects(
DWORD grfFlags, //Value specifying what is to be enumerated
IEnumUnknown **ppenum
//Address of output variable that receives the
// IEnumUnknown interface pointer
);
Parameters
grfFlags
[in] Value that specifies which objects in a container are to be enumerated, as
defined in the enumeration OLECONTF.
ppenum
[out] Address of IEnumUnknown* pointer variable that receives the interface poin
ter to the enumerator object. Each time a container receives a successful call t
o EnumObjects, it must increase the reference count on the *ppenum pointer the m
ethod returns. It is the caller's responsibility to call IUnknown::Release when
it is done with the pointer. If an error is returned, the implementation must se
t *ppenum to NULL.
Return Values
This method supports the standard return value E_FAIL, as well as the following:
S_OK
Enumerator successfully returned.
E_NOTIMPL
Object enumeration not supported.
Remarks
A container should implement EnumObjects to enable programmatic clients to find
out what objects it holds. This method, however, is not called in standard linki
ng scenarios.
See Also
IEnumUnknown, IOleItemContainer, OLECONTF

IOleContainer::LockContainer
Keeps an embedded object's container running.
HRESULT LockContainer(
BOOL fLock //Value indicating lock or unlock
);
Parameter
fLock
[in] Value that specifies whether to lock (TRUE) or unlock (FALSE) a container.
Return Values
This method supports the standard return values E_FAIL and E_OUTOFMEMORY, as wel
l as the following:
S_OK
Container was locked successfully.
Remarks
An embedded object calls IOleContainer::LockContainer to keep its container runn
ing when the object has link clients that require an update. If an end user sele
cts File Close from the container's menu, however, the container ignores all out
standing LockContainer locks and closes the document anyway.
Notes to Callers
When an embedded object changes from the loaded to the running state, it should
call IOleContainer::LockContainer with the fLock parameter set to TRUE. When the
embedded object shuts down (transitions from running to loaded), it should call
IOleContainer::LockContainer with the fLock parameter set to FALSE.
Each call to LockContainer with fLock set to TRUE must be balanced by a call to
LockContainer with fLock set to FALSE. Object applications typically need not ca
ll LockContainer; the default handler makes these calls automatically for object
applications implemented as .EXEs as the object makes the transition to and fro
m the running state. Object applications not using the default handler, such as
DLL object applications, must make the calls directly.
An object should have no strong locks on it when it registers in the Running Obj
ect Table, but it should be locked as soon as the first external client connects
to it. Therefore, following registration of the object in the Running Object Ta
ble, object handlers and DLL object applications, as part of their implementatio
n of IRunnableObject::Run, should call IOleContainer::LockContainer(TRUE) to loc
k the object.
Notes to Implementers
The container must keep track of whether and how many calls to LockContainer(TRU
E) have been made. To increment or decrement the reference count, IOleContainer:
:LockContainer calls CoLockObjectExternal with a flag set to match fLock.
See Also
CoLockObjectExternal, IRunnableObject::Run
5.4.4 IPersistMoniker
Objects, especially asynchronous-aware objects, can expose the IPersistMoniker i
nterface to obtain more control over the way they bind to their persistent data.
Existing moniker implementations call QueryInterface on the client objectfor per
sistence interfaces such as IPersistFile, IPersistStream[Init], or IPersistStora
ge as part of their IMoniker::BindToObject implementation when they are instanti
ating and initializing the object. The IPersistMoniker interface allows moniker
implementations and other applications that instantiate objects from persistent
data to give control to the object being instantiated over binding to its persis
tent data. An object could, for example, implement IPersistMoniker::Load by call
ing IMoniker::BindToStorage for the interface it prefers: IStorage, IStream, asy
nchronous binding, etc.
Unlike some other persistent object interfaces, IPersistMoniker does not include
an InitNew method. This means that IPersistMoniker cannot be used to initialize
an object to a freshly initialized state. Clients of IPersistMoniker who wish t
o initialize the object should QueryInterface for a different persistence interf
ace that contains an InitNew method, such as IPersistStreamInit, IPersistMemory,
or IPersistPropertyBag. Then, the client can use the InitNew method found in th
e other persistence interface to initialize the object. The client can still saf
ely used IPersistMoniker to save the persistent state of the object.
The IPersistMoniker contract inherits its definition from the IPersist interface
, and includes the GetClassID method of IPersist.
When to Implement
Implement IPersistMoniker on any object that can be saved persistently to multip
le storage mediums or can take advantage of any of the asynchronous stream, stor
age, or IMoniker::BindToStorage behavior described above.
When to Use
Custom moniker implementations should support IPersistMoniker as the most flexib
le persistence interface in their implementation of IMoniker::BindToObject if th
ey are instantiating an arbitrary class and need to initialize it from persisten
t data. Typically, these monikers should use the published persistence interface
s in the following order: IPersistMoniker, IPersistStream[Init], IPersistStorage
, IPersistFile, and IPersistMemory.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the object.
IPersistMoniker Methods Description
IsDirty Checks an object for changes since it was last saved.
Load Loads an object using a specified moniker.
Save Saves the object, specifying a destination moniker.
SaveCompleted Notifies the object that the save operation is complete.
GetCurMoniker Gets the current moniker for the object.

IPersistMoniker::GetCurMoniker
Retrieves the moniker that refers to the object's persistent state.
HRESULT GetCurMoniker(
IMoniker **ppmkCur //Address of output variable that receives the
//IMoniker interface pointer
);
Parameter
ppmkCur
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the object's current persistent state.
Return Values
S_OK
A valid absolute path was successfully returned.
E_INVALIDARG
The ppmkCur parameter is invalid.
Remarks
Typically, this method returns the moniker last passed to the object by means of
IPersistMoniker::Load, IPersistMoniker::Save, or IPersistMoniker::SaveCompleted
.
See Also
IPersistMoniker::Load, IPersistMoniker::Save, IPersistMoniker::SaveCompleted

IPersistMoniker::IsDirty
Checks an object for changes since it was last saved.
HRESULT IsDirty(void);
Return Values
S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.
Remarks
IPersistMoniker::IsDirty checks whether an object has changed since it was last
saved so you can avoid losing information in objects that have not yet been save
d.
See Also
IPersistMoniker::Save
IPersistMoniker::Load
Loads the object from its persistent state indicated by a supplied moniker.
HRESULT Load(
BOOL fFullyAvailable,
//Indicates whether the object was already loaded
IMoniker *pmkSrc,
//Pointer to source moniker that references the
//persistent state to be loaded
IBindCtx *pbc, //Pointer to the moniker's bind context
DWORD grfMode //Access mode values taken from the STGM
//enumeration
);
Parameters
fFullyAvailable
[in] If TRUE, then the data referred to by the moniker has already been loaded o
nce, and subsequent binding to the moniker should be synchronous. If FALSE, then
an asynchronous bind operation should be launched.
pmkSrc
[in] Pointer to a moniker that references the persistent state for the object to
be loaded.
pbc
[in] Pointer to the IBindCtx interface for the bind context to be used for any m
oniker binding during this method.
grfMode
[in] A combination of values from the STGM enumeration which indicate the access
mode to use when binding to the persistent state. The IPersistMoniker::Load met
hod can treat this value as a suggestion, adding more restrictive permissions if
necessary. If grfMode is zero, the implementation should bind to the persistent
state using default permissions.
Return Values
S_OK
The object was successfully loaded.
E_INVALIDARG
One or more parameters are invalid.
Remarks
Typically, the object will immediately bind to its persistent state through a ca
ll to the source moniker's IMoniker::BindToStorage method, requesting either the
IStream or IStorage interface.
See Also
IPersistMoniker::Save, IPersistMoniker::SaveCompleted

IPersistMoniker::Save
Requests that the object save itself to the location referred to by pmkDst.
HRESULT Save(
IMoniker *pmkDst, //Pointer to destination moniker
IBindCtx *pbc, //Pointer to the moniker's bind context
BOOL fRemember //Specifies whether the destination moniker is
//to be the current working one
);
Parameters
pmkDst
[in] Pointer to the moniker referencing the location where the object should per
sistently store itself. The object typically binds to the location through a cal
l to pmkDst->BindToStorage, requesting either the IStream or IStorage interface.
This parameter can be NULL, in which case the object should save itself to the
same location referred to by the moniker passed to it in IPersistMoniker::Load.
Using the NULL value, can act as an optimization to prevent the object from bind
ing, since it has typically already bound to the moniker when it was loaded.
pbc
[in] Pointer to IBindCtx for the bind context to be used for any moniker binding
during this method.
fRemember
[in] Indicates whether pmkDst is to be used as the reference to the current pers
istent state after the save. If TRUE, pmkDst becomes the reference to the curren
t persistent state and the object should clear its dirty flag after the save. If
FALSE, this save operation is a "Save A Copy As ..." operation. In this case, t
he reference to the current persistent state is unchanged, and the object should
not clear its dirty flag. If pmkDst is NULL, the implementation should ignore t
he fRemember flag.
Return Values
S_OK
The object was successfully saved.
E_INVALIDARG
One or more parameters are invalid.
See Also
IPersistMoniker::GetCurMoniker, IPersistMoniker::Load, IPersistMoniker::SaveComp
leted

IPersistMoniker::SaveCompleted
Notifies the object that it has been completely saved and points it to its new p
ersisted state.
HRESULT SaveCompleted(
IMoniker *pmkNew, //Pointer to moniker for the object's new
//persistent state
IBindCtx *pbc //Bind context for binding during this method
);
Parameter
pmkNew
[in] Pointer to the moniker for the object's new persistent state. This paramete
r can be NULL if the moniker to the object's new persistent state is the same as
the previous moniker to the object's persistent state. This optimization is all
owed only if there was a prior call to IPersistMoniker::Save with the fRemember
parameter set to TRUE, in which case the object need not rebind to pmkNew.
pbc
[in] Pointer to the bind context to use for any moniker binding during this meth
od.
Return Value
S_OK
The operation was successful.
E_INVALIDARG
One or more parameters are invalid.
Remarks
Typically, the object will immediately bind to its persistent state through a ca
ll to pmkNew->BindToStorage method, requesting either the IStream or IStorage in
terface, as in IPersistMoniker::Load.
See Also
IPersistMoniker::Load, IPersistMoniker::Save
5.4.5 IRunnableObject
The IRunnableObject interface enables a container to control the running of its
embedded objects. In the case of an object implemented with a local server, call
ing IRunnableObject::Run launches the server's .EXE file. In the case of an obje
ct implemented with an in-process server, calling the Run method causes the obje
ct .DLL file to transition into the running state.
When to Implement
Object handlers should implement IRunnableObject to provide their containers wit
h a way to run them and manage their running state. DLL object applications shou
ld implement IRunnableObject to support silent updates of their objects.
When to Use
Containers call IRunnableObject to determine if an embedded object is running, t
o force an object to run, to lock an object into the running state, or to inform
an object handler whether its object is being run as either a simple embedding
or as a link source.
Methods VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.

IRunnableObject Methods Description


GetRunningClass Returns CLSID of a running object.
Run Forces an object to run.
IsRunning Determines if an object is running.
LockRunning Locks an object into running state.
SetContainedObject Indicates that an object is embedded.

IRunnableObject::GetRunningClass
Returns the CLSID of a running object.
HRESULT GetRunningClass(
LPCLSID lpClsid //Pointer to an object's CLSID
);
Parameter
lpClsid
[out] Pointer to the object's class identifier.
Return Values
This method supports the standard return values E_INVALIDARG and E_UNEXPECTED, a
s well as the following:
S_OK
CLSID was returned successfully.
Remarks
If an embedded document was created by an application that is not available on t
he user's computer, the document, by a call to CoTreatAsClass, may be able to di
splay itself for editing by emulating a class that is supported on the user's ma
chine. In this case, the CLSID returned by a call to IRunnableObject::GetRunning
Class will be that of the class being emulated, rather than the document's nativ
e class.
See Also
CoTreatAsClass
IRunnableObject::IsRunning
Determines whether an object is currently in the running state.
BOOL IsRunning();
Return Values
TRUE
The object is in the running state.
FALSE
The object is not in the running state.
Remarks
A container application could call IRunnableObject::IsRunning when it needs to k
now if the server is immediately available.
An object handler could call IRunnableObject::IsRunning when it wants to avoid c
onflicts with a running server or when the running server might have more up-to-
date information
OleIsRunning is a helper function that conveniently repackages the functionality
offered by IRunnableObject::IsRunning. With the release of OLE 2.01, the implem
entation of OleIsRunning was changed so that it calls QueryInterface, asks for I
RunnableObject, and then calls IRunnableObject::IsRunning. In other words, you c
an use the interface and the helper function interchangeably.
See Also
OleIsRunning

IRunnableObject::LockRunning
Locks an already running object into its running state or unlocks it from its ru
nning state.
HRESULT LockRunning(
BOOL fLock, //Flag indicating whether object is locked
BOOL fLastUnlockCloses
//Flag indicating whether to close object
);
Parameters
fLock
[in] TRUE locks the object into its running state. FALSE unlocks the object from
its running state.
fLastUnlockCloses
[in] TRUE specifies that if the connection being released is the last external l
ock on the object, the object should close. FALSE specifies that the object shou
ld remain open until closed by the user or another process.
Return Values
This method supports the standard return values E_FAIL, E_INVALIDARG, E_OUTOFMEM
ORY and E_UNEXPECTED, as well as the following:
S_OK
If the value of fLock is TRUE, the object was successfully locked; if the value
of fLock is FALSE, the object was successfully unlocked.
Remarks
Most implementations of IRunnableObject::LockRunning call CoLockObjectExternal.
OleLockRunning is a helper function that conveniently repackages the functionali
ty offered by IRunnableObject::LockRunning. With the release of OLE 2.01, the im
plementation of OleLockRunning was changed to call QueryInterface, ask for IRunn
ableObject, and then call IRunnableObject::LockRunning. In other words, you can
use the interface and the helper function interchangeably.
See Also
CoLockObjectExternal
IRunnableObject::Run
Runs an object.
HRESULT Run(
LPBC lpbc //Pointer to binding context
);
Parameter
lpbc
[in] Pointer to the binding context of the run operation. May be NULL.
Return Values
This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED,
as well as the following:
S_OK
The object was successfully placed in the running state.
Remarks
Containers call IRunnableObject::Run to force their objects to enter the running
state. If the object is not already running, calling IRunnableObject::Run can b
e an expensive operation, on the order of many seconds. If the object is already
running, then this method has no effect on the object.
Notes to Callers
When called on a linked object that has been converted to a new class since the
link was last activated, IRunnableObject::Run may return OLE_E_CLASSDIFF.
OleRun is a helper function that conveniently repackages the functionality offer
ed by IRunnableObject::Run. With the release of OLE 2.01, the implementation of
OleRun was changed so that it calls QueryInterface, asks for IRunnableObject, an
d then calls IRunnableObject::Run. In other words, you can use the interface and
the helper function interchangeably.
Notes to Implementers
The object should register in the running object table if it has a moniker assig
ned. The object should not hold any strong locks on itself; instead, it should r
emain in the unstable, unlocked state. The object should be locked when the firs
t external connection is made to the object.
An embedded object must hold a lock on its embedding container while it is in th
e running state. The Default handler provided by OLE 2 takes care of locking the
embedding container on behalf of objects implemented by an EXE object applicati
on.
See Also
OleRun

IRunnableObject::SetContainedObject
Notifies an object that it is embedded in an OLE container, which ensures that r
eference counting is done correctly for containers that support links to embedde
d objects.
HRESULT SetContainedObject(
BOOL fContained //Flag indicating whether object is embedded
);
Parameter
fContained
[in] TRUE specifies that the object is contained in an OLE container. FALSE indi
cates that it is not.
Return Values
This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY AND
E_UNEXPECTED, as well as the following:
S_OK
Object has been marked as a contained embedding.
Remarks
The IRunnableObject::SetContainedObject method enables a container to inform an
object handler that it is embedded in the container, rather than acting as a lin
k. This call changes the container's reference on the object from strong, the de
fault for external connections, to weak. When the object is running visibly, thi
s method is of little significance because the end user has a lock on the object
. During a silent update of an embedded link source, however, the container shou
ld not be able to hold an object in the running state after the link has been br
oken. For this reason, the container's reference to the object must be weak.
Notes to Callers
A container application must call IRunnableObject::SetContainedObject if it supp
orts linking to embedded objects. It normally makes the call immediately after c
alling OleLoad or OleCreate and never calls the method again, even before it clo
ses. Moreover, a container almost always calls this method with fContained set t
o TRUE. The use of this method with fContained set to FALSE is rare.
Calling IRunnableObject::SetContainedObject is optional only when you know that
the embedded object will not be referenced by any client other than the containe
r. If your container application does not support linking to embedded objects; i
t is preferable, but not necessary, to call IRunnableObject::SetContainedObject.
OleSetContainedObject is a helper function that conveniently repackages the func
tionality offered by IRunnableObject::SetContainedObject. With the release of OL
E 2.01, the implementation of OleSetContainedObject was changed to call QueryInt
erface, ask for IRunnableObject, and then call IRunnableObject::SetContainedObje
ct. In other words, you can use the interface and the helper function interchang
eably.
See Also
OleSetContainedObject, OleNoteObjectVisible, CoLockObjectExternal

5.5 COM Library API Descriptions


5.5.1 CoGetMalloc
Retrieves a pointer to the default COM task memory allocator (which supports the
system implementation of the IMalloc interface) so applications can call its me
thods to manage memory.
HRESULT CoGetMalloc(
DWORD dwMemContext, //Indicates if memory is private or shared
LPMALLOC * ppMalloc //Address of output variable that receives a
// pointer to the memory allocator
);
Parameters
dwMemContext
[in] Reserved; value must be 1.
ppMalloc
[out] Indirect pointer to an IMalloc interface onAddress of IMalloc* pointer var
iable that receives the interface pointer to the memory allocator.
Return Values
This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY
, as well as the following:
S_OK
Indicates the allocator was retrieved successfully.
Remarks
The pointer to the IMalloc interface pointer received through the ppMalloc param
eter cannot be used from a remote process¾each process must have its own allocator
.
See Also
IMalloc, CoTaskMemAlloc
5.5.2 CoInitialize
Initializes the COM library on the current apartment and identifies the concurre
ncy model as single-thread apartment (STA). Applications must initialize the COM
library before they can call COM library functions other than CoGetMalloc and m
emory allocation functions.
New applications should call CoInitializeEx instead of CoInitialize. HRESULT CoI
nitialize(
LPVOID pvReserved //Reserved, must be NULL
);
Parameter
pvReserved
[in] Reserved; must be NULL.
Return Values
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, a
nd E_UNEXPECTED, as well as the following:
S_OK
The COM library was initialized successfully on this apartment.
S_FALSE
The COM library is already initialized on this apartment.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified the concurrency model for this apart
ment as multithread apartment (MTA).
Remarks
CoInitializeEx provides the same functionality as CoInitialize and also provides
a parameter to explicitly specify the apartment's concurrency model. CoInitiali
ze calls CoInitializeEx and specifies the concurrency model as single-thread apa
rtment. Applications developed today should call CoInitializeEx rather than CoIn
itialize.
You need to initialize the COM library on an apartment before you call any of th
e library functions except CoGetMalloc, to get a pointer to the standard allocat
or, and the memory allocation functions.
Typically, the COM library is initialized on an apartment only once. Subsequent
calls will succeed, as long as they do not attempt to change the concurrency mod
el, but will return S_FALSE. To close the COM library gracefully, each successfu
l call to CoInitialize or CoInitializeEx, including those that return S_FALSE, m
ust be balanced by a corresponding call to CoUninitialize.
Once the concurrency model for an apartment is set, it cannot be changed. A call
to CoInitialize on an apartment that was previously initialized as multithreade
d will fail and return RPC_E_CHANGED_MODE.
See Also
CoInitializeEx, CoUninitialize, OleInitialize, Processes and Threads
5.5.3 CoInitializeEx
Initializes the COM library for use by the current apartment and specifies the a
partment's concurrency model.
HRESULT CoInitializeEx(
void * pvReserved, //Reserved
DWORD dwCoInit //COINIT value
);
Parameters
pvReserved
[in] Reserved; must be NULL.
dwCoInit
[in] Flags specifying the concurrency model and initialization options for the t
hread. Values for this parameter are taken from the COINIT enumeration. This may
contain any combination of values from the COINIT enumeration, except that the
COINIT_APARTMENTTHREADED and COINIT_MULTITHREADED flags cannot both be set.
Return Values
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, a
nd E_UNEXPECTED, as well as the following:
S_OK
The COM library was initialized successfully.
S_FALSE
The COM library is already initialized.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified a different concurrency model for th
is thread.
Remarks
If neither concurrency model is specified by the dwCoInit parameter, the default
is COINIT_APARTMENTTHREADED.
Objects created on a COM thread in a multithread apartment (MTA) must be able to
receive method calls from other threads at any time. You would typically implem
ent some form of concurrency control in a multithreaded object's code using Win3
2 synchronization primitives such as critical sections, semaphores, or mutexes t
o protect the object's data.
Objects created in a single-threaded apartment (STA) receive method calls only f
rom their apartment's thread, so calls are serialized and arrive only at message
-queue boundaries (PeekMessage, SendMessage).
Apartments must call CoInitializeEx or CoInitialize before calling any other COM
library functions except CoGetMalloc function and other memory allocation calls
(CoTaskMemAlloc, CoTaskMemFree, CoTaskMemReAlloc, and the IMalloc methods on th
e task allocator supplied by CoGetMalloc).
CoInitializeEx provides the same functionality as CoInitialize and also provides
a parameter to explicitly specify the thread's concurrency model. The current i
mplementation of CoInitialize calls CoInitializeEx and specifies the concurrency
model as single-thread apartment. Applications developed today should call CoIn
itializeEx rather than CoInitialize.
Typically, CoInitializeEx is called only once by each apartment in the process t
hat uses the COM library. For a multithread apartment, one call is sufficient fo
r all threads in the apartment.
Multiple calls to CoInitializeEx by the same thread are allowed as long as they
pass the same concurrency flag, but subsequent valid calls return S_FALSE. To cl
ose the library gracefully, each successful call to CoInitialize or CoInitialize
Ex, including calls that return S_FALSE, must be balanced by a corresponding cal
l to CoUninitialize.
Once the concurrency model for an apartment is set, it cannot be changed. A call
to CoInitializeEx on an apartment that was previously initialized with a differ
ent concurrency model will fail and return RPC_E_CHANGED_MODE.
Because COM technologies are not thread-safe, the OleInitialize function calls C
oInitializeEx with the COINIT_APARTMENTTHREADED flag. As a result, an apartment
that is initialized for multithreaded object concurrency cannot use the features
enabled by OleInitialize.
See Also
COINIT, CoInitialize, Processes and Threads
5.5.4 CoTaskMemAlloc
Allocates a block of task memory in the same way that IMalloc::Alloc does.
LPVOID CoTaskMemAlloc(
ULONG cb //Size in bytes of memory block to be allocated
);
Parameter
cb
[in] Size, in bytes, of the memory block to be allocated.
Return Values
Allocated memory block
Memory block allocated successfully.
NULL
Insufficient memory available.
Remarks
The CoTaskMemAlloc function uses the default allocator to allocate a memory bloc
k in the same way that IMalloc::Alloc does. It is not necessary to call the CoGe
tMalloc function before calling CoTaskMemAlloc.
The initial contents of the returned memory block are undefined there is no guar
antee that the block has been initialized. The allocated block may be larger tha
n cb bytes because of the space required for alignment and for maintenance infor
mation.
If cb is zero, CoTaskMemAlloc allocates a zero-length item and returns a valid p
ointer to that item. If there is insufficient memory available, CoTaskMemAlloc r
eturns NULL.
Note
Applications should always check the return value from this method, even when re
questing small amounts of memory, because there is no guarantee the memory will
be allocated.
See Also
IMalloc::Alloc, CoGetMalloc, CoTaskMemFree, CoTaskMemRealloc
5.5.5 CoTaskMemFree
Frees a block of task memory previously allocated through a call to the CoTaskMe
mAlloc or CoTaskMemRealloc function.
void CoTaskMemFree(
void pv //Pointer to memory block to be freed
);
Parameter
pv
[in] Pointer to the memory block to be freed.
Remarks
The CoTaskMemFree function, using the default COM allocator, frees a block of me
mory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemReall
oc function.
The number of bytes freed equals the number of bytes that were originally alloca
ted 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, IMalloc::Free
5.5.6 CoTaskMemRealloc
Changes the size of a previously allocated block of task memory.
LPVOID CoTaskMemRealloc(
LPVOID pv, //Pointer to memory block to be reallocated
ULONG cb //Size of block to be reallocated
);
Parameters
pv
[in] Pointer to the memory block to be reallocated. It can be a NULL pointer, as
discussed in the Remarks.
cb
[in] Size, in bytes, of the memory block to be reallocated. It can be zero, as d
iscussed in the following remarks.
Return Values
Reallocated memory block
Memory block successfully reallocated.
NULL
Insufficient memory or cb is zero and pv is not NULL.
Remarks
The CoTaskMemRealloc function changes the size of a previously allocated memory
block in the same way that IMalloc::Realloc does. It is not necessary to call th
e CoGetMalloc function to get a pointer to the COM allocator before calling CoTa
skMemRealloc.
The pv argument points to the beginning of the memory block. If pv is NULL, CoTa
skMemRealloc allocates a new memory block in the same way as the CoTaskMemAlloc
function. If pv is not NULL, it should be a pointer returned by a prior call to
CoTaskMemAlloc.
The cb argument specifies the size (in bytes) of the new block. The contents of
the block are unchanged up to the shorter of the new and old sizes, although the
new block can be in a different location. Because the new block can be in a dif
ferent memory location, the pointer returned by CoTaskMemRealloc is not guarante
ed 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 argume
nt is not NULL, or if there is not enough memory available to expand the block t
o 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 al
igned for storage of any type of object. To get a pointer to a type other than v
oid, use a type cast on the return value.
See Also
CoTaskMemAlloc, CoTaskMemFree, CoGetMalloc, IMalloc::Realloc
5.5.7 CoUninitialize
Closes the COM library on the current apartment, unloads all DLLs loaded by the
apartment, frees any other resources that the apartment maintains, and forces al
l RPC connections on the apartment to close.
void CoUninitialize( );
Remarks
CoUninitializeAn apartment must call CoUninitialize once for each successful cal
l it has made to CoInitialize or CoInitializeEx. Only the CoUninitialize or CoIn
itializeEx call corresponding to the CoInitialize call that initialized the libr
ary can close it.
Calls to OleInitialize must be balanced by calls to OleUnitialize. The OleUninit
ialize function calls CoUninitialize internally, so applications that call OleUn
initialize do not also need to call CoUninitialize.
CoUninitialize should be called on application shutdown, as the last call made t
o the COM library after the application hides its main windows and falls through
its main message loop. If there are open conversations remaining, CoUninitializ
e starts a modal message loop and dispatches any pending messages from the conta
iners or server for this COM application. By dispatching the messages, CoUniniti
alize ensures that the application does not quit before receiving all of its pen
ding messages. Non-COM messages are discarded.
See Also
CoInitialize, CoInitializeEx
5.5.8 CoRegisterMallocSpy
Registers an implementation of the IMallocSpy interface in COM, thereafter requi
ring COM to call its wrapper methods around every call to the corresponding IMal
loc method. IMallocSpy is defined in COM to allow developers to debug memory all
ocations.
HRESULT CoRegisterMallocSpy(
LPMALLOCSPY pMallocSpy //Pointer to the interface
);
Parameter
pMallocSpy
[in] Pointer to an instance of the IMallocSpy implementation.
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
The IMallocSpy object is successfully registered.
CO_E_OBJISREG
There is already a registered spy.
Remarks
The CoRegisterMallocSpy function registers the IMallocSpy object, which is used
to debug calls to IMalloc methods. The function calls QueryInterface on the poin
ter pMallocSpy for the interface IID_IMallocSpy. This is to ensure that pMallocS
py really points to an implementation of IMallocSpy. By the rules of COM, it is
expected that a successful call to QueryInterface has added a reference (through
the AddRef method) to the IMallocSpy object. That is, CoRegisterMallocSpy does
not directly call AddRef on pMallocSpy, but fully expects that the QueryInterfac
e call will.
When the IMallocSpy object is registered, whenever there is a call to one of the
IMalloc methods, COM first calls the corresponding IMallocSpy pre-method. Then,
after executing the IMalloc method, COM calls the corresponding IMallocSpy post
-method. For example, whenever there is a call to IMalloc::Alloc, from whatever
source, COM calls IMallocSpy::PreAlloc, calls IMalloc::Alloc, and after that all
ocation is completed, calls IMallocSpy::PostAlloc.
See Also
IMallocSpy, CoRevokeMallocSpy, CoGetMalloc
5.5.9 CoRevokeMallocSpy
Revokes a registered IMallocSpy object.
HRESULT CoRevokeMallocSpy( );
Return Values
S_OK
The IMallocSpy object is successfully revoked.
CO_E_OBJNOTREG
No spy is currently registered.
E_ACCESSDENIED
Spy is registered but there are outstanding allocations (not yet freed) made whi
le this spy was active.
Remarks
The IMallocSpy object is released when it is revoked. This release corresponds t
o the call to IUnknown::AddRef in the implementation of the QueryInterface funct
ion by the CoRegisterMallocSpy function. The implementation of the IMallocSpy in
terface should then do any appropriate cleanup.
If the return code is E_ACCESSDENIED, there are still outstanding allocations th
at were made while the spy was active. In this case, the registered spy cannot b
e revoked at this time because it may have attached arbitrary headers and/or tra
ilers to these allocations that only the spy knows about. Only the spy s PreFree (
or PreRealloc) method knows how to account for these headers and trailers. Befor
e returning E_ACCESSDENIED, CoRevokeMallocSpy notes internally that a revoke is
pending. When the outstanding allocations have been freed, the revoke proceeds a
utomatically, releasing the IMallocSpy object. Thus, it is necessary to call CoR
evokeMallocSpy only once for each call to CoRegisterMallocSpy, even if E_ACCESSD
ENIED is returned.
See Also
IMallocSpy, CoRegisterMallocSpy, CoGetMalloc
5.5.10 CLSIDFromProgID
Looks up a CLSID in the registry, given a ProgID.
HRESULT CLSIDFromProgID(
LPCOLESTR lpszProgID, //Pointer to the ProgID
LPCLSID pclsid //Pointer to the CLSID
);
Parameters
lpszProgID
[in] Pointer to the ProgID whose CLSID is requested.
pclsid
[out] Pointer to the retrieved CLSID on return.
Return Values
S_OK
The CLSID was retrieved successfully.
CO_E_CLASSSTRING
The registered CLSID for the ProgID is invalid.
REGDB_E_WRITEREGDB
An error occurred writing the CLSID to the registry. See Remarks below.
Remarks
Given a ProgID, CLSIDFromProgID looks up its associated CLSID in the registry. I
f the ProgID cannot be found in the registry, CLSIDFromProgID creates an OLE 1 C
LSID for the ProgID and a CLSID entry in the registry. Because of the restrictio
ns placed on OLE 1 CLSID values, CLSIDFromProgID and CLSIDFromString are the on
ly two functions that can be used to generate a CLSID for an OLE 1 object.
See Also
ProgIDFromCLSID

5.5.11 CLSIDFromString
Converts a string generated by the StringFromCLSID function back into the origin
al CLSID.
HRESULT CLSIDFromString(
LPOLESTR lpsz, //Pointer to the string representation of the CLSID
LPCLSID pclsid //Pointer to the CLSID
);
Parameters
lpsz
[in] Pointer to the string representation of the CLSID.
pclsid
[out] Pointer to the CLSID on return.
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
NOERROR
The CLSID was obtained successfully.
CO_E_CLASSTRING
The class string was improperly formatted.
REGDB_E_WRITEREGDB
The CLSID corresponding to the class string was not found in the registry.
Remarks
Because of the restrictions placed on OLE 1 CLSID values, CLSIDFromProgID and CL
SIDFromString are the only two functions that can be used to generate a CLSID fo
r an OLE 1 object.
See Also
CLSIDFromProgID, StringFromCLSID

5.5.12 CoCreateGuid
Creates a GUID, a unique 128-bit integer used for CLSIDs and interface identifie
rs.
HRESULT CoCreateGuid(
GUID *pguid //Pointer to the GUID on return
);
Parameter
pguid
[out] Pointer to the requested GUID on return.
Return Value
S_OK
The GUID was successfully created.
Win32 errors are returned by UuidCreate but wrapped as an HRESULT.
Remarks
The CoCreateGuid function calls the RPC function UuidCreate, which creates a GUI
D, a globally unique 128-bit integer. Use the CoCreateGuid function when you nee
d an absolutely unique number that you will use as a persistent identifier in a
distributed environment.To a very high degree of certainty, this function return
s a unique value no other invocation, on the same or any other system (networked
or not), should return the same value.
See Also
UuidCreate (documented in the RPC Programmer s Guide and Reference)

5.5.13 CoDosDateTimeToFileTime
Converts the MS-DOS representation of the time and date to a FILETIME structure,
which Win32 uses to determine the date and time.
BOOL CoDosDateTimeToFileTime(
WORD nDosDate, //16-bit MS-DOS date
WORD nDosTime, //16-bit MS-DOS time
FILETIME * lpFileTime //Pointer to the structure
);
Parameters
nDosDate
[in] 16-bit MS-DOS date.
nDosTime
[in] 16-bit MS-DOS time.
lpFileTime
[out] Pointer to the FILETIME structure.
Return Values
TRUE
The FILETIME structure was created successfully.
FALSE
The FILETIME structure was not created successfully, probably because of invalid
arguments.
Remarks
The FILETIME structure and the CoDosDateTimeToFileTime and CoFileTimeToDosDateTi
me functions are part of the Win32 API definition. They are provided for compati
bility in all COM 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

5.5.14 CoFileTimeNow
Returns the current time as a FILETIME structure.
HRESULT CoFileTimeNow(
FILETIME * lpFileTime //Pointer to return the structure
);
Parameter
lpFileTime
[out] Pointer to return the FILETIME structure.
Return Values
S_OK
The current time was converted to a FILETIME structure.
See Also
CoDosDateTimeToFileTime, CoFileTimeToDosDateTime
5.5.15 CoFileTimeToDosDateTime
Converts a FILETIME into MS-DOS date and time values.
BOOL CoFileTimeToDosDateTime(
FILETIME * lpFileTime, //Pointer to the structure to be converted
LPWORD lpDosDate, //Pointer to the 16-bit MS-DOS date
LPWORD lpDosTime //Pointer to the 16-bit MS-DOS time
);
Parameters
lpFileTime
[in] Pointer to the FILETIME structure to be converted.
lpDosDate
[out] Pointer to the 16-bit MS-DOS date.
lpDosTime
[out] Pointer to the 16-bit MS-DOS time.
Return Values
TRUE
The FILETIME structure was converted successfully.
FALSE
The FILETIME structure was not converted successfully.
Remarks
This is the inverse of the operation provided by the CoDosDateTimeToFileTime fun
ction.
See Also
CoDosDateTimeToFileTime, CoFileTimeNow

5.5.16 CoGetCurrentProcess
Returns a value that is unique to the current thread. It can be used to avoid PR
OCESSID reuse problems.
DWORD CoGetCurrentProcess( );
Return Value
DWORD value
Unique value for the current thread that can be used to avoid PROCESSID reuse pr
oblems.
Remarks
The CoGetCurrentProcess function returns a value that is effectively unique, bec
ause it is not used again until 232 more threads have been created on the curren
t workstation or until the workstation is rebooted.
Using the value returned from a call to CoGetCurrentProcess can help you maintai
n tables that are keyed by threads or in uniquely identifying a thread to other
threads or processes.
Using the value returned by CoGetCurrentProcess is more robust than using the HT
ASK task handle value returned by the Win32 function GetCurrentTask, because Win
dows task handles can be reused relatively quickly when a window s task dies.
5.5.17 IIDFromString
Converts a string generated by the StringFromIID function back into the original
interface identifier (IID).
WINOLEAPI IIDFromString(
LPOLESTR lpsz, //Pointer to the string representation of the IID
LPIID lpiid //Pointer to the requested IID on return
);
Parameters
lpsz
[in] Pointer to the string representation of the IID.
lpiid
[out] Pointer to the requested IID on return.
Return Values
This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY
, as well as the following:
S_OK
The string was successfully converted.
Remarks
The function converts the interface identifier in a way that guarantees differen
t interface identifiers will always be converted to different strings.
See Also
StringFromIID
5.5.18 IsEqualGUID
Determines whether two GUIDs are equal.
BOOL IsEqualGUID(
REFGUID rguid1, //GUID to compare to rguid2
REFGUID rguid2 //GUID to compare to rguid1
);
Parameters
rguid1
[in] GUID to compare to rguid2.
rguid2
[in] GUID to compare to rguid1.
Return Values
TRUE
The GUIDs are equal.
FALSE
The GUIDs are not equal.
Remarks
IsEqualGUID is used by the IsEqualCLSID and IsEqualIID functions.
See Also
IsEqualCLSID, IsEqualIID
5.5.19 IsEqualCLSID
Determines whether two CLSIDs are equal.
BOOL IsEqualCLSID(
REFCLSID rclsid1, //CLSID to compare to rclsid2
REFCLSID rclsid2 //CLSID to compare to rclsid1
);
Parameters
rclsid1
[in] CLSID to compare to rclsid2.
rclsid2
[in] CLSID to compare to rclsid1.
Return Values
TRUE
The CLSIDs are equal.
FALSE
The CLSIDs are not equal.
See Also
IsEqualGUID, IsEqualIID
5.5.20 IsEqualIID
Determines whether two interface identifiers are equal.
BOOL IsEqualIID(
REFGUID riid1, //Interface identifier to compare to riid2
REFGUID riid2 //Interface identifier to compare to riid1
);
Parameters
riid1
[in] Interface identifier to compare with riid2.
riid2
[in] Interface identifier to compare with riid1.
Return Values
TRUE
The interface identifiers are equal.
FALSE
The interface identifiers are not equal.
See Also
IsEqualGUID, IsEqualCLSID
5.5.21 GetRunningObjectTable
Supplies a pointer to the IRunningObjectTable interface on the local Running Obj
ect Table (ROT).
WINOLEAPI GetRunningObjectTable(
DWORD reserved, //Reserved
LPRUNNINGOBJECTTABLE *pprot //Address of output variable that receives the
// IRunningObjectTable interface pointer
);
Parameters
reserved
[in] Reserved for future use; must be zero.
pprot
[out] Address of IRunningObjectTable* pointer variable that receives the interfa
ce pointer to the local ROT. When the function is successful, the caller is resp
onsible for calling IUnknown::Release on the interface pointer. If an error occu
rs, pprot is undefined.
Return Values
This function supports the standard return value E_UNEXPECTED, as well as the fo
llowing:
S_OK
An IRunningObjectTable pointer was successfully returned.
Remarks
Each workstation has a local ROT that maintains a table of the objects that have
been registered as running on that machine. This function returns an IRunningOb
jectTable interface pointer, which provides access to that table.
Moniker providers, which hand out monikers that identify objects so they are acc
essible to others, should call GetRunningObjectTable. Use the interface pointer
returned by this function to register your objects when they begin running, to r
ecord the times that those objects are modified, and to revoke their registratio
ns when they stop running. See the IRunningObjectTable interface for more inform
ation.
Compound-document link sources are the most common example of moniker providers.
These include server applications that support linking to their documents (or p
ortions of a document) and container applications that support linking to embedd
ings within their documents. Server applications that do not support linking can
also use the ROT to cooperate with container applications that support linking
to embeddings.
If you are implementing the IMoniker interface to write a new moniker class, and
you need an interface pointer to the ROT, call IBindCtx::GetRunningObjectTable
rather than the GetRunningObjectTable function. This allows future implementatio
ns of the IBindCtx interface to modify binding behavior.
See Also
IBindCtx::GetRunningObjectTable, IMoniker, IRunningObjectTable

5.5.22 ProgIDFromCLSID
Retrieves the ProgID for a given CLSID.
WINOLEAPI ProgIDFromCLSID(
REFCLSID clsid, //CLSID for which the ProgID is requested
LPOLESTR * lplpszProgID //Address of output variable that receives a

// pointer // pointer to the requested ProgID string


)
);
Parameters
clsid
[in] Specifies the CLSID for which the ProgID is requested.
lplpszProgID
[out] Address of LPOLESTR pointer variable that receives a pointer to the ProgID
string.
Return Values
S_OK
The ProgID was returned successfully.
REGDB_E_CLASSNOTREG
Class not registered in the registry.
REGDB_E_READREGDB
Error reading registry.
Remarks
Every COM object class listed in the Insert Object dialog box must have a progra
mmatic identifier (ProgID), a string that uniquely identifies a given class, sto
red in the registry. In addition to determining the eligibility for the Insert O
bject 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 f
or an object of an COM class that is placed in an OLE 1 container.
The ProgIDFromCLSID function uses entries in the registry to do the conversion.
COM application authors are responsible for ensuring that the registry is config
ured correctly in the application s setup program.
The ProgID string must be different than the class name of any OLE 1 application
, including the OLE 1 version of the same application, if there is one. In addit
ion, a ProgID string must not contain more than 39 characters, start with a digi
t, or, except for a single period, contain any punctuation (including underscore
s).
The ProgID must never be shown to the user in the user interface..
Call the CLSIDFromProgID function to find the CLSID associated with a given Prog
ID. CLSIDs can be freed with the task allocator (refer to the CoGetMalloc functi
on).
See Also
CLSIDFromProgID

5.5.23 StringFromCLSID
Converts a CLSID into a string of printable characters. Different CLSIDs always
convert to different strings.
WINOLEAPI StringFromCLSID(
REFCLSID rclsid, //CLSID to be converted
LPOLESTR * ppsz //Address of output variable that receives a
// pointer to the resulting string
);
Parameters
rclsid
[in] CLSID to be converted.
ppsz
[out] Address of LPOLESTR pointer variable that receives a pointer to the result
ing string.
Return Values
This function supports the standard return value E_OUTOFMEMORY; as well as the f
ollowing:
S_OK
Indicates the CLSID was successfully converted and returned.
Remarks
The StringFromCLSID function calls the StringFromGuid2 function to convert a glo
bally unique identifier (GUID) into a string of printable characters.
See Also
CLSIDFromString, StringFromGuid2

5.5.24 StringFromGUID2
Converts a globally unique identifier (GUID) into a string of printable characte
rs.
StringFromGUID2(
REFGUID rguid, //Interface identifier to be converted
LPOLESTR lpsz, //Pointer to the resulting string on return
int cbMax //Maximum size the returned string is expected to be
);
Parameters
rguid
[in] Interface identifier to be converted.
lpsz
[out] Pointer to the resulting string on return.
cbMax
[in] Maximum size the returned string is expected to be.
Return Values
0 (zero)
Buffer is too small for returned string.
Non-zero value
The number of characters in the returned string, including the null terminator.
Remarks
The string that the lpsz parameter receives has a format like that of the follow
ing sample:
{c200e360-38c5-11ce-ae62-08002b2b79ef}
where the successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WO
RD.DWORD covering the 128-bit GUID. The string includes enclosing braces, which
are a COM convention.
See Also
StringFromCLSID

5.5.25 StringFromIID
Converts an interface identifier into a string of printable characters.
WINOLEAPI StringFromIID(
REFIID rclsid, //Interface identifier to be converted
LPOLESTR * lplpsz //Address of output variable that receives a
// pointer to the resulting string
);
Parameters
rclsid
[in] Interface identifier to be converted.
lplpsz
[out] Address of LPOLESTR pointer variable that receives a pointer to the result
ing string.
Return Values
This function supports the standard return value E_OUTOFMEMORY; as well as the f
ollowing:
S_OK
The character string was successfully returned.
Remarks
The string returned by the function is freed in the standard way, using the task
allocator (refer to the CoGetMallocfunction).
See Also
IIDFromString, CoGetMalloc
5.5.26 CoRegisterMessageFilter
Registers with OLE the instance of an EXE application's IMessageFilter interface
, which is to be used for handling concurrency issues. DLL object applications c
annot register a message filter.
HRESULT CoRegisterMessageFilter(

LPMALLOCSPY lpMessageFilter //Pointer to the interface


LPMESSAGEFILTER * lplpMessageFilter //Address of output variable that
//receives the IMessageFilter
//interface pointer of a previously
); //registered message filter

Parameters
lpMessageFilter
[in] Pointer to theIMessageFilter interface on the message filter supplied by th
e application. Can be NULL, indicating that the current IMessageFilter registrat
ion should be revoked.
lplpMessageFilter
[out] Address of IMessageFilter* pointer variable that receives the interface po
inter to the previously registered message filter. If there was no previously re
gistered message filter, the value of *lplpMessageFilter is NULL. The value cont
ained in the output variable is rarely NULL, however, containing instead a point
er to the default message filter.
Return Values
S_OK
The IMessageFilter instance registered or revoked successfully.
S_FALSE
Error registering or revoking IMessageFilter instance.

5.6 COM Library Enumeration Definitions


5.6.1 COINIT
A set of values from the COINIT enumeration is passed as the dwCoInit parameter
to CoInitializeEx. This value determines the concurrency model used for incoming
calls to objects created by this thread. This concurrency model can be either a
partment-threaded or multi-threaded.
The COINIT enumeration is defined as follows:
typedef enum tagCOINIT{
COINIT_APARTMENTTHREADED = 0x2, // Apartment model
COINIT_MULTITHREADED = 0x0, // COM calls objects on any thread.
} COINIT;
Elements
COINIT_MULTITHREADED
Initializes the thread for multi-threaded object concurrency (see Remarks).
COINIT_APARTMENTTHREADED
Initializes the thread for apartment-threaded object concurrency (see Remarks).
Remarks
When a thread is initialized through a call to CoInitializeEx, you choose whethe
r to initialize it as apartment-threaded or multi-threaded by designating one of
the members of COINIT as its second parameter. This designates how incoming cal
ls to any object created by that thread are handled, that is, the object s concurr
ency.
Apartment-threading, while allowing for multiple threads of execution, serialize
s all incoming calls by requiring that calls to methods of objects created by th
is thread always run on the same thread the apartment/thread that created them.
In addition, calls can arrive only at message-queue boundaries (i.e., only durin
g a PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serializat
ion, it is not typically necessary to write concurrency control into the code fo
r the object, other than to avoid calls to PeekMessage and SendMessage during pr
ocessing that must not be interrupted by other method invocations or calls to ot
her objects in the same apartment/thread.
Multi-threading (also called free-threading) allows calls to methods of objects
created by this thread to be run on any thread. There is no serialization of cal
ls many calls may occur to the same method or to the same object or simultaneous
ly. Multi-threaded object concurrency offers the highest performance and takes t
he best advantage of multi-processor hardware for cross-thread, cross-process, a
nd cross-machine calling, since calls to objects are not serialized in any way.
This means, however, that the code for objects must enforce its own concurrency
model, typically through the use of Win32 synchronization primitives, such as cr
itical sections, semaphores, or mutexes. In addition, because the object doesn t c
ontrol the lifetime of the threads that are accessing it, no thread-specific sta
te may be stored in the object (in Thread-Local-Storage).
See Also
CoInitializeEx, Processes and Threads

6. COM Threading Models


A process is a collection of virtual memory space, code, data, and system resour
ces, while a thread is code that is to be serially executed within a process. A
processor executes threads, not processes, so each application has at least one
process and one thread. Prior to the introduction of multiple threads of executi
on, applications were all designed to run on a single thread of execution. Proce
sses communicate with one another through messages, using RPC to pass informatio
n between processes. There is no difference to the caller in a call coming from
a process on a remote machine, and a call from another process on the same machi
ne.
While COM defines three multiple-threading models, some information applies to t
hreads and processes in general. A process always has at least one thread of exe
cution, known as the primary thread, and can have multiple threads in addition t
o this. Once a thread begins to execute, it continues until it is killed or unti
l it is interrupted by a thread with higher priority (by a user action or the ke
rnel s thread scheduler). Each thread can run separate sections of code, or multip
le threads can execute the same section of code. Threads executing the same bloc
k of code maintain separate stacks. Each thread in a process shares that process s
global variables and resources.
When and how often to a thread is executed is platform dependent.
Multi-threaded applications must avoid two threading problems: deadlocks and rac
es. A deadlock occurs when each thread is waiting for the other to do something.
A race condition occurs when one thread finishes before another on which it dep
ends, causing the former to use a bogus value because the latter has not yet sup
plied a valid one.
The COM call control helps prevent deadlocks in calls between objects. COM suppl
ies some functions specifically designed to help avoid race conditions in out-of
-process servers; for information refer to Out-of-process Server Implementation
Helpers.
While COM supports the single-thread-per-process model prevalent before the intr
oduction of multiple threads of execution, writing code to take advantage of mul
tiple threads make it possible to create more efficient applications than ever b
efore by allowing a thread that is waiting for some time-consuming operation to
allow another thread to be executed.
It is worth noting that using multiple threads is not a guarantee of better perf
ormance. In fact, because thread-factoring is a difficult problem, using multiple
threads often causes performance problems . The key is to use multiple threads o
nly if you are very sure of what you are doing.
In general, the simplest way to view COM s threading architecture is to think of a
ll the COM objects in the process as divided into groups called apartments. A CO
M object lives in exactly one apartment, in the sense that its methods can legal
ly be called directly only by a thread that belongs to that apartment. Any other
thread that wants to call the object must go through a proxy.
There are two types of apartments: single-threaded apartments, and multi-threade
d apartments.
Single-threaded Apartments each thread that uses COM is in a separate apartment , a
nd COM synchronizes all incoming calls with the windows message queue. A process
with a single thread of execution is simply a special case of this model.
Multi-threaded Apartments Multiple threads in a single free-threaded apartment u
se COM and calls to COM objects are synchronized by the objects themselves.
A description of communication between single-threaded apartments and multi-thre
aded apartments within the same process is in Single-/Multi-threaded Communicati
on.
Single-threaded apartments consist of exactly one thread, so all COM objects tha
t live in a single-threaded apartment can receive method calls only from the one
thread that belongs to that apartment. All method calls to a COM object in a si
ngle-threaded apartment are synchronized with the windows message queue for the
single-threaded apartment s thread.
Multi-threaded apartments consist of one or more threads, so all COM objects tha
t live in an multi-threaded apartment can receive method calls directly from any
of the threads that belong to the multi-threaded apartment. Threads in a multi-
theaded apartment use a model called free-threading . COM does not provide any sync
hronization of method calls to COM objects in a multi-threaded apartment. In par
ticular, this means that the COM object must provide it s own synchronization if n
eeded.
A process can have zero or more single-threaded apartments, and zero or one mult
i-threaded apartment. One way of looking at this is the following:
A process that consists of just one single-threaded apartment is referred to as
a single-threaded process
A process that has two or more single-threaded apartments and no multi-threaded
apartments is called an apartment model process
A process that has a multi-threaded apartment and no single-threaded apartments
is referred to as a free-threaded process
A process that has a multi-threaded apartment and one or more single-threaded ap
artments is a mixed model process.
In reality, however, all process are apartment-model, it is just that some apart
ments have a single thread and some apartments have multiple threads. The thread
ing model really applies to an apartment, not to a process. It can also apply to
a class of objects, but it doesn t really apply to a component, such as a DLL, bu
t to the object classes within the DLL. Different classes in a DLL can have diff
erent threading models.
In a process, the main apartment is the first to be initialized. In a single-thr
eaded process, this remains the only apartment. Call parameters are marshaled be
tween apartments, and COM handles the synchronization through messaging. If you
designate multiple threads in a process to be free-threaded, all free threads re
side in a single apartment, parameters are passed directly to any thread in the
apartment, and you must handle all synchronization. In a process with both free-
threading and apartment threading, all free threads reside in a single apartment
, and all other apartments are single-threaded apartments. A process that does C
OM work is a collection of apartments with, at most, one multi-threaded apartmen
t but any number of single-threaded apartments.
The threading models in COM provide the mechanism for clients and servers that u
se different threading architectures to work together. Calls among objects with
different threading models in different processes are naturally supported. From
the perspective of the calling object, all calls to objects outside a process be
have identically, no matter how the object being called is threaded. Likewise, f
rom the perspective of the object being called, arriving calls behave identicall
y, regardless of the threading model of the caller.
Interaction between a client and an out-of-process object is straightforward eve
n when they use different threading models because the client and object are in
different processes and COM is involved in remoting calls from the client to the
object. COM, interposed between the client and the server, can provide the code
for the threading models to interoperate, with standard marshaling and RPC. For
example, if a single-threaded object is called simultaneously by multiple free-
threaded clients, the calls will be synchronized by COM by placing corresponding
window messages in the server's message queue. The object's apartment will rece
ive one call each time it retrieves and dispatches messages.
Some care must be taken to ensure that in-process servers interact properly with
their clients. These issues are described in In-process Server Threading Issues
.
The most important issue in programming with a multithreaded model is to ensure
that the code is thread-safe, so messages intended for a particular thread go on
ly to that thread, and access to threads is protected.
6.1 Choosing the Threading Model
Choosing the threading model for an object depends on the object s function. An ob
ject that does extensive I/O might support free-threading to provide maximum res
ponse to clients by allowing interface calls during I/O latency. On the other ha
nd, an object that interacts with the user might support apartment threading to
synchronize incoming COM calls with its window operations.
It is easier to support apartment threading in single-threaded apartments becaus
e COM provides synchronization. Supporting free-threading is more difficult beca
use the object must implement synchronization, but response to clients may be be
tter because synchronization can be implemented for smaller sections of code. In
single-threaded apartments, COM provides synchronization on a per-call basis.
6.2 Single-threaded Apartments
Using single-threaded apartments (apartment model) offers a message-based paradi
gm for dealing with multiple objects running concurrently. It allows you to writ
e more efficient code by allowing a thread that is waiting for some time-consumi
ng operation to allow another thread to be executed.
Each thread in a process that is initialized as apartment-model, and which retri
eves and dispatches window messages, is a single-threaded apartment thread. Each
of these threads live within its own apartment. Within an apartment, interface
pointers can be passed without marshaling. Thus, all objects in one single-threa
ded apartment thread communicate directly. Interface pointers must be marshaled
when passed between apartments.
A logical grouping of related objects that all execute on the same thread, and s
o must have synchronous execution could live on the same single-threaded apartme
nt thread. An apartment-model object cannot, however, reside on more than one th
read. Calls to objects in other processes must be made within the context of the
owning process, so distributed COM switches threads for you automatically when
you call on a proxy.
The inter-process and inter-thread models are similar. When it is necessary to p
ass an interface pointer to an object in another apartment (on another thread) w
ithin the same process, you use the same marshaling model that objects in differ
ent processes use to pass pointers across process boundaries. By getting a point
er to the standard marshaling object, you can marshal interface pointers across
thread boundaries (between apartments) in the same way you do between processes.
Rules for single-threaded apartments are simple, but it is important to follow t
hem carefully:
Every object should live on only one thread (within a single-threaded apartment)
.
Initialize the COM library for each thread.
Marshal all pointers to objects when passing them between apartments.
Each single-threaded apartment must allow the, platform specific, message pump t
o handle calls from other processes and apartments within the same process.
DLL-based or in-process objects do not call the COM initialization functions; in
stead, they register their threading model with the ThreadingModel named-value u
nder the InprocServer32 key in the registry. Apartment-aware objects must also w
rite DLL entry points carefully. There are special considerations that apply to
threading in-process servers. For more information, see In-process Server Thread
ing Issues.
While multiple objects can live on a single thread, no apartment-model object ca
n live on more than one thread.
Each thread of a client process or out-of-process server must call or call CoIni
tializeEx, and specify COINIT_APARTMENTTHREADED for the dwCoInit parameter. The
main apartment is the thread that calls CoInitializeEx first. For information on
in-process servers, refer to In-process Server Threading Issues.
All calls to an object must be made on its thread (within its apartment). It is
forbidden to call an object directly from another thread; using objects in this
free-threaded manner could cause problems for applications. The implication of t
his rule is that all pointers to objects must be marshaled when passed between a
partments. COM provides two functions for this purpose. CoMarshalInterThreadInte
rfaceInStream, marshals an interface into a stream object that is returned to th
e caller, and CoGetInterfaceAndReleaseStream unmarshals an interface pointer fro
m a stream object and releases it. These functions wrap calls to CoMarshalInterf
ace and CoUnmarshalInterface functions, which require the use of the MSHCTX_INPR
OC flag.
In general, the marshaling is accomplished automatically by COM. For example, wh
en passing an interface pointer as a parameter in a method call on a proxy to an
object in another apartment, or when calling CoCreateInstance, COM does the mar
shaling automatically. However, in some special cases, where the application wri
ter is passing interface pointers between apartments without using the normal CO
M mechanisms, the application writer must be aware that he is passing a pointer
between apartments, and must handle the marshaling himself.
If one apartment (Apartment 1) in a process has an interface pointer and another
apartment (Apartment 2) requires its use, Apartment 1 must call CoMarshalInterT
hreadInterfaceInStream to marshal the interface. The stream that is created by t
his function is thread-safe and must be stored in a variable that is accessible
by Apartment 2. Apartment 2 must pass this stream to CoGetInterfaceAndReleaseStr
eam to unmarshal the interface, and will get back a pointer to a proxy through w
hich it can access the interface. The main apartment must remain alive until the
client has completed all COM work (because some in-process objects are loaded i
n the main-apartment, as described in In-process Server Threading Issues. After
one object has been passed between threads in this manner, it is very easy to pa
ss interface pointers as parameters. That way distributed COM does the marshalin
g and thread switching for the application.
To handle calls from other processes and apartments within the same process, eac
h single-threaded apartment must have a message loop. This means that the thread s
work function must have a message loop.
COM creates a hidden message sink in each single-threaded apartment. A call to a
n object is received as a message to this message sink. When the object's apartm
ent retrieves and dispatches the message, the hidden window will receive it. The
window procedure will then call the corresponding interface method of the objec
t.
When multiple clients call an object, the calls are queued in the message queue
and the object will receive a call each time its apartment retrieves and dispatc
hes messages. Because the calls are synchronized by COM and the calls are always
delivered by the thread that belongs to the object's apartment, the object's in
terface implementations need not provide synchronization. Single-threaded apartm
ents can implement IMessageFilter to permit them to cancel calls or receive mess
ages when necessary.
The object can be re-entered if one of its interface method implementations retr
ieves and dispatches messages or makes an ORPC call to another thread, thereby c
ausing another call to be delivered to the object (by the same apartment). COM d
oes not prevent re-entrancy on the same thread but it provides thread safety. Th
is is identical to the way in which a window procedure can be re-entered if it r
etrieves and dispatches messages while processing a message.
6.3 Multi-threaded Apartments
In a multi-threaded apartment, all the threads in the process that have been ini
tialized as free-threading reside in a single apartment. Therefore, there is no
need to marshal between threads. The threads need not retrieve and dispatch mess
ages because COM does not use messages in this model.
Calls to methods of objects in the multi-threaded apartment can be run on any th
read in the apartment. There is no serialization of calls many calls may occur t
o the same method or to the same object simultaneously. Objects created in the m
ulti-threaded apartment must be able to handle calls on their methods from other
threads at any time.
Multi-threaded object concurrency offers the highest performance and takes the b
est advantage of multi-processor hardware for cross-thread, cross-process, and c
ross-machine calling, since calls to objects are not serialized in any way. This
means, however, that the code for objects must provide synchronization in their
interface implementations, typically through the use of platform dependent sync
hronization primitives. In addition, because the object doesn t control the lifeti
me of the threads that are accessing it, no thread-specific state may be stored
in the object (in Thread-Local-Storage).
COM provides call synchronization for single-threaded apartments only. Multi-thr
eaded apartments (containing free-threaded threads) do not receive calls while m
aking calls (on the same thread). Multi-threaded apartments cannot make input sy
nchronized calls. Asynchronous calls are converted to synchronous calls in multi
-threaded apartments. The message filter is not called for any thread in a multi
-threaded apartment.
To initialize a thread as free-threaded, call CoInitializeEx, specifying COINIT_
MULTITHREADED. For information on in-process server threading, see In-process Se
rver Threading Issues.
Multiple clients can call an object that supports free-threading simultaneously
from different threads: In free threaded out-of-process servers, COM, through th
e RPC sub-system, creates a pool of threads in the server process and a client c
all (or multiple client calls) can be delivered by any of these threads at any t
ime. An out-of-process server must also implement synchronization in its class f
actory. Free threaded, in-process objects can receive direct calls from multiple
threads of the client.
The client can do COM work in multiple threads. All threads belong to the same m
ulti-threaded apartment. Interface pointers are passed directly from thread to t
hread within a multi-threaded apartment so interface pointers are not marshaled
between its threads. Message filters (implementations of IMessageFilter) are not
used in multi-threaded apartments. The client thread will suspend when it makes
a COM call to out-of-apartment objects and will resume when the call returns. C
alls between processes are still handled by RPC.
Threads initialized with the free-threading model must implement their own synch
ronization.
6.4 Single-/Multi-threaded Communication
A client or server that supports both single and multi-threaded apartments will
have one multi-threaded apartment, containing all threads initialized as free-th
readed, and one or more single-threaded apartments. Interface pointers must be m
arshaled between apartments but can be used without marshaling within an apartme
nt. Calls to objects in a single-threaded apartment will be synchronized by COM.
Calls to objects in the multi-threaded apartment will not be synchronized by CO
M.
All of the information on single-threaded apartments applies to the threads mark
ed as apartment model, and all of the information on multi-threaded apartments a
pplies to all of the threads marked as free -threaded. Apartment threading rules
apply to inter-apartment communication, requiring that interface pointers be ma
rshaled between apartments with calls to CoMarshalInterThreadInterfaceInStream a
nd CoGetInterfaceAndReleaseStream, as described in the Single-threaded Apartment
s section. For information on free-threading, see the Multi-threaded apartments
section. Some special considerations apply when dealing with in-process servers,
as described in In-process Server Threading Issues.
6.5 In-process server Threading Issues
An in-process server does not call CoInitialize, or CoInitializeEx to mark its t
hreading model. For thread-aware DLL-based or in-process objects, you need to se
t the threading model in the registry. The default model when you do not specify
a threading model is single-thread-per-process. To specify a model, you add the
ThreadingModel named-value to the InprocServer32 key in the registry.
DLLs that support instantiation of a class object must implement and export the
functions DllGetClassObject and DllCanUnloadNow. When a client wants an instance
of the class the DLL supports, a call to CoGetClassObject (either directly or t
hrough a call to CoCreateInstance) calls DllGetClassObject to get a pointer to i
ts class object when the object is implemented in a DLL. DllGetClassObject shoul
d therefore be able to give away multiple class objects or a single thread-safe
object (essentially just using the equivalent to the Win32 InterlockedIncrement/
InterlockedDecrement APIs on their internal reference count).
As its name implies, DllCanUnloadNow is called to determine whether the DLL that
implements it is in use, so the caller can safely unload it if it is not. Calls
to CoFreeUnusedLibraries from any thread always route through the main apartmen
t s thread to call DllCanUnloadNow.
Like other servers, in-process servers can be single-threaded, apartment-threade
d, free-threaded, or both. Each of these servers can be used by any client, rega
rdless of the threading model used by that client. There are certain considerati
ons peculiar to client/inprocess-server interoperation.
All combinations of threading model interoperability are allowed between clients
and in-process objects. Interaction between a client and an in-process object t
hat use different threading models is exactly like the interaction between clien
ts and out-of-process servers. For an in-process server, when the threading mode
l of the client and inprocess server differ, COM must interpose itself between t
he client and the object.
When an in-process object that supports the single threading model is be called
simultaneously by multiple threads of a client, COM cannot allow the client thre
ads to directly access the object's interface because the object was not designe
d for such access. Instead COM must ensure that calls are synchronized and are m
ade only by the client thread that created the object. Therefore, COM creates th
e object in the client's main apartment and requires all the other client apartm
ents to access the object using proxies.
When a free-threaded apartment (multi-threaded apartment) in a client creates an
apartment-threaded in-process server, COM spins up a single-threaded apartment
model 'host' thread in the client. This host thread will create the object and t
he interface pointer will be marshaled back to the client s free-threaded apartmen
t. Similarly, when a single-threaded apartment in an apartment-model client crea
tes a free threading in-process server, COM spins up a free-threading host threa
d (multi-threaded apartment on which the object will be created and marshaled ba
ck to the client single-threaded apartment.
In general, if you design a custom interface on an in-process server, you should
also provide the marshaling code for it so COM can marshal the interface betwee
n client apartments.
COM protects access to objects provided by a single-threaded DLL by requiring ac
cess from the same client apartment in which they were created. In addition, all
of the DLL s entry points (like DllGetClassObject and DllCanUnloadNow) and global
data should always be accessed by the same apartment. COM creates such objects
in the main apartment of the client, giving the main apartment direct access to
the object s pointers. Calls from the other apartments use inter-thread marshaling
to go from the proxy to the stub in the main apartment (using inter-thread mars
haling) and then to the object. This allows COM to synchronize calls to the obje
ct. Inter-thread calls are slow, so it is recommended that these servers be rewr
itten to support multiple apartments.
Like a single-threaded in-process server, an object provided by an apartment-mod
el DLL must be accessed by the same client apartment from which it was created.
Objects provided by this server, however, can be created in multiple apartments
of the client, so the server must implement its entry points (like DllGetClassOb
ject and DllCanUnloadNow) for multi-threaded use. For example, if two apartments
of a client try to create two instances of the in-process object simultaneously
, DllGetClassObject can be called simultaneously by both apartments. DllCanUnloa
dNow must be written so the DLL is protected from being unloaded while code is s
till executing in the DLL.
If the DLL provides only one instance of the class factory to create all the obj
ects, the class factory implementation must also be designed for multi-threaded
use because it will be accessed by multiple client apartments. If the DLL create
s a new instance of the class factory each time DllGetClassObject is called, the
class factory need not be thread-safe.
Objects created by the class factory need not be thread-safe. Once created by a
thread, the object is always accessed through that thread and all calls to the o
bject are synchronized by COM. The apartment-model apartment of a client that cr
eates this object will get a direct pointer to the object. Client apartments whi
ch are different from the apartment in which the object was created must access
the object through proxies. These proxies are created when the client marshals t
he interface between its apartments.
When an in-process DLL s ThreadingModel named-value is set to Both, an object prov
ided by this DLL can be created and used directly (without a proxy) in single- o
r multi-threaded client apartments. However, it can only be used directly within
the apartment in which it was created. To give the object to any other apartmen
t, the object must be marshaled. The DLL s object must implement its own synchroni
zation and can be accessed by multiple client apartments at the same time.
To speed performance for free-threaded access to in-process DLL objects, COM pro
vides the CoCreateFreeThreadedMarshaler function. This function creates a free-t
hreaded marshaling object that can be aggregated with an in-process server objec
t. When a client apartment in the same process needs access to an object in anot
her apartment, aggregating the free threaded marshaler provides the client with
a direct pointer to the server object, rather than to a proxy, when the client m
arshals the object's interface to a different apartment. The client does not nee
d to do any synchronization. This works only within the same process standard ma
rshaling is used for a reference to the object that is sent to another process.
An object provided by in-process DLL that supports only free threading is a free
-threaded object. It implements its own synchronization and can be accessed by m
ultiple client threads at the same time. This server does not marshal interfaces
between threads so this server can be created and used directly (without a prox
y) only by multi-threaded apartments in a client. Single-threaded apartments tha
t create it will access it through a proxy.
6.6 Process and Thread Related Interface Descriptions
6.6.1 IMessageFilter
The IMessageFilter interface provides COM servers and applications with the abil
ity to selectively handle incoming and outgoing COM messages while waiting for r
esponses from synchronous calls. Filtering messages helps to ensure that calls a
re handled in a manner that improves performance and avoids deadlocks. COM messa
ges can be synchronous, asynchronous, or input-synchronized; the majority of int
erface calls are synchronous.
Synchronous calls require the caller to wait for a reply before continuing. COM
enters a modal loop while waiting for the reply. During this time, the caller is
still able to receive and dispatch incoming messages.
Asynchronous calls allow the caller to proceed without waiting for a response fr
om the called object. Today, in COM, the only asynchronous calls are to an objec
t s IAdviseSink interface. While the object is processing an asynchronous call, it
is prohibited from making any synchronous calls back to the calling object.
Input-synchronized calls require the called object to complete the call before r
elinquishing control, ensuring that behaviors such as focus management and type-
ahead function correctly.
6.6.1.1 When to Implement
You will probably want to implement your own message filter. The default impleme
ntation provided by COM offers only minimal message filtering capability. Althou
gh message filtering is no longer as significant as it was with 16-bit applicati
ons, since the size of the message queue is now virtually unlimited, you still s
hould implement IMessageFilter as a way of resolving deadlocks.
COM will call your implementation of IMessageFilter to find out if an applicatio
n is blocking, so that you can task-switch to that application and give the user
an opportunity to deal with the situation. For example, if you have Microsoft W
ord talking to Microsoft Excel, with Excel running in the background in formula
mode, in which formulas are being applied to data on the worksheet to compute di
fferent or what if results, Excel won t check all calls, thereby blocking further ac
tion. IMessageFilter would put up a dialog box indicating which task was blockin
g and provide the user with an opportunity to deal with the deadlock.
Although it is probably obvious from the method descriptions, it may still be us
eful to point out that HandleIncomingCall is an object-based method and RetryRej
ectedCall and MessagePending are client-based methods. Clearly, the object must
have some way of handling incoming calls from external clients. HandleIncomingCa
ll provides that functionality by allowing the object to handle or defer some in
coming calls and reject others. The client also needs to know how an object is g
oing to handle its call. so that it can respond appropriately. The client needs
to know if a call has been rejected, or just deferred temporarily, so that it ca
n retry rejected calls after some specified time. The client also needs to be ab
le to respond to Windows messages, while at the same time waiting for replies to
pending messages.
You will use CoRegisterMessageFilter to register your message filter. Once regis
tered, COM then calls your message filter instead of the default implementation.
6.6.1.2 When to Use
You don t call this interface directly. It s provided by the COM server or applicati
on and called by the COM.
6.6.1.3 Application Shutdown with WM_QUERYENDSESSION and WM_ENDSESSION
When a user exits Windows, each open application receives a WM_QUERYENDSESSION m
essage followed by a WM_ENDSESSION message, provided the exit is not canceled. T
hese messages are invoked with SendMessage, which unfortunately restricts the in
itiation of all outgoing LRPC calls. This is a problem for container application
s that have open embedded objects when they receive the shutdown request because
LRPC is needed to close those objects.
Container and container/server applications with open documents typically displa
y a message box on receipt of the WM_QUERYENDSESSION message that asks if the us
er wants to save changes before exiting. A positive response is usually the defa
ult. The recommendation for dealing with the situation described above is for th
e application to display an alternate message box asking if the user wants to di
scard changes; a negative response should be the default. If the user chooses to
discard the changes, TRUE should be returned for WM_QUERYENDSESSION, which .sig
nals to Windows that it can terminate. If the user does not want to discard the
changes, FALSE should be returned. No attempt should be made to close or release
running embeddings.
Server applications should return TRUE for WM_QUERYENDSESSION without prompting
the user.On receipt of a WM_ENDSESSION message, all COM applications should exec
ute the normal close sequence for each application's documents and/or objects. A
t the same time, you should ignore any errors resulting from any cross-process c
alls or calls to IUnknown::Release. All storage pointers (IStorage and IStream i
nterface pointers) must be released to properly flush any temporary files mainta
ined by the compound file implementation of structured storage.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns a pointer to a specified interface.
AddRef Increments the reference count.
Release Decrements the reference count.
IMessageFilter Methods Description
HandleIncomingCall Provides a single entry point for incoming calls.
RetryRejectedCall Provides application with opportunity to display a dialo
g box offering retry or cancel or task switching options.
MessagePending Indicates a Windows message has arrived while COM is waiting to
respond to a remote call.
See Also
CoRegisterMessageFilter OLEUIBUSY, In the WIN32 SDK: Messages and Message Queues
6.6.1.4 IMessageFilter::HandleInComingCall
An object-based method that provides the ability to filter or reject incoming ca
lls (or call backs) to an object or a process. This method is called prior to ea
ch method invocation originating outside the current process.
DWORD HandleInComingCall(
DWORD dwCallType, //Type of incoming call
HTASK threadIDCaller, //Task handle calling this task
DWORD dwTickCount, //Elapsed tick count
LPINTERFACEINFO lpInterfaceInfo //Pointer to INTERFACEINFO structure
);
Parameters
dwCallType
[in] Kind of incoming call that has been received. Valid values are from the enu
meration CALLTYPE. See the chapter on enumerations for details.
threadIDCaller
[in] Handle of the task calling this task.
dwTickCount
[in] Elapsed tick count since the outgoing call was made if dwCallType is not CA
LLTYPE_TOPLEVEL. If dwCallType is CALLTYPE_TOPLEVEL, dwTickCount should be ignor
ed.
lpInterfaceInfo
[in] Pointer to an INTERFACEINFO structure, which identifies the object, the int
erface, and the method making the call
Return Values
SERVERCALL_ISHANDLED
The application might be able to process the call.
SERVERCALL_REJECTED
The application cannot handle the call due to an unforeseen problem, such as net
work unavailability, or if it is in the process of terminating.
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
If implemented, IMessageFilter::HandleInComingCall is called by COM when an inco
ming COM message is received.
Depending on an application s current state, a call is either accepted and process
ed or rejected (permanently or temporarily). If SERVERCALL_ISHANDLED is returned
, the application may be able to process the call, though success depends on the
interface for which the call is destined. If the call cannot be processed, COM
returns RPC_E_CALL_REJECTED.
Input-synchronized and asynchronous calls are dispatched even if the application
returns SERVERCALL_REJECTED or SERVERCALL_RETRYLATER.
IMessageFilter::HandleInComingCall should not be used to hold off updates to obj
ects during operations such as band printing. For that purpose, use IViewObject:
:Freeze.
You can also use IMessageFilter::HandleInComingCall to set up the application s st
ate so the call can be processed in the future.
See Also
CALLTYPE, INTERFACEINFO
6.6.1.5 IMessageFilter::MessagePending
A client-based method called by COM when a Windows message appears in an COM app
lication s message queue while the application is waiting for a reply to a remote
call. Handling input while waiting for an outgoing call to finish can introduce
complications. The application should determine whether to process the message w
ithout interrupting the call, continue waiting, or cancel the operation.
DWORD MessagePending(
HTASK threadIDCallee, //Called applications task handle
DWORD dwTickCount, //Elapsed tick count
DWORD dwPendingType //Call type
);
Parameters
threadIDCallee
[in] Task handle of the called application that has not yet responded.
dwTickCount
[in] Number of ticks since the call was made. It is calculated from the Windows
GetTickCount function.
dwPendingType
[in] Type of call made during which a message or event was received. Valid value
s are from the enumeration PENDINGTYPE (where PENDINGTYPE_TOPLEVEL means the out
going call was not nested within a call from another application and PENDINTGYPE
_NESTED means the outgoing call was nested within a call from another applicatio
n).
Return Values
PENDINGMSG_CANCELCALL
Cancel the outgoing call. This should be returned only under extreme conditions.
Canceling a call that has not replied or been rejected can create orphan transa
ctions and lose resources. COM fails the original call and returns RPC_E_CALL_CA
NCELLED.
PENDINGMSG_WAITNOPROCESS
Continue waiting for the reply and do not dispatch the message unless it is a ta
sk-switching or window-activation message. A subsequent message will trigger ano
ther call to IMessageFilter::MessagePending. Leaving messages or events in the q
ueue enables them to be processed normally, if the outgoing call is completed. N
ote that returning PENDINGMSG_WAITNOPROCESS can cause the message queue to fill.

PENDINGMSG_WAITDEFPROCESS
Because of the increased resources available in 32-bit systems, you are unlikely
to get this return value. It now indicates the same state as PENDINGMSG_WAITNOP
ROCESS.
Keyboard and mouse messages are no longer dispatched, as was done with PENDINGMS
G_WAITDEFPROCESS. However there are some cases where mouse and keyboard messages
could cause the system to deadlock, and in these cases, mouse and keyboard mess
ages are discarded. WM_PAINT messages are dispatched. Task-switching and activat
ion messages are handled as before.
Remarks
COM calls IMessageFilter::MessagePending after an application has made an COM me
thod call and a Windows message occurs before the call has returned. A Windows m
essage is sent, for example, when the user selects a menu command or double-clic
ks an object. Before COM makes the IMessageFilter::MessagePending call, it calcu
lates the elapsed time since the original COM method call was made. COM delivers
the elapsed time in the dwTickCount parameter. In the meantime, COM does not re
move the message from the queue.
Windows messages that appear in the caller s queue should remain in the queue unti
l sufficient time has passed to ensure that the messages are probably not the re
sult of typing ahead, but are, instead, an attempt to get attention. Set the del
ay with the dwTickCount parameter a two- or three-second delay is recommended. I
f that amount of time passes and the call has not been completed, the caller sho
uld flush the messages from the queue, and the COM UI busy dialog box should be
displayed offering the user the choice of retrying the call (continue waiting) o
r switching to the task identified by the threadIDCallee parameter. This ensures
that:
· If calls are completed in a reasonable amount of time, type ahead will be treate
d 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 reque
sts without responding when they are in modal dialog boxes.
Handling input while waiting for an outgoing call to finish can introduce compli
cations. 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 COM call, the application can cancel t
he call and restore the COM object to a consistent state by calling IStorage::Re
vert on its storage. The object can be released when the container can shut down
. However, canceling a call can create orphaned operations and resource leaks. C
anceling should be used only as a last resort. It is strongly recommended that a
pplications not allow such calls to be canceled.
See Also
IStorage::Revert, IStorage::Revert, OLEUIBUSY, In the WIN32 SDK: GetTickCount
6.6.1.6 IMessageFilter::RetryRejectedCall
A client-based method that gives the application an opportunity to display a dia
log box so the user can retry or cancel the call, or switch to the task identifi
ed by threadIDCallee.
DWORD RetryRejectedCall(
HTASK threadIDCallee, //Server task handle
DWORD dwTickCount, //Elapsed tick count
DWORD dwRejectType //Returned rejection message
);
Parameters
threadIDCallee
[in] Handle of the server task that rejected the call.
dwTickCount
[in] Number of elapsed ticks since the call was made.
dwRejectType
[in] Specifies either SERVERCALL_REJECTED or SERVERCALL_RETRYLATER, as returned
by the object application.
Return Values
-1
The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the origi
nal method call.
Value >= 0 and <100
The call is to be retried immediately.
Value >= 100
COM will wait for this many milliseconds and then retry the call.
Remarks
COM calls RetryRejectedCall on the caller s IMessageFilter immediately after recei
ving SERVERCALL_RETRYLATER or SERVERCALL_REJECTED from the IMessageFilter::Handl
eInComingCall method on the callee s IMessageFilter.
If a called task rejects a call, the application is probably in a state where it
cannot handle such calls, possibly only temporarily. When this occurs, COM retu
rns to the caller and issues IMessageFilter::RetryRejectedCall to determine if i
t should retry the rejected call.
Applications should silently retry calls that have returned with SERVERCALL_RETR
YLATER. If, after a reasonable amount of time has passed, say about 30 seconds,
the application should display the busy dialog box; a standard implementation of
this dialog box is available in the OLEDLG library. The callee may momentarily
be in a state where calls can be handled. The option to wait and retry is provid
ed for special kinds of calling applications, such as background tasks executing
macros or scripts, so that they can retry the calls in a nonintrusive way.
If, after a dialog box is displayed, the user chooses to cancel, RetryRejectedCa
ll returns -1 and the call will appear to fail with RPC_E_CALL_REJECTED.
6.7 Process and Thread Related API Descriptions
6.7.1 CoCreateFreeThreadedMarshaler
Creates an aggregatable object capable of context-dependent marshaling.
HRESULT CoCreateFreeThreadedMarshaler(
LPUNKNOWN punkOuter, //Pointer to object aggregating the marshaler ob
ject
LPUNKNOWN * ppunkMarshaler //Address of output variable that receives the
IUnknown
//interface pointer to the marshaler object
);
Parameters
punkOuter
[in] Pointer to the aggregating object's controlling IUnknown.
ppunkMarshaler
[out] Address of IUnknown* pointer variable that receives the interface pointer
to the aggregatable marshaler.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The marshaler was created.
Remarks
The CoCreateFreeThreadedMarshaler function enables an object to efficiently mars
hal interface pointers between threads in the same process. If your objects do n
ot support interthread marshaling, you have no need to call this function. It is
intended for use by free-threaded DLL servers that must be accessed directly by
all threads in a process, even those threads associated with single-threaded ap
artments. It custom-marshals the real memory pointer over into other apartments
as a bogus "proxy" and thereby gives direct access to all callers, even if they
are not free-threaded.
The CoCreateFreeThreadedMarshaler function performs the following tasks:
1. Creates a free-threaded marshaler object.
2. Aggregates this marshaler to the object specified by the punkOut
er parameter. This object is normally the one whose interface pointers are to be
marshaled.
The aggregating object's implementation of IMarshal should delegate QueryInterfa
ce calls for IID_IMarshal to the IUnknown of the free-threaded marshaler. Upon r
eceiving a call, the free-threaded marshaler performs the following tasks:
1. Checks the destination context specified by the CoMarshalInterfa
ce function's dwDestContext parameter.
2. If the destination context is MSHCTX_INPROC, copies the interfac
e pointer into the marshaling stream.
3. If the destination context is any other value, finds or creates
an instance of COM's default (standard) marshaler and delegates marshaling to it
.
Values for dwDestContext come from the MSHCTX enumeration. MSHCTX_INPROC indicat
es that the interface pointer is to be marshaled between different threads in th
e same process. Because both threads have access to the same address space, the
client thread can dereference the pointer directly rather than having to direct
calls to a proxy. In all other cases, a proxy is required, so CoCreateFreeThread
edMarshaler delegates the marshaling job to COM's default implementation.
Great care should be exercised in using the CoCreateFreeThreadedMarshaler functi
on. This is because the performance of objects which aggregate the free-threaded
marshaler is obtained through a calculated violation of the rules of COM, with
the ever-present risk of undefined behavior unless the object operates within ce
rtain restrictions. The most important restrictions are:
A free-threaded marshaler object cannot hold direct pointers to interfaces on an
object that does not aggregate the free-threaded marshaler as part of its state
. If the object were to use direct references to ordinary single-threaded aggreg
ate objects, it may break their single threaded property. If the object were to
use direct references to ordinary multi-threaded aggregate objects, these object
s can behave in ways that show no sensitivity to the needs of direct single-thre
aded aggregate clients. For example, these objects can spin new threads and pass
parameters to the threads that are references to ordinary single-threaded aggre
gate objects.
A free-threaded marshaler object cannot hold references to proxies to objects in
other apartments. Proxies are sensitive to the threading model and can return R
PC_E_WRONG_THREAD if called by the wrong client.
See Also
CoMarshalInterThreadInterfaceInStream, CoGetInterfaceAndReleaseStream
6.7.2 CoGetInterfaceAndReleaseStream
Unmarshals a buffer containing an interface pointer and releases the stream when
an interface pointer has been marshaled from another thread to the calling thre
ad.
HRESULT CoGetInterfaceAndReleaseStream(
LPSTREAM pStm, //Pointer to the stream from which the object is to be m
arshaled
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
Parameters
pStm
[in] Pointer to the IStream interface on the stream to be unmarshaled.
riid
[in] Reference to the identifier of the interface requested from the unmarshaled
object.
ppv
[out] IndirectAddress of pointer variable that receives the interface pointer re
quested in riid. Upon successful return, *ppv contains the requested interface p
ointer to the unmarshaled interface.
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates the output interface was unmarshaled and the stream was released.
This function can also return any of the values returned by CoUnmarshalInterface
.
Remarks
The CoGetInterfaceAndReleaseStream function performs the following tasks:
1. Calls CoUnmarshalInterface to unmarshal an interface pointer pre
viously passed in a call to CoMarshalInterThreadInterfaceInStream.
2. Releases the stream pointer. Even if the unmarshaling fails, the
stream is still released because there is no effective way to recover from a fa
ilure of this kind.
See Also
CoMarshalInterThreadInterfaceInStream, CoUnmarshalInterface
6.7.3 CoMarshalInterThreadInterfaceInStream
Marshals an interface pointer from one thread to another thread in the same proc
ess.
HRESULT CoMarshalInterThreadInterfaceInStream(
REFIID riid, //Reference to the identifier of the interface
LPUNKNOWN pUnk, //Pointer to the interface to be marshaled
LPSTREAM * ppStm //Address of output variable that receives the
// IStream interface pointer for the marshaled
// interface
);
Parameters
riid
[in] Reference to the identifier of the interface to be marshaled.
pUnk
[in] Pointer to the interface to be marshaled, which must be derived from IUnkno
wn; can be NULL.
ppStm
[out] Address of IStream* pointer variable that receives the interface pointer t
o the stream that contains the marshaled interface.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The interface was marshaled successfully.
Remarks
The CoMarshalInterThreadInterfaceInStream function enables an object to easily a
nd reliably marshal an interface pointer to another thread in the same process.
The stream returned in ppStm is guaranteed to behave correctly when a client run
ning in the receiving thread attempts to unmarshal the pointer. The client can t
hen call the CoGetInterfaceAndReleaseStream to unmarshal the interface pointer a
nd release the stream object.
The CoMarshalInterThreadInterfaceInStream function performs the following tasks:
1. Creates a stream object.
2. Passes the stream object s IStream pointer to CoMarshalInterface.
3. Returns the IStream pointer to the caller.
See Also
CoGetInterfaceAndReleaseStream
6.8 Process and Thread Related Structure Descriptions
6.8.1 INTERFACEINFO
The INTERFACEINFO structure contains information about incoming calls. The struc
ture is defined as follows:
typedef struct tagINTERFACEINFO
{
LPUNKNOWN pUnk;
IID iid;
WORD wMethod;
} INTERFACEINFO, * LPINTERFACEINFO;
Members
pUnk
Pointer to the IUnknown interface on the object.
iid
Identifier of the requested interface
wMethod
Interface method.
See Also
IMessageFilter::HandleIncomingCall
6.9 Process and Thread Related Enumeration Descriptions
6.9.1 CALLTYPE
The CALLTYPE enumeration constant specifies the call types used by IMessageFilte
r::HandleInComingCall.
typedef enum tagCALLTYPE
{
CALLTYPE_TOPLEVEL = 1,
CALLTYPE_NESTED = 2,
CALLTYPE_ASYNC = 3,
CALLTYPE_TOPLEVEL_CALLPENDING = 4,
CALLTYPE_ASYNC_CALLPENDING = 5
} CALLTYPE;
Elements
CALLTYPE_TOPLEVEL
A top-level call has arrived and that the object is not currently waiting for a
reply from a previous outgoing call. Calls of this type should always be handled
.
CALLTYPE_NESTED
A call has arrived bearing the same logical thread identifier as that of a previ
ous outgoing call for which the object is still awaiting a reply. Calls of this
type should always handled.
CALLTYPE_ASYNC
An aysnchronous call has arrived. Calls of this type cannot be rejected. COM alw
ays delivers calls of this type.
CALLTYPE_TOPLEVEL_CALLPENDING
A new top-level call has arrived with a new logical thread identifier and that t
he object is currently waiting for a reply from a previous outgoing call. Calls
of this type may be handled or rejected.
CALLTYPE_ASYNC_CALLPENDING
An asynchronous call has arrived with a new logical thread identifier and that t
he object is currently waiting for a reply from a previous outgoing call. Calls
of this type cannot be rejected.
async call - can NOT be rejected
See Also
IMessageFilter::HandleInComingCall, IMessageFilter
6.9.2 PENDINGMESSAGE
The PENDINGMSG enumeration constants are return values of IMessageFilter::Messag
ePending.
Defined in the IMessageFilter interface (msgflt.idl).
typedef enum tagPENDINGMSG
{
PENDINGMSG_CANCELCALL = 0,
PENDINGMSG_WAITNOPROCESS = 1,
PENDINGMSG_WAITDEFPROCESS = 2
} PENDINGMSG;
Elements
PENDINGMSG_CANCELCALL
Cancel the outgoing call.
PENDINGMSG_WAITNOPROCESS
Wait for the return and don't dispatch the message.
PENDINGMSG_WAITDEFPROCESS
Wait and dispatch the message.
See Also
IMessageFilter::MessagePending
6.9.3 PENDINGTYPE
The PENDINGTYPE enumeration constants indicate the level of nesting in the IMess
ageFilter::MessagePending method.
typedef enum tagPENDINGTYPE
{
PENDINGTYPE_TOPLEVEL = 1,
PENDINGTYPE_NESTED = 2
} PENDINGTYPE;
Elements
PENDINGTYPE_TOPLEVEL
Top-level call.
PENDINGTYPE_NESTED
Nested call.
See Also
IMessageFilter::MessagePending
6.9.4 SERVERCALL
The SERVERCALL enumeration constants indicate the status of server call ¾ returned
by IMessageFilter::HandleInComingCall and passed to IMessageFilter::RetryReject
edCall.
Defined in the IMessageFilter interface (msgflt.idl).
typedef enum tagSERVERCALL
{
SERVERCALL_ISHANDLED = 0,
SERVERCALL_REJECTED = 1,
SERVERCALL_RETRYLATER = 2
} SERVERCALL;
Elements
SERVERCALL_ISHANDLED
The object may be able to process the call.
SERVERCALL_REJECTED
The object cannot handle the call due to an unforeseen problem, such as network
unavailability.
SERVERCALL_RETRYLATER
The object cannot handle the call at this time. For example, an application migh
t return this value when it is in a user-controlled modal state.
See Also
IMessageFilter::HandleInComingCall, IMessageFilter::RetryRejectedCall
7. COM Clients
As described in earlier chapters, a COM Client is simply any piece of code that
makes use of another object through that object s interfaces. In this sense, a COM
Client may itself be a COM Server acting in the capacity of a client by virtue
of using (or reusing) some other object.
If the client is an application, that is, an executable program as opposed to a
DLL, then it must follow all the requirements for a COM Application as detailed
in Chapter 5. That aside, clients have a number of ways to actually get at an ob
ject to use as discussed in a previous chapter. The client may call a specific f
unction to create an object, it might ask an existing object to create another,
or it might itself implement an object to which some other code hands yet anothe
r object s interface pointer. Not all of these objects must have CLSID.
This chapter, however, is concerned with those clients that want to create an ob
ject based on a CLSID, because at some point or another, many operations that do
n t directly involve a CLSID do eventually resolve to this process. For example, m
oniker binding internally uses a CLSID but shields clients from that fact. In an
y case, whatever client code uses a CLSID will generally perform the following o
perations in order to make use of an object:
Identify the class of object to use.
Obtain the class factory for the object class and ask it to create an uninitialize
d instance of the object class, returning an interface pointer to it.
Initialize the newly created object by calling an initialization member function
of the initialization interface, that is, one of a generally small set of interfa
ces that have such functions.
Make use of the object which generally includes calling QueryInterface to obtain
additional working interface pointers on the object. The client must be prepare
d for the potential absence of a desired interface.
Release the object when it is no longer needed.
The following sections cover the functions and interfaces involved in each of th
ese steps. In addition, the client may want to more closely manage the loading a
nd unloading of server modules (DLLs or EXEs) for optimization purposes, so this
chapter includes a section of such management.
As far as the client is concerned, the COM Library exists to provide fundamental
implementation locator and object creation services and to handle remote proced
ure calls to local or remote objects (in addition to memory management services,
of course). How a server facilitates these functions is the topic of Chapter 8.
Before examining the details of object creating and manipulation, realize that a
fter the object is created and the client has its first interface pointer to tha
t object, the client cannot distinguish an in-process object from a local object
from a remote object by virtue of examining the interface pointer or any other
interfaces on that object. That is, all objects appear identically to the client
such that after creation, all requests made to the object s services are made by
calling interface member functions. Period. There are not special exceptions tha
t a client must make at run-time based on the distance of the object in question
. The COM Library provides any underlying glue to insure that a call made to a l
ocal or remote object is, in fact, marshaled properly to the other process or th
e other machine, respectively. This operation is transparent to the client, who
always sees any call to an object as a function call to the objects interfaces a
s if that object were in-process. This consistency is a key benefit for COM clie
nts as it can treat all objects identically regardless of their actual execution
context. If you are interested in understanding how this transparency is achiev
ed, please see Chapter 9, Communicating via Interfaces: Remoting for more details.
There you will find that all clients do, in fact, always call an in-process obj
ect first, but in local and remote cases that in-process object is just a proxy
that takes care of generating a remote procedure call.
7.1 Getting a Pointer to an Object
Because COM does not have a strict class model, there are several ways to instan
tiate or to get a pointer to an interface on an object. There are, in fact, four
methods through which a client obtains its first interface pointer to a given o
bject:
Call a COM Library API function that creates an object of a pre-determined type ¾
that is, the function will only return a pointer to one specific interface for a
specific object class.
Call a COM Library API function that can create an object based on a class ident
ifier (CLSID) and that returns any type of interface pointer requested.
Call a method of some interface that creates another object (or connects to an e
xisting one) and returns an interface pointer on that separate object.
Implement an object with an interface through which other objects pass their int
erface pointer to the client directly.
For information on getting pointers to other interfaces on an object once you ha
ve the first one, see QueryInterface: Navigating in an Object.
There are numerous COM functions that return pointers to specific interface impl
ementations, such as CoGetMalloc, which retrieves a pointer to the standard COM
memory allocator. Most of these are helper functions, which retrieve a pointer t
o an COM implementation of an interface on an object, as does CoGetMalloc. Most
of these functions are described in the specific area they are related to, such
as storage or data transfer.
There are several functions that, given a CLSID, a client can call to create an
object instance and get a pointer to it. All of these functions are based on the
function CoGetClassObject, which creates a class object and supplies a pointer
to an interface that allows you to create instances of that class. While there m
ust be information that says which system the server resides on, there is no nee
d for that information to be contained in the client. The client needs to know o
nly the CLSID, and never the absolute path of the server code. For more informat
ion, see Creating an Object through a Class Object.
Among the many interface methods that return a pointer to a separate object are
several that create and return a pointer to an enumerator object, which allows y
ou to determine how many items of a given type an object maintains. COM defines
interfaces for enumerating a wide variety of items, such as strings, several str
uctures important in various COM technologies, monikers, and IUnknown interface
pointers. The typical way to create an enumerator instance and get a pointer to
its interface is to call a method from another interface. For example, the IData
Object interface defines two methods, EnumDAdvise and EnumFormatEtc, that return
pointers to interfaces on two different enumeration objects. There are many oth
er examples in COM of methods that return pointers to objects.
The fourth way to get a pointer to an object is used when two objects, such as a
n OLE Compound Document container and server, need bi-directional communication.
Each implements an object containing an interface method to which other objects
can pass interface pointers. In the case of containers and servers, each object
then passes its pointer to the other object. The implementing object, which is
also the client of the created object, can then call the method and get the poin
ter that was passed.
7.1.1 Creating an Object through a Class Object
With the increasing importance of computer networks, it has become necessary for
clients and servers to interact easily and efficiently, whether they reside on
the same machine or across a network. Crucial to this is the ability of a client
to be able to launch a server, create an instance of the server s object, and hav
e access to the methods of the interfaces on the object.
COM provides extensions to this basic COM process that make it virtually seamles
s across a network. If a client is able to identify the server through its CLSID
, calling a few simple functions permit COM to do all the work of locating and l
aunching the server, and activating the object. New subkeys have been added to t
he registry that allow remote servers to register their location, so the client
does not require that information. For applications that want to take advantage
of networking features, object creation functions are provided that allow more f
lexibility and efficiency.
7.1.2 COM Class Objects and CLSIDs
A COM server is implemented as a COM class. A COM class is an implementation of
a group of interfaces in code executed whenever you interact with a given object
. There is an important distinction between a C++ class and a COM class. In C++,
a class is a type. A COM class is simply a definition of the object, and carrie
s no type, although a C++ programmer might implement it using a C++ class. COM i
s designed to allow a class to be used by different applications, including appl
ications written without knowledge of that particular class s existence. Therefore
, class code for a given type of object exists either in a dynamic linked librar
y (DLL) or in another application (EXE).
Each COM class is identified by a CLSID, a unique 128-bit GUID, which the server
must register. COM uses this CLSID, at the request of a client, to associate sp
ecific data with the DLL or EXE containing the code that implements the class, t
hus creating an instance of the object. For information on registering a server,
see Registering COM Servers, and GUID Creation and Optimizations.
For clients and servers on the same machine, the model previously supported, the
CLSID of the server is all the client ever needs. On each machine, COM maintain
s a database (it makes use of the system registry on Windows and Macintosh platf
orms) of all the CLSIDs for the servers installed on the system. This is a mappi
ng between each CLSID and the location of the DLL or EXE that houses the code fo
r that CLSID. COM consults this database whenever a client wants to create an in
stance of a COM class and use its services, so the client never needs to know th
e absolute location of the code on the machine.
For distributed systems, COM provides registry entries that allow a remote serve
r to register itself for use by a client. While applications need know only a se
rver s CLSID, because they can rely on the registry to locate the server, COM allo
ws clients to override registry entries and to specify server locations, to take
full advantage of the network (see Locating a Remote Object).
The basic way to create an instance of a class is through a COM class object. Th
is is simply an intermediate object that supports functions common to creating n
ew instances of a given class. Most class objects used to create objects from a
CLSID support the IClassFactory interface, an interface that includes the import
ant method CreateInstance. You implement an IClassFactory interface for each cla
ss of object that you offer to be instantiated. For information on implementing
IClassFactory, refer to Implementing IClassFactory.
Note
Servers that support some other custom class factory interface are not required
to support IClassFactory specifically. However, calls to activation functions ot
her than CoGetClassObject (such as CoCreateInstanceEx) require that the server s
upport IClassFactory.
When a client wants to create an instance of the server s object, it uses the desi
red object s CLSID in a call to CoGetClassObject. (This call can either be direct
or implicit, through one of the object creation helper functions.)
COM has just a few API functions on which many of the others are built. The most
important of these is probably CoGetClassObject, which underlies all of the ins
tance creation functions. This function locates the code associated with the CLS
ID, and creates a class object, and supplies a pointer to the interface requeste
d (CoGetClassObject takes a riid param that specifies the client s desired interfa
ce pointer).
With this pointer, the caller can create an instance of the object, and retrieve
a pointer to a requested interface on the object. This is usually an initializa
tion interface, used to activate the object (put it in the running state), so th
e client can do whatever work with the object that it wants to. Using these basi
c functions, the client must also take care to release all object pointers. COM
provides several helper functions that reduce the work of creating object instan
ces. These are described in Instance Creation Helper Functions.
Another mechanism for activating object instances is through the class moniker.
Class monikers bind to the class object of the class for which they are created.
For more information, see Class Monikers.
7.1.3 Locating a Remote Object
COM uses the basic model for object creation described in COM Class Objects and
CLSIDs, and adds more than one way to locate an object that may reside on anothe
r system in a network, without overburdening the client application.
COM has registry keys that permit a server to register the name of the machine o
n which it resides, or the machine where an existing storage is located. Thus, c
lient applications need know only the CLSID of the server.
CoGetClassObject takes a COSERVERINFO structure, which allows a client to specif
y the location of a server. Another important value in this function is the CLSC
TX enumeration, which specifies whether the expected object is to be run in-proc
ess, out-of-process local, or out-of-process remote. Taken together, these two v
alues and the values in the registry determine how and where the object is to be
run. Instance creation calls, when they specify a server location, can override
a registry setting. The algorithm COM uses for doing this is described in the r
eference for the CLSCTX enumeration.
Remote activation depends on the security relationship between client and server
. For more information, see Security in COM.
7.1.4 Instance Creation Helper Functions
In previous releases of COM, the primary mechanism used to create an object inst
ance was the CoCreateInstance function. This function encapsulates the process o
f creating a class object, using that to create a new instance and releasing the
class object.
To smooth the process of instance creation on distributed systems, COM has intro
duced four important new instance creation mechanisms:
· Class Monikers and IClassActivator
· CoCreateInstanceEx
· CoGetInstanceFromFile
· CoGetInstanceFromIStorage
Class monikers are a mechanism that permit you to identify the class of an objec
t, and are typically used with another moniker, like a file moniker, to indicate
the location of the object. This permits you to bind to an object ans specify t
he server that is to be launched for that object. Class monikers may also be com
posed to the right of monikers supporting binding to the IClassActivator interfa
ce. For more information, see Class Monikers.
CoCreateInstanceEx extends CoCreateInstance to make it possible to create a sing
le uninitialized object associated with the given CLSID on a specified remote ma
chine. In addition, rather than requesting a single interface and obtaining a si
ngle pointer to that interface, CoCreateInstanceEx makes it possible to query fo
r multiple interfaces and (if available) receive pointers to them in a single ro
und trip, thus permitting fewer round trips between machines. This can make remo
te object interaction much more efficient. To do this, the function uses an arra
y of MULTI_QI structures.
Creating an object through CoCreateInstanceEx still requires that the object be
initialized through a call to one of the initialization interfaces (such as IPer
sistStorage:::Load). The two helper functions, CoGetInstanceFromFile and CoGetIn
stanceFromIStorage encapsulate both the instance creation power of CoCreateInsta
nceEx and initialization, the former from a file, and the latter from a storage.
7.2 Initializing the Object
After the client has successfully created an object of a given class it must ini
tialize that object. By definition, any new object created using IClassFactory::
CreateInstance (or variant or wrapper thereof) is uninitialized. Initialization
generally happens through a single call to a member function of the initializatio
n interface. This interface is usually the one requested by the client in its cal
l to create the object, but this is not required. Before an object is initialize
d, the only calls that are guaranteed to work on the object (besides the initial
izing functions themselves) are the IUnknown functions (of any interface) unless
otherwise explicitly specified in the definition of an interface. In addition,
QueryInterface is only guaranteed to work for IUnknown and any initialization in
terface, but not guaranteed for a non-initialization interface.
Some objects will not require initialization before they are function through al
l of their interfaces. Those that do require initialization will define, either
explicitly through documentation of the object or implicitly through the scenari
os in which the object is used, which member of which interface can be used for
initialization.
For example, objects that can serialize their persistent data to a file will imp
lement the IPersistFile interface (see Persistent Storage Interfaces for Objects i
n Chapter 14). The function IPersistFile::Load, which instructs the object to lo
ad its data from a file, is the initialization function and IPersistFile is the
initialization interface. Other examples are objects that can serialize to stora
ges or streams, where the objects implement the initialization interfaces IPersi
stStorage or IPersistStream, respectively (again, see Chapter 14). The Load func
tions in these interfaces are initialization functions as is IPersistStorage::In
itNew, which initializes a new object with storage instead of loading a previous
ly saved version.
7.3 Managing the Object
Once an object is initialized, it is entirely up to the client to determine what
it intends to do with that object. It is often the case that the initializing i
nterface is not the working interface through which the client will primarily use
the object. The creation sequence only nets the client a single interface pointe
r that has a limited scope of functionality. If the client wishes to perform an
operation outside that scope, it must call the known interface s QueryInterface fu
nction to ask for another interface on the same object.
For example, say a client has created and initialized an object but now wishes t
o obtain a graphical presentation, say a bitmap, from that object by calling IDa
taObject::GetData (see Chapter 16 for details on this function). The client must
call QueryInterface to obtain an IDataObject pointer before calling the functio
n.
It is important to note that all operations on that object will occur through ca
lls to the member functions of the object s various interfaces. Any additional API
functions that the client might call to affect the object itself are usually wr
apper functions of common sequences of interface function calls. There simply is
no other way to affect the object other than through it s interfaces.
Because a client must ask for an interface before it can possibly ask the object
to perform the actions defined in the interface, the client cannot ask the obje
ct to perform an action the object does not support. This is a primary strength
of the QueryInterface function as described in the early chapters of this docume
nt. Calling QueryInterface for access to an object s functionality is not problema
tic nor inconvenient because the client usually makes the call specifically at t
he point where the client wants to perform some action on the object. That is, c
lients generally do not call QueryInterface for all possible interfaces after th
e object is created so as to have all the pointers on hand instead, the client cal
ls QueryInterface before attempting to perform some action with the object.
In practice this means that the client must be prepared for the failure of a cal
l to QueryInterface. Instead of being a complete pain to implementation, such pr
eparation defines a mechanism through which the client can make dynamic choices
based on the functionality of the object itself on an object-by-object basis.
For example, consider a client application that has created a number of objects
and it now wants to save the application s state, which includes saving the state
of each object. Let s say the client is using structured storage for its native fi
le representation, so its first choice will be to assign an individual storage e
lement in that file for each object. Each object can then store structured infor
mation itself and it indicates its ability to do by implementing the IPersistSto
rage interface. However, some object may not know how to write to a storage but
know how to write to a stream and indicate the capability by implementing IPersi
stStream. Yet others may only know how to write information to a file themselves
and thus implement IPersistFile. Finally, some objects may not know how to seri
alize themselves at all, but can provide a binary memory copy of the their nativ
e data through IDataObject.
In this case the client s strategy will be as follows: if an object supports IPers
istStorage, then give it an IStorage instance and ask it to save its data into i
t by calling IPersistStorage::Save. If that object does not provide such support
, check if it supports IPersistStream, and if so, create a client-controlled str
eam for it (in perhaps a separate client-controlled storage element) and pass th
at IStream pointer to the object through IPersistStream::Save. If the object doe
s not support streams, then check for IPersistFile. If the object supports seria
lization to a file, then have the object write its data into a temporary file by
calling IPersistFile::Save, then make a binary copy of that file in a client-co
ntrolled stream element within a client-controlled storage element. If all else
fails, attempt to retrieve the object s binary data from IDataObject::GetData usin
g the first format the object supports, and write that binary data into a client
-controlled stream in a client-controlled storage.
Reloading these objects would be a similar procedure, but the client would know,
from the structure of its storage and other information it saved about the obje
cts itself, which method to use to reload the object from the storage. The clien
t wants to insure that it uses the same method to load the object that it did fo
r saving it originally, that is, use the same interface instead of querying for
the best one. The reason is that while the data was passively stored on disk, th
e object that wrote that data might have been updated such that where it once on
ly supported IPersistStream, for example, it now supports IPersistStorage. In th
at case the client should ask it to load the data using IPersistStream::Load.
However, when the client goes to save the object again, it will now successfully
find that the object supports IPersistStorage and can now have the object save
into a storage element instead. (The container would also insure that the old cl
ient-controlled stream was deleted as it is no longer in use for that object.) T
his demonstrates how an object can be updated and new interfaces supported witho
ut any recompilation on the part of existing clients while at the same time sudd
enly working with clients on a higher level of integration than before. In order
to remain compatible the object must insure that it supports the older interfac
es (such as IPersistStream) but is free to add new contracts new interfaces such a
s IPersistStorage as it wants to provide new functionality.
The point of this example, which is also true for clients that use any other int
erfaces an object might support in other scenarios, is that the client is empowe
red to make dynamic decisions on a per-object basis through the QueryInterface f
unction. Containers programmed to be dynamic as such allow object to improve ind
ependently while insuring that the container will work as good and generally bette
r as it always has with any given object. All of this is due to the powerful and i
mportant QueryInterface mechanism that for all intents and purposes is the singl
e most important aspect of true system component software.
7.4 Releasing the Object
The final operation required in a COM client when dealing with an object from so
me other server is to free that object when the client no longer needs it. This
is achieved by calling the Release member function of all interfaces obtained du
ring the course of using the object.
Recall that a function that creates or synthesizes a new interface pointer is re
sponsible for calling AddRef through that pointer before returning it to the cal
ler of the function. This applies to the IClassFactory::CreateInstance function
as well as CoCreateInstance (and for that matter, CoGetClassObject, too, which i
s why you must call IClassFactory::Release after creating the object). Therefore
, as far as the client is concerned, the object will have a reference count of o
ne after creation. The object may, in fact, have a higher reference count if it
is also being used from other clients as well, but each client is only responsib
le and cognizant of the reference counts added on its behalf.
The other primary function that creates new interface pointers is QueryInterface
. Every call the client makes to QueryInterface to obtain another interface poin
ter will internally generate another call to AddRef in that object, incrementing
the reference count. Therefore, in addition to calling Release through the inte
rface pointer obtained in the creation sequence, the client must also call Relea
se through any interface pointer obtained from QueryInterface (this is illustrat
ed in the pseudo-code of the previous section).
The bottom line is that the client is responsible for matching any operation tha
t generates a call to AddRef through a given interface pointer with a call to Re
lease through that same interface pointer. It is not necessary to call Release i
n the opposite order of calls to AddRef; it is just necessary to match the pairs
. Failure to do so will cause memory leaks as objects are not freed and servers
are not allowed to shut down properly. This is no different that forgetting to f
ree memory obtained through malloc.
Finally, although the client matches its calls to AddRef and Release, the actual
object may still continue to run and the server may continue to execute as well
without any objects in service. The object will continue if other clients are u
sing that same object and thus have reference counts on it. Only when all client
s have released their references will that object free itself. The server will,
of course, continue to execute as long as there is an object to serve, but the c
lient does have some power over keeping a server running even without objects. T
hat is the purpose of Server Management functions in COM.
7.5 Server Management
As mentioned in previous sections, a client has the ability to manage servers on
the server level to keep them running even when they are not serving any object
s. The client s primary mechanism for this is the IClassFactory::LockServer functi
on described above. By calling this function with the TRUE parameter, the client
places a lock on the server. As long as the server either has objects created or
has one or more locks on it, the server will continue to execute. When the serve
r detects a zero object and zero lock condition, it can unload itself (which dif
fers between DLL and EXE servers, as described in Chapter 8).
A client can place more than one lock on a server by calling IClassFactory::Lock
Server(TRUE) more than once. Each call to LockServer(TRUE) must be matched with
a call to LockServer(FALSE) the server maintains a lock count for the server as it
maintains a reference count for its served objects. But while AddRef and Releas
e affect objects, LockServer affects the server itself.
LockServer affects all servers in-process, local, and remote identically. The client
does have some additional control over in-process objects as it normally would
for other DLLs through the functions CoLoadLibrary, CoFreeUnusedLibraries, and C
oFreeAllLibraries, as described below. Normally only CoFreeUnusedLibraries is ca
lled from a client whereas the others are generally used inside the COM Library
to implement other API functions. In addition, the COM Library supplies one addi
tional function that has meaning in this context, CoIsHandlerConnected, that tel
ls the container if an object handler is currently working in association with a
local server as described in its entry below.
7.6 Client Related Interface Descriptions
7.6.1 ICatInformation
The ICatInformation interface provides methods for obtaining information about t
he categories implemented or required by a certain class, as well as information
about the categories registered on a given machine.
7.6.1.1.1 When to Implement
There is no need to implement this interface. The Component Category Manager, a
system-provided COM object that can be instantiated by using CoCreateInstance, i
mplements ICatInformation.
7.6.1.1.2 When to Use
Call the methods of ICatInformation to obtain a listing of available categories,
enumerate classes that belong to a particular category, and determine if a clas
s belongs to a specific category.
Information on using component categories can be found in Component Categories M
anager Implementation.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
ICatInformation Methods Description
EnumCategories Returns an enumerator for the component categories registered on
the system.
GetCategoryDesc Retrieves the localized description string for a specific catego
ry ID.
EnumClassesOfCategories Returns an enumerator over the classes that implement/re
quire a certain set of categories.
IsClassOfCategories Determines if a class implements/requires the specified
categories.
EnumImplCategoriesOfClass Returns an enumerator over the CATIDs implemente
d by the specified class.
EnumReqCategoriesOfClass Returns an enumerator over the CATIDs required b
y the specified class.
See Also
ICatRegister

7.6.1.2 ICatInformation::EnumCategories
Returns an enumerator for the component categories registered on the system.
HRESULT EnumCategories(
LCID lcid, //Requested locale for returned szDescription of enumerated CATE
GORYINFOs
IEnumCATEGORYINFO** ppenumCatInfo //Location in which to return an IEnumCA
TEGORYINFO interface
);
Parameters
lcid
[in] Identifies the requested locale for any return szDescription of the enumera
ted CATEGORYINFOs. Typically, the caller specifies GetUserDefaultLCID for this p
arameter.
ppenumCatInfo
[out] Specifies the location to return an IEnumCATEGORYINFO interface. This can
be used to enumerate the CATIDs and localized description strings of the compone
nt categories registered with the system.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
See Also
ICatInformation::EnumClassesOfCategories, ICatInformation::EnumImplCategoriesOfC
lass, ICatInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDes
c, ICatInformation::IsClassOfCategories

7.6.1.3 ICatInformation::EnumClassesOfCategories
Returns an enumerator over the classes that implement one or more of rgcatidImpl
. If a class requires a category not listed in rgcatidReq, it is not included in
the enumeration.
HRESULT EnumClassesOfCategories(
ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
CATID rgcatidImpl, //Array of category identifiers
ULONG cRequired, //Number of category IDs in the rgcatidReq array

CATID rgcatidReq, //Array of category identifiers


IEnumCLSID** ppenumCLSID //Location in which to return an IEnumCLSID inte
rface
);
Parameters
cImplemented
[in] The number of category IDs in the rgcatidImpl array. This value cannot be z
ero. If this value is ((ULONG) -1), classes are included in the enumeration, reg
ardless of the categories they implement.
rgcatidImpl
[in] An array of category identifiers.
cRequired
[in] The number of category IDs in the rgcatidReq array. This value can be zero.
If this value is ((ULONG) -1), classes are included in the enumeration, regardl
ess of the categories they require.
rgcatidReq
[in] An array of category identifiers.
ppenumCLSID
[out] The location in which to return an IEnumCLSID interface that can be used t
o enumerate the CLSIDs of the classes that implement category rcatid.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
See Also
ICatInformation::EnumCategories, ICatInformation::EnumImplCategoriesOfClass, ICa
tInformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatIn
formation::IsClassOfCategories

7.6.1.4 ICatInformation::EnumImplCategoriesOfClass
Returns an enumerator for the CATIDs implemented by the specified class.
HRESULT EnumImplCategoriesOfClass(
REFCLSID rclsid, //Class ID
IEnumCATID** ppenumCATD //Location in which to return an IEnumCATID inte
rface
);
Parameters
rclsid
[in] Specifies the class ID.
ppenumCATD
[out] Specifies the location to return an IEnumCATID interface. This can be used
to enumerate the CATIDs that are implemented by rclsid.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
See Also
ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatI
nformation::EnumReqCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInfo
rmation::IsClassOfCategories

7.6.1.5 ICatInformation::EnumReqCategoriesOfClass
Returns an enumerator for the CATIDs required by the specified class.
HRESULT EnumReqCategoriesOfClass(
REFCLSID rclsid, //Class ID
IEnumCATID** ppenumCATD //Location in which to return an IEnumCATID inte
rface
);
Parameters
rclsid
[in] Specifies the class ID.
ppenumCATD
[out] Specifies the location to return an IEnumCATID interface. This can be used
to enumerate the CATIDs that are required by rclsid.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
See Also
ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatI
nformation::EnumImplCategoriesOfClass, ICatInformation::GetCategoryDesc, ICatInf
ormation::IsClassOfCategories

7.6.1.6 ICatInformation::GetCategoryDesc
Retrieves the localized description string for a specific category ID.
HRESULT GetCategoryDesc(
REFCATID rcatid, //Category for which the string is to be returned
LCID lcid, //Locale in which the resulting string is returned
PWCHAR* ppszDesc //Pointer to the string pointer that contains the descri
ption
);
Parameters
rcatid
[in] Identifies the category for which the description string is to be returned.
lcid
[in] Specifies the locale in which the resulting string is returned.
ppszDesc
[out] A pointer to the string pointer that contains the description. This must b
e released by the caller using CoMemTaskFree.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
E_OUTOFMEMORY
Insufficient memory to create and return an enumerator object.
CAT_E_CATIDNOEXIST
The category ID rcatid is not registered.
CAT_E_NODESCRIPTION
There is no description string for rcatid with the specified locale.
See Also
ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatI
nformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass
, ICatInformation::IsClassOfCategories

7.6.1.7 ICatInformation::IsClassOfCategories
Determines if a class implements one or more categories. If the class requires a
category not listed in rgcatidReq, it is not included in the enumeration.
HRESULT IsClassOfCategories(
REFCLSID rclsid, //Class ID of the class to query
ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
CATID rgcatidImpl, //Array of category identifiers
ULONG cRequired, //Number of category IDs in the rgcatidReq array
CATID rgcatidReq //Array of category identifiers
);
Parameters
rclsid
[in] The class ID of the relevent class to query.
cImplemented
[in] The number of category IDs in the rgcatidImpl array. This value cannot be z
ero. If this value is ((ULONG) -1), the implemented categories are not tested.
rgcatidImpl
[in] An array of category identifiers.
cRequired
[in] The number of category IDs in the rgcatidReq array. This value can be zero.
If this value is ((ULONG) -1), the required categories are not tested.
rgcatidReq
[in] An array of category identifiers.
Return Values
S_OK
rclsid is of category rcatid.
S_FALSE
rclsid is not of category rcatid.
See Also
ICatInformation::EnumCategories, ICatInformation::EnumClassesOfCategories, ICatI
nformation::EnumImplCategoriesOfClass, ICatInformation::EnumReqCategoriesOfClass
, ICatInformation::GetCategoryDesc

7.6.2 IMultiQI
The IMultiQI interface enables a client to query an object proxy, or handler, fo
r multiple interfaces, using a single RPC call. By using this interface, instead
of relying on separate calls to IUnknown::QueryInterface, clients can reduce th
e number of RPC calls that have to cross thread, process, or machine boundaries
and, therefore, the amount of time required to obtain the requeseted interface p
ointers.
7.6.2.1.1 When to Implement
You never have to implement this interface because there is no situation in whic
h it is required. COM server applications that rely on COM s standard remoting sup
port get the interface for free because COM implements it on every object proxy.
The only situation in which you might want to implement this interface yourself
is if you are writing a custom marshaler that handles interface remoting. Even
here, implementing IMultiQI yourself is not recommended, particularly if your ob
ject is aggregatable.
7.6.2.1.2 When to Use
When more than one interface pointer is sought, client applications should Query
Interface for IMultiQI and use it if available.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IMultiQI Methods Description
QueryMultipleInterfaces Queries for multiple interfaces.
See Also
IUnknown::QueryInterface
7.6.2.2 IMultiQI::QueryMultipleInterfaces
Fills a caller-provided array of structures with pointers to multiple interfaces
.Calling this method is equivalent to issuing a series of separate QueryInterfac
e calls except that you do not incur the overhead of a corresponding number of R
PC calls. In multithreaded applications and distributed environments, keeping RP
C calls to a minimum is essential for optimal performance.
HRESULT QueryMultipleInterfaces(
ULONG cMQIs, //Number of structures in array
MULTI_QI *pMQIs //Pointer to first structure in array
);
Parameters
cMQIs
[in] Pointer to the number of elements in an array of MULTI_QI structures, each
of which contains the IID of a single interface.
pMQIs
[in, out] Pointer to the first MULTI_QI strucutre in the array.
Return Value
S_OK
Pointers were returned to all requested interfaces.
S_FALSE
Pointers were returned to some, but not all, of the requested interfaces.
E_NOINTERFACE
Pointers were returned to none of the requested interfaces.
Remarks
The QueryMultipleInterfaces method takes as input an array of MULTI_QI structure
s. Each structure specifies an interface IID and contains two additional blank f
ields for receiving an interface pointer and return value.
This method obtains as many requested interface pointers as possible directly fr
om the object proxy. For each interface not implemented on the proxy, the method
calls the server to obtain a pointer. Upon receiving an interface pointer from
the server, the method builds a corresponding interface proxy and returns its po
inter along with pointers to the interfaces it already implements.
Notes to Callers
A caller should begin by querying the object proxy for the IMultiQI interface. I
f the object proxy returns a pointer to this interface, the caller should then c
reate a MULTI_QI structure for each interface it wants to obtain. Each structure
should specify an interface IID and set its pItf member to NULL. Failure to set
the pItf member to NULL will cause the object proxy to ignore the structure.
On return, QueryMultipleInterfaces writes the requested interface pointer and a
return value into each MULTI_QI structure in the client s array. The pItf field re
ceives the pointer; the hr field receives the return value.
If the value returned from a call to QueryMultipleInterfaces is S_OK, then point
ers were returned for all requested interfaces. If the return value is E_NOINTER
FACE, then pointers were returned for none of the requested interfaces. If the r
eturn value is S_FALSE, then pointers to one or more requested interfaces were n
ot returned.In this event, the client should check the hr field of each MULTI_QI
structure to determine which interfaces were acquired and which were not.
If a client knows ahead of time that it will be using several of an object s inter
faces, it can call QueryMultipleInterfaces up front and then, later, if a QueryI
nterface is done for one of the interfaces already acquired through QueryMultipl
eInterfaces, no RPC call will be necessary.
On return, the caller should check the hr field of each MULTI_QI structure to de
termine which interface pointers were and were not returned.
The client is responsible for releasing each of the acquired interfaces by calli
ng IUnknown::Release.
See Also
IUnknown
7.7 Client Related API Descriptions
7.7.1 CoCreateInstance
Creates a single uninitialized object of the class associated with a specified C
LSID. Call CoCreateInstance when you want to create only one object on the local
system. To create a single object on a remote system, call CoCreateInstanceEx.
To create multiple objects based on a single CLSID, refer to the CoGetClassObjec
t function.
STDAPI CoCreateInstance(
REFCLSID rclsid, //Class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to whether object is or isn t part of an
aggregate
DWORD dwClsContext, //Context for running executable code
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives
// the interface pointer requested in riid
);
Parameters
rclsid
[in] CLSID associated with the data and code that will be used to create the obj
ect.
pUnkOuter
[in] If NULL, indicates that the object is not being created as part of an aggre
gate. If non-NULL, pointer to the aggregate object s IUnknown interface (the contr
olling IUnknown).
dwClsContext
[in] Context in which the code that manages the newly created object will run. T
he values are taken from the enumeration CLSCTX.
riid
[in] Reference to the identifier of the interface to be used to communicate with
the object.
ppv
[out] Address of pointer variable that receives the interface pointer requested
in riid. Upon successful return, *ppv contains the requested interface pointer.
Return Values
S_OK
An instance of the specified object class was successfully created.
REGDB_E_CLASSNOTREG
A specified class is not registered in the registration database. Also can indic
ate that the type of server you requested in the CLSCTX enumeration is not regis
tered or the values for the server types in the registry are corrupt.
CLASS_E_NOAGGREGATION
This class cannot be created as part of an aggregate.
Remarks
The CoCreateInstance helper function provides a convenient shortcut by connectin
g to the class object associated with the specified CLSID, creating an uninitial
ized instance, and releasing the class object. As such, it encapsulates the foll
owing functionality:
CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, &pCF);
hresult = pCF->CreateInstance(pUnkOuter, riid, ppvObj)
pCF->Release();
It is convenient to use CoCreateInstance when you need to create only a single i
nstance of an object on the local machine. If you are creating an instance on re
mote machine, call CoCreateInstanceEx. When you are creating multiple instances,
it is more efficient to obtain a pointer to the class object s IClassFactory inte
rface and use its methods as needed. In the latter case, you should use the CoGe
tClassObject function.
In the CLSCTX enumeration, you can specify the type of server used to manage the
object. The constants can be CLSCTX_INPROC_SERVER, CLSTCTX_INPROC_HANDLER, CLSC
TX_LOCAL_SERVER, or any combination of these values. The constant CLSCTX_ALL is
defined as the combination of all three. For more information about the use of o
ne or a combination of these constants, refer to CLSCTX.
See Also
CoGetClassObject, IClassFactory::CreateInstance, CoCreateInstanceEx, CLSCTX, Ins
tance Creation Helper Functions
7.7.2 CoCreateInstanceEx
Creates an instance of a specific class on a specific machine.
HRESULT CoCreateInstanceEx(
REFCLSID rclsid, //CLSID of the object to be created
IUnknown * punkOuter, //If part of an aggregate, the controlling IUnkn
own
DWORD dwClsCtx, //CLSCTX values
COSERVERINFO* pServerInfo, //Machine on which the object is to be instantia
ted
ULONG cmq, //Number of MULTI_QI structures in pResults
MULTI_QI pResults //Array of MULTI_QI structures
);
Parameters
rclsid
[in] CLSID of the object to be created.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggreg
ate, and punkOuter is to be used as the new instance s controlling IUnknown. Aggre
gation is currently not supported cross-process or cross-machine. When instantia
ting an object out of process, CLASS_E_NOAGGREGATION will be returned if punkOut
er is non-NULL.
dwClsCtx
[in] Values taken from the CLSCTX enumeration.
pServerInfo
[in] Machine on which to instantiate the object. May be NULL, in which case the
object is instantiated on the current machine or at the machine specified in the
registry under the class s RemoteServerName named-value, according to the interpr
etation of the dwClsCtx parameter. See the CLSCTX documentation for details).
cmq
[in] Number of MULTI_QI structures in pResults. Must be greater than zero.
pResults
Array of MULTI_QI structures. Each structure has three members: the identifier f
or a requested interface (pIID), the location to return the interface pointer (p
Itf) and the return value of the call to QueryInterface (hr).
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the pResults array were
successfully retrieved. The hr field of each of the MULTI_QI structures in pRes
ults indicates with S_OK or E_NOINTERFACE whether the specific interface was ret
urned.
E_NOINTERFACE
None of the interfaces requested in the pResults array were successfully retriev
ed.
Remarks
CoCreateInstanceEx creates a single uninitialized object associated with the giv
en CLSID on a specified remote machine. This is an extension of the function CoC
reateInstance, which creates an object on the local machine only. In addition, r
ather than requesting a single interface and obtaining a single pointer to that
interface, CoCreateInstanceEx makes it possible to specify an array of structure
s, each pointing to an interface identifier (IID) on input, and, on return, cont
aining (if available) a pointer to the requested interface and the return value
of the QueryInterface call for that interface. This permits fewer round trips be
tween machines.
The CoCreateInstanceEx helper function encapsulates three calls: first, to CoGet
ClassObject to connect to the class object associated with the specified CLSID,
specifying the machine location of the class; second, to IClassFactory::CreateIn
stance to create an uninitialized instance, and finally, to IClassFactory::Relea
se, to release the class object.
The object so created must still be initialized through a call to one of the ini
tialization interfaces (such as IPersistStorage:::Load). The two helper function
s, CoGetInstanceFromFile and CoGetInstanceFromIStorage encapsulate both the inst
ance creation and initialization from the obvious sources.
See Also
CoGetInstanceFromFile, CoGetInstanceFromIStorage, CLSCTX, COSERVERINFO, Instance
Creation Helper Functions
7.7.3 CoFreeAllLibraries
Frees all the DLLs that have been loaded with the CoLoadLibrary function (called
internally by CoGetClassObject), regardless of whether they are currently in us
e. This function is usually not called directly, because CoUninitialize and OleU
ninitialize call it internally.
void CoFreeAllLibraries( );
Remarks
To unload libraries, CoFreeAllLibraries uses a list of loaded DLLs for each proc
ess that the COM library maintains. The CoUninitialize function calls CoFreeAllL
ibraries internally, so COM applications usually have no need to call this funct
ion directly.
See Also
CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries, CoGetClassObject, CoUniniti
alize
7.7.4 CoFreeLibrary
Frees a library that, when loaded, was specified to be freed explicitly.
void CoFreeLibrary(
HINSTANCE hInst //Handle of the library module to be freed
);
Parameter
hInst
[in] Handle to the library module to be freed, as returned by CoLoadLibrary.
Remarks
The CoFreeLibrary function should be called to free a library that is to be free
d explicitly. This is established when the library is loaded with the bAutoFree
parameter of CoLoadLibrary set to FALSE. It is illegal to free a library explici
tly when the corresponding CoLoadLibrary call specifies that it be freed automat
ically (the bAutoFree parameter is set to TRUE).
See Also
CoFreeAllLibraries, CoFreeUnusedLibraries, CoLoadLibrary
7.7.5 CoFreeUnusedLibraries
Unloads any DLLs that are no longer in use and that, when loaded, were specified
to be freed automatically.
void CoFreeUnusedLibraries( );
Remarks
Applications can call CoFreeUnusedLibraries periodically to free resources. It i
s most efficient to call it either at the top of a message loop or in some idle-
time task. DLLs that are to be freed automatically have been loaded with the bAu
toFree parameter of the CoLoadLibrary function set to TRUE. CoFreeUnusedLibraries
internally calls DllCanUnloadNow for DLLs that implement and export that function
.
See Also
CoFreeLibrary, CoFreeUnusedLibraries, CoLoadLibrary, DLLCanUnloadNow
7.7.6 CoGetClassObject
Provides a pointer to an interface on a class object associated with a specified
CLSID. CoGetClassObject locates, and if necessary, dynamically loads the execut
able code required to do this.
Call CoGetClassObject directly when you want to create multiple objects through
a class object for which there is a CLSID in the system registry. You can also r
etrieve a class object from a specific remote machine. Most class objects implem
ent the IClassFactory interface. You would then call IClassFactory::CreateInstan
ce to create an uninitialized object. It is not always necessary to go through t
his process. To create a single object, call instead the either the CoCreateInst
anceEx function, which allows you to create an instance on a remote machine. Thi
s replaces the CoCreateInstance function, which can still be used to create an i
nstance on a local machine. Both functions encapsulate connecting to the class o
bject, creating the instance, and releasing the class object. Two other function
s, CoGetInstanceFromFile and CoGetInstanceFromIStorage, provide both instance cr
eation on a remote system, and object activation. COM also provides many other w
ays to create an object in the form of numerous helper functions and interface m
ethods whose function is to create objects of a single type and provide a pointe
r to an interface on that object.
STDAPI CoGetClassObject(
REFCLSID rclsid, //CLSID associated with the class object
DWORD dwClsContext, //Context for running executable code
COSERVERINFO * pServerInfo, //Pointer to machine on which the object is to b
e instantiated
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
Parameters
rclsid
[in] CLSID associated with the data and code that you will use to create the obj
ects.
dwClsContext
[in] Context in which the executable code is to be run. To enable a remote activ
ation, CLSCTX_REMOTE_SERVER must be included. For more information on the contex
t values and their use, see the CLSCTX enumeration.
pServerInfo
[in] Pointer to machine on which to instantiate the class object. May be NULL, i
n which case the class object is instantiated on the current machine or at the m
achine specified under the class s RemoteServerName key in the registry, according
to the interpretation of the dwClsCtx parameter (see the CLSCTX documentation f
or details).
riid
[in] Reference to the identifier of the interface, which will be supplied in ppv
on successful return. This interface will be used to communicate with the class
object. Typically this value is IID_IClassFactory, although other values such a
s IID_IClassFactory2 which supports a form of licensing are allowed. All COM-def
ined interface IIDs are defined in the COM header files as IID_interfacename, wh
ere interfacename is the name of the interface.
ppv
[out] On successful return, indirect pointer to the requested interfaceAddress o
f pointer variable that receives the interface pointer requested in riid. Upon s
uccessful return, *ppv contains the requested interface pointer.
Return Values
S_OK
Location and connection to the specified class object was successful.
REGDB_E_CLASSNOTREG
CLSID is not properly registered. Can also indicate that the value you specified
in dwClsContext is not in the registry.
E_NOINTERFACE
Either the object pointed to by ppv does not support the interface identified by
riid, or the QueryInterface operation on the class object returned E_NOINTERFAC
E.
REGDB_E_READREGDB
Error reading the registration database.
CO_E_DLLNOTFOUND
In-process DLL or handler DLL not found (depends on context).
CO_E_APPNOTFOUND
EXE not found (CLSCTX_LOCAL_SERVER only).
E_ACCESSDENIED
General access failure (returned from LoadLib/CreateProcess).
CO_E_ERRORINDLL
EXE has error in image.
CO_E_APPDIDNTREG
EXE was launched, but it didn t register class object (may or may not have shut do
wn).
Remarks
A class object in COM is an intermediate object that supports an interface that
permits operations common to a group of objects. The objects in this group are i
nstances derived from the same object definition represented by a single CLSID.
Usually, the interface implemented on a class object is IClassFactory, through w
hich you can create object instances of a given definition (class).
A call to CoGetClassObject creates, initializes, and gives the caller access (th
rough a pointer to an interface specified with the riid parameter) to the class
object. The class object is the one associated with the CLSID that you specify i
n the rclsid parameter. The details of how the system locates the associated cod
e and data within a given machine are transparent to the caller, as is the dynam
ic loading of any code that is not already loaded.
If the class context is CLSCTX_REMOTE_SERVER, indicating remote activation is re
quired, the COSERVERINFO structure provided in the pServerInfo parameter allows
you to specify the machine on which the server is located. For information on th
e algorithm used to locate a remote server when pServerInfo is NULL, refer to th
e CLSCTX enumeration.
There are two places to find a CLSID for a given class:
· The registry holds an association between CLSIDs and file suffixes, and between
CLSIDs and file signatures for determining the class of an object.
· When an object is saved to persistent storage, its CLSID is stored with its data
.
To create and initialize embedded or linked COM document objects, it is not nece
ssary to call CoGetClassObject directly. Instead, call one of the OleCreateor Ol
eCreateXxx helper functions. These functions encapsulate the entire object insta
ntiation and initialization process, and call, among other functions, CoGetClass
Object.
The riid parameter specifies the interface the client will use to communicate wi
th the class object. In most cases, this interface is IClassFactory. This provid
es access to the IClassFactory::CreateInstance method, through which the caller
can then create an uninitialized object of the kind specified in its implementat
ion. All classes registered in the system with a CLSID must implement IClassFact
ory.
In rare cases, however, you may want to specify some other interface that define
s operations common to a set of objects. For example, in the way COM implements
monikers, the interface on the class object is IParseDisplayName, used to transf
orm the display name of an object into a moniker.
The dwClsContext parameter specifies the execution context, allowing one CLSID t
o be associated with different pieces of code in different execution contexts. T
he CLSCTX enumeration, defined in Compobj.H, specifies the available context fla
gs. CoGetClassObject consults (as appropriate for the context indicated) both th
e registry and the class objects that are currently registered by calling the Co
RegisterClassObject function.
To release a class object, use the class object s Release method. The function CoR
evokeClassObject is to be used only to remove a class object s CLSID from the syst
em registry.
See Also
CoCreateInstanceEx, CoRegisterClassObject, CoRevokeClassObject, CLSCTX, Creating
an Object through a Class Object
7.7.7 CoGetInstanceFromFile
Creates a new object and initializes it from a file using IPersistFile::Load.
HRESULT CoGetInstanceFromFile(
COSERVERINFO * pServerInfo, //Pointer to COSERVERINFO struct indicat
ing remote system
CLSID* pclsid, //Pointer to the class of the object to create
Iunknown * punkOuter, //If part of an aggregate, pointer to the contro
lling IUnknown
DWORD dwClsCtx, //CLSCTX values
OLECHAR* szName, //File to initialize the object with
ULONG cmq, //Number of MULTI_QI structures in rgmqResults
MULTI_QI * rgmqResults //Array of MULTI_QI structures
);
Parameters
pServerInfo
[in] Pointer to a COSERVERINFO structure that specifies the machine on which to
instantiate the object and the authentication setting to be used. May be NULL, i
n which case the object is instantiated (1) on the current machine, (2) at the m
achine specified under the RemoteServerName named-value for the class in the reg
istry, or (3) at the machine where the szName file resides if the ActivateAtStor
age named-value is specified for the class in the registry or there is no local
registry information.
pclsid
[in] Pointer to the class of the object to create. May be NULL, in which case th
ere is a call to GetClassFile, using szName as its parameter to get the class of
the object to be instantiated.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggreg
ate, and punkOuter is to be used as the pointer to the new instance s controlling
IUnknown. Aggregation is currently not supported cross-process or cross-machine.
When instantiating an object out of process, CLASS_E_NOAGGREGATION will be retu
rned if punkOuter is non-NULL.
dwClsCtx
[in] Values taken from the CLSCTX enumeration.
grfMode
[in] Flags specifying how the file is to be opened. Values are taken from the ST
GM enumeration.
szName
[in] File to initialize the object with using IPersistFile::Load. May not be NUL
L.
cmq
[in] Number of MULTI_QI structures in rgmqResults. Must be greater than zero.
rgmqResults
[in] Array of MULTI_QI structures. Each structure has three members: the identif
ier for a requested interface (pIID), the location to return the interface point
er (pItf) and the return value of the call to QueryInterface (hr).
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the rgmqResults array w
ere successfully retrieved. The hr field of each of the MULTI_QI structures in r
gmqResults indicates with S_OK or E_NOINTERFACE whether or not the specific inte
rface was returned.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retr
ieved.
Remarks
CoGetInstanceFromFile creates a new object and initializes it from a file using
IPersistFile::Load. The result of this function is similar to creating an instan
ce with a call to CoCreateInstanceEx, followed by an initializing call to IPersi
stFile::Load, with the following important distinctions:
· Fewer network round trips are required by this function when instantiating an ob
ject on a remote machine.
· In the case where dwClsCtx is set to CLSCTX_REMOTE_SERVER and pServerInfo is NUL
L, if the class is registered with the ActivateAtStorage sub-key or has no assoc
iated registry information, this function will instantiate an object on the mach
ine where szName resides, providing the least possible network traffic. For exam
ple, if szName specified \\myserver\users\johndo\file , the object would be instant
iated on the myserver machine, and the object would access the file directly.
See Also
CoCreateInstanceEx, CoGetInstanceFromIStorage, CLSCTX, Instance Creation Helper
Functions
7.7.8 CoGetInstanceFromIStorage
Creates a new object and initializes it from a storage object through an interna
l call to IPersistStorage::Load.
HRESULT CoGetInstanceFromIStorage(
COSERVERINFO * pServerInfo, //Pointer to COSERVERINFO struct indicating remo
te system
CLSID * pclsid, //Pointer to the CLSID of the object to be created
Iunknown * punkOuter, //If part of an aggregate, pointer to the contro
lling IUnknown
DWORD dwClsCtx, //Values taken from the CLSCTX enumeration
Istorage * pstg, //Pointer to storage from which object is to be initiali
zed
ULONG cmq, //Number of MULTI_QI structures in rgmqResults
MULTI_QI * rgmqResults //Array of MULTI_QI structures
);
Parameters
pServerInfo
[in] Pointer to a COSERVERINFO structure that specifies the machine on which to
instantiate the object and the authentication setting to be used. May be NULL, i
n which case the object is either instantiated (1) on the current machine, (2) a
t the machine specified under the RemoteServerName named-value for the class in
the registry, or (3) at the machine where the storage object pointed to by pstg
is located if the class is registered with ActivateAtStorage specified or has no
local registry information.
pclsid
[in] Pointer to the class identifier (CLSID) of the object to be created. May be
NULL, in which case there is a call to IStorage:Stat to find the class of the o
bject.
punkOuter
[in] When non-NULL, indicates the instance is being created as part of an aggreg
ate, and punkOuter is to be used as the pointer to the new instance s controlling
IUnknown. Aggregation is currently not supported cross-process or cross-machine.
When instantiating an object out of process, CLASS_E_NOAGGREGATION will be retu
rned if punkOuter is non-NULL.
dwClsCtx
Values taken from the CLSCTX enumeration.
pstg
Pointer to storage to initialize the object with using IPersistStorage::Load. Ma
y not be NULL.
cmq
Number of MULTI_QI structures in rgmqResults. Must be greater than zero.
rgmqResults
Array of MULTI_QI structures. Each structure has three members: the identifier f
or a requested interface (pIID), the location to return the interface pointer (p
Itf) and the return value of the call to QueryInterface (hr).
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
CO_S_NOTALLINTERFACES
At least one, but not all of the interfaces requested in the rgmqResults array w
ere successfully retrieved. The hr field of each of the MULTI_QI structures in r
gmqResults indicates with S_OK or E_NOINTERFACE whether the specific interface p
ointer was retrieved.
E_NOINTERFACE
None of the interfaces requested in the rgmqResults array were successfully retr
ieved.
Remarks
CoGetInstanceFromIStorage creates a new object and initializes it from a storage
object through a call to IPersistStorage::Load. This function is similar to cre
ating an instance using CoCreateInstanceEx followed by a call to IPersistStorage
::Load, with the following important distinctions:
· Fewer network round trips are required by thisfunction when instantiating remote
ly.
· In the case where dwClsCtx is set to CLSCTX_REMOTE_SERVER and pServerInfo is NUL
L, if the class is registered with the ActivateAtStorage named value or has no a
ssociated registry information, this function will instantiate an object on the
same machine where the storage object pointed to by pstg resides, providing the
least possible network traffic. For example, if pstg were obtained through a cal
l to StgCreateDocfile, specifying \\myserver\users\johndo\file , the object would b
e instantiated on the myserver machine, and the object would access the storage ob
ject directly.
See Also
CoCreateInstanceEx, CoGetInstanceFromFile, CLSCTX, Instance Creation Helper Func
tions
7.7.9 CoGetTreatAsClass
Returns the CLSID of an object that can emulate the specified object.
HRESULT CoGetTreatAsClass(
ld, //CLSID of object that is being emulated
LPCLSID pclsidNew //Pointer to CLSID for object that can emulate clsidOld
);
Parameters
clsidOld
[in] CLSID of the object that can be emulated (treated as) an object with a diff
erent CLSID.
pclsidNew
[out] Pointer to where the CLSID that can emulate clsidOld objects is retrieved.
This parameter cannot be NULL. If there is no emulation information for clsidOl
d objects, the clsidOld parameter is supplied.
Return Values
S_OK
A new CLSID was successfully returned.
S_FALSE
No emulation information for the clsidOld parameter and that the pclsidNew param
eter is set to clsidOld.
REGDB_E_READREGDB
An error reading the registry.
This function can also return any of the error values returned by the CLSIDFromS
tring function.
Remarks
CoGetTreatAsClass returns the TreatAs entry in the registry for the specified ob
ject. The TreatAs entry, if set, is the CLSID of a registered object (an applica
tion) that can emulate the object in question. The TreatAs entry is set through
a call to the CoTreatAsClass function. Emulation allows an application to open a
nd edit an object of a different format, while retaining the original format of
the object. Objects of the original CLSID are activated and treated as objects o
f the second CLSID. When the object is saved, this may result in loss of edits n
ot supported by the original format. If there is no TreatAs entry for the specfi
ed object, this function returns the CLSID of the original object (clsidOld).
See Also
CoTreatAsClass
7.7.10 CoIsHandlerConnected
Determines whether a remote object is connected to the corresponding in-process
object.
BOOL CoIsHandlerConnected(
LPUNKNOWN pUnk //Pointer to the remote object
);
Parameter
pUnk
[in] Pointer to the controlling IUnknown interface on the remote object.
Return Values
TRUE
The object is not remote or that it is remote and is still connected to its remo
te handler.
FALSE
The object is remote and is invalid (no longer connected to its remote handler).
Remarks
The CoIsHandlerConnected function determines the status of a remote object. You
can use it to determine when to release a remote object. You specify the remote
object by giving the function a pointer to its controlling IUnknown interface (t
he pUnk parameter). A TRUE returned from the function indicates either that the
specified object is not remote, or that it is remote and is still connected to i
ts remote handler. A FALSE returned from the function indicates that the object
is remote but is no longer connected to its remote handler; in this case, the ca
ller should respond by releasing the object.
7.7.11 CoLoadLibrary
Loads a specific DLL into the caller s process. The CoGetClassObject function call
s CoLoadLibrary internally; applications should not call it directly.
HINSTANCE CoLoadLibrary(
LPOLESTR lpszLibName, //Pointer to the name of the library to be loade
d
BOOL bAutoFree //Whether library is automatically freed
);
Parameters
lpszLibName
[in] Pointer to the name of the library to be loaded. The use of this name is th
e same as in the Win32 function LoadLibrary.
bAutoFree
[in] If TRUE, indicates that this library is freed when it is no longer needed,
through a call to either the CoFreeUnusedLibraries or CoUninitialize functions.
If FALSE, the library should be explicitly freed with the CoFreeLibrary function
.
Return Values
Module Handle
Handle of the loaded library.
NULL
Library could not be loaded.
Remarks
The CoLoadLibrary function is called internally by the CoGetClassObject function
when the class context (CLSCTX) indicates a DLL. CoLoadLibrary loads a DLL spec
ified by the lpszLibName parameter into the process that called CoGetClassObject
. Containers should not call CoLoadLibrary directly.
Internally, a reference count is kept on the loaded DLL, by using CoLoadLibrary
to increment the count and the CoFreeLibrary function to decrement it.
See Also
CoFreeAllLibraries, CoFreeLibrary, CoFreeUnusedLibraries, CoGetClassObject
7.7.13 CoTreatAsClass
Establishes or removes an emulation, in which objects of one class are treated a
s objects of a different class.
STDAPI CoTreatAsClass(
REFCLSID clsidOld, //CLSID for the original object to be emulated
REFCLSID clsidNew //CLSID for the new object that emulates the original
);
Parameters
clsidOld
[in] CLSID of the object to be emulated.
clsidNew
[in] CLSID of the object that should emulate the original object. This replaces
any existing emulation for clsidOld. Can be CLSID_NULL, in which case any existi
ng emulation for clsidOld is removed.
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
The emulation was successfully established or removed.
REGDB_E_CLASSNOTREG
The clsidOld parameter is not properly registered in the registration database.
REGDB_E_READREGDB
Error reading from registration database.
REGDB_E_WRITEREGDB
Error writing to registration database.
Remarks
This function sets the TreatAs entry in the registry for the specified object, a
llowing the object to be emulated by another application. Emulation allows an ap
plication to open and edit an object of a different format, while retaining the
original format of the object. After this entry is set, whenever any function li
ke CoGetClassObject specifies the object s original CLSID (clsidOld), it is transp
arently forwarded to the new CLSID (clsidNew), thus launching the application as
sociated with the TreatAs CLSID. When the object is saved, it can be saved in it
s native format, which may result in loss of edits not supported by the original
format.
You would call CoTreatAsClass in two situations if your application supports emu
lation:
· In response to an end-user request (through a conversion dialog box) that a spec
ified object be treated as an object of a different class (an object created und
er one application be run under another application, while retaining the origina
l format information).
· In a setup program, to register that one class of objects be treated as objects
of a different class.
An example of the first case is that an end user might wish to edit a spreadshee
t created by one application using a different application that can read and wri
te the spreadsheet format of the original application. For an application that s
upports emulation, CoTreatAsClass can be called to implement a Treat As option i
n a conversion dialog box.
An example of the use of CoTreatAsClass in a setup program would be in an update
d version of an application. When the application is updated, the objects create
d with the earlier version can be activated and treated as objects of the new ve
rsion, while retaining the previous format information. This would allow you to
give the user the option to convert when they save, or to save it in the previou
s format, possibly losing format information not available in the older version.
To ensure that existing emulation information is removed when you install an app
lication, your setup programs should call CoTreatAsClass, setting the clsidNew p
arameter to CLSID_NULL to remove any existing emulation for the classes they ins
tall.
If there is no CLSID assigned to the AutoTreatAs key in the registry, setting cl
sidNew and clsidOld to the same value removes the TreatAs entry, so there is no
emulation. If there is a CLSID assigned to the AutoTreatAs key, that CLSID is as
signed to the TreatAs key.
The CoTreatAsClass function does not validate whether an appropriate registry en
try for clsidNew currently exists.
See Also
CoGetTreatAsClass
7.8 COM Client Related Structure Definitions
7.8.1 MULTI_QI
To optimize network performance, most remote activation functions take an array
of MULTI_QI structures rather than just a single IID as input and a single point
er to the requested interface on the object as output, as do local machine activ
ation functions. This allows a set of pointers to interfaces to be returned from
the same object in a single round-trip to the server. In network scenarios, req
uesting multiple interfaces at the time of object construction can save consider
able time over using a number of calls to the QueryInterface method for unique i
nterfaces, each of which would require a round-trip to the server.
typedef struct _MULTI_QI {
const IID* pIID;
IUnknown * pItf;
HRESULT hr;
} MULTI_QI;
Members
pIID
[in] Pointer to an interface identifier.
pItf
[out] Pointer to the interface requested in pIID. Must be set to NULL on entry.
hr
[out] Return value of the QueryInterface call made to satisfy the request for th
e interface requested in pIID. Common return values are S_OK and E_NOINTERFACE.
Must be set to zero on entry.
See Also
CoGetInstanceFromFile, CoGetInstanceFromIStorage, CoCreateInstanceEx
7.9 COM Client Related Enumeration Definitions
7.9.1 CLSCTX
Values from the CLSCTX enumeration are used in activation calls to indicate the
execution contexts in which an object is to be run. These values are also used i
n calls to CoRegisterClassObject to indicate the set of execution contexts in wh
ich a class object is to be made available for requests to construct instances.
typedef enum tagCLSCTX
{
CLSCTX_INPROC_SERVER = 1,
CLSCTX_INPROC_HANDLER = 2,
CLSCTX_LOCAL_SERVER = 4
CLSCTX_REMOTE_SERVER = 16
} CLSCTX;
#define CLSCTX_SERVER (CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_RE
MOTE_SERVER)
#define CLSCTX_ALL (CLSCTX_INPROC_HANDLER | CLSCTX_SERVER)
Elements
CLSCTX_INPROC_SERVER
The code that creates and manages objects of this class runs in the same process
as the caller of the function specifying the class context.
CLSCTX_INPROC_HANDLER
The code that manages objects of this class is an in-process handler. This is a
DLL that runs in the client process and implements client-side structures of thi
s class when instances of the class are accessed remotely.
CLSCTX_LOCAL_SERVER
The EXE code that creates and manages objects of this class is loaded in a separ
ate process space (runs on same machine but in a different process).
CLSCTX_REMOTE_SERVER
A remote machine context. The LocalServer32 or LocalService code that creates an
d manages objects of this class is run on a different machine.
Defined Terms
CLSCTX_SERVER
Indicates server code, whether in-process, local, or remote. This definition ORs
CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER, and CLSCTX_REMOTE_SERVER.
CLSCTX_ALL
Indicates all class contexts. This definition ORs CLSCTX_INPROC_HANDLER and CLSC
TX_SERVER.
Remarks
Values from the CLSCTX enumeration are used in activation calls (CoCreateInstanc
e, CoCreateInstanceEx, CoGetClassObject, etc.) to indicate the preferred executi
on contexts in-process, local, or remote in which an object is to be run. They a
re also used in calls to CoRegisterClassObject to indicate the set of execution
contexts in which a class object is to be made available for requests to constru
ct instances (IClassFactory::CreateInstance).
To indicate that more than one context is acceptable, you can string multiple va
lues together with Boolean ORs. The contexts are tried in the order in which the
y are listed.
The following table shows how other COM functions and methods that call CoGetCla
ssObject use the CLSCTX values:
Function Called Context Flag Used
CoUnMarshalInterface CLSCTX_INPROC_HANDLER
Unmarshaling needs the form of the class designed for remote access.
IMoniker::BindToObject, for a file moniker created through a call to CreateFileM
oniker In this case, uses CLSCTX_SERVER interally to create the instance after
calling GetClassFile to determine the class to be instantiated.
The CLSCTX_REMOTE_SERVER value is added to the CLSCTX enumeration for distribute
d COM. The CLSCTX_SERVER and CLSCTX_ALL constants are further updated to include
the CLSCTX_REMOTE_SERVER value.
Given a set of CLSCTX flags, dwClsCtx, the execution context to be used depends
on the availability of registered class codes and other parameters according to
the following algorithm:
The first part of the processing determines whether CLSCTX_REMOTE SERVER should
be specified as follows:
1. If the call specifies either
a) an explicit COSERVERINFO structure indicating a machine differen
t from the current machine, or
b) there is no explicit COSERVERINFO structure specified, but the s
pecified class is registered with either the RemoteServerName or ActivateAtStora
ge named-value.
then CLSCTX_REMOTE_SERVER is implied and is added to dwClsCtx. The second case a
llows applications written prior to the release of distributed COM to be the con
figuration of classes for remote activation to be used by client applications av
ailable prior to DCOM and the CLSCTX_REMOTE_SERVER flag. The cases in which ther
e would be no explicit COSERVERINFO structure are 1) The value is specified as N
ULL, or 2) It is not one of the function parameters, as would be the case in cal
ls to CoCreateInstance or CoGetClassObject in existing applications.
2. If the explicit COSERVERINFO parameter indicates the current mac
hine, CLSCTX_REMOTE_SERVER is removed (if present) from dwClsCtx.
The rest of the processing proceeds by looking at the value(s) of dwClsCtx in th
e following sequence.
1. If dwClsCtx includes CLSCTX_REMOTE_SERVER and no COSERVERINFO pa
rameter is specified, if the activation request indicates a persistent state fro
m which to initialize the object (with CoGetInstanceFromFile, CoGetInstanceFromI
Storage, or, for a file moniker, in a call to IMoniker::BindToObject) and the cl
ass has an ActivateAtStorage sub-key or no class registry information whatsoever
, the request to activate and initialize is forwarded to the machine where the p
ersistent state resides. (Refer to the remote activation functions listed in the
See Also section for details.)
2. If dwClsCtx includes CLSCTX_INPROC_SERVER, the class code in the
DLL found under the class s InprocServer32 key is used if this key exists. The cl
ass code will run within the same process as the caller.
3. If dwClsCtx includes CLSCTX_INPROC_HANDLER, the class code in th
e DLL found under the class s InprocHandler32 key is used if this key exists. The
class code will run within the same process as the caller.
4. If dwClsCtx includes CLSCTX_LOCAL_SERVER, the class code in the
Win32 service found under the class s LocalService key is used if this key exists.
If no Win32 service is specified, but an EXE is specified under that same key,
the class code associated with that EXE is used. The class code (in either case)
will be run in a separate service process on the same machine as the caller.
5. If dwClsCtx is set to CLSCTX_REMOTE_SERVER and an additional COS
ERVERINFO parameter to the function specifies a particular remote machine, a req
uest to activate is forwarded to this remote machine with dwClsCtx modified to b
e CLSCTX_LOCAL_SERVER. The class code will run in its own process on this specif
ic machine, which must be different from that of the caller.
6. Finally, if dwClsCtx includes CLSCTX_REMOTE_SERVER and no COSERV
ERINFO parameter is specified, if a machine name is given under the class s Remote
ServerName named-value, the request to activate is forwarded to this remote mach
ine with dwClsCtx modified to be CLSCTX_LOCAL_SERVER. The class code will run in
its own process on this specific machine, which must be different from that of
the caller.
See Also
CoCreateInstance, CoGetClassObject, CoRegisterClassObject, CoGetInstanceFromFile
, CoGetInstanceFromIStorage, CoCreateInstanceEx, COSERVERINFO structure, Creatin
g an Object through a Class Object, Registering a Running EXE Server
8. COM Servers
As described in earlier chapters, a COM Server is some module of code, a DLL or
an EXE, that implements one or more object classes (each with their own CLSID).
A COM server structures the object implementations such that COM clients can cre
ate an use objects from the server using the CLSID to identify the object throug
h the processes described in Chapter 7.
In addition, COM servers themselves may be clients of other objects, usually whe
n the server is using those other objects to help implement part of its own obje
cts. This chapter will cover the various methods of using an object as part of a
nother through the mechanisms of containment and aggregation.
Another feature that servers might support is the ability to emulate a different
server of a different CLSID. The COM Library provides a few API functions to su
pport this capability that are covered at the end of this chapter.
8.1 COM Server Responsibilities
One of the most important ways for a client to get a pointer to an object is for
the client to ask that a server be launched, and that an instance of the object
provided by the server be created and activated. It is the responsibility of th
e server to ensure that this happens properly. There are several important parts
to this.
The server must implement code for a class object through an implementation of e
ither the IClassFactory or IClassFactory2 interface.
The server must register its CLSID in the system registry on the machine on whic
h it resides, and further, has the option of publishing its machine location to
other systems on a network to allow clients to call it without requiring the cli
ent to know the server s location.
The server is primarily responsible for security that is, for the most part, the
server determines whether it will provide a pointer to one of its objects to a
client.
In-process servers should implement and export certain functions that allow the
client process to instantiate them.
8.2 Implementing IClassFactory
When a client uses a CLSID to request the creation of an object instance, the fi
rst step is creation of a class object, an intermediate object that contains an
implementation of the methods of the IClassFactory interface. While COM provides
several instance creation functions, the first step in the implementation of th
ese functions is the creation of a class object.
As a result, all servers must implement the methods of the IClassFactory interfa
ce. This interface contains two methods: CreateInstance and LockServer. CreateIn
stance must create an uninitialized instance of the object, and return a pointer
to a requested interface on the object.
The LockServer method just increments the reference count on the class object to
ensure that the server stays in memory, and does not shut down before the clien
t is ready for it to do so.
To enable a server to be responsible for its own licensing, COM defines IClassFa
ctory2, which inherits its definition from IClassFactory. Thus, a server impleme
nting IClassFactory2 must, by definition, implement the methods of IClassFactory
. For more information on IClassFactory2, see Licensing and IClassFactory2.
COM also provides helper functions for implementing out-of-process servers. For
more information, see Out-of-process Server Implementation Helpers.
8.3 Licensing and IClassFactory2
The IClassFactory interface on a class object provides the basic object creation
mechanism of COM. Using IClassFactory, a server can control object creation on
a machine basis. The implementation of the IClassFactory::CreateInstance method
can allow or disallow object creation based the existence of a machine license.
A machine license is a piece of information separate from the application that e
xists on a machine to indicate that the software was installed from a valid sour
ce, such as the vendor s installation disks. If the machine license does not exist
, the server can disallow object creation. Machine licensing prevents piracy in
cases where a user attempts to copy the software from one machine to another; be
cause the license information is not copied with the software, and the machine t
hat receives the copy is not licensed.
However, in a component software industry, vendors need a finer level of control
over licensing. In addition to machine license control, the a vendor needs to a
llow some clients to create a component object while preventing other clients fr
om the same capability. This kind of licensing requires that the client applicat
ion obtain a license key from component while the client application is still un
der development. The client application uses the license key later at run-time t
o create objects on an unlicensed machine.
For example, if a vendor provides a library of controls to developers, the devel
oper who purchases the library will have a full machine license, allowing the ob
jects to be created on the development machine. The developer can then build a c
lient application on the licensed machine incorporating one or more of the contr
ols. When the resulting client application is run on another machine, the contro
ls used in the client application must be created on the other machine even if t
hat machine does not possess a machine license to the controls from the original
vendor.
The IClassFactory2 interface provides this level of control. To allow key-based
licensing for any given component, you implement IClassFactory2 on the class fac
tory object for that component. IClassFactory2 is derived from IClassFactory, so
by implementing IClassFactory2 the class factory object fulfills the basic COM
requirements.
The GetLicInfo method fills a LICINFO structure with information describing the
licensing behavior of the class factory. For example, the class factory can prov
ide license keys for run-time licensing if the fRunTimeKeyAvail member is TRUE.
The RequestLicKey method provides a license key for the component. A machine lic
ense must be available when the client calls this method.
The CreateInstanceLic method creates an instance of the licensed component if th
e license key parameter (BSTR bstrKey) is valid.
In its type information, a component uses the attribute licensed to mark the coc
lass that supports licensing through IClassFactory2.
To incorporate a licensed component into your client application, you use the me
thods in IClassFactory2.
First, you need a separate development tool that is also a client of the license
d component. The purpose of this tool is to obtain the run-time license key and
save it in your client application. This tool runs only on a machine that posses
ses a machine license for the component. The tool calls the GetLicInfo and Reque
stLicKey methods to obtain the run-time license key and then saves the license k
ey in your client application. For example, the development tool could create a
.H file containing the BSTR license key. Then, you would include that .H file in
your client application.
To instantiate the component within your client application, you first try to in
stantiate the object directly with IClassFactory::CreateInstance. If CreateInsta
nce succeeds, then the second machine is itself licensed for the component and o
bjects can be created at will. If CreateInstance fails with the return code CLAS
S_E_NOTLICENSED, the only way to create the object is to pass the run-time key t
o the CreateInstanceLic method. CreateInstanceLic verifies the key and creates t
he object if the key is valid.
In this way an application built with components (such as controls), can run on
a machine that has no other license only the client application containing the run
-time license is allowed to create the component objects in question.
The IClassFactory2 interface supports flexibility in licensing schemes. For exam
ple, the server implementor can encrypt license keys in the component for added
security. Server implementers can also enable or disable levels of functionality
in their objects by providing different license keys for different functions. F
or example, one key might allow a base level of functionality, while another wou
ld allow basic and advanced functionality, and so on. See OLE Controls Inside Ou
t published by MS Press for detailed consideration of these issues.
8.4 Registering COM Servers
After you have defined a class in code (ensuring that it implements IClassFactor
y or IClassFactory2) and assigned it a CLSID, you need to put information in the
registry that will allow COM, on request of a client with the CLSID, to create
instances of its objects. This information tells the system, for a given CLSID,
where the DLL or EXE code for that class is located, and how it is to be launche
d. There is more than one way of registering a class in the registry. In additio
n, there are other ways of registering a class with the system when it is running,
so the system is aware that a running object is currently in the system. These
topics are described in the following sections.
8.4.1 Registering a Class at Installation
If a class is intended to be available to clients at any time, as most applicati
ons are, you usually register it through an installation and setup program. This
means putting information about the application into the registry, including ho
w and where its objects are to be instantiated. This information must be registe
red for all CLSIDs. Other information is optional. Win32 tools, such as Regsvr32
, make it simple to write a setup program that registers servers at installation
.
If you are not relying on system defaults, there are two important keys in the r
egistry: CLSID and AppID. Among the important pieces of information under these
keys is how the object is to be instantiated. Objects can be designated as in-pr
ocess, out-of-process local, or out-of-process remote.
Under the new AppID key, are several named-values that define information specif
ic to that application. Among these are RemoteServerName, and ActivateAtStorage,
both of which can be used to permit a client with no built-in knowledge of the
location of the server, to create an object. For more information on remote inst
antiation, see Locating a Remote Object and Instance Creation Helper Functions.
A server or ROT object that is not a Win32 service or run under a specific user
account can be referred to as an activate as activator server. For these servers,
the security context and the window station/desktop of the client must match the
server s.
When a class is registered as in-process, a call to CoGetClassObject to create i
ts class object is automatically passed by COM to the DllGetClassObject function
, which the class must implement to give the calling object a pointer to its cla
ss object.
Classes implemented in executables can specify that COM should execute their pro
cess and wait for the process to register their class object s IClassFactory throu
gh a call to the CoRegisterClassObject function.
For detailed COM registry information, see Registering Object Applications.
8.4.2 Registering a Running EXE Server
When an executable (EXE) server is launched, it should call CoRegisterClassObjec
t, which registers the CLSID for the server in what is called the class table (t
his is a different table than the running object table). When a server is regist
ered in the class table, it allows the SCM to determine that it is not necessary
to launch the class again; because the server is already running. Only if the s
erver is not listed in the class table will the SCM check the registry for appro
priate values and launch the server associated with the given CLSID.
You pass CoRegisterClassObject the CLSID for the class and a pointer to its IUnk
nown interface. Clients who subsequently call CoGetClassObject with this CLSID w
ill retrieve a pointer to their requested interface, as long as security does no
t forbid it. There are several instance creation and activation functions descri
bed in Instance Creation Helper Functions.
The server for a class object should call CoRevokeClassObject to revoke the clas
s object (remove its registration) when all of the following are true:
There are no existing instances of the object definition
There are no locks on the class object
The application providing services to the class object is not under user control
(not visible to the user on the display).
8.4.3 Registering Objects in the ROT
Typically, when a client asks a server to create an object instance, the server
typically creates moniker for the object, and registers it in the running object
table (ROT) through a call to IRunningObjectTable::Register.
A few additional issues arise when registering ROT objects for use by remote cli
ents. When the server calls CreateFileMoniker to create a file moniker to be reg
istered in the ROT, servers should pass local file names that are drive-based, n
ot in UNC format. This ensures that the moniker comparison data that is generate
d by the ROT register call will match what is used while doing a ROT lookup on t
he part of a remote client. This is because when the distribed COM service recei
ves an activation request for a file local to the server from a remote client, t
he file is converted to a local-drive-based path.
8.5 Self-Registration
As component software continues to grow as a market, there will be more and more
instances where a user obtains a new software component as a single DLL or EXE
module, such as downloading a new component from an on-line service or receiving
one from a friend on a floppy disk. In these cases, it is not practical to requ
ire the user to go through a lengthy installation procedure or setup program. Be
sides the licensing issues, which are handled through IClassFactory2, an install
ation procedure typically creates the necessary registry entries for a component
to run properly in the COM context.
Self-Registration is the standard means through which a server module can packag
e its own registry operations, both registration and unregistration, into the mo
dule itself. When used with licensing handled through IClassFactory2, a server c
an become an entirely self-contained module with no need for external installati
on programs or .REG files.
Any self-registering module, DLL or EXE, should first include a string called Ol
eSelfRegister in the StringFileInfo section of its version information resource:
The existence of this data allows any interested party, such as an application t
hat wishes to integrate this new component, to determine if the server supports
self-registration without having to load the DLL or EXE first.
If the server is packaged in a DLL module, the DLL must export the functions Dll
RegisterServer and DllUnregisterServer. Any application that wishes to instruct
the server to register itself (that is, all its CLSIDs and type library IDs) can
obtain a pointer to DllRegisterServer through the Win32 API function GetProcAdd
ress. Within DllRegisterServer, the DLL creates all its necessary registry entri
es, storing the correct path to the DLL for all InprocServer32 or InprocHandler3
2 entries.
When an application wishes to remove the component from the system, it should un
register that component by calling DllUnregisterServer. Within this call, the se
rver removes exactly those entries it previously created in DllRegisterServer. T
he server should not blindly remove all entries for its classes because other so
ftware may have stored additional entries, such as a TreatAs key.
If the server is packaged in an EXE module, then the application wishing to regi
ster the server launches the EXE server with the command-line argument /RegServe
r or -RegServer (case-insensitive). If the application wishes to unregister the
server, it launches the EXE with the command-line argument /UnregServer or -Unre
gServer. The self-registering EXE detects these command-line arguments and invok
es the same operations as a DLL would within DllRegisterServer and DllUnregister
Server, respectively, registering its module path under LocalServer32 instead of
InprocServer32 or InprocHandler32.
The server must register the full path to the installation location of the DLL o
r EXE module for their respective InprocServer32, InprocHandler32, and LocalServ
er32 keys in the registry. The module path is easily obtained through the Win32
API function GetModuleFileName.
8.6 Out-of-process Server Implementation Helpers
Four helper functions that can be called by out-of-process servers are now avail
able to simplify the job of writing server code. COM clients and COM in-process
servers typically would not call them. These functions are designed to help prev
ent race conditions in server activation when the servers have multiple apartmen
ts or multiple class objects. They can also, however, as easily be used for sing
le-threaded and single class object servers. The functions are as follows:
CoAddRefServerProcess
CoReleaseServerProcess
CoSuspendClassObjects
CoResumeClassObjects
To shut down properly, a COM server must keep track of how many object instances
it has instantiated and how many times its IClassFactory::LockServer method has
been called. Only when both of these counts reach zero, can a server shut down.
In single-threaded COM servers, the decision to shut down was coordinated with
incoming activation requests by the fact that the requests were serialized by th
e message queue. The server, upon receiving a Release on it s final object instanc
e and deciding to shut down, would revoke its class objects before any more acti
vation requests were dispatched. If an activation request did come in after this
point, COM would recognize that the class objects were revoked, and would retur
n an error to the SCM, which would then cause a new instance of the local server
process to be run.
However, in an apartment model server, in which different class objects are regi
stered on different apartments, and in all free-threaded servers, this decision
to shut down must be co-ordinated with activation requests across multiple threa
ds, so one thread of the server does not decide to shut down while another threa
d of the server is busy handing out class objects or object instances. One class
ical but cumbersome approach to solving this is to have the server, after it has
revoked its class objects, recheck its instance count and stay alive until all
instances have been released.
To make it easier for server writers to handle these types of race conditions, C
OM provides two new reference counting functions. CoAddRefServerProcess incremen
ts a global per-process reference count. CoReleaseServerProcess decrements the g
lobal per-process reference count. When the global per-process reference count r
eaches zero, COM automatically does a CoSuspendClassObjects, which prevents any
new activation requests from coming in. The server can then deregister its vario
us class objects from its various threads at leisure without worry that another
activation request may come in. All new activation requests are henceforth handl
ed by the SCM launching a new instance of the local server process.
The simplest way for a local server application to make use of these APIs is to
call CoAddRefServerProcess in the constructor for each of its instance objects,
and in each of its IClassFactory::LockServer methods when the fLock parameter is
TRUE. The server application should also call CoReleaseServerProcess in the des
tructor of each of its instance objects, and in each of its IClassFactory::LockS
erver methods when the fLock parameter is FALSE.
Finally, the server application should pay attention to the return code from CoR
eleaseServerProcess and if it returns 0, the server application should initiate
its cleanup, which, for a server with multiple threads, typically means that it
should signal it s various threads to exit their message loops and call CoRevokeCl
assObject and CoUninitialize. Note that if these functions are used at all, they
must be used in both the object instances and the LockServer method, otherwise,
the server application may be shut down prematurely.
In the latest versions of Windows NT, when a CoGetClassObject request is made, C
OM contacts the server, marshals the IClassFactory interface of the class object
, returns to the client process, unmarshals the IClassFactory interface, and ret
urns this to the client. At this point, clients typically call IClassFactory::Lo
ckServer(TRUE) to prevent the server process from shutting down. However, there
is a window of time between when the class object is marshaled and when the clie
nt calls LockServer, in which another client could connect to the same server, g
et an instance and Release that instance causing the server to shutdown and leav
ing the first client high and dry with a disconnected IClassFactory pointer. To
prevent this race condition, COM adds an implicit IClassFactory::LockServer(TRUE
) to the class object when it marshals the IClassFactory interface, and an impli
cit IClassFactory::LockServer(FALSE) when the client releases the IClassFactory
interface. Because of this change, it is no longer necessary to remote LockServe
r calls back to the server, so the proxy for IClassFactory::LockServer simply re
turns S_OK without actually remoting the call.
There is another activation-related race condition during initialization of an o
ut-of-process server process. A COM server that registers multiple classes typic
ally calls CoRegisterClassObject(....REGCLS_LOCAL_SERVER) for each CLSID it supp
orts. After it has done this for all classes, the server enters it s message loop.
For a single-threaded COM server, all activation requests are blocked until the
server enters the message loop. However, for an apartment model server that reg
isters different class objects in different apartments, and for all free-threade
d servers, activation requests can arrive earlier than this. In the case of apar
tment model servers, activation requests could arrive as soon as any one thread
has entered its message loop. In the case of free-threaded servers, an activatio
n request could arrive as soon as the first class object is registered. Since an
activation can happen this early, it is also possible for the final Release to
occur (and hence cause the server to begin shutting down) before the rest of the
server has had a chance to finish initializing.
To eliminate these race conditions and simplify the job of the server writer, an
y server that wants to register multiple class objects with COM should call CoRe
gisterClassObject(...., REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED) for each differe
nt CLSID the server supports. After all classes have been registered and the ser
ver process is ready to accept incoming activation requests, the server should m
ake one call to CoResumeClassObjects. This API tells COM to inform the SCM about
all the registered classes, and it begins letting activation requests into the
server process. Using these APIs has serveral advantages. First, only one call i
s made to the SCM regardless of how many CLSIDs are registered, thus reducing th
e overall registration time (and hence startup time of the server application).
The second advantage is that if the server has multiple apartments and different
CLSIDs are registered in different apartments, or if the server is a free-threa
ded server, no activation requests will come in until the server calls CoResumeC
lassObjects, giving the server a chance to register all of its CLSIDs and get pr
operly set up before having to deal with activation requests, and possible shut
down requests.
8.7 Object Handlers
As mentioned earlier this specification, object handlers from one perspective ar
e special cases of in-process servers that talk to their local or remote servers
as well as a client. From a second perspective, an object handler is really jus
t a fancy proxy for a local or remote server that does a little more than just f
orward calls through RPC. The latter view is more precise architecturally: a hand
ler is simply the piece of code that runs in the client s space on behalf of a remo
te object; it can be used synonymously with the term proxy object. The handler may
be a trivial one, one that simply forwards all of its calls on to the remote ob
ject, or it may implement some amount of non-trivial client side processing. (In
practice, the term proxy object is most often reserved for use with trivial handl
ers, leaving handler for the more general situation.)
The structure of an object handler is exactly the same as a full-in process serv
er: an object handler implements an object, a class factory, and the two functio
ns DllGetClassObject and DllCanUnloadNow exactly as described above.
The key difference between handlers and full DLL servers (and simple proxy objec
ts, for that matter) is the extent to which they implement their respective obje
cts. Whereas the full DLL server implements the complete object (using other obj
ects internally, if desired), the handler only implements a partial object depen
ding on a local or remote server to complete the implementation. Again, the reas
ons for this is that sometimes a certain interface can only be useful when imple
mented on an in-process object, such as when member functions of that interface
contain parameters that cannot be shared between processes. Thus the object in t
he handler would implement the restricted in-process interface but leave all oth
ers for implementation in the local or remote server.
8.8 Object Reusability
With object-oriented programming it is often true that there already exists some
object that implements some of what you want to implement, and instead of rewri
ting all that code yourself you would like to reuse that other object for your o
wn implementation. Hence we have the desire for object reusability and a number
means to achieve it such as implementation inheritance, which is exploited in C+
+ and other languages. However, as discussed in the Object Reusability section of
Chapter 3, implementation inheritance has some significant drawbacks and problem
s that do not make it a good object reusability mechanism for a system object mo
del.
For that reason COM supports two notions of object reuse, containment and aggreg
ation, that were also described in Chapter 3. In that chapter we saw that contai
nment, the most common and simplest for of object reuse, is where the outer objec
t simply uses other inner objects for their services. The outer object is nothing m
ore than a client of the inner objects. We also saw in Chapter 3 the notion of a
ggregation, where the outer object exposes interfaces from inner objects as if t
he outer object implemented those interfaces itself. We brought up the catch tha
t there has to be some mechanism through which the IUnknown behavior of inner ob
ject interfaces exposed in this manner is appropriate to the outer object. We ar
e now in a position to see exactly how the solution manifests itself.
The following sections treat Containment and Aggregation in more detail using th
e TextRender object as an example. To refresh our memory of this object s purpose,
the following list reiterates the specific features of the TextRender object th
at implements the IPersistFile and IDataObject interfaces:
Read text from a file through IPersistFile::Load
Write text to a file through IPersistFile::Save
Accept a memory copy of the text through IDataObject::SetData
Render a memory copy of the text through IDataObject::GetData
Render metafile and bitmap images of the text also through IDataObject::GetData
8.8.1 Reusability Through Containment
Let s say that when we decide to implement the TextRender object we find that anot
her object exists with CLSID_TextImage that is capable of accepting text through
IDataObject::SetData but can do nothing more than render a metafile or bitmap f
or that text through IDataObject::GetData. This TextImage object cannot render mem
ory copies of the text and has no concept of reading or writing text to a file.
But it does such a good job implementing the graphical rendering that we wish to
use it to help implement our TextRender object.
In this case the TextRender object, when asked for a metafile or bitmap of its c
urrent text in IDataObject::GetData, would delegate the rendering to the TextIma
ge object. TextRender would first call TextImage s IDataObject::SetData to give it
the most recent text (if it has changed since the last call) and then call Text
Image s IDataObject::GetData asking for the metafile or bitmap format. This delega
tion is illustrated in Figure 8-1.
Figure 8-1: An outer object that uses inner objects through
containment is a client of the inner objects.
To create this configuration, the TextRender object would, during its own creati
on, instantiate the TextImage object with the following code, storing the TextIm
age s IDataObject pointer in a TextImage field m_pIDataObjImage:
//TextRender initialization
HRESULT hr;
hr=CoCreateInstance(CLSID_TextImage, CLSCTX_SERVER, NULL, IID_IDataObject, (void
*)&m_pIDataObjImage);
if (FAILED(hr))
//TextImage not available, either fail or disable graphic rendering
//Success: can now make use of TextImage object.
8.8.2 Reusability Through Aggregation
Let s now say that we are planning to revise our TextRender object at a later time
than out initial containment implementation in the previous section. At that ti
me we find that the implementor of the TextImage object at the time the implemen
tor of the TextRender object sat down to work (or perhaps is making a revision o
f his object) that the vendor of the TextImage object has improved TextImage suc
h that it implements everything that TextRender would like to do through its IDa
taObject interface. That is, TextImage still accepts text through SetData but ha
s recently added the ability to make copies of its text and provide those copies
through GetData in addition to metafiles and bitmaps.
In this case, the implementor of TextRender now sees that TextImage s implementati
on of IDataObject is exactly the implementation that TextRender requires. What w
e, as the implementors of TextRender, would like to do now is simply expose Text
Image s IDataObject as our own as shown in Figure 8-2.
Figure 8-2: When an inner object does a complete job implementing an
interface, outer objects may want to expose the interface directly.
The only catch is that we must implement the proper behavior of the IUnknown mem
bers in the inner object s (TextImage) IDataObject interface: AddRef and Release h
ave to affect the reference count on the outer object (TextRender) and not the r
eference count of the inner object. Furthermore, QueryInterface has to be able t
o return the TextRender object s IPersistFile interface. The solution is to inform
the inner object that it is being used in an aggregation such that when it sees
IUnknown calls to its interfaces it can delegate those calls to the outer objec
t.
One other catch remains: the outer object must have a means to control the lifet
ime of the inner object through AddRef and Release as well as have a means to qu
ery for the interfaces that only exist on the inner object. For that reason, the
inner object must implement an isolated version of IUnknown that controls the i
nner object exclusively and never delegates to the outer object. This requires t
hat the inner object separates the IUnknown members of its functional interfaces
from an implementation of IUnknown that strictly controls the inner object itse
lf. In other words, the inner object, to support aggregation, must implement two
sets of IUnknown functions: delegating and non-delegating.
This, then, is the mechanism for making aggregation work:
When creating the inner object, the outer object must pass its own IUnknown to t
he inner object through the pUnkOuter parameter of IClassFactory::CreateInstance
. pUnkOuter in this case is called the controlling unknown.
The inner object must check pUnkOuter in its implementation of CreateInstance. I
f this parameter is non-NULL, then the inner object knows it is being created as
part of an aggregate. If the inner object does not support aggregation, then it
must fail with CLASS_E_NOAGGREGATION. If aggregation is supported, the inner ob
ject saves pUnkOuter for later use, but does not call AddRef on it. The reason i
s that the inner object s lifetime is entirely contained within the outer object s l
ifetime, so there is no need for the call and to do so would create a circular r
eference.
If the inner object detects a non-NULL pUnkOuter in CreateInstance, and the call
requests the interface IUnknown itself (as is almost always the case), the inne
r object must be sure to return its non-delegating IUnknown.
If the inner object itself aggregates other objects (which is unknown to the out
er object) it must pass the same pUnkOuter pointer it receives down to the next
inner object.
When the outer object is queried for an interface it exposes from the inner obje
ct, the outer object calls QueryInterface in the non-delegating IUnknown to obta
in the pointer to return to the client.
The inner object must delegate to the controlling unknown, that is, pUnkOuter, a
ll IUnknown calls occurring in any interface it implements other than the non-de
legating IUnknown.
Through these steps, the inner object is made aware of the outer object, obtains
an IUnknown to which it can delegate calls to insure proper behavior of referen
ce counting and QueryInterface, and provides a way for the outer object to contr
ol the inner object s lifetime separately. The mechanism is illustrated in Figure
8-3.
Figure 8-3: Aggregation requires an explicit implementation of IUnknown on the i
nner
object and delegation of IUnknown function of any other interface to the outer o
bject s
IUnknown functions.
Now let s look at how this mechanism manifests in code. First off, the TextRender
object no longer needs it s own IDataObject implementation and can thus remove it
from it s class, but will need to add a member m_pUnkImage to maintain the TextIma
ge s non-delegating IUnknown:
class CTextRender : public IPersistFile {
private:
ULONG m_cRef; //Reference Count
char * m_pszText; //Pointer to allocated t
ext
ULONG m_cchText; //Number of characters i
n m_pszText
IUnknown * m_pUnkImage; //TextImage IUnknown
//Other internal member functions here
public:
[Constructor, Destructor]
//Outer object IUnknown
HRESULT QueryInterface(REFIID iid, void ** ppv);
ULONG AddRef(void);
ULONG Release(void);
//IPersistFile Member overrides
...
};
In the previous section we saw how the TextRender object would create a TextImag
e object for containment using CoCreateInstance with the pUnkOuter parameter set
to NULL. In aggregation, this parameter will be TextRender s own IUnknown (obtain
ed using a typecast). Furthermore, TextRender must request IUnknown initially fr
om TextImage (storing the pointer in m_pUnkImage):
//TextRender initialization
HRESULT hr;
hr=CoCreateInstance(CLSID_TextImage, CLSCTX_ SERVER, (IUnknown *)this, IID_IUnkn
own, (void *)&m_pUnkImage);
if (FAILED(hr))
//TextImage not available, either fail or disable graphic rendering
//Success: can now make use of TextImage object.
Now, since TextRender does not have it s own IDataObject any longer, its implement
ation of QueryInterface will use m_pUnkImage to obtain interface pointers:
HRESULT CTextRender::QueryInterface(REFIID iid, void ** ppv) {
*ppv=NULL;
//This code assumes an overloaded == operator for GUIDs exists
if (IID_IUnknown==iid)
*ppv=(void *)(IUnknown *)this;
if (IID_IPersitFile==iid)
*ppv=(void *)(IPersistFile *)this;
if (IID_IDataObject==iid)
return m_pUnkImage->QueryInterface(iid, ppv);
if (NULL==*ppv)
return E_NOINTERFACE; //iid not supported.
//Any call to anyone s AddRef is our own.
AddRef();
return NOERROR;
}
Note that delegating QueryInterface to the inner object is done only for those i
nterfaces that the outer object knows it wants to expose. The outer object shoul
d not delegate the query as a default case, for such blind forwarding without an
understanding of the semantic being forwarded will almost assuredly break the o
uter object should the inner one be revised with new functionality.
8.8.2.1 Caching interfaces on the inner object
In order to avoid reference counting cycles, special action is needed if the out
er object wishes to cache pointers to the inner object s interfaces.
Specifically, if the outer object wishes to cache a to an inner object s interface
, once it has obtained the interface from the inner object, the outer object sho
uld invoke Release on the punkOuter that was given to the inner object at its in
stantiation time.
// Obtaining inner object interface pointer
pUnkInner->QueryInterface(IID_IFoo, &pIFoo);
pUnkOuter->Release();
// Releasing inner object interface pointer
pUnkOuter->AddRef();
pIFoo->Release();
It is suggested that to allow inner objects to do better resource management tha
t controlling objects delay the acquisition of cached pointers and release them
when there is no possible use for them.
8.8.2.2 Efficiency at any Depth of Aggregation
Aggregation has one interesting aspect when aggregates are used on more than one
level of an object implementation. Imagine that the TextImage object in the pre
vious example is itself an aggregate object that uses other inner objects. In su
ch a case TextImage will be passing some controlling unknown to those other inne
r objects. If TextImage is not being aggregated by anyone else, then the control
ling unknown is its own; otherwise it passes the pUnkOuter from IClassFactory::C
reateInstance on down the line, and any other inner objects that are aggregates
themselves do the same.
The net result is that any object in an aggregation, no matter how deeply it is
buried in the overall structure, will almost always delegate directly to the con
trolling unknown if it s interface is exposed from that final outer object. Theref
ore performance and efficiency of multiple levels of aggregation is not an issue
. At worst each delegation is a single extra function call.
8.9 Emulating Other Servers
The final topic related to COM Servers for this chapter is what is known as emul
ation: the ability for one server associated with one CLSID to emulate a server
of another CLSID. A server that can emulate another is responsible for providing
compatible behavior for a different class through a different implementation. T
his forms the basis for allowing end-users the choice in which servers are used
for which objects, as long as the behavior is compatible between those servers.
As far as COM is concerned, it only has to provide some way for a server to indi
cate that it wishes to emulate some CLSID. To that end, the COM Library supplies
the function CoTreatAsClass to establish an emulation that remains in effect (p
ersistently) until canceled or changed. In addition it supplies CoGetTreatAsClas
s to allow a caller to determine if a given CLSID is marked for emulation.
8.10 COM Server Related Interface Descriptions
8.10.1 ICatRegister
The ICatRegister interface provides methods for registering and unregistering co
mponent category information in the Registry. This includes both the human-reada
ble names of categories and the categories implemented/required by a given compo
nent or class.
8.10.1.1.1 When to Implement
There is no need to implement this interface. The Component Category Manager, a
system-provided COM object that can be instantiated by using CoCreateInstance, i
mplements ICatRegister.
8.10.1.1.2 When to Use
The owner of a category uses this interface to register or unregister the human-
readable names. The owner of a component uses this interface to add or remove ca
tegories implemented or required by this component.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
ICatRegister Methods Description
RegisterCategories Registers one or more component categories.
UnRegisterCategories Removes the registration of one or more component catego
ries.
RegisterClassImplCategories Registers the class as implementing one or more
component categories.
UnRegisterClassImplCategories Removes one or more implemented category identif
iers from a class.
RegisterClassReqCategories Registers the class as requiring one or more com
ponent categories.
UnRegisterClassReqCategories Removes one or more required category identifier
s from a class.

8.10.1.2 ICatRegister::RegisterCategories
Registers one or more component categories. Each component category consists of
a CATID and a list of locale-dependent description strings.
HRESULT RegisterCategories(

ULONG cCategories, //Number of component categories


CATEGORYINFO* rgCategoryInfo //Array of cCategories CATEGORYINFO stru
ctures
);
Parameters
cCategories
[in] The number of component categories to register.
rgCategoryInfo
[in] The array of cCategories CATEGORYINFO structures. By providing the same CAT
ID for multiple CATEGORYINFO structures, multiple locales can be registered for
the same component category.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
This function can only be called by the owner of a category, usually as part of
the installation or de-installation of the operating system or application.
See Also
ICatRegister::RegisterClassImplCategories, ICatRegister::RegisterClassReqCategor
ies, ICatRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCatego
ries, ICatRegister::UnRegisterClassReqCategories

8.10.1.3 ICatRegister::RegisterClassImplCategories
Registers the class as implementing one or more component categories.
HRESULT RegisterClassImplCategories(
REFCLSID rclsid, //Class ID of the relevent class
ULONG cCategories, //Number of category CATIDs
CATID* rgcatid //Array of cCategories CATID
);
Parameters
rclsid
[in] The class ID of the relevent class for which category information will be s
et.
cCategories
[in] The number of category CATIDs to associate as category identifiers for the
class.
rgcatid
[in] The array of cCategories CATID to associate as category identifiers for the
class.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
In case of an error, this function does not ensure that the Registry is restored
to the state prior to the call. This function can only be called by the owner o
f a class, usually as part of the installation of the component.
See Also
ICatRegister::RegisterCategories, ICatRegister::RegisterClassReqCategories, ICat
Register::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, ICa
tRegister::UnRegisterClassReqCategories

8.10.1.4 ICatRegister::RegisterClassReqCategories
Registers the class as requiring one or more component categories.
HRESULT RegisterClassReqCategories(
REFCLSID rclsid, //Class ID of the relevent class
ULONG cCategories, //Number of category CATIDs
CATID* rgcatid //Array of cCategories CATID
);
Parameters
rclsid
[in] The class ID of the relevent class for which category information will be s
et.
cCategories
[in] The number of category CATIDs to associate as category identifiers for the
class.
rgcatid
[in] The array of cCategories CATID to associate as category identifiers for the
class.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
In case of an error, this function does not ensure that the Registry is restored
to the state prior to the call. This function can only be called by the owner o
f a class, usually as part of the installation of the component.
See Also
ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICa
tRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, IC
atRegister::UnRegisterClassReqCategories

8.10.1.5 ICatRegister::UnRegisterCategories
Removes the registration of one or more component categories. Each component cat
egory consists of a CATID and a list of locale-dependent description strings.
HRESULT UnRegisterCategories(
ULONG cCategories, //Number of cCategories CATIDs to be removed
REFCATID rgcatid //Array of cCategories CATIDs
);
Parameters
cCategories
[in] The number of cCategories CATIDs to be removed.
rgcatid
[in] Identifies the categories to be removed.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
This function will be successful even if one or more of the category IDs specifi
ed are not registered. This function can only be called by the owner of a catego
ry, usually as part of the installation or de-installation of the operating syst
em or application.
Note
This method does not remove the component category tags from individual classes.
To do this, use the ICatRegister::UnRegisterClassCategories method.
See Also
ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICa
tRegister::RegisterClassReqCategories, ICatRegister::UnRegisterClassImplCategori
es, ICatRegister::UnRegisterClassReqCategories

8.10.1.6 ICatRegister::UnRegisterClassImplCategories
Removes one or more implemented category identifiers from a class.
HRESULT UnRegisterClassImplCategories(
REFCLSID rclsid, //Class ID of the relevent class
ULONG cCategories, //Number of category CATIDs
CATID* rgcatid //Array of cCategories CATID
);
Parameters
rclsid
[in] The class ID of the relevant class to be manipulated.
cCategories
[in] The number of category CATIDs to remove.
rgcatid
[in] The array of cCategories CATID that are to be removed. Only the category ID
s specified in this array are removed.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
In case of an error, this function does not ensure that the Registry is restored
to the state prior to the call. The call will be successful even if one or more
of the category IDs specified are not registered for the class. This function c
an only be called by the owner of a class, usually as part of the de-installatio
n of the component.
See Also
ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICa
tRegister::RegisterClassReqCategories, ICatRegister::UnRegisterCategories, ICatR
egister::UnRegisterClassReqCategories

8.10.1.7 ICatRegister::UnRegisterClassReqCategories
Removes one or more required category identifiers from a class.
HRESULT UnRegisterClassReqCategories(
REFCLSID rclsid, //Class ID of the relevent class
ULONG cCategories, //Number of category CATIDs
CATID* rgcatid* //Array of cCategories CATID
);
Parameters
rclsid
[in] The class ID of the relevent class to be manipulated.
cCategories
[in] The number of category CATIDs to remove.
rgcatid
[in] The array of cCategories CATID that are to be removed. Only the category ID
s specified in this array are removed.
Return Values
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
Remarks
In case of an error, this function does not ensure that the Registry is restored
to the state prior to the call. The call will be successful even if one or more
of the category IDs specified are not registered for the class.
See Also
ICatRegister::RegisterCategories, ICatRegister::RegisterClassImplCategories, ICa
tRegister::UnRegisterCategories, ICatRegister::UnRegisterClassImplCategories, IC
atRegister::RegisterClassReqCategories

8.10.2 IClassFactory
The IClassFactory interface contains two methods intended to deal with an entire
class of objects, and so is implemented on the class object for a specific clas
s of objects (identified by a CLSID). The first method, CreateInstance, creates
an uninitialized object of a specified CLSID, and the second, LockServer, locks
the object s server in memory, allowing new objects to be created more quickly.
8.10.2.1 When to Implement
You must implement this interface for every class that you register in the syste
m registry and to which you assign a CLSID, so objects of that class can be crea
ted.
8.10.2.2 When to Use
After calling the CoGetClassObject function to get an IClassFactory interface po
inter to the class object, call the CreateInstance method of this interface to c
reate a new uninitialized object.
It is not, however, always necessary to go through this process to create an obj
ect. To create a single uninitialized object, you can, instead, just call CoCrea
teInstance. COM also provides numerous helper functions (with names of the form
OleCreateXxx) to create compound document objects.
Call the LockServer method to keep the object server in memory and enhance perfo
rmance only if you intend to create more than one object of the specified class.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IClassFactory Methods Description
CreateInstance Creates an uninitialized object.
LockServer Locks object application open in memory.
See Also
CoGetClassObject, CoCreateInstance
8.10.2.3 IClassFactory::CreateInstance
Creates an uninitialized object.
HRESULT CreateInstance(
IUnknown * pUnkOuter, //Pointer to whether object is or isn t part of an
aggregate
REFIID riid, //Reference to the identifier of the interface
void ** ppvObject //Indirect pointer to the interface
);
Parameters
pUnkOuter
[in] If the object is being created as part of an aggregate, pointer to the cont
rolling IUnknown interface of the aggregate. Otherwise, pUnkOuter must be NULL.
riid
[in] Reference to the identifier of the interface to be used to communicate with
the newly created object. If pUnkOuter is NULL, this parameter is frequently th
e IID of the initializing interface; if pUnkOuter is non-NULL, riid must be IID_
IUnknown (defined in the header as the IID for IUnknown).
ppvObject
[out] Indirect pointer to the requested interface. If the object does not suppor
t the interface specified in riid, ppvObject must be set to NULL.
Return Values
This method supports the standard return values E_UNEXPECTED, E_OUTOFMEMORY, and
E_INVALIDARG, as well as the following:
S_OK
The specified object was created.
CLASS_E_NOAGGREGATION
The pUnkOuter parameter was non-NULL and the object does not support aggregation
.
E_NOINTERFACE
The object that ppvObject points to does not support the interface identified by
riid.
Remarks
The IClassFactory interface is always on a class object. The CreateInstance meth
od creates an uninitialized object of the class identified with the specified CL
SID. When an object is created in this way, the CLSID must be registered in the
system registry with CoRegisterClassObject.
The pUnkOuter parameter indicates whether the object is being created as part of
an aggregate. Object definitions are not required to support aggregation they m
ust be specifically designed and implemented to support it.
The riid parameter specifies the IID (interface identifier) of the interface thr
ough which you will communicate with the new object. If pUnkOuter is non-NULL (i
ndicating aggregation), the value of the riid parameter must be IID_IUnknown. If
the object is not part of an aggregate, riid often specifies the interface thou
gh which the object will be initialized.
For COM embeddings, the initialization interface is IPersistStorage, but in othe
r situations, other interfaces are used. To initialize the object, there must be
a subsequent call to an appropriate method in the initializing interface. Commo
n initialization functions include IPersistStorage::InitNew (for new, blank embe
ddable components), IPersistStorage::Load (for reloaded embeddable components),
IPersistStream::Load, (for objects stored in a stream object) or IPersistFile::L
oad (for objects stored in a file).
In general, if an application supports only one class of objects, and the class
object is registered for single use, only one object can be created. The applica
tion must not create other objects, and a request to do so should return an erro
r from IClassFactory::CreateInstance. The same is true for applications that sup
port multiple classes, each with a class object registered for single use; a Cre
ateInstance for one class followed by a CreateInstance for any of the classes sh
ould return an error.
To avoid returning an error, applications that support multiple classes with sin
gle-use class objects can revoke the registered class object of the first class
by calling CoRevokeClassObject when a request for instantiating a second is rece
ived. For example, suppose there are two classes, A and B. When IClassFactory::C
reateInstance is called for class A, revoke the class object for B. When B is cr
eated, revoke the class object for A. This solution complicates shutdown because
one of the class objects might have already been revoked (and cannot be revoked
twice).
See Also
CoRegisterClassObject, CoRevokeClassObject, CoCreateInstance, CoGetClassObject
8.10.2.4 IClassFactory::LockServer
Called by the client of a class object to keep a server open in memory, allowing
instances to be created more quickly.
HRESULT LockServer(
BOOL fLock //Increments or decrements the lock count
);
Parameter
fLock
[in] If TRUE, increments the lock count; if FALSE, decrements the lock count.
Return Values
This method supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_UNE
XPECTED, as well as the following:
S_OK
The specified object was either locked ( fLock = TRUE) or unlocked from memory (
fLock = FALSE).
Remarks
IClassFactory::LockServer controls whether an object s server is kept in memory. K
eeping the application alive in memory allows instances to be created more quick
ly.
8.10.2.4.1 Notes to Callers
Most clients do not need to call this function. It is provided only for those cl
ients that require special performance in creating multiple instances of their o
bjects.
8.10.2.4.2 Notes to Implementers
If the lock count is zero, there are no more objects in use, and the application
is not under user control, the server can be closed. One way to implement IClas
sFactory::LockServer is to call CoLockObjectExternal.
The process that locks the object application is responsible for unlocking it. O
nce the class object is released, there is no mechanism that guarantees the call
er connection to the same class later (as in the case where a class object is re
gistered as single-use). It is important to count all calls, not just the last o
ne, to IClassFactory::LockServer, because calls must be balanced before attempti
ng to release the pointer to the IClassFactory interface on the class object or
an error results. For every call to LockServer with fLock set to TRUE, there mus
t be a call to LockServer with fLock set to FALSE. When the lock count and the c
lass object reference count are both zero, the class object can be freed.
See Also
CoLockObjectExternal
8.10.3 IClassFactory2
The IClassFactory2 interface enables a class factory object, in any sort of obje
ct server, to control object creation through licensing. This interface is an ex
tension to IClassFactory. This extension enables a class factory executing on a
licensed machine to provide a license key that can be used later to create an ob
ject instance on an unlicensed machine. Such considerations are important for ob
jects like controls that are used to build applications on a licensed machine. S
ubsequently, the application built must be able to run on an unlicensed machine.
The license key gives only that one client application the right to instantiate
objects through IClassFactory2 when a full machine license does not exist.
8.10.3.1.1 When to Implement
Implement this interface on a class factory object if you need to control object
creation through a license. A class that supports licensing should be marked in
an object s type information with the [licensed] attribute on the object s coclass
entry.
The CreateInstance method inherited from IClassFactory is allowed to return CLAS
S_E_NOTLICENSED to indicate that object creation is controlled through licensing
. The caller can create an instance of this object only through IClassFactory2::
CreateInstanceLic if the caller has a license key obtained from IClassFactory2::
RequestLicKey. Otherwise, no object creation is allowed.
8.10.3.1.2 When to Use
Use this interface to create licensed objects or to obtain a license key that ca
n be used in later creations.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IClassFactory Methods Description
CreateInstance Creates an uninitialized object.
LockServer Locks object application open in memory.
IClassFactory2 Methods Description
GetLicInfo Fills a LICINFO structure with information on the licensing capa
bilities of this class factory.
RequestLicKey Creates and returns a license key that the caller can save and u
se later in calls to IClassFactory2::CreateInstanceLic.
CreateInstanceLic Creates an instance of the licensed object given a licen
se key from IClassFactory2::RequestLicKey.
See Also
IClassFactory
8.10.3.2 IClassFactory2::CreateInstanceLic
Creates an instance of the object class supported by this class factory, given a
license key previously obtained from IClassFactory2::RequestLicKey. This method
is the only possible means to create an object on an otherwise unlicensed machi
ne.
HRESULT CreateInstanceLic(
IUnknown* pUnkOuter, //Pointer to controlling unknown of aggregated o
bject
IUnknown* pUnkReserved, //Unused. Must be NULL.
REFIID riid, //Reference to the identifier of the interface
BSTR bstrKey, //License key provided by IClassFactory2::RequestLicKey
void** ppvObject //Indirect pointer to the interface of the type specifie
d in riid
);
Parameters
pUnkOuter
[in] Pointer to the controlling IUnknown interface on the outer unknown if this
object is being created as part of an aggregate. If the object is not part of an
aggregate, this parameter must be NULL.
pUnkReserved
[in] Unused. Must be NULL.
riid
[in] Reference to the identifier of the interface to be used to communicate with
the newly created object.
bstrKey
[in] Run-time license key previously obtained from IClassFactory2::RequestLicKey
that is required to create an object.
ppvObject
[out] Indirect pointer to the interface of the type specified in riid. This para
meter is set to NULL on failure.
Return Values
This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and
E_UNEXPECTED, as well as the following:
S_OK
The license was successfully created.
E_NOTIMPL
This method is not implemented because objects can only be created on fully lice
nsed machines through IClassFactory::CreateInstance.
E_POINTER
The pointers passed in bstrKey or ppvObject are not valid. For example, it may b
e NULL.
E_NOINTERFACE
The object can be created (and the license key is valid) except the object does
not support the interface specified by riid.
CLASS_E_NOAGGREGATION
The pUnkOuter parameter is non-NULL, but this object class does not support aggr
egation.
CLASS_E_NOTLICENSED
The key provided in bstrKey is not a valid license key.
Remarks
8.10.3.2.1 Notes to Implementers
If the class factory does not provide a license key (that is, IClassFactory2::Re
questLicKey returns E_NOTIMPL and the fRuntimeKeyAvail field in LICINFO is set t
o FALSE in IClassFactory2::GetLicInfo), then this method can also return E_NOTIM
PL. In such cases, the class factory is implementing IClassFactory2 simply to sp
ecify whether or not the machine is licensed at all through the fLicVerified fie
ld of LICINFO.
See Also
IClassFactory2::GetLicInfo, IClassFactory2::RequestLicKey, LICINFO
8.10.3.3 IClassFactory2::GetLicInfo
Fills a caller-allocated LICINFO structure with information describing the licen
sing capabilities of this class factory.
HRESULT GetLicInfo(
LICINFO* pLicInfo //Pointer to the structure
);
Parameters
pLicInfo
[out] Pointer to the caller-allocated LICINFO structure to be filled on output.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The LICINFO structure was successfully filled in.
E_POINTER
The address in pLicInfo is not valid. For example, it may be NULL.
Remarks
8.10.3.3.1 Notes to Implementers
E_NOTIMPL is not allowed as a return value since this method provides critical i
nformation for the client of a licensed class factory.
See Also
IClassFactory2::CreateInstanceLic, IClassFactory2::RequestLicKey, LICINFO
8.10.3.4 IClassFactory2::RequestLicKey
If the fRuntimeKeyAvail field in LICINFO has been returned as TRUE from IClassFa
ctory2::GetLicInfo, then this method creates and returns a license key. The call
er can save the license key persistently and use it later in calls to IClassFact
ory2::RequestLicKey.
HRESULT RequestLicKey(
DWORD dwReserved , //Unused. Must be zero.
BSTR* pbstrKey //Pointer to the license key
);
Parameters
dwReserved
[in] Unused. Must be zero.
pbstrKey
[out] Pointer to the caller-allocated BSTR variable that receives the callee-all
ocated license key on successful return from this method. This parameter is set
to NULL on any failure.
Return Values
This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and
E_UNEXPECTED, as well as the following:
S_OK
The license key was successfully created.
E_NOTIMPL
This class factory does not support run-time license keys.
E_POINTER
The address in pbstrKey is not valid. For example, it may be NULL.
CLASS_E_NOTLICENSED
This class factory supports run-time licensing, but the current machine itself i
s not licensed. Thus, a run-time key is not available on this machine.
Remarks
The caller can save the license key for subsequent calls to IClassFactory2::Crea
teInstanceLic to create objects on an otherwise unlicensed machine.
8.10.3.4.1 Notes to Callers
The caller must free the BSTR with SysFreeString when the key is no longer neede
d. The value of fRuntimeKeyAvail is returned through a previous call to IClassFa
ctory2::GetLicInfo.
8.10.3.4.2 Notes to Implementers
This method allocates the BSTR key with SysAllocString or SysAllocString[Len], a
nd the caller becomes responsible for this BSTR once this method returns success
fully.
This method need not be implemented when a class factory does not support run-ti
me license keys.
See Also
IClassFactory2::CreateInstanceLic, IClassFactory2::GetLicInfo, LICINFO
8.10.4 IExternalConnection
The IExternalConnection interface enables an embedded object to keep track of ex
ternal locks on it, thereby enabling the safe and orderly shutdown of the object
following silent updates. An object that supports links either to itself or to
some portion of itself (a range of cells in a spreadsheet, for example) should i
mplement this interface to prevent possible loss of data during shutdown.
Such data loss can occur when an object happens to have unsaved changes at a tim
e when its stub manager s count of strong external references has reached zero. Th
is situation would arise, for example, at the end of a silent update, when the f
inal link client breaks its connection to the object. With the severing of this
connection, the stub manager s count of strong external references would reach zer
o, causing it to release its pointers to an interface on the object and initiate
shutdown of the object.
If the object manages its own count of external locks, rather than relying on th
e stub manager to do so, it can save its data before the stub manager has a chan
ce to release its pointers. An object can obtain a count of external connections
by implementing the IExternalConnection interface. The stub manager calls this
interface whenever a new strong external reference is added or deleted. The obje
ct combines this count with its own tally of strong internal references to maint
ain an accurate total of all locks.
8.10.4.1.1 When to Implement
All embeddable compound-document objects that support links to themselves or por
tions of themselves should implement IExternalConnection to prevent possible dat
a loss during shutdown. In addition, an in-place container should call OleLockRu
nning to hold a strong lock on its embedded objects.
8.10.4.1.2 When to Use
An object s stub manager should call IExternalConnection whenever an external conn
ection is added or released.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IExternalConnection Methods Description
AddConnection Increments count of external locks.
ReleaseConnection Decrements count of external locks.

8.10.4.2 IExternalConnection::AddConnection
Increments an object s count of its strong external connections (links).
HRESULT AddConnection(
DWORD exconn, //Type of external connection
DWORD dwreserved //Used by COM to pass connection information
);
Parameters
exconn
[in] Type of external connection to the object. The only type of external connec
tion currently supported by this interface is strong, which means that the objec
t must remain alive as long as this external connection exists. Strong external
connections are represented by the value EXTCONN_STRONG = 0x0001, which is defin
ed in the enumeration EXTCONN.
dwreserved
[in] Passes information about the connection. This parameter is reserved for use
by COM. Its value can be zero, but not necessarily. Therefore, implementations
of AddConnection should not contain blocks of code whose execution depends on wh
ether a zero value is returned.
Return Value
DWORD value
The number of reference counts on the object; used for debugging purposes only.
Remarks
An object created by a EXE object server relies on its stub manager to call IExt
ernalConnection::AddConnection whenever a link client activates and therefore cr
eates an external lock on the object. When the link client breaks the connection
, the stub manager calls IExternalConnection::ReleaseConnection to release the l
ock.
Since DLL object applications exist in the same process space as their objects,
they do not use RPC (remote procedure calls) and therefore do not have stub mana
gers to keep track of external connections. Therefore, DLL servers that support
external links to their objects must implement IExternalConnection so link clien
ts can directly call the interface to inform them when connections are added or
released.
The following is a typical implementation for the AddConnection method:
DWORD XX::AddConnection(DWORD extconn, DWORD dwReserved)
{
return extconn&EXTCONN_STRONG ? ++m_cStrong : 0;
}
See Also
IExternalConnection::ReleaseConnection, IRunnableObject::LockRunning
8.10.4.3 IExternalConnection::ReleaseConnection
Decrements an object s count of its strong external connections (references).
HRESULT ReleaseConnection(
DWORD extconn, //Type of external connection
DWORD dwreserved, //Used by COM to pass connection information
BOOL fLastReleaseCloses //Indicates whether connection is last one or no
t
);
Parameters
extconn
[in] Type of external connection to the object. The only type of external connec
tion currently supported by this interface is strong, which means that the objec
t must remain alive as long as this external connection exists. Strong external
connections are represented by the value EXTCONN_STRONG = 0x0001, which is defin
ed in the enumeration EXTCONN.
dwreserved
[in] Passes information about the connection. This parameter is reserved for use
by COM. Its value can be zero, but not necessarily. Therefore, implementations
of ReleaseConnection should not contain blocks of code whose execution depends o
n whether a zero value is returned.
fLastReleaseCloses
[in] TRUE specifies that if the connection being released is the last external l
ock on the object, the object should close. FALSE specifies that the object shou
ld remain open until closed by the user or another process.
Return Value
DWORD value
The number of connections to the object; used for debugging purposes only.
Remarks
If fLastReleaseCloses equals TRUE, calling ReleaseConnection causes the object t
o shut itself down. Calling this method is the only way in which a DLL object, r
unning in the same process space as the container application, will know when to
close following a silent update.
See Also
IExternalConnection::AddConnection
8.11 COM Server Related API Descriptions
8.11.1 CoDisconnectObject
Disconnects all remote process connections being maintained on behalf of all the
interface pointers that point to a specified object. Only the process that actu
ally manages the object should call CoDisconnectObject.
STDAPI CoDisconnectObject(
IUnknown * pUnk, //Pointer to the interface on the object
DWORD dwReserved //Reserved for future use
);
Parameters
pUnk
[in] Pointer to any IUnknown-derived interface on the object to be disconnected.
dwReserved
[in] Reserved for future use; must be zero.
Return Values
S_OK
All connections to remote processes were successfully deleted.
Remarks
The CoDisconnectObject function enables a server to correctly disconnect all ext
ernal clients to the object specified by pUnk.
The CoDisconnectObject function performs the following tasks:
1. Checks to see if the object to be disconnected implements the IM
arshal interface. If so, it gets the pointer to that interface; if not, it gets
a pointer to the standard marshaler s (i.e., COM s) IMarshal implementation.
2. Using whichever IMarshal interface pointer it has acquired, the
function then calls IMarshal::DisconnectObject to disconnect all out-of-process
clients.
An object s client does not call CoDisconnectObject to disconnect itself from the
server (clients should use IUnknown::Release for this purpose). Rather, an COM s
erver calls CoDisconnectObject to forcibly disconnect an object s clients, usually
in response to a user closing the server application.
Similarly, an COM container that supports external links to its embedded objects
can call CoDisconnectObject to destroy those links. Again, this call is normall
y made in response to a user closing the application.
CoDisconnectObject does not necessarily disconnect out-of-process clients immedi
ately. If any marshaled calls are pending on the server object, CoDisconnectObje
ct disconnects the object only when those calls have returned. In the meantime,
CoDisconnectObject sets a flag that causes any new marshaled calls to return CO_
E_OBJECTNOTCONNECTED.
See Also
IMarshal::DisconnectObject
8.11.2 CoLockObjectExternal
Called either to lock an object to ensure that it stays in memory, or to release
such a lock. Call CoLockObjectExternal to place a strong lock on an object to e
nsure that it stays in memory.
STDAPI CoLockObjectExternal(
IUnknown * pUnk, //Pointer to object to be locked or unlocked
BOOL fLock, //TRUE = lock, FALSE = unlock
BOOL fLastUnlockReleases //TRUE = release all pointers to object
);
Parameters
pUnk
[in] Pointer to the IUnknown interface on the object to be locked or unlocked.
fLock
[in] Whether the object is to be locked or released. Specifying TRUE holds a ref
erence to the object (keeping it in memory), locking it independently of externa
l or internal AddRef/Release operations, registrations, or revocations. If fLock
is TRUE, fLastLockReleases is ignored. FALSE releases a lock previously set wit
h a call to this function.
fLastUnlockReleases
[in] Whether a given lock is the last reference that is supposed to keep an obje
ct alive. If it is, TRUE releases all pointers to the object (there may be other
references that are not supposed to keep it alive).
Return Values
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, a
nd E_UNEXPECTED, as well as the following:
S_OK
The object was locked successfully.
Remarks
The CoLockObjectExternal function prevents the reference count of an object from
going to zero, thereby locking it into existence until the lock is released. The
same function (with different parameters) releases the lock. The lock is impleme
nted by having the system call IUnknown::AddRef on the object. The system then w
aits to call IUnknown::Release on the object until a later call to CoLockObjectE
xternal with fLock set to FALSE. This function can be used to maintain a referen
ce count on the object on behalf of the end user, because it acts outside of the
object, as does the user.
The CoLockObjectExternal function must be called in the process in which the obj
ect actually resides (the EXE process, not the process in which handlers may be
loaded).
Calling CoLockObjectExternal sets a strong lock on an object. A strong lock keep
s an object in memory, while a weak lock does not. Strong locks are required, fo
r example, during a silent update to an OLE embedding. The embedded object s conta
iner must remain in memory until the update process is complete. There must also
be a strong lock on an application object to ensure that the application stays
alive until it has finished providing services to its clients. All external refe
rences place a strong reference lock on an object.
The CoLockObjectExternal function is typically called in the following situation
s:
· Object servers should call CoLockObjectExternal with both fLock and fLastLockRel
eases set to TRUE when they become visible. This call creates a strong lock on b
ehalf of the user. When the application is closing, free the lock with a call to
CoLockObjectExternal, setting fLock to FALSE and fLastLockReleases to TRUE.
· A call to CoLockObjectExternal on the server can also be used in the implementat
ion of IOleContainer::LockContainer.
There are several things to be aware of when you use CoLockObjectExternal in the
implementation of IOleContainer::LockContainer. An embedded object would call I
OleContainer::LockContainer on its container to keep it running (to lock it) in
the absence of other reasons to keep it running. When the embedded object become
s visible, the container must weaken its connection to the embedded object with
a call to the OleSetContainedObject function, so other connections can affect th
e object.
Unless an application manages all aspects of its application and document shutdo
wn completely with calls to CoLockObjectExternal, the container must keep a priv
ate lock count in IOleContainer::LockContainer so that it exits when the lock co
unt reaches zero and the container is invisible. Maintaining all aspects of shut
down, and thereby avoiding keeping a private lock count, means that CoLockObject
External should be called whenever one of the following conditions occur:
· A document is created and destroyed or made visible or invisible.
· An application is started and shut down by the user.
· A pseudo-object is created and destroyed.
For debugging purposes, it may be useful to keep a count of the number of extern
al locks (and unlocks) set on the application.
Note
The end user has explicit control over the lifetime of an application, even if t
here are external locks on it. That is, if a user decides to close the applicati
on (File, Exit), it must shut down. In the presence of external locks (such as t
he lock set by CoLockObjectExternal), the application can call the CoDisconnectO
bject function to force these connections to close prior to shutdown.

8.11.3 CoRevokeClassObject
Informs COM that a class object, previously registered with the CoRegisterClassO
bject function, is no longer available for use.
HRESULT CoRevokeClassObject(
DWORD dwRegister //Token on class object
);
Parameter
dwRegister
[in] Token previously returned from the CoRegisterClassObject function.
Return Values
This function supports the standard return values E_INVALIDARG,
E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:
S_OK
The class object was successfully revoked.
Remarks
A successful call to CoRevokeClassObject means that the class object has been re
moved from the global class object table (although it does not release the class
object). If other clients still have pointers to the class object and have caus
ed the reference count to be incremented by calls to IUnknown::AddRef, the refer
ence count will not be zero. When this occurs, applications may benefit if subse
quent calls (with the obvious exceptions of IUnknown::AddRef and IUnknown::Relea
se) to the class object fail.
An object application must call CoRevokeClassObject to revoke registered class o
bjects before exiting the program. Class object implementers should call CoRevok
eClassObject as part of the release sequence. You must specifically revoke the c
lass object even when you have specified the flags value REGCLS_SINGLEUSE in a c
all to CoRegisterClassObject, indicating that only one application can connect t
o the class object.
See Also
CoGetClassObject, CoRegisterClassObject
8.11.4 CoRegisterClassObject
Registers an EXE class object with COM so other applications can connect to it.
EXE object applications should call CoRegisterClassObject on startup. It can als
o be used to register internal objects for use by the same EXE or other code (su
ch as DLLs) that the EXE uses.
STDAPI CoRegisterClassObject(
REFCLSID rclsid, //Class identifier (CLSID) to be registered
IUnknown * pUnk, //Pointer to the class object
DWORD dwClsContext, //Context for running executable code
DWORD flags, //How to connect to the class object
LPDWORD * lpdwRegister //Pointer to the value returned
);
Parameters
rclsid
[in] CLSID to be registered.
pUnk
[in] Pointer to theIUnknown interface on the class object whose availability is
being published.
dwClsContext
[in] Context in which the executable code is to be run. For information on these
context values, see the CLSCTX enumeration.
flags
[in] How connections are made to the class object. For information on these flag
s, see the REGCLS enumeration.
lpdwRegister
[out] Pointer to a value that identifies the class object registered; later used
by the CoRevokeClassObject function to revoke the registration.
Return Values
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, a
nd E_UNEXPECTED, as well as the following:
S_OK
The class object was registered successfully.
CO_E_OBJISREG
Already registered in the class object table.
Remarks
Only EXE object applications call CoRegisterClassObject. Object handlers or DLL
object applications do not call this function instead, they must implement and e
xport the DllGetClassObject function.
At startup, a multiple-use EXE object application must create a class object (wi
th the IClassFactory interface on it), and call CoRegisterClassObject to registe
r the class object. Object applications that support several different classes (
such as multiple types of embeddable objects) must allocate and register a diffe
rent class object for each.
Multiple registrations of the same class object are independent and do not produ
ce an error. Each subsequent registration yields a unique key in lpdwRegister.
Multiple document interface (MDI) applications must register their class objects
. Single document interface (SDI) applications must register their class objects
only if they can be started by means of the /Embedding switch.
The server for a class object should call CoRevokeClassObject to revoke the clas
s object (remove its registration) when all of the following are true:
· There are no existing instances of the object definition
· There are no locks on the class object
· The application providing services to the class object is not under user control
(not visible to the user on the display).
After the class object is revoked, when its reference count reaches zero, the cl
ass object can be released, allowing the application to exit.
For information on the flags parameter, refer to the REGCLS enumeration.
See Also
CoGetClassObject, CoRevokeClassObject, DllGetClassObject, REGCLS, CLSCTX
8.11.5 DllCanUnloadNow
Determines whether the DLL that implements this function is in use. If not, the
caller can safely unload the DLL from memory.
Note
COM does not provide this function. DLLs that support the Component Object Model
(COM) should implement and export DllCanUnloadNow.
STDAPI DllCanUnloadNow();
Return Values
S_OK
The DLL can be unloaded.
S_FALSE
The DLL cannot be unloaded now.
Remarks
A call to DllCanUnloadNow determines whether the DLL from which it is exported i
s still in use. A DLL is no longer in use when it is not managing any existing o
bjects (the reference count on all of its objects is 0).
8.11.5.1 Notes to Callers
You should not have to call DllCanUnloadNow directly. COM calls it only through
a call to the CoFreeUnusedLibraries function. When it returns S_OK, CoFreeUnused
Libraries safely frees the DLL.
8.11.5.2 Notes to Implementers
You need to implement DllCanUnloadNow in, and export it from, DLLs that are to b
e dynamically loaded through a call to the CoGetClassObject function. (You also
need to implement and export the DllGetClassObject function in the same DLL).
If a DLL loaded through a call to CoGetClassObject fails to export DllCanUnloadN
ow, the DLL will not be unloaded until the application calls the CoUninitialize
function to release the COM libraries.
If the DLL links to another DLL, returning S_OK from DllCanUnloadNow will also c
ause the second, dependent DLL to be unloaded. To eliminate the possibility of a
crash, the primary DLL should call the CoLoadLibrary function, specifying the p
ath to the second DLL as the first parameter, and setting the auto free paramete
r to TRUE. This forces the COM library to reload the second DLL and set it up fo
r a call to CoFreeUnusedLibraries to free it separately when appropriate.
DllCanUnloadNow should return S_FALSE if there are any existing references to ob
jects that the DLL manages.
See Also
DllGetClassObject
8.11.6 DllGetClassObject
Retrieves the class object from a DLL object handler or object application. DllG
etClassObject is called from within the CoGetClassObject function when the class
context is a DLL.
Note
COM does not provide this function. DLLs that support the Component Object Model
(COM) must implement DllGetClassObject in COM object handlers or DLL applicatio
ns.
STDAPI DllGetClassObject(
REFCLSID rclsid, //CLSID for the class object
REFIID riid, //Reference to the identifier of the interface that comm
unicates with the class object
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
Parameters
rclsid
[in] CLSID that will associate the correct data and code.
riid
[in] Reference to the identifier of the interface that the caller is to use to c
ommunicate with the class object. Usually, this is IID_IClassFactory (defined in
the COM headers as the interface identifier for IClassFactory).
ppv
[out] Address of pointer variable that receives the interface pointer requested
in riid. Upon successful return, *ppv contains the requested interface pointer.
If an error occurs, the interface pointer is NULL.
Return Values
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY an
d E_UNEXPECTED, as well as the following:
S_OK
The object was retrieved successfully.
CLASS_E_CLASSNOTAVAILABLE
The DLL does not support the class (object definition).
Remarks
If a call to the CoGetClassObject function finds the class object that is to be
loaded in a DLL, CoGetClassObject uses the DLL s exported DllGetClassObject functi
on.
8.11.6.1 Notes to Callers
You should not call DllGetClassObject directly. When an object is defined in a D
LL, CoGetClassObject calls the CoLoadLibrary function to load the DLL, which, in
turn, calls DllGetClassObject.
8.11.6.2 Notes to Implementers
You need to implement DllGetClassObject in (and export it from) DLLs that suppor
t the Component Object Model.
Example
Following is an example (in C++) of an implementation of DllGetClassObject. In t
his example, DllGetClassObject creates a class object and calls its QueryInterfa
ce method to retrieve a pointer to the interface requested in riid. The implemen
tation safely releases the reference it holds to the IClassFactory interface bec
ause it returns a reference-counted pointer to IClassFactory to the caller.
HRESULT_export PASCAL DllGetClassObject
(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj)
{
HRESULT hres = E_OUTOFMEMORY;
*ppvObj = NULL;
CClassFactory *pClassFactory = new CClassFactory(rclsid);
if (pClassFactory != NULL) {
hRes = pClassFactory->QueryInterface(riid, ppvObj);
pClassFactory->Release();
}
return hRes;
}
See Also
CoGetClassObject, DllCanUnloadNow

8.11.7 DllRegisterServer
Instructs an in-process server to create its registry entries for all classes su
pported in this server module. If this function fails, the state of the registry
for all its classes is indeterminate.
STDAPI DllRegisterServer(void);
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
The registry entries were created successfully.
SELFREG_E_TYPELIB
The server was unable to complete the registration of all the type libraries use
d by its classes.
SELFREG_E_CLASS
The server was unable to complete the registration of all the object classes.
Remarks
E_NOTIMPL is not a valid return code.
See Also
DllUnregisterServer
8.11.8 DllUnregisterServer
Instructs an in-process server to remove only those entries created through DllR
egisterServer.
STDAPI DllUnregisterServer(void);
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
The registry entries were created successfully.
S_FALSE
Unregistration of this server s known entries was successful, but other entries st
ill exist for this server s classes.
SELFREG_E_TYPELIB
The server was unable to remove the entries of all the type libraries used by it
s classes.
SELFREG_E_CLASS
The server was unable to remove the entries of all the object classes.
Remarks
The server must not disturb any entries that it did not create which currently e
xist for its object classes. For example, between registration and unregistratio
n, the user may have specified a TreatAs relationship between this class and ano
ther. In that case, unregistration can remove all entries except the TreatAs key
and any others that were not explicitly created in DllRegisterServer. The Win32
registry functions specifically disallow the deletion of an entire populated tr
ee in the registry. The server can attempt, as the last step, to remove the CLSI
D key, but if other entries still exist, the key will remain.
See Also
DllRegisterServer
8.11.9 CoAddRefServerProcess
Increments a global per-process reference count.
ULONG CoAddRefServerProcess(void);
Return Values
S_OK
The CLSID was retrieved successfully.
Remarks
Servers can call CoAddRefServerProcess to increment a global per-process referen
ce count. This function is particularly helpful to servers that are implemented
with multiple threads, either multi-apartmented or free-threaded. Servers of the
se types must coordinate the decision to shut down with activation requests acro
ss multiple threads. Calling CoAddRefServerProcess increments a global per-proce
ss reference count, and calling CoReleaseServerProcess decrements that count.
When that count reaches zero, COM automatically calls CoSuspendClassObjects, whi
ch prevents new activation requests from coming in. This permits the server to d
eregister its class objects from its various threads without worry that another
activation request may come in. New activation requests result in launching a ne
w instance of the local server process.
The simplest way for a local server application to make use of these API functio
ns is to call CoAddRefServerProcess in the constructor for each of it s instance o
bjects, and in each of its IClassFactory::LockServer methods when the fLock para
meter is TRUE. The server application should also call CoReleaseServerProcess in
the destruction of each of its instance objects, and in each of its IClassFacto
ry::LockServer methods when the fLock parameter is FALSE. Finally, the server ap
plication should pay attention to the return code from CoReleaseServerProcess an
d if it returns 0, the server application should initiate its cleanup, which, fo
r a server with multiple threads, typically means that it should signal its var
ious threads to exit their message loops and call CoRevokeClassObject and CoUnin
itialize.
If these APIs are used at all, they must be called in both the object instances
and the LockServer method, otherwise the server application may be shut down pre
maturely. In-process servers typically should not call CoAddRefServerProcess or
CoReleaseServerProcess.
See Also
CoReleaseServerProcess, IClassFactory::LockServer, Out-of-process Server Impleme
ntation Helpers
8.11.10 CoReleaseServerProcess
Decrements the global per-process reference count
ULONG CoReleaseServerProcess(void);
Return Values
0
The server application should initiate its cleanup.
Other values
Server application should not yet initiate its cleanup.
Remarks
Servers can call CoReleaseServerProcess to decrement a global per-process refere
nce count incremented through a call to CoAddRefServerProcess
When that count reaches zero, COM automatically calls CoSuspendClassObjects, whi
ch prevents new activation requests from coming in. This permits the server to d
eregister its class objects from its various threads without worry that another
activation request may come in. New activation requests result in launching a ne
w instance of the local server process.
The simplest way for a local server application to make use of these API functio
ns is to call CoAddRefServerProcess in the constructor for each of its instance
objects, and in each of its IClassFactory::LockServer methods when the fLock par
ameter is TRUE. The server application should also call CoReleaseServerProcess i
n the destruction of each of its instance objects, and in each of its IClassFact
ory::LockServer methods when the fLock parameter is FALSE. Finally, the server a
pplication must check the return code from CoReleaseServerProcess; if it returns
0, the server application should initiate its cleanup. This typically means tha
t a server with multiple threads should signal its various threads to exit their
message loops and call CoRevokeClassObject and CoUninitialize.
If these APIs are used at all, they must be called in both the object instances
and the LockServer method, otherwise the server application may be shutdown prem
aturely. In-process Servers typically should not call CoAddRefServerProcess or C
oReleaseServerProcess.
See Also
CoSuspendClassObjects, CoReleaseServerProcess, IClassFactory::LockServer, Out-of
-process Server Implementation Helpers
8.11.11 CoResumeClassObjects
Called by a server that can register multiple class objects to inform the COM SC
M about all registered classes, and permits activation requests for those class
objects.
WINOLEAPI CoResumeClassObjects(void);
Return Values
S_OK
The CLSID was retrieved successfully.
Remarks
Servers that can register multiple class objects call CoResumeClassObjects once,
after having first called CoRegisterClassObject, specifying REGCLS_LOCAL_SERVER
| REGCLS_SUSPENDED for each CLSID the server supports. This function causes COM
to inform the SCM about all the registered classes, and begins letting activati
on requests into the server process.
This reduces the overall registration time, and thus the server application star
tup time, by making a single call to the SCM, no matter how many CLSIDs are regi
stered for the server. Another advantage is that if the server has multiple apar
tments with different CLSIDs registered in different apartments, or is a free-th
readed server, no activation requests will come in until the server calls CoRes
umeClassObjects. This gives the server a chance to register all of its CLSIDs an
d get properly set up before having to deal with activation requests, and possib
ly shutdown requests.
See Also
CoRegisterClassObject, CoSuspendClassObjects, Out-of-process Server Implementati
on Helpers
8.11.12 CoSuspendClassObjects
Prevents any new activation requests from the SCM on all class objects registere
d within the process.
WINOLEAPI CoSuspendClassObjects(void);
Return Values
S_OK
The CLSID was retrieved successfully.
Remarks
CoSuspendClassObjects prevents any new activation requests from the SCM on all c
lass objects registered within the process. Even though a process may call this
API, the process still must call CoRevokeClassObject for each CLSID it has regis
tered, in the apartment it registered in. Applications typically do not need to
call this API, which is generally only called internally by COM when used in con
junction with CoReleaseServerProcess.
See Also
CoRevokeClassObject, CoReleaseServerProcess, Out-of-process Server Implementatio
n Helpers
8.12 COM Server Related Structure Definitions
8.12.1 LICINFO
The LICINFO structure contains parameters that describe the licensing behavior o
f a class factory that supports licensing. The structure is filled during the IC
lassFactory2::GetLicInfo method.
typedef struct tagLICINFO
{
ULONG cbLicInfo;
BOOL fRuntimeKeyAvail;
BOOL fLicVerified;
} LICINFO;

Members
cbLicInfo
Size of the LICINFO structure.
fRuntimeKeyAvail
Whether this class factory allows the creation of its objects on a unlicensed ma
chine through the use of a license key. If TRUE, IClassFactory2::RequestLicKey c
an be called to obtain the key. If FALSE, objects can be created only on a fully
licensed machine.
fLicVerified
Whether a full machine license exists so that calls to IClassFactory::CreateInst
ance and IClassFactory2::RequestLicKey will succeed. If TRUE, the full machine l
icense exists. Thus, objects can be created freely. and a license key is availab
le if fRuntimeKeyAvail is also TRUE. If FALSE, this class factory cannot create
any instances of objects on this machine unless the proper license key is passed
to IClassFactory2::CreateInstanceLic.
See Also
IClassFactory::CreateInstance, IClassFactory2::CreateInstanceLic, IClassFactory2
::GetLicInfo, IClassFactory2::RequestLicKey
8.13 COM Server Related Enumeration Definitions
8.13.1 REGCLS
The REGCLS enumeration defines values used in CoRegisterClassObject to control t
he type of connections to a class object. It is defined as follows:
typedef enum tagREGCLS
{
REGCLS_SINGLEUSE = 0,
REGCLS_MULTIPLEUSE = 1,
REGCLS_MULTI_SEPARATE = 2,
REGCLS_SUSPENDED = 4,
REGCLS_SURROGATE = 8,
} REGCLS;
Elements
REGCLS_SINGLEUSE
Once an application is connected to a class object with CoGetClassObject, the cl
ass object is removed from public view so that no other applications can connect
to it. This value is commonly used for single document interface (SDI) applicat
ions. Specifying this value does not affect the responsibility of the object app
lication to call CoRevokeClassObject; it must always call CoRevokeClassObject wh
en it is finished with an object class.
REGCLS_MULTIPLEUSE
Multiple applications can connect to the class object through calls to CoGetClas
sObject. If both the REGCLS_MULTIPLEUSE and CLSCTX_LOCAL_SERVER are set in a cal
l to CoRegisterClassObject, the class object is also automatically registered as
an in-process server, whether or not CLSCTX_INPROC_SERVER is explicitly set.
REGCLS_MULTI_SEPARATE
Multiple applications can connect to the class object through calls to CoGetClas
sObject. If REGCLS_MULTI_SEPARATE is set, each execution context must be set sep
arately: CoRegisterClassObject does not automatically register an out-of-process
server (for which CLSCTX_LOCAL_SERVER is set) as an in-process server. When a c
lass object is registered with only the REGCLS_MULTI_SEPARATE and CLSCTX_LOCAL_S
ERVER flags set and the server tries to bind to an object with its own CLSID, an
other instance of the server is started.
REGCLS_SUSPENDED
Suspends registration and activation requests for the specified CLSID until ther
e is a call to CoResumeClassObjects. This is used typically to register the CLSI
Ds for servers that can register multiple class objects to reduce the overall re
gistration time, and thus the server application startup time, by making a singl
e call to the SCM, no matter how many CLSIDs are registered for the server.
REGCLS_SURROGATE
The class object is a surrogate process used to run DLL servers. The class facto
ry registered by the surrogate process is not the actual class factory implement
ed by the DLL server, but a generic class factory implemented by the surrogate.
This generic class factory delegates instance creation and marshaling to the cla
ss factory of the DLL server running in the surrogate.
Remarks
In CoRegisterClassObject, members of both the REGCLS and the CLSCTX enumerations
, taken together, determine how the class object is registered.
An EXE surrogate (in which DLL servers are run) calls CoRegisterClassObject to r
egister a class factory using a new REGCLS value, REGCLS_SURROGATE.
The following table summarizes the allowable REGCLS value combinations and the o
bject registrations affected by the combinations.

REGCLS_
SINGLEUSE
REGCLS_
MULTIPLEUSE REGCLS_
MULTI_
SEPARATE
Other
CLSCTX_
INPROC_
SERVER Error In-process In-process Error
CLSCTX_
LOCAL_
SERVER Local In-process/ local Local Error
Both of the above Error In-process/ local In-process/ local
Error
Other Error Error Error Error
All class factories for dll surrogates should be registered with REGCLS_SURROGAT
E set. Do not set REGCLS_SINGLUSE or REGCLS_MULTIPLEUSE when you register a sur
rogate for DLL servers.
See Also
CoGetClassObject, CoRegisterClassObject, CoRevokeClassObject, DllGetClassObject,
9. Interface Remoting and Marshaling
In COM, clients communicate with objects solely through the use of vtable-based
interface instances. The state of the object is manipulated by invoking function
s on those interfaces. For each interface method, the object provides an impleme
ntation that does the appropriate manipulation of the object internals.
Interface remoting provides the infrastructure and mechanisms to allow a method
invocation to return an interface pointer to an object that is in a different pr
ocess, perhaps even on a different machine. The infrastructure that performs the
remoting of interfaces is transparent to both the client and the object server.
Neither the client or object server is necessarily aware that the other party i
s in fact in a different process.
This chapter first explains how interface remoting works giving mention to the i
nterfaces and COM API functions involved. The specifications for the interfaces
and the API functions themselves are given later in this chapter. There is also
a brief discussion about concurrency management at the end of the chapter that i
nvolves an interface called IMessageFilter.
9.1 How Interface Remoting Works
The crux of the problem to be addressed in interface remoting can be stated as f
ollows:
Given an already existing remoted-interface connection between a client process a
nd a server process, how can a method invocation through that connection return
a new interface pointer so as to create a second remoted-interface connection be
tween the two processes?
We state the problem in this way so as to avoid for the moment the issue of how
an initial connection is made between the client and the server process; we will
return to that later.
Let s look at an example. Suppose we have an object in a server process which supp
orts an interface IFoo, and that interface of the object (and IUnknown) has some
time in the past been remoted to a client process through some means not here sp
ecified. In the client process, there is an object proxy which supports the exac
t same interfaces as does the original server object, but whose implementations
of methods in those interfaces are special, in that they forward calls they rece
ive on to calls on the real method implementations back in the server object. We s
ay that the method implementations in the object proxy marshal the data, which i
s then conveyed to the server process, where it is unmarshaled. That is, marshali
ng refers to the packaging up of method arguments for transmission to a remote pr
ocess; unmarshaling refers to the unpackaging of this data at the receiving end. N
otice that in a given call, the method arguments are marshaled and unmarshaled i
n one direction, while the return values are marshaled and unmarshaled in the ot
her direction.
For concreteness, let us suppose that the IFoo interface is defined as follows:
interface IFoo : IUnknown {
HRESULT ReturnABar([out]IBar **ppBar);
};
If the in the client process pFoo->ReturnABar(&pBar) is invoked, then the object
proxy will forward this call on to the IFoo::ReturnABar() method in the server
object, which will do whatever this method is supposed to do in order to come up
with some appropriate IBar*. The server object is then required to return this
IBar* back to the client process. The act of doing this will end up creating a s
econd connection between the two processes:
It is the procedure by which this second connection is established which is the
subject of our discussion here. This process involves two steps:
1. On the server side, the IBar* is packaged or marshaled into a data packe
t.
2. The data packet is conveyed by some means to the client process, where t
he data it contains is unmarshaled to create the new object proxy.
The term marshaling is a general one that is applied in the industry to the packag
ing of any particular data type, not just interface pointers, into a data packet
for transmission through an RPC infrastructure. Each different data type has di
fferent rules for how it is to marshaled: integers are to be stored in a certain
way, strings are to be stored in a certain way, etc. Likewise, marshaled interf
ace pointers are to be stored in a certain way; the Component Object Model funct
ion CoMarshalInterface() contains the knowledge of how this is to be done (note
that we will in this document not mention further any kind of marshaling other t
han marshaling of interface pointers; that subject is well-explored in existing
RPC systems).
The process begins with the code doing the marshaling of the returned IBar* inte
rface. This code has in hand a pointer to an interface that it knows in fact to
be an IBar* and that it wishes to marshal. To do so it calls CoMarshalInterface(
). The first step in CoMarshalInterface() involves finding out whether the objec
t of which this is an interface in fact supports custom object marshaling (often
simply referred to as custom marshaling ). Custom object marshaling is a mechanism
that permits an object to be in control of creation of remote object proxies to
itself. In certain situations, custom object marshaling can be used to create a
more efficient object proxy than would otherwise be the case. Use of custom mar
shaling is completely optional on the object s part; if the object chooses not to
support custom marshaling, then standard interface marshaling is used to marshal
the IBar*. Standard interface marshaling uses a system-provided object proxy im
plementation (called the proxy manager) in the client process. This standard imp
lementation is a generic piece of code, in that it can be used as the object pro
xy for any interface on any object. However, the act of marshaling (and unmarsha
ling) method arguments and return values is inherently interface-specific, since
it is highly sensitive to the semantics and data types used in the particular m
ethods in question. To accommodate this, the standard implementation dynamically
loads in interface-specific pieces of code as needed in order to do the paramet
er marshaling.
We shall discuss in great detail in a moment how standard interface marshaling w
orks. First, however, we shall review custom object marshaling, as this provides
a solid framework in which standard marshaling can be better understood.
9.2 Architecture of Custom Object Marshaling
Imagine that we are presently in a piece of code whose job it is to marshal an i
nterface pointer that it has in hand. For clarity, in what follows we ll refer to
this piece of code as the original marshaling stub. The general case is that the o
riginal marshaling stub does not statically know the particular interface identi
fier (IID) to which the pointer conforms; the IID may be passed to this code as
a second parameter. This is a common paradigm in the Component Object Model. Ext
ant examples of this paradigm include:
IUnknown::QueryInterface(REFIID riid, void** ppvObject);
IOleItemContainer::GetObject(..., REFIID riid, void** ppvObject);
IClassFactory::CreateInstance(..., REFIID riid, void** ppvNewlyCreatedObject);
Let us assume the slightly less general case where the marshaling stub in fact d
oes know a little bit about the IID: that the interface in fact derives from IUn
known. This is a requirement for remoting: it is not possible to remote interfac
es which are not derived from IUnknown.
To find out whether the object to which it has an interface supports custom mars
haling, the original marshaling stub simply does a QueryInterface() for the inte
rface IMarshal. That is, an object signifies that it wishes to do custom marshal
ing simply by implementing the IMarshal interface. IMarshal is defined as follows
:
[
local,
object,
uuid(00000003-0000-0000-C000-000000000046)
]
interface IMarshal : IUnknown {
HRESULT GetUnmarshalClass ( [in] REFIID riid, [in, unique] void *pv,
[in] DWORD dwDestContext, [in, unique] void *pvDestContext,
[in] DWORD mshlflags, [out] CLSID *pCid);
HRESULT GetMarshalSizeMax ([in] REFIID riid, [in, unique] void *pv,
[in] DWORD dwDestContext, [in, unique] void *pvDestContext,
[in] DWORD mshlflags, [out] DWORD *pSize);
HRESULT MarshalInterface ([in, unique] IStream *pStm, [in] REFIID riid,
[in, unique] void *pv,
[in] DWORD dwDestContext, [in, unique] void *pvDestContext, [in]
DWORD mshlflags);
HRESULT UnmarshalInterface ( [in, unique] IStream *pStm, [in] REFIID rii
d, [out] void **ppv);
HRESULT ReleaseMarshalData ( [in, unique] IStream *pStm);
HRESULT DisconnectObject ( [in] DWORD dwReserved);
}
The idea is that if the object says Yes, I do want to do custom marshaling that th
e original marshaling stub will use this interface in order to carry out the tas
k. The sequence of steps that carry this out is:
Using GetUnmarshalClass, the original marshaling stub asks the object which kind
of (i.e.: which class of) proxy object it would like to have created on its beh
alf in the client process.
(optional on the part of the marshaling stub) Using GetMarshalSizeMax, the stub
asks the object how big of a marshaling packet it will need. When asked, the obj
ect will return an upper bound on the amount of space it will need.
The marshaling stub allocates a marshaling packet of appropriate size, then crea
tes an IStream* which points into the buffer. Unless in the previous step the ma
rshaling stub asked the object for an upper bound on the space needed, the IStre
am* must be able to grow its underlying buffer dynamically as IStream::Write cal
ls are made.
The original marshaling stub asks the object to marshal its data using MarshalIn
terface.
We will discuss the methods of this interface in detail later in this chapter.
At this point, the contents of the memory buffer pointed to by the IStream* toge
ther with the class tag returned in step (1) comprises all the information neces
sary in order to be able to create the proxy object in the client process. It is
the nature of remoting and marshaling that original marshaling stubs such as we h
ave been discussing know how to communicate with the client process; recall that
we are assuming that an initial connection between the two processes had alread
y been established. The marshaling stub now communicates to the client process,
by whatever means is appropriate, the class tag and the contents of the memory t
hat contains the marshaled interface pointer. In the client process, the proxy o
bject is created as an instance of the indicated class using the standard COM in
stance creation paradigm. IMarshal is used as the initialization interface; the
initialization method is IMarshal::UnmarshalInterface(). The unmarshaling process lo
oks something like the following:
void ExampleUnmarshal(CLSID& clsidProxyObject, IStream* pstm, IID& iidOriginally
Marshalled, void** ppvReturn)
{
IClassFactory* pcf;
IMarshal* pmsh;
CoGetClassObject(clsidProxyObject, CLSCTX_INPROC_HANDLER, NULL, IID_IClassFacto
ry, (void**)&pcf);
pcf->CreateInstance(NULL, IID_IMarshal, (void**)pmsh);
pmsh->UnmarshalInterface(pstm, iidOriginallyMarshalled, ppvReturn);
pmsh->ReleaseMarshalData(pstm)
pmsh->Release();
pcf->Release();
}
There are several important reasons why an object may choose to do custom marsha
ling.
It permits the server implementation, transparently to the client, to be in comp
lete control of the nature of the invocations that actually transition across th
e network. In designing component architectures, one often runs into a design te
nsion between the interface which for simplicity and elegance one wishes to exhi
bit to client programmers and the interface that is necessary to achieve efficie
nt invocations across the network. The former, for example, might naturally wish
to operate in terms of small-grained simple queries and responses, whereas the
latter might wish to batch requests for efficient retrieval. The client and the
network interfaces are in design tension; custom marshaling is the crucial hook
that allows us to have our cake and eat it too by giving the server implementor
the ability to tune the network interface without affecting the interface seen b
y its client.
When the object does custom marshaling, the client loses any "COM provided" comm
unication to the original object. If the proxy wants to "keep in touch", it has
to connect through some other means (RPC, Named pipe...) to the original object.
Custom Object Marshaling can not be done on a per interface basic, because obje
ct identity is lost! Custom Object Marshaling is a sophisticated way for an obje
ct to pass a copy of an existing instance of itself into another execution conte
xt.
Some objects are of the nature that once they have been created, they are immuta
ble: their internal state does not subsequently change. Many monikers are an exa
mple of such objects. These sorts of objects can be efficiently remoted by makin
g independent copies of themselves in client processes. Custom marshaling is the
mechanism by which they can do that, yet have no other party be the wiser for i
t.
Objects which already are proxy objects can use custom marshaling to avoid creat
ing proxies to proxies; new proxies are instead short-circuited back to the origi
nal server. This is both an important efficiency and an important robustness con
sideration.
Object implementations whose whole state is kept in shared memory can often be r
emoted to other process on the same machine by creating an object in the client
that talks directly to the shared memory rather than back to the original object
. This can be a significant performance improvement, since access to the remoted
object does not result in context switches. The present Microsoft Compound File
implementation is an example of objects using this kind of custom marshaling.
9.3 Architecture of Standard Interface / Object Marshaling
If the object being marshaled chooses not to implement custom object marshaling,
a default or standard object marshaling technique is used. An important part of thi
s standard marshaling technique involves locating and loading the interface-spec
ific pieces of code that are responsible for marshaling and unmarshaling remote
calls to instances of that interface. We call these interface-specific pieces of
code used in standard marshaling and unmarshaling interface proxies and interface
stubs respectively. (It is important not to confuse interface proxies with the ob
ject proxy, which relates to the whole representative in the client process, rat
her than just one interface on that representative. We apologize for the subtlet
ies of the terminology.)
The following figure gives a slightly simplified view of how the standard client
- and server-side structures cooperate.
Simplified conceptual view of client - server remoting structures
When an interface of type IFoo needs to be remoted, a system registry is consult
ed under a key derived from IID_IFoo to locate a class id that implements the in
terface proxy and interface stub for the given interface. Both the interface pro
xies and the interface stubs for a given interface must be implemented by the sa
me class. This class is typically either a system provided class that can provid
e marshaling support for any interface described in a type library or a class ge
nerated by the IDL compiler.
The IDL compiler is a tool whose input is a description of the function signatur
es and semantics of the interface, written in some interface description language
, often known as IDL. However, while highly recommended and encouraged for accuracy s
sake, the use of such a tool is by no means required; interface proxies and stu
bs are merely Component Object Model components which are used by the RPC infras
tructure, and as such, can be written in any manner desired so long as the corre
ct external contracts are upheld. From a logical perspective, it is ultimately t
he programmer who is the designer of a new interface who is responsible for ensu
ring that all interface proxies and stubs that ever exist agree on the represent
ation of their marshaled data. The programmer has the freedom to achieve this by
whatever means he sees fit, but with that freedom comes the responsibility for
ensuring the compatibility.
In the figure, the stub manager is conceptual in the sense that while it useful in t
his documentation to have a term to refer to the pieces of code and state on in
the server-side RPC infrastructure which service the remoting of a given object,
there is no direct requirement that the code and state take any particular well
-specified form. In contrast, on the client side, there is an identifiable piece
of state and associated behavior which appears to the client code to be the one
, whole object. The term proxy manager is used to refer to the COM Library provide
d code that manages the client object identity, etc., and which dynamically load
s in interface proxies as needed (per QueryInterface calls). The proxy manager i
mplementation is intimate with the client-side RPC channel implementation, and the
server-side RPC channel implementation is intimate with the stub manager implement
ation.
Interface proxies are created by the client-side COM Library infrastructure usin
g a code sequence resembling the following:
clsid = LookUpInRegistry(key derived from iid)
CoGetClassObject(clsid, CLSCTX_SERVER, NULL, IID_IPSFactoryBuffer, &pPSFactory))
;
pPSFactory->CreateProxy(pUnkOuter, riid, &pProxy, &piid);
Interface stubs are created by the server-side RPC infrastructure using a code s
equence resembling:
clsid = LookUpInRegistry(key derived from iid)
CoGetClassObject(clsid, CLSCTX_SERVER, NULL, IID_IPSFactoryBuffer, &pPSFactory))
;
pPSFactory->CreateStub(iid, pUnkServer, &pStub);
In particular, notice that the class object is talked-to with IPSFactoryBuffer i
nterface rather than the more common IClassFactory.
The interfaces mentioned here are as follows:
interface IPSFactoryBuffer : IUnknown {
HRESULT CreateProxy(pUnkOuter, iid, ppProxy, ppv);
HRESULT CreateStub(iid, pUnkServer, ppStub);
};
interface IRpcChannelBuffer : IUnknown {
HRESULT GetBuffer(pMessage, riid);
HRESULT SendReceive(pMessage, pStatus);
HRESULT FreeBuffer(pMessage);
HRESULT GetDestCtx(pdwDestCtx, ppvDestCtx);
HRESULT IsConnected();
};
interface IRpcProxyBuffer : IUnknown {
HRESULT Connect(pRpcChannelBuffer);
void Disconnect();
};
interface IRpcStubBuffer : IUnknown {
HRESULT Connect(pUnkServer);
void Disconnect();
HRESULT Invoke(pMessage, pChannel);
IRPCStubBuffer* IsIIDSupported(iid);
ULONG CountRefs();
HRESULT DebugServerQueryInterface(ppv);
void DebugServerRelease(pv);
};
Suppose an interface proxy receives a method invocation on one of it s interfaces
(such as IFoo, IBar, or IBaz in the above figure). The interface proxy s implement
ation of this method first obtains a marshaling packet from its RPC channel usin
g IRpcChannelBuffer::GetBuffer(). The process of marshaling the arguments will c
opy data into the buffer. When marshaling is complete, the interface proxy invok
es IRpcChannelBuffer::SendReceive() to send the method invocation across the wire to
the corresponding interface stub. When IRpcChannelBuffer::SendReceive() returns
, the contents of buffer into which the arguments were marshaled will have been
replaced by the return values marshaled from the interface stub. The interface p
roxy unmarshals the return values, invokes IRpcChannelBuffer::FreeBuffer() to fr
ee the buffer, then returns the return values to the original caller of the meth
od.
It is the implementation of IRpcChannelBuffer::SendReceive() that actually sends
the request over to the server process. It is only the channel who knows or car
es how to identify the server process and object within that process to which th
e request should be sent; this encapsulation allows the architecture we are desc
ribing here to function for a variety of different kinds of channels: intra-mach
ine channels, inter-machine channels (i.e.: across the network), etc. The channe
l implementation knows how to forward the request onto the appropriate stub mana
ger object in the appropriate process. From the perspective of this specificatio
n, the channel and the stub manager are intimate with each other (and intimate w
ith the proxy manager, for that matter). Through this intimacy, eventually the a
ppropriate interface stub receives an IRpcStubBuffer::Invoke() call. The stub un
marshals the arguments from the provided buffer, invokes the indicated method on
the server object, and marshals the return values back into a new buffer, alloc
ated by a call to IRpcChannelBuffer::GetBuffer(). The stub manager and the chann
el then cooperate to ferry the return data packet back to the interface proxy, w
ho is still in the middle of IRpcChannelBuffer::SendReceive(). IRpcChannelBuffer
::SendReceive() returns to the proxy, and we proceed as just described above.
When created, interface proxies are always aggregated into the larger object pro
xy: at interface-proxy-creation time, the proxy is given the IUnknown* to which
it should delegate its QueryInterface(), etc., calls, as per the usual aggregati
on rules. When connected, the interface proxy is also given (with IRpcProxyBuffer:
:Connect()) a pointer to an IRpcChannelBuffer interface instance. It is through th
is pointer that the interface proxy actually sends calls to the server process.
Interface proxies bring a small twist to the normal everyday aggregation scenari
o. In aggregation, each interface supported by an aggregateable object is classi
fied as either external or internal. External interfaces are the norm. They are the
ones whose instances are exposed directly to the clients of the aggregate as who
le. It is always the case that a QueryInterface() that requests an external inte
rface of an aggregated object should be delegated by the object to its controlli
ng unknown (ditto for AddRef() and Release()). Internal interfaces, on the other
hand, are never exposed to outside clients. Instead, they are solely for the us
e of the controlling unknown in manipulating the aggregated object. QueryInterfa
ce() for internal interfaces should never be delegated to the controlling unknow
n (ditto again). In the common uses of aggregation, the IUnknown interface on th
e object is the only internal interface. The twist that interface proxies bring
is that IRpcProxyBuffer is also an internal interface.
Interface stubs, by contrast with interface proxies, are not aggregated, since t
here is no need that they appear to some external client to be part of a larger
whole. When connected, an interface stub is given (with IRpcStubBuffer::Connect(
)) a pointer to the server object to which they should forward invocations that
they receive.
A given interface proxy instance can if it chooses to do so service more than on
e interface. For example, in the above figure, one interface proxy could have ch
osen to service both IFoo and IBar. To accomplish this, in addition to installin
g itself under the appropriate registry entries, the proxy should support QueryIn
terface()ing from one supported interface (and from IUnknown and IRpcProxyBuffer)
to the other interfaces, as usual. When the Proxy Manager in a given object pro
xy finds that it needs the interface proxy for some new interface that it doesn t
already have, before it goes out to the registry to load in the appropriate code
using the code sequence described above, it first does a QueryInterface() for t
he new interface id (IID) on all of its existing interface proxies. If one of th
em supports the interface, then it is used rather than loading a new interface p
roxy.
Interface stub instances, too, can service more than one interface on a server o
bject. However, the extent to which they can do so is quite restricted: a given
interface stub instance may support one or more interfaces only if that set of in
terfaces has in fact a strict single-inheritance relationship. In short, a given
interface stub needs to know how to interpret a given method number that it is a
sked to invoke without at that same time also being told the interface id (IID)
in which that method belongs; the stub must already know the relevant IID. The I
ID which an interface stub is initially created to service is passed as paramete
r to IPSFactoryBuffer::CreateStub(). After creation, the interface stub may from time
to time be asked using IRpcStubBuffer::IsIIDSupported() if it in fact would also lik
e be used to service another IID. If the stub also supports the second IID, then
it should return the appropriate IRpcStubBuffer* for that IID; otherwise, the s
tub buffer should return NULL. This permits the stub manager in certain cases to
optimize the loading of interface stubs.
Both proxies and stubs will at various times have need to allocate or free memor
y. Interface proxies, for example, will need to allocate memory in which to retu
rn out parameters to their caller. In this respect interface proxies and interfa
ce stubs are just normal Component Object Model components, in that they should
use the standard task allocator; see CoGetMalloc(). See also the earlier discuss
ion regarding specific rules for passing in, out, and in out pointers.
On Microsoft Windows platforms, the key derived from IID under which the registry
is consulted to learn the proxy/stub class is as follows:
Interfaces
{IID}
ProxyStubClsid32 = {CLSID}
Here {CLSID} is a shorthand for any class id; the actual value of the unique id
is put between the {}'s; e.g. {DEADBEEF-DEAD-BEEF-C000-000000000046}; all digits
are upper case hex and there can be no spaces. This string format for a unique
id (without the {} s) is the same as the OSF DCE? standard and is the result of t
he StringFromCLSID routine. {IID} is a shorthand for an interface id; this is si
milar to {CLSID}; StringFromIID can be used to produce this string.
9.4 Architecture of Handler Marshaling
Handler marshaling is a third variation on marshaling, one closely related to st
andard marshaling. Colloquially, one can think of it as a middle ground between
raw standard marshaling and full custom marshaling.
In handler marshaling, the object specifies that it would like to have some amou
nt of client-side state; this is designated by the class returned by IStdMarshal
Info::GetClassForHandler. However, this handler class rather than fully taking o
ver the remoting to the object instead aggregates in the default handler, which
carries out the remoting in the standard manner as described above.
9.5 Standards for Marshaled Data Packets
In the architecture described here, nothing has yet to be said about representat
ion or format standards for the data that gets placed in marshaling packets. The
re is a good reason for this. In the Component Object Model architecture, the on
ly two parties that have to agree on what goes into a marshaling packet are the
code that marshals the data into the packet and the code that unmarshals it out
again: the interface proxies and the interface stubs. So long as we are dealing
only with intra-machine procedure calls (i.e.: non-network), then we can reasona
bly assume that pairs of interface proxies and stubs are always installed togethe
r on the machine. In this situation, we have no need to specify a packet format
standard; the packet format can safely be a private matter between the two piece
of code.
However, once a network is involved, relying on the simultaneous installation of
corresponding interface proxies and stubs (on different machines) is no longer
a reasonable thing to do. Thus, when the a method invocation is in fact remoted
over a network, it is strongly recommended that the data marshaled into the pack
et to conform to a published standard (NDR), though, as pointed out above, it is
technically the interface-designer s responsibility to achieve this correspondenc
e by whatever means he sees fit.
9.6 Creating an Initial Connection Between Processes
Earlier we said we would later discuss how an initial remoting connection is est
ablished between two processes. It is now time to have that discussion.
The real truth of the matter is that the initial connection is established by so
me means outside of the architecture that we have been discussing here. The mini
mal that is required is some primitive communication channel between the two proc
esses. As such, we cannot hope to discuss all the possibilities. But we will poi
nt out some common ones.
One common approach is that initial connections are established just like other c
onnections: an interface pointer is marshaled in the server process, the marshal
ed data packet is ferried the client process, and it is unmarshaled. The only tw
ist is that the ferrying is done by some means other than the RPC mechanism whic
h we ve been describing. There are many ways this could be accomplished. The most
important, by far is one where the marshaled data is passed as an out-parameter
from an invocation on a well-known endpoint to a Service Control Manager.
9.7 Interface Remoting and Marshaling Interface Descriptions
Having discussed on a high level how various remoting related interfaces work to
gether, we now present each of them in detail.
9.7.1 IMarshal
The IMarshal interface enables an COM object to define and manage the marshaling
of its interface pointers. The alternative is to use COM s default implementation
, the preferred choice in all but a few special cases (see When to Implement ).
Marshaling is the process of packaging data into packets for transmission to a dif
ferent process or machine. Unmarshaling is the process of recovering that data at
the receiving end. In any given call, method arguments are marshaled and unmarsh
aled in one direction, while return values are marshaled and unmarshaled in the
other.
Although marshaling applies to all data types, interface pointers require specia
l handling. The fundamental problem is how client code running in one address sp
ace can correctly dereference a pointer to an interface on an object residing in
a different address space. COM s solution is for a client application to communic
ate with the original object through a surrogate object, or proxy, which lives i
n the client s process. The proxy holds a reference to an interface on the origina
l object and hands the client a pointer to an interface on itself. When the clie
nt calls an interface method on the original object, its call is actually going
to the proxy. Therefore, from the client s point of view, all calls are in-process
.
On receiving a call, the proxy marshals the method arguments and, through some m
eans of interprocess communication, such as RPC, passes them along to code in th
e server process, which unmarshals the arguments and passes them to the original
object. This same code marshals return values for transmission back to the prox
y, which unmarshals the values and passes them to the client application.
IMarshal provides methods for creating, initializing, and managing a proxy in a
client process; it does not dictate how the proxy should communicate with the or
iginal object. COM s default implementation of IMarshal uses RPC. When you impleme
nt this interface yourself, you are free to choose any method of interprocess co
mmunication you deem to be appropriate for your application shared memory, named
pipe, window handle, RPC in short, whatever works.
9.7.1.1 When to Implement
Implement IMarshal only when you believe that you can realize significant optimi
zations to COM s default implementation. In practice, this will rarely be the case
. However, there are occasions where implementing IMarshal may be preferred:
· The objects you are writing keep their state in shared memory. In this case, bot
h the original process and the client process uses proxies that refer to the sha
red memory. This type of custom marshaling is possible only if the client proces
s is on the same machine as the original process. COM-provided implementations o
f IStorage and IStream are examples of this type of custom marshaling.
· The objects you are writing are immutable, that is, their state does not change
after creation. Instead of forwarding method calls to the original objects, you
simply create copies of those objects in the client process. This technique avoi
ds the cost of switching from one process to another. Some monikers are examples
of immutable objects; if you are implementing your own moniker class, you shoul
d evaluate the costs and benefits of implementing IMarshal on your moniker objec
ts.
· Objects that themselves are proxy objects can use custom marshaling to avoid cre
ating proxies to proxies. Instead, the existing proxy can refer new proxies back
to the original object. This capability is important for the sake of both effic
iency and robustness.
· Your server application wants to manage how calls are made across the network wi
thout affecting the interface exposed to clients. For example, if an end user we
re making changes to a database record, the server might want to cache the chang
es until the user has committed them all, at which time the entire transaction w
ould be forwarded in a single packet. Using a custom proxy would enable the cach
ing and batching of changes in this way.
When you choose to implement IMarshal, you must do so for both your original obj
ect and the proxy you create for it. When implementing the interface on either o
bject or proxy, you simply return E_NOTIMPL for the methods that are not impleme
nted.
COM uses your implementation of IMarshal in the following manner: When it s necess
ary to create a remote interface pointer to your object (that is, when a pointer
to your object is passed as an argument in a remote function call), COM queries
your object for the IMarshal interface. If your object implements it, COM uses
your IMarshal implementation to create the proxy object. If your object does not
implement IMarshal, COM uses its default implementation.
How you choose to structure the proxy is entirely up to you. You can write the p
roxy to use whatever mechanisms you deem appropriate for communicating with the
original object. You can also create the proxy as either a stand-alone object or
as part of a larger aggregation such as a handler. However you choose to struct
ure the proxy, it must implement IMarshal to work at all. You must also generate
a CLSID for the proxy to be returned by your implementation of IMarshal::GetUnm
arshalClass on the original object.
9.7.1.2 When to Use
COM calls this interface as part of system-provided marshaling support. COM s call
s are wrapped in calls to CoMarshalInterface and CoUnmarshalInterface. Your code
typically will not need to call this interface. Special circumstances where you
might choose to do so are discussed in the Notes to Callers section for each meth
od.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IMarshal Methods Description
GetUnmarshalClass Returns CLSID of unmarshaling code.
GetMarshalSizeMax Returns size of buffer needed during marshaling.
MarshalInterface Marshals an interface pointer.
UnmarshalInterface Unmarshals an interface pointer.
ReleaseMarshalData Destroys a marshaled data packet.
DisconnectObject Severs all connections.
See Also
IStdMarshalInfo
9.7.1.3 IMarshal::DisconnectObject
Forcibly releases all external connections to an object. The object s server calls
the object s implementation of this method prior to shutting down.
HRESULT DisconnectObject(
DWORD dwReserved //Reserved for future use
);
Parameter
dwReserved
[in] Reserved for future use; must be zero. To ensure compatibility with future
use, DisConnectObject must not check for zero.
Return Values
The method supports the standard return value E_FAIL, as well as the following:
S_OK
The object was disconnected successfully.
Remarks
This method is implemented on the object, not the proxy.
9.7.1.3.1 Notes to Callers
The usual case in which this method is called occurs when an end user forcibly c
loses a COM server that has one or more running objects that implement IMarshal.
Prior to shutting down, the server calls the CoDisconnectObject helper function
to sever external connections to all its running objects. For each object that
implements IMarshal, however, this function calls IMarshal::DisconnectObject, so
that each object that manages its own marshaling can take steps to notify its p
roxy that it is about to shut down.
9.7.1.3.2 Notes to Implementers
As part of its normal shutdown code, a server should call the CoDisconnectObject
function, which in turn calls IMarshal::DisconnectObject, on each of its runnin
g objects that implements IMarshal.
The outcome of any implementation of this method should be to enable a proxy to
respond to all subsequent calls from its client by returning RPC_E_DISCONNECTED
or CO_E_OBJECTNOTCONNECTED rather than attempting to forward the calls on to the
original object. It is up to the client, of course, to destroy the proxy.
If you are implementing this method for an immutable object, such as a moniker,
your implementation doesn t need to do anything because such objects are typically
copied whole into the client s address space. Therefore, they have neither a prox
y nor a connection to the original object. For more information on marshaling im
mutable objects, see IMarshal, When to Implement.
See Also
CoDisconnectObject
9.7.1.4 IMarshal::GetMarshalSizeMax
Returns an upper bound on the number of bytes needed to marshal the specified in
terface pointer on the specified object.
HRESULT GetMarshalSizeMax(
REFIID riid, //Reference to the identifier of the interface to be mar
shaled
void *pv, //Interface pointer to be marshaled
DWORD dwDestContext, //Destination process
void * pvDestContext, //Reserved for future use
DWORD mshlflags, //Reason for marshaling
ULONG * pSize //Pointer to upper-bound value
);
Parameters
riid
[in] Reference to the identifier of the interface to be marshaled.
pv
[in] Interface pointer to be marshaled; can be NULL.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Val
ues for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling
can occur either in another apartment of the current process (MSHCTX_INPROC) or
in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back
to the client process the normal case or written to a global table, where it can
be retrieved by multiple clients. Valid values come from the MSHLFLAGS enumerat
ion.
pSize
[out] Pointer to the upper bound on the amount of data to be written to the mars
haling stream.
Return Values
The method supports the standard return value E_FAIL, as well as the following:
S_OK
The maximum size was returned successfully.
E_NOINTERFACE
The specified interface was not supported.
Remarks
This method is called indirectly, in a call to CoGetMarshalSizeMax, by whatever
code in the server process is responsible for marshaling a pointer to an interfa
ce on an object. This marshaling code is usually a stub generated by COM for one
of several interfaces that can marshal a pointer to an interface implemented on
an entirely different object. Examples include the IClassFactory and IOleItemCo
ntainer interfaces. For purposes of discussion, the code responsible for marshal
ing a pointer is here called the marshaling stub.
To create a proxy for an object, COM requires two pieces of information from the
original object: the amount of data to be written to the marshaling stream and
the proxy s CLSID.
The marshaling stub obtains these two pieces of information with successive call
s to CoGetMarshalSizeMax and CoMarshalInterface.
9.7.1.4.1 Note to Callers
The marshaling stub, through a call to CoGetMarshalSizeMax, calls the object s imp
lementation of this method to preallocate the stream buffer that will be passed
to IMarshal::MarshalInterface.
You do not explicitly call this method if you are:
· Implementing existing COM interfaces, or
· Defining your own custom interfaces, using the Microsoft Interface Definition La
nguage (MIDL).
In both cases, the MIDL-generated stub automatically makes the call.
If you are not using MIDL to define your own interface (see Writing a Custom Inte
rface ), your marshaling stub does not have to call GetMarshalSizeMax, though doin
g so is highly recommended. An object knows better than an interface stub what t
he maximum size of a marshaling data packet is likely to be. Therefore, unless y
ou are providing an automatically growing stream that is so efficient that the o
verhead of expanding it is insignificant, you should call this method even when
implementing your own interfaces.
The value returned by this method is only guaranteed to be valid as long as the
internal state of the object being marshaled does not change. Therefore, the act
ual marshaling should be done immediately after this function returns, or the st
ub runs the risk that the object, because of some change in state, might require
more memory to marshal than it originally indicated.
9.7.1.4.2 Notes to Implementers
Your implementation of MarshalInterface will use this buffer to write marshaling
data into the stream. If the buffer is too small, the marshaling operation will
fail. Therefore, the value returned by this method must be a conservative estim
ate of the amount of data that will, in fact, be needed to marshal the interface
. Violation of this requirement should be treated as a catastrophic error.
In a subsequent call to IMarshal::MarshalInterface, your IMarshal implementation
cannot rely on the caller actually having called GetMarshalSizeMax beforehand.
It must still be wary of STG_E_MEDIUMFULL errors returned by the stream and be p
repared to handle them gracefully.
To ensure that your implementation of GetMarshalSizeMax will continue to work pr
operly as new destination contexts are supported in the future, delegate marshal
ing to COM s default implementation for all dwDestContext values that your impleme
ntation does not understand. To delegate marshaling to COM s default implementatio
n, call the CoGetStandardMarshal function.
See Also
CoGetMarshalSizeMax, IMarshal::MarshalInterface
9.7.1.5 IMarshal::GetUnmarshalClass
Returns the CLSID that COM uses to locate the DLL containing the code for the co
rresponding proxy. COM loads this DLL to create an uninitialized instance of the
proxy.
HRESULT GetUnmarshalClass(
REFIID riid, //Reference to the identifier of the interface to be mar
shaled
void * pv, //Interface pointer being marshaled
DWORD dwDestContext, //Destination process
void * pvDestContext, //Reserved for future use
DWORD mshlflags, //Reason for marshaling
CLSID * pCid //Pointer to CLSID of proxy
);
Parameters
riid
[in] Reference to the identifier of the interface to be marshaled.
pv
[in] Pointer to the interface to be marshaled; can be NULL if the caller does no
t have a pointer to the desired interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Value
s for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling ca
n occur either in another apartment of the current process (MSHCTX_INPROC) or in
another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Whether the data to be marshaled is to be transmitted back to the client pr
ocess the normal case or written to a global table, where it can be retrieved by
multiple clients. Valid values come from the MSHLFLAGS enumeration.
pCid
[out] Pointer to the CLSID to be used to create a proxy in the client process.
Return Value
Returns S_OK if successful; otherwise, S_FALSE.
Remarks
This method is called by whatever code in the server process may be responsible
for marshaling a pointer to an interface on an object. This marshaling code is u
sually a stub generated by COM for one of several interfaces that can marshal a
pointer to an interface implemented on an entirely different object. Examples in
clude the IClassFactory and IOleItemContainer interfaces. For purposes of this d
iscussion, the code responsible for marshaling a pointer is called the marshaling
stub.
To create a proxy for an object, COM requires two pieces of information from the
original object: the amount of data to be written to the marshaling stream and
the proxy s CLSID.
The marshaling stub obtains these two pieces of information with successive call
s to CoGetMarshalSizeMax and CoMarshalInterface.
9.7.1.5.1 Note to Callers
The marshaling stub calls the object s implementation of this method to obtain the
CLSID to be used in creating an instance of the proxy. The client, upon receivi
ng the CLSID, loads the DLL listed for it in the system registry.
You do not explicitly call this method if you are:
· Implementing existing COM interfaces, or
· Defining your own interfaces using the Microsoft Interface Definition Language (
MIDL).
In both cases, the stub automatically makes the call. See Writing a Custom Interf
ace.
If you are not using MIDL to define your own interface, your stub must call this
method, either directly or indirectly, to get the CLSID that the client-side CO
M Library needs to create a proxy for the object implementing the interface.
If the caller has a pointer to the interface to be marshaled, it should, as a ma
tter of efficiency, use the pv parameter to pass that pointer. In this way, an i
mplementation that may use such a pointer to determine the appropriate CLSID for
the proxy does not have to call IUnknown::QueryInterface on itself. If a caller
does not have a pointer to the interface to be marshaled, it can pass NULL.
9.7.1.5.2 Notes to Implementers
COM calls GetUnmarshalClass to obtain the CLSID to be used for creating a proxy
in the client process. The CLSID to be used for a proxy is normally not that of
the original object (see Notes to Implementers for the exception), but one you wil
l have generated (using the GUIDGEN.EXE tool supplied with the Win32 SDK) specif
ically for your proxy object.
Implement this method for each object that provides marshaling for one or more o
f its interfaces. The code responsible for marshaling the object writes the CLSI
D, along with the marshaling data, to a stream; COM extracts the CLSID and data
from the stream on the receiving side.
If your proxy implementation consists simply of copying the entire original obje
ct into the client process, thereby eliminating the need to forward calls to the
original object, the CLSID returned would be the same as that of the original o
bject. This strategy, of course, is advisable only for objects that are not expe
cted to change.
If the pv parameter is NULL and your implementation needs an interface pointer,
it can call IUnknown::QueryInterface on the current object to get it. The pv par
ameter exists merely to improve efficiency.
To ensure that your implementation of GetUnmarshalClass continues to work proper
ly as new destination contexts are supported in the future, delegate marshaling
to COM s default implementation for all dwDestContext values that your implementat
ion does not handle. To delegate marshaling to COM s default implementation, call
the CoGetStandardMarshal function.
9.7.1.6 IMarshal::MarshalInterface
Writes into a stream the data required to initialize a proxy object in some clie
nt process.
HRESULT MarshalInterface(
IStream *pStm, //Pointer to stream used for marshaling
REFIID riid, //Reference to the identifier of the interface to be mar
shaled
void *pv, //Interface pointer to be marshaled
DWORD dwDestContext, //Destination context
void *pvDestContext, //Reserved for future use
DWORD mshlflags //Reason for marshaling
);
Parameters
pStm
[in] Pointer to the stream to be used during marshaling.
riid
[in] Reference to the identifier of the interface to be marshaled. This interfac
e must be derived from the IUnknown interface.
pv
[in] Pointer to the interface pointer to be marshaled; can be NULL if the caller
does not have a pointer to the desired interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Val
ues for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling
can occur either in another apartment of the current process (MSHCTX_INPROC) or
in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be zero.
mshlflags
[in] Whether the data to be marshaled is to be transmitted back to the client pr
ocess the normal case or written to a global table, where it can be retrieved by
multiple clients. Valid values come from the MSHLFLAGS enumeration.
Return Values
The method supports the standard return value E_FAIL, as well as the following:
S_OK
The interface pointer was marshaled successfully.
E_NOINTERFACE
The specified interface is not supported.
STG_E_MEDIUMFULL
The stream is full.
Remarks
This method is called indirectly, in a call to CoMarshalInterface, by whatever c
ode in the server process may be responsible for marshaling a pointer to an inte
rface on an object. This marshaling code is usually a stub generated by COM for
one of several interfaces that can marshal a pointer to an interface implemented
on an entirely different object. Examples include the IClassFactory and IOleIte
mContainer interfaces. For purposes of this discussion, the code responsible for
marshaling a pointer is called the marshaling stub.
9.7.1.6.1 Notes to Callers
Normally, rather than calling IMarshal::MarshalInterface directly, your marshali
ng stub instead should call the CoMarshalInterface function, which contains a ca
ll to this method. The stub makes this call to command an object to write its ma
rshaling data into a stream. The stub then either passes the marshaling data bac
k to the client process or writes it to a global table, where it can be unmarsha
led by multiple clients. The stub s call to CoMarshalInterface is normally precede
d by a call to CoGetMarshalSizeMax, to get the maximum size of the stream buffer
into which the marshaling data will be written.
You do not explicitly call this method if you are:
· Implementing existing COM interfaces, or
· Defining your own interfaces using the Microsoft Interface Definition Language (
MIDL).
In both cases, the MIDL-generated stub automatically makes the call.
If you are not using MIDL to define your own interface, your marshaling stub mus
t call this method, either directly or indirectly.Your stub implementation shoul
d call MarshalInterface immediately after its previous call to IMarshal::GetMars
halSizeMax returns. Because the value returned by GetMarshalSizeMax is guarantee
d to be valid only so long as the internal state of the object being marshaled d
oes not change, a delay in calling MarshalInterface runs the risk that the objec
t will require a larger stream buffer than originally indicated.
If the caller has a pointer to the interface to be marshaled, it should, as a ma
tter of efficiency, use the pv parameter to pass that pointer. In this way, an i
mplementation that may use such a pointer to determine the appropriate CLSID for
the proxy does not have to call IUnknown::QueryInterface on itself. If a caller
does not have a pointer to the interface to be marshaled, it can pass NULL.
9.7.1.6.2 Notes to Implementers
Your implementation of IMarshal::MarshalInterface must write to the stream whate
ver data is needed to initialize the proxy on the receiving side. Such data woul
d include a reference to the interface to be marshaled, a MSHLFLAGS value specif
ying whether the data should be returned to the client process or written to a g
lobal table, and whatever is needed to connect to the object, such as a named pi
pe, handle to a window, or pointer to an RPC channel.
Your implementation should not assume that the stream is large enough to hold al
l the data. Rather, it should gracefully handle a STG_E_MEDIUMFULL error. Just b
efore exiting, your implementation should position the seek pointer in the strea
m immediately after the last byte of data written.
If the pv parameter is NULL and your implementation needs an interface pointer,
it can call IUnknown::QueryInterface on the current object to get it. The pv par
ameter exists merely to improve efficiency.
To ensure that your implementation of MarshalInterface continues to work properl
y as new destination contexts are supported in the future, delegate marshaling t
o COM s default implementation for all dwDestContext values that your implementati
on does not handle. To delegate marshaling to COM s default implementation, call t
he CoGetStandardMarshal helper function.
Using the MSHLFLAGS enumeration, callers can specify whether an interface pointe
r is to be marshaled back to a single client or written to a global table, where
it can be unmarshaled by multiple clients. You must make sure that your object
can handle calls from the multiple proxies that might be created from the same i
nitialization data.
See Also
CoGetStandardMarshal, IMarshal::GetMarshalSizeMax, IMarshal::GetUnmarshalClass,
IMarshal::UnmarshalInterface, Writing a Custom Interface
9.7.1.7 IMarshal::ReleaseMarshalData
Destroys a marshaled data packet.
HRESULT ReleaseMarshalData(
IStream * pStm //Pointer to stream used for unmarshaling
);
Parameter
pStm
[in] Pointer to a stream that contains the data packet to be destroyed.
Return Values
The method supports the standard return value E_FAIL, as well as the following:
S_OK
The data packet was released successfully.
IStream errors
This function can also return any of the stream-access error values for the IStr
eam interface.
Remarks
If an object s marshaled data packet does not get unmarshaled in the client proces
s space, and the packet is no longer needed. The client calls ReleaseMarshalData
on the proxy s IMarshal implementation to instruct the object to destroy the data
packet. The call occurs within the CoReleaseMarshalData function. The data pack
et serves as an additional reference on the object, and releasing the data is li
ke releasing an interface pointer by calling IUnknown::Release.
If the marshaled data packet somehow does not arrive in the client process, or R
eleaseMarshalData is not successfully re-created in the proxy, COM can call this
method on the object itself.
9.7.1.7.1 Notes to Callers
You will rarely if ever have occasion to call this method yourself. A possible e
xception would be if you were to implement IMarshal on a class factory for a cla
ss object on which you are also implementing IMarshal. In this case, if you are
marshaling the object to a table, where it can be retrieved by multiple clients,
you might, as part of your unmarshaling routine, call ReleaseMarshalData to rel
ease the data packet for each proxy.
9.7.1.7.2 Notes to Implementers
If your implementation stores state information about marshaled data packets, yo
u can use this method to release the state information associated with the data
packet represented by pStm. Your implementation should also position the seek po
inter in the stream past the last byte of data.
See Also
CoUnMarshalInterface, CoReleaseMarshalData

9.7.1.8 IMarshal::UnmarshalInterface
Initializes a newly created proxy and returns an interface pointer to that proxy
.
HRESULT UnmarshalInterface(
IStream * pStm, //Pointer to the stream to be unmarshaled
REFIID riid, //Reference to the identifier of the interface to be unm
arshaled
void ** ppv //Indirect pointer to interface
);
Parameters
pStm
[in] Pointer to the stream from which the interface pointer is to be unmarshaled
.
riid
[in] Reference to the identifier of the interface to be unmarshaled.
ppv
[out] Indirect pointer to the interface.
Return Values
The method supports the standard return value E_FAIL, as well as the following:
S_OK
The interface pointer was unmarshaled successfully.
E_NOINTERFACE
The specified interface was not supported.
Remarks
The COM library in the process where unmarshaling is to occur calls the proxy s im
plementation of this method.
9.7.1.8.1 Notes to Callers
You do not call this method directly. There are, however, some situations in whi
ch you might call it indirectly through a call to CoUnmarshalInterface. For exam
ple, if you are implementing a stub, your implementation would call CoUnmarshalI
nterface when the stub receives an interface pointer as a parameter in a method
call.
9.7.1.8.2 Notes to Implementers
The proxy s implementation should read the data written to the stream by the origi
nal object s implementation of IMarshal::MarshalInterface and use that data to ini
tialize the proxy object whose CLSID was returned by the marshaling stub s call to
the original object s implementation of IMarshal::GetUnmarshalClass.
To return the appropriate interface pointer, the proxy implementation can simply
call IUnknown::QueryInterface on itself, passing the riid and ppv parameters. H
owever, your implementation of UnmarshalInterface is free to create a different
object and, if necessary, return a pointer to it.
Just before exiting, even if exiting with an error, your implementation should r
eposition the seek pointer in the stream immediately after the last byte of data
read.
See Also
IMarshal::GetUnmarshalClass, IMarshal::MarshalInterface
9.7.1.9 IMarshal - Default Implementation
COM uses its own internal implementation of the IMarshal interface to marshal an
y object that does not provide its own implementation. COM makes this determinat
ion by querying the object for IMarshal. If the interface is missing, COM defaul
ts to its internal implementation.
COM s default implementation of IMarshal uses a generic proxy for each object, and
creates individual stubs and proxies, as they are needed, for each interface im
plemented on the object. This mechanism is necessary because COM cannot know in
advance what particular interfaces a given object may implement. Developers who
do not use COM s default marshaling, electing instead to write their own proxy and
marshaling routines, know at compile time all the interfaces to be found on the
ir objects and therefore understand exactly what marshaling code is required. CO
M, in providing marshaling support for all objects, must do so at run time.
The interface proxy resides in the client process; the interface stub, in the se
rver. Together, each pair handles all marshaling for its interface. The job of e
ach interface proxy is to marshal arguments and unmarshal return values and out
parameters that are passed back and forth in subsequent calls to its interface.
The job of each interface stub is to unmarshal function arguments and pass them
along to the original object, then marshal the return values and out parameters
that the object returns.
Proxy and stub communicate by means of an RPC (remote procedure call) channel, w
hich utilizes the system s RPC infrastructure for interprocess communication. The
RPC channel implements a single interface IRpcChannelBuffer, an internal interfa
ce to which both interface proxies and stubs hold a pointer. The proxy and stub
call the interface to obtain a marshaling packet, send the data to their counter
part, and destroy the packet when they are done. The interface stub also holds a
pointer to the original object.
For any given interface, the proxy and stub are both implemented as instances of
the same class, which is listed for each interface in the system registry under
the label ProxyStubClsid32 (or ProxyStubClsid on 16-bit systems). This entry ma
ps the interface s IID to the CLSID of its proxy and stub objects. When COM needs
to marshal an interface, it looks in the system registry to obtain the appropria
te CLSID. The server identified by this CLSID implements both the interface prox
y and interface stub.
Most often, the class to which this CLSID refers is automatically generated by a
tool whose input is a description of the function signatures and semantics of a
given interface, written in some interface description language. While using su
ch a language is highly recommended and encouraged for accuracy s sake, doing so i
s by no means required. Proxies and stubs are merely Component Object Model comp
onents used by the RPC infrastructure and, as such, can be written in any manner
desired so long as the correct external contracts are upheld. The programmer wh
o designs a new interface is responsible for ensuring that all interface proxies
and stubs that ever exist agree on the representation of their marshaled data.
When created, interface proxies are always aggregated into a larger proxy, which
represents the object as a whole. This object proxy also aggregates COM s generic
proxy object, which is known as the proxy manager. The proxy manager implements
two interfaces: IUnknown and IMarshal. All of the other interfaces that may be
implemented on an object are exposed in its object proxy through the aggregation
of individual interface proxies. A client holding a pointer to the object proxy
believes it holds a pointer to the actual object.
A proxy representing the object as a whole is required in the client process so
that a client can distinguish calls to the same interfaces implemented on entire
ly different objects. Such a requirement does not exist in the server process, h
owever, where the object itself resides, because all interface stubs communicate
only with the objects for which they were created. No other connection is possi
ble.
Interface stubs, by contrast with interface proxies, are not aggregated, because
there is no need that they appear to some external client to be part of a large
r whole. When connected, an interface stub is given a pointer to the server obje
ct to which it should forward method invocations that it receives. Although it i
s useful to refer conceptually to a stub manager, meaning whatever pieces of code
and state in the server-side RPC infrastructure that service the remoting of a g
iven object, there is no direct requirement that the code and state take any par
ticular, well-specified form.
The first time a client requests a pointer to an interface on a particular objec
t, COM loads an IClassFactory stub in the server process and uses it to marshal
the first pointer back to the client. In the client process, COM loads the gener
ic proxy for the class factory object and calls its implementation of IMarshal t
o unmarshal that first pointer. COM then creates the first interface proxy and h
ands it a pointer to the RPC channel. Finally, COM returns the IClassFactory poi
nter to the client, which uses it to call IClassFactory::CreateInstance, passing
it a reference to the interface.
Back in the server process, COM now creates a new instance of the object, along
with a stub for the requested interface. This stub marshals the interface pointe
r back to the client process, where another object proxy is created, this time f
or the object itself. Also created is a proxy for the requested interface, a poi
nter to which is returned to the client. With subsequent calls to other interfac
es on the object, COM will load the appropriate interface stubs and proxies as n
eeded.
When a new interface proxy is created, COM hands it a pointer to the proxy manag
er s implementation of IUnknown, to which it delegates all QueryInterface calls. E
ach interface proxy implements two interfaces of its own: the interface it repre
sents and IRpcProxyBuffer. The interface proxy exposes its own interface directl
y to clients, which can obtain its pointer by calling QueryInterface on the prox
y manager. Only COM, however, can call IRpcProxyBuffer, which it uses to connect
and disconnect the proxy to the RPC channel. A client cannot query an interface
proxy to obtain a pointer to the IRpcProxyBuffer interface.
On the server side, each interface stub implements IRpcStubBuffer, an internal i
nterface. The server code acting as a stub manager calls IRpcStubBuffer::Connect
and passes the interface stub the IUnknown pointer of its object.
When an interface proxy receives a method invocation, it obtains a marshaling pa
cket from its RPC channel through a call to IRpcChannelBuffer::GetBuffer. The pr
ocess of marshaling the arguments will copy data into the buffer. When marshalin
g is complete, the interface proxy invokes IRpcChannelBuffer::SendReceive to sen
d the marshaled packet to the corresponding interface stub. When IRpcChannelBuff
er::SendReceive returns, the buffer into which the arguments were marshaled will
have been replaced by a new buffer containing the return values marshaled from
the interface stub. The interface proxy unmarshals the return values, invokes IR
pcChannelBuffer::FreeBuffer to free the buffer, then returns the return values t
o the original caller of the method.
It is the implementation of IRpcChannelBuffer::SendReceive that actually sends t
he request to the server process and that knows how to identify the server proce
ss and, within that process, the object to which the request should be sent. The
channel implementation also knows how to forward the request on to the appropri
ate stub manager in that process. The interface stub unmarshals the arguments fr
om the provided buffer, invokes the indicated method on the server object, and m
arshals the return values back into a new buffer, allocated by a call to IRpccha
nnelBuffer::GetBuffer. The channel then transmits the return data packet back to
the interface proxy, which is still in the middle of IRpcchannelBuffer::SendRec
eive, which returns to the interface proxy.
A particular instance of an interface proxy can be used to service more than one
interface, so long as two conditions are met. First, the IIDs of the affected i
nterfaces must be mapped to the the appropriate ProxyStubClsid in the system reg
istry. Second, the interface proxy must support calls to QueryInterface from one
supported interface to the other interfaces, as usual, as well as from IUnknown
and IRpcProxyBuffer.
A single instance of an interface stub can also service more than one interface,
but only if that set of interfaces has a strict single-inheritance relationship
. This restriction exists because the stub can direct method invocations to mult
iple interfaces only where it knows in advance which methods are implemented on
which interfaces.
Both proxies and stubs will at various times have need to allocate or free memor
y. Interface proxies, for example, will need to allocate memory in which to retu
rn out parameters to their caller. In this respect, interface proxies and interf
ace stubs are just normal COM components, in that they should use the standard t
ask allocator (see CoGetMalloc).
9.7.1.9.1 When to Use
You should use COM s default implementation of IMarshal except in those very few c
ases where your application has special requirements that COM s default implementa
tion does not address, or where you can achieve optimizations over the marshalin
g code COM has provided. For examples of such special cases, see IMarshal, When t
o Implement.
See Also
IMarshal
9.7.2 IPSFactoryBuffer
IPSFactoryBuffer is the interface through which proxies and stubs are created.
It is used to create proxies and stubs that support IRpcProxyBuffer and IRpcStub
Buffer respectively. Each proxy / stub DLL must support IPSFactory interface on t
he class object accessible through its DllGetClassObject() entry point. As was d
escribed above, the registry is consulted under a key derived from the IID to be
remoted in order to learn the proxy/stub class that handles the remoting of the
indicated interface. The class object for this class is retrieved, asking for t
his interface. A proxy or a stub is then instantiated as appropriate.
interface IPSFactoryBuffer : IUnknown {
HRESULT CreateProxy(pUnkOuter, iid, ppProxy, ppv);
HRESULT CreateStub(iid, pUnkServer, ppStub);
};
9.7.2.1 IPSFactoryBuffer::CreateProxy
HRESULT IPSFactoryBuffer::CreateProxy(pUnkOuter, iid, ppProxy, ppv)
Create a new interface proxy object. This function returns both an IRpcProxy ins
tance and an instance of the interface which the proxy is being created to servi
ce in the first place. The newly created proxy is initially in the unconnected s
tate.
Argument Type Description
pUnkOuter IUnknown * the controlling unknown of the aggregate in whic
h the proxy is being created.
iid REFIID the interface id which the proxy is being created to service, an
d of which an instance should be returned through ppv.
ppProxy IRpcProxyBuffer** on exit, contains the new IRpcProxyBuffer instan
ce.
ppv void ** on exit, contains an interface pointer of type indicated by iid.
return value HRESULT S_OK, E_OUTOFMEMORY, E_NOINTERFACE, E_UNEXPECTED, no oth
ers.
9.7.2.2 IPSFactoryBuffer::CreateStub
HRESULT IPSFactoryBuffer::CreateStub(iid, pUnkServer, ppStub)
Create a new interface stub object. The stub is created in the connected state o
n the object indicated by pUnkServer.
If pUnkServer is non-NULL, then before this function returns the stub must verif
y (by using QueryInterface()) that the server object in fact supports the interfa
ce indicated by iid. If it does not, then this function should fail with the err
or E_NOINTERFACE.
Argument Type Description
iid REFIID the interface that the stub is being created to service
pUnkServer IUnknown* the server object that is being remoted. The stu
b should delegate incoming calls (see IRpcStubBuffer::Invoke()) to the appropria
te interface on this object. pUnkServer may legally be NULL, in which case the c
aller is responsible for later calling IRpcStubBuffer::Connect() before using IR
pcStubBuffer::Invoke().
ppStub IRpcStubBuffer** the place at which the newly create stub is to b
e returned.
return value HRESULT S_OK, E_OUTOFMEMORY, E_NOINTERFACE, E_UNEXPECTED, no oth
ers.
9.7.3 IRpcChannelBuffer
IRpcChannelBuffer is the interface through which interface proxies send calls th
rough to the corresponding interface stub. This interface is implemented by the
RPC infrastructure. The infrastructure provides an instance of this interface to
interface proxies in IRpcProxyBuffer::Connect(). The interface proxies hold on
to this instance and use it each time they receive an incoming call.
interface IRpcChannelBuffer : IUnknown {
HRESULT GetBuffer(pMessage, riid);
HRESULT SendReceive(pMessage, pStatus);
HRESULT FreeBuffer(pMessage);
HRESULT GetDestCtx(pdwDestCtx, ppvDestCtx);
HRESULT IsConnected();
};
9.7.3.1 RPCOLEMESSAGE and related structures
Common to several of the methods in IRpcChannelBuffer is a data structure of typ
e RPCOLEMESSAGE. This structure is defined as is show below. The structure is to
be packed so that there are no holes in its memory layout.
typedef struct RPCOLEMESSAGE {
void * reserved1;
RPCOLEDATAREP dataRepresentation; // in NDR transfer synta
x: info about endianness, etc.
void * pvBuffer;
// memory buffer used for marshalling
ULONG cbBuffer;
// size of the marshalling buffer
ULONG iMethod;
// the method number being invoked
void * reserved2[5];
ULONG rpcFlags;
} on the ultimate destination machine MESSAGE;
The most significant member of this structure is pvBuffer. It is through the mem
ory buffer to which pvBuffer points that marshaled method arguments are transfer
red. cbBuffer is used to indicate the size of the buffer. iMethod is indicates a
particular method number within the interface being invoked. The IID of that in
terface is identified through other means: on the client side as a parameter to
GetBuffer(), and on the server side as part of the internal state of each interf
ace stub.
At all times all reserved values in this structure are to be initialized to zero
by non-RPC-infrastructure parties (i.e.: parties other than the channel / RPC ru
ntime implementor) who allocate RPCOLEMESSAGE structures. However, the RPC channe
l (more generally, the RPC runtime infrastructure) is free to modify these reser
ved fields. Therefore, once initialized, the reserved fields must be ignored by
the initializing code; they cannot be relied on to remain as zero. Further, ther
e are very carefully specified rules as to what values in these structures may or
may not be modified at various times and by which parties. In almost all cases,
aside from actually reading and writing data from the marshaling buffer, which
is done by proxies and stubs, only the channel may change these fields. See the
individual method descriptions for details.
Readers familiar with the connection-oriented DCE protocol may notice that the tr
ansfer syntax used for marshaling the arguments, the particular set of rules and
conventions according to which data is marshaled, is not explicitly called out.
Architecturally speaking, it is only the interface proxy for a given interface an
d its corresponding interface stub that cares at all about what set of marshalin
g rules is in fact used. However, in the general case these interface proxies an
d stubs may be installed on different machines with a network in the middle, be w
ritten by different development organizations on different operating systems, etc
. Accordingly, in cases where the author of an interface proxy for a given IID c
annot guarantee that all copies of the corresponding interface stub are in fact
always revised and updated in synchrony with his interface proxy, a well-defined
convention should be used for the transfer syntax. Indeed, formal transfer synta
x standards exist for this purpose. The one most commonly used is known as Networ
k Data Representation (NDR), originally developed by Apollo Corporation and subse
quently enhanced and adopted by the Open Software Foundation as part of their Dis
tributed Computing Environment (DCE).
When NDR transfer syntax is used (and whether it is in use or not is implicitly
known by the proxy or stub), the member dataRepresentation provides further info
rmation about the rules by which data in the buffer is marshaled. NDR is a multi-
canonical standard, meaning that rather than adopting one standard for things like
byte-order, character set, etc., multiple standards (a fixed set of them) are a
ccommodated. Specifically, this is accommodated by a reader make right policy: the
writer / marshaler of the data is free to write the data in any of the supporte
d variations and the reader / unmarshaler is expected to be able to read any of
them. The particular data type in use is conveyed in an RPCOLEDATAREP structure,
which is defined as follows. Note that this structure, too, is packed; the size
of the entire structure is exactly four bytes. The actual layout of the structur
e in all cases always corresponds to the data representation value as defined in
the DCE standard; the particular structure shown here is equivalent to that layou
t in Microsoft s and other common compilers.
typedef RPCOLEDATAREP {
UINT uCharacterRep : 4; // least signficant nibb
le of first byte
UINT uByteOrder : 4; // most signfica
nt nibble of first byte
BYTE uFloatRep;
BYTE uReserved;
BYTE uReserved2;
} RPCOLEDATAREP;
The values which may legally be found in these fields are as shown in Table . Fu
rther information on the interpretation of this field can be found in the NDR Tr
ansfer Syntax standards documentation.
Field Name Meaning of Field Value in field Interpretation
uCharacterRep determines interpretation of single-byte-character valued and si
ngle-byte-string valued entities 0
1 ASCII
EBCDIC
uByteOrder integer and floating point byte order 0
1 Big-endian (Motorola)
Little-endian (Intel)
uFloatRep representation of floating point numbers 0
1
2
3 IEEE
VAX
Cray
IBM
Table . Interpretation of dataPresentation
9.7.3.2 IRpcChannelBuffer::GetBuffer
HRESULT IRpcChannelBuffer::GetBuffer(pMessage, iid)
This method returns a buffer into which data can be marshaled for subsequent tra
nsmission over the wire. It is used both by interface proxies and by interface s
tubs, the former to marshal the incoming arguments for transmission to the serve
r, and the latter to marshal the return values back to the client.
Upon receipt of an incoming call from the client of the proxy object, interface
proxies use GetBuffer() to get a buffer into which they can marshaling the incom
ing arguments. A new buffer must be obtained for every call operation; old buffe
rs cannot be reused by the interface proxy. The proxy needs to ask for and corre
ctly manage a new buffer even if he himself does not have arguments to marshal (
i.e.: a void argument list). Having marshaled the arguments, the interface proxy
then calls SendReceive() to actually invoke the operation. Upon return from Sen
dReceive(), the buffer no longer contains the marshaled arguments but instead co
ntains the marshaled return values (and out parameter values). The interface pro
xy unmarshals these values, calls FreeBuffer() to free the buffer, then returns
to its calling client.
On the server side (in interface stubs), the sequence is somewhat different. The
server side will not be explored further here; see instead the description of I
RpcStubBuffer::Invoke() for details.
On the client side, the RPCOLEMESSAGE structure argument to GetBuffer() has been
allocated and initialized by the caller (or by some other party on the caller s beh
alf). Interface proxies are to initialize the members of this structure as follow
s.
Member Name Value to initalize to
reserved members as always, reserved values must be initialized to zero /
NULL.
pvBuffer must be NULL.
cbBuffer the size in bytes that the channel should allocate for the buffe
r; that is, the maximum size in bytes needed to marshal the arguments. The inter
face proxy will have determined this information by considering the function sig
nature and the particular argument values passed in.
It is explicitly legal to have this value be zero, indicating that that the call
er does not himself require a memory buffer.
iMethod the zero-based method number in the interface iid which is being invoked
dataRepresentation if NDR transfer syntax is being used, then this indicate
s the byte order, etc., by which the caller will marshal data into the returned
buffer.
rpcFlags ¨ Exact values to be listed here.
If the GetBuffer() function is successful, then upon function exit pvBuffer will
have been changed by the channel to point to a memory buffer of (at least) cbBu
ffer bytes in size into which the method arguments can now be marshaled (if cbBu
ffer was zero, pvBuffer may or may not be NULL). The reserved fields in the RPCO
LEMESSAGE structure may or may not have been changed by the channel. However, ne
ither the cbBuffer nor iMethod fields of RPCOLEMESSAGE will have been changed; t
he channel treats these as read-only. Furthermore, until such time as the now-al
located memory buffer is subsequently freed (see SendReceive() and FreeBuffer())
, no party other than the channel may modify any of the data accessible from pMe
ssage with the lone exceptions of the data pointed to by pvBuffer and the member
cbBuffer, which may be modified only in limited ways; see below.
The arguments to GetBuffer() are as follows:
Argument Type Description
pMessage RPCOLEMESSAGE * a message structure initialized as discussed abo
ve.
iid REFIID the interface identifier of the interface being invoked.
return value HRESULT S_OK, E_OUTOFMEMORY, E_UNEXPECTED
9.7.3.3 IRpcChannelBuffer::SendReceive
HRESULT IRpcChannelBuffer::SendReceive(pMessage, pStatus)
Cause an invocation to be sent across to the server process. The caller will hav
e first obtained access to a transmission packet in which to marshal the argumen
ts by calling IRpcChannelBuffer::GetBuffer(). The same pMessage structure passed
as an argument into that function is passed here to the channel a second time.
In the intervening time period, the method arguments will have been marshaled in
to the buffer pointed to by pMessage->pvBuffer. However, the pvBuffer pointer pa
rameter must on entry to SendReceive() be exactly as it was when returned from G
etBuffer(). That is, it must point to the start of the memory buffer. The caller
should in addition set pMessage->cbBuffer to the number of bytes actually writt
en into the buffer (zero is explicitly a legal value). No other values accessibl
e from pMessage may be different than they were on exit from GetBuffer().
Upon successful exit from SendReceive(), the incoming buffer pointed to by pvBuf
fer will have been freed by the channel. In its place will be found a buffer con
taining the marshaled return values / out parameters from the interface stub: pM
essage->pvBuffer points to the new buffer, and pMessage->cbBuffer indicates the
size thereof. If there are no such return values, then pMessage->cbBuffer is set
to zero, while pMessage?>pvBuffer may or may not be NULL.
On error exit from SendReceive(), the incoming buffer pointed to by pvBuffer may
or may not have been freed. If it has been freed, then on error exit pMessage?>p
vBuffer is set to NULL and pMessage->cbBuffer is set to zero. If in contrast, pM
essage->pvBuffer is on error exit not NULL, then that pointer, the data to which
it points, and the value pMessage->cbBuffer will contain exactly as they did on
entry; that is, the marshaled arguments will not have been touched. Thus, on err
or exit from SendReceive(), in no case are any marshaled return values passed ba
ck; if a marshaling buffer is in fact returned, then it contains the marshaled a
rguments as they were on entry.
The exact cases on error exit when the incoming buffer has or has not been freed
needs careful attention. There are three cases:
1) The channel implementation knows with certainty either that all of the i
ncoming data was successfully unmarshaled or that if any errors occurred during u
nmarshaling that the interface stub correctly cleaned up. In practical terms, thi
s condition is equivalent to the stub manager having actually called IRpcStubBuf
fer::Invoke() on the appropriate interface stub.
In this case, on exit from SendReceive() the incoming arguments will alwa
ys have been freed.
2) The channel implementation knows with certainty the situation in case 1)
has not occurred.
In this case, on exit from SendReceive(), the incoming arguments will ne
ver have been freed.
3) The channel implementation does not know with certainty that either of t
he above two cases has occurred.
In this case, on exit from SendReceive(), the incoming arguments will al
ways have been freed. This is a possible resource leakage (due to, for example,
CoReleaseMarshalData() calls that never get made), but it safely avoids freeing
resources that should not be freed.
If pMessage->pvBuffer is returned as non-NULL, then the caller is responsible fo
r subsequently freeing it; see FreeBuffer(). A returned non-NULL pMessage->pvBuf
fer may in general legally be (and will commonly be, the success case) different
than the (non-NULL) value on entry; i.e.: the buffer may be legally be reallocate
d. Further, between the return from SendReceive() and the subsequent freeing cal
l no data accessible from pMessage may be modified, with the possible exception o
f the data actually in the memory buffer.
Upon successful exit from SendReceive(), the pMessage->dataRepresentation field
will have been modified to contain whatever was returned by the interface stub i
n field of the same name value on exit to IRpcStubBuffer::Invoke(). This is partic
ularly important when NDR transfer syntax is used, as dataRepresentation indicat
es critical things (such as byte order) which apply to the marshaled return / ou
t values. Upon error exit from SendReceive(), pMessage?>dataRepresentation is und
efined.
Argument Type Description
pMessage RPCOLEMESSAGE * message structure containing info to transmit to
server.
pStatus ULONG * may legally be NULL. If non-NULL, then if either 1) an RPC-infras
tructure-detected server-object fault (e.g.: a server object bug caused an except
ion which was caught by the RPC infrastructure) or 2) an RPC communications fail
ure occurs, then at this location a status code is written which describes what
happened. In the two error cases, the errors E_RPCFAULT and E_RPCSTATUS are (res
pectively) returned (and are always returned when these errors occur, irrespecti
ve of the NULL-ness of pStatus).
return value HRESULT S_OK, E_RPCFAULT, E_RPCSTATUS
9.7.3.4 IRpcChannelBuffer::FreeBuffer
HRESULT IRpcChannelBuffer::FreeBuffer(pMessage)
Free a memory buffer in pMessage->pvBuffer that was previously allocated by the
channel.
At various times the RPC channel allocates a memory buffer and returns control o
f same to a calling client. Both GetBuffer() and SendReceive() do so, for exampl
e. FreeBuffer() is the means by which said calling client informs the channel th
at it is done with the buffer.
On function entry, the buffer which is to be freed is pMessage->pvBuffer, which
explicitly may or may not be NULL. If pMessage->pvBuffer is non-NULL, then FreeB
uffer() frees the buffer, NULLs the pointer, and returns NOERROR; if pMessage->p
vBuffer is NULL, then FreeBuffer() simply returns NOERROR (i.e.: passing NULL is
not an error). Thus, on function exit, pMessage->pvBuffer is always NULL. Notic
e that pMessage?>cbBuffer is never looked at or changed.
There are strict rules as to what data accessible from pMessage may have been mo
dified in the intervening time between the time the buffer was allocated and the
call to FreeBuffer(). In short, very little modification is permitted; see abov
e and below for precise details.
Argument Type Description
pMessage RPCOLEMESSAGE * pointer to structure containing pointer to buffe
r to free.
return value HRESULT S_OK, E_UNEXPECTED
9.7.3.5 IRpcChannelBuffer::GetDestCtx
HRESULT IRpcChannelBuffer::GetDestCtx(pdwDestCtx, ppvDestCtx)
Return the destination context for this RPC channel. The destination context her
e is as specified in the description of the IMarshal interface.
Argument Type Description
pdwDestCtx DWORD * the place at which the destination context is to be retu
rned.
ppvDestCtx void ** May be NULL. If non-NULL, then this is the place at whic
h auxiliary information associated with certain destination contexts will be ret
urned. Interface proxies may not hold on to this returned pointer in their inter
nal state; rather, they must assume that a subsequent call to IRpcChannel::Call(
) may in fact invalidate a previously returned destination context.
return value HRESULT S_OK, E_OUTOFMEMORY, E_UNEXPECTED, but no others.
9.7.3.6 IRpcChannelBuffer::IsConnected
HRESULT IRpcChannelBuffer::IsConnected()
Answers as to whether the RPC channel is still connected to the other side. A ne
gative reply is definitive: the connection to server end has definitely been ter
minated. A positive reply is tentative: the server end may or may not be still u
p. Interface proxies can if they wish use this method as an optimization by whic
h they can quickly return an error condition.
Argument Type Description
return value HRESULT S_OK, S_FALSE. No error values may be returned.
9.7.4 IRpcProxyBuffer
IRpcProxyBuffer interface is the interface by which the client-side infrastructu
re (i.e. the proxy manager) talks to the interface proxy instances that it manag
es. When created, proxies are aggregated into some larger object as per the norm
al creation process (where pUnkOuter in IPSFactoryBuffer::CreateProxy() is non-N
ULL). The controlling unknown will then QueryInterface() to the interface that i
t wishes to expose from the interface proxy.
interface IRpcProxyBuffer : IUnknown {
virtual HRESULT Connect(pRpcChannelBuffer) = 0;
virtual void Disconnect() = 0;
};
9.7.4.1 IRpcProxyBuffer::Connect
HRESULT IRpcProxyBuffer::Connect(pRpcChannelBuffer)
Connect the interface proxy to the indicated RPC channel. The proxy should hold
on to the channel, AddRef()ing it as per the usual rules. If the proxy is curren
tly connected, then this call fails (with E_UNEXPECTED); call Disconnect() first i
f in doubt.
Argument Type Description
pRpcChannelBuffer IRpcChannelBuffer* the RPC channel that the interfa
ce proxy is to use to effect invocations to the server object. May not be NULL.
return value HRESULT S_OK, E_OUTOFMEMORY, E_NOINTERFACE, E_UNEXPECTED
9.7.4.2 IRpcProxyBuffer::Disconnect
void IRpcProxyBuffer::Disconnect()
Informs the proxy that it should disconnect itself from any RPC channel that it
may currently be holding on to. This will involve Release()ing the IRpcChannel p
ointer to counteract the AddRef() done in IRpcProxy::Connect().
Notice that this function does not return a value.
9.7.5 IRpcStubBuffer
IRpcStubBuffer is the interface used on the server side by the RPC runtime infra
structure (herein referred to loosely as the channel ) to communicate with interfac
e stubs that it dynamically loads into a server process.
interface IRpcStubBuffer : IUnknown {
virtual HRESULT Connect(pUnkServer) = 0;
virtual void Disconnect() = 0;
virtual HRESULT Invoke(pMessage, pChannel) = 0;
virtual IRpcStubBuffer* IsIIDSupported(iid) = 0;
virtual ULONG CountRefs() = 0;
virtual HRESULT DebugServerQueryInterface(ppv) = 0;
virtual void DebugServerRelease(pv) = 0;
};
9.7.5.1 IRpcStubBuffer::Connect
HRESULT IRpcStubBuffer::Connect(pUnkServer)
Informs the interface stub of server object to which it is now to be connected,
and to which it should forward all subsequent Invoke() operations. The stub will
have to QueryInterface() on pUnkServer to obtain access to appropriate interface
s. The stub will of course follow the normal AddRef() rules when it stores point
ers to the server object in its internal state.
If the stub is currently connected, then this call fails with E_UNEXPECTED.
Argument Type Description
pUnkServer IUnknown * the new server object to which this stub is now
to be connected.
return value HRESULT S_OK, E_OUTOFMEMORY, E_NOINTERFACE, E_UNEXPECTED
9.7.5.2 IRpcStubBuffer::Disconnect
void IRpcStubBuffer::Disconnect()
Informs the stub that it should disconnect itself from any server object that it
may currently be holding on to. Notice that this function does not return a val
ue.
9.7.5.3 IRpcStubBuffer::Invoke
HRESULT IRpcStubBuffer::Invoke(pMessage, pChannel)
Invoke the pMessage->iMethod th method in the server object interface instance to
which this interface stub is currently connected. The RPC runtime infrastructure
(the channel ) calls this method on the appropriate interface stub upon receipt of
an incoming request from some remote client. See the discussion on page regardi
ng how interface stubs implicitly know the IID which they are servicing.
On entry, the members of pMessage are set as follows:
Member Name Value on entry to Invoke()
reserved members indeterminate. These members are neither to be read nor
to be changed by the stub.
pvBuffer points to a buffer which contains the marshaled incoming argumen
ts. In the case that there are no such arguments (i.e.: cbBuffer == 0), pvBuffer
may be NULL, but will not necessarily be so.
cbBuffer the size in bytes of the memory buffer to which pvBuffer points.
If pvBuffer is NULL, then cbBuffer will be zero (but the converse is not necess
arily true, as was mentioned in pvBuffer).
iMethod the zero-based method number in the interface which is being invoked
dataRepresentation if NDR transfer syntax is being used, then this indicate
s the byte order, etc., according to which the data in pvBuffer has been marshal
ed.
rpcFlags indeterminate. Neither to be read nor to be changed by the stub.
The stub is to do the following:
· unmarshal the incoming arguments,
· invoke the designated operation in the server object,
· ask the channel to allocate a new buffer for the return values and out values,
· marshal the return values and out values into the buffer, then
· return successfully (i.e.: NOERROR) from Invoke().
Errors may of course occur at various places in this process. Such errors will c
ause the stub to return an error from Invoke() rather than NOERROR. In cases whe
re such an error code is returned, it is the stub s responsibility to have cleaned
up any data and other resources allocated by the unmarshaling and marshaling pr
ocesses or returned as out values from the server object. However, the stub is n
ot responsible for invoking FreeBuffer() to free the actual marshaling buffer (i
.e.: it is illegal for the stub to do so); rather, on error return from Invoke()
the caller of Invoke() will ignore pvBuffer, and will also free it if non-NULL.
Having made that general statement as to the exit conditions of Invoke(), let us
examine its operation in greater detail.
If the stub cannot deal with the indicated dataRepresentation, it is to return R
PC_E_SERVER_INVALIDDATAREP. If it understands the data representation, the stub is t
o then unmarshal the arguments from the buffer provided in pMessage->pvBuffer, t
he size of which is passed in pMessage->cbBuffer. If the argument data cannot be
completely unmarshaled, the server is to free any partially unmarshaled data, t
hen return RPC_E_SERVER_CANTUNMARSHALDATA from Invoke().
If the data is successfully completely unmarshaled, then the interface stub is t
o invoke the designated method in the designated interface on the server object.
Notice that the incoming pvBuffer memory buffer is at this time still valid, an
d that therefore the stub may if it wishes and if appropriate for the argument a
nd data representations in question pass to the server object pointers which poi
nt directly into this buffer. The memory allocation and data copying that is thu
s avoided can at times be a significant performance optimization.
Once the invocation of the server object returns, the stub is to marshal the ret
urn value and out parameters returned from the server back to the client. It doe
s so irrespective of whether the server object invocation returned an error or s
uccess code; that is, the stub marshals back to the client whatever the server o
bject returned. The stub gets a reply buffer into which to do this marshaling by
calling pChannel->GetBuffer(), passing in the pMessage structure that it receiv
ed in Invoke(). Before calling GetBuffer(), the stub is to set the cbBuffer memb
er to the size that it requires for the to-be-allocated reply buffer. Zero is ex
plicitly a legal value for cbBuffer, and the stub must always call GetBuffer() (
more precisely, to be clear about the error case: the stub must always call GetB
uffer() if the server object method has actually been invoked) to allocate a rep
ly buffer, even if the stub itself does not require one (such as would be the ca
se if for a void-returning function with no out parameters). The stub must also
set dataRepresentation as appropriate for the standard by which it intends to ma
rshal the returning values (or would marshal them if there were some). Aside fro
m cbBuffer, dataRepresentation and possibly the contents of the bytes inside the
memory buffer, on entry to GetBuffer() no other data accessible from pMessage m
ay be different than they were on entry to Invoke().
Before it allocates a reply buffer, the call to GetBuffer() has the side effect
of freeing the memory buffer to which pvBuffer presently points. Thus, the act b
y the interface stub of allocating a reply buffer for the return values necessar
ily terminates access by the stub to the incoming marshaled arguments.
If GetBuffer() successfully allocates a reply buffer (see GetBuffer() for a desc
ription of how the stub determines this), then the stub is to marshal the return
value and returned out parameters into the buffer according to the rules of the
transfer syntax. Once this is complete, the stub is to set the cbBuffer member t
o the number of bytes it actually marshaled (if it marshaled nothing, then it mu
st explicitly set this to zero (but see also GetBuffer())), and then return NOER
ROR from Invoke().
If an error occurs during the unmarshaling of the incoming arguments or the mars
haling of the return values, then the interface stub is responsible for correctl
y freeing any resources consumed by the marshaled data. See in particular CoRele
aseMarshalData(). See also the discussion of this topic in IRpcChannelBuffer::SendRe
cieve().
Argument Type Description
pMessage RPCOLEMESSAGE * channel-allocated message structure.
pChannel IRpcChannelBuffer * the channel to use for buffer management
, etc.
return value HRESULT S_OK, RPC_E_SERVER_INVALIDDATAREP, RPC_E_SERVER_CANTUNMARSH
A, RPC_E_SERVER_CANTMARSHALDATA
9.7.5.4 IRpcStubBuffer::IsIIDSupported
IRpcStubBuffer* IRpcStubBuffer::IsIIDSupported(iid)
Answer whether this stub is designed to handle the unmarshaling of the indicated
interface.
If the stub buffer supports the specified IID, then it should return an appropri
ate IRpcStubBuffer* for that interface. Otherwise, the it should return NULL.
When presented with the need to remote a new IID on a given object, the RPC runt
ime typically calls this function on all the presently-connected interface stubs
in an attempt to locate one that can handle the marshaling for the request befo
re it goes to the trouble of creating a new stub.
As in IPSFactoryBuffer::CreateStub(), if this stub is presently connected to a s
erver object, then not only must this function verify that the stub can handle t
he requested interface id, but it must also verify (using QueryInterface()) that
the connected server object in fact supports the indicated interface (depending
on the IID and previous interface servicing requests, it may have already done s
o).
A common special case is the following: interface stubs which are designed to on
ly support one interface id (as most are designed to do) can simply check if iid
designates the one interface that they handle. If not, return false. Otherwise,
then if connected check that the server object supports the interface. Otherwis
e return true.
Argument Type Description
iid REFIID the interface that the caller wishes to know if the stub can han
dle. iid is never to be IID_IUnknown.
return value IRpcStubBuffer* see above.
9.7.5.5 IRpcStubBuffer::CountRefs
ULONG IRpcStub::CountRefs()
Return the total number of references that this stub interface instance has on t
he server object.
Argument Type Description
return value ULONG the number of such references.
9.7.5.6 IRpcStubBuffer::DebugServerQueryInterface
HRESULT IRpcStubBuffer::DebugServerQueryInterface(ppv)
This function exists in order to facilitate the support of debuggers which wish
to provide transparency when single-stepping, etc., across remote invocations on
objects. As such, the semantics of this function are a little strange in order
to avoid the unnecessarily disturbing the state of the actual server object.
If the stub is not presently connected then set *ppv to NULL (per the usual erro
r-case convention) and return E_UNEXPECTED. If connected but this stub does not s
upport the indicated interface (in the sense expressed in IsIIDSupported()), the
n (set *ppv to NULL and) return E_NOINTERFACE instead.
Otherwise, return the interface pointer on the connected server object which wou
ld be used by an immediate subsequent invocation of Invoke() on this interface s
tub (see the discussion on page regarding how interface stubs implicitly know t
he IID which they are servicing). DebugServerQueryInterface() is analogous to invokin
QueryInterface() on the server itself with the important difference that the ca
ller will later call DebugServerRelease() to indicate that he is done with the p
ointer instead of releasing the returned pointer himself. It is required that De
bugServerRelease() be called before the interface stub itself is destroyed or, i
n fact, before it is disconnected.
In the vast majority of interface stub implementations, DebugServerQueryInterfac
e() can therefore be implemented simply by returning an internal state variable i
nside the interface stub itself without doing an AddRef() on the server or other
wise running any code in the actual server object. In such implementations, DebugS
erverRelease() will be a completely empty no-op. The other rational implementati
on is one where DebugServerQueryInterface() does a QueryInterface() on the server o
bject and DebugServerRelease() does a corresponding Release(), but as this actua
lly runs server code, the former implementation is highly preferred if at all ac
hievable.
Argument Type Description
ppv void** the place at which the interface pointer is to be returned.
return value HRESULT S_OK, E_NOINTERFACE, E_UNEXPECTED
9.7.5.7 IRpcStubBuffer::DebugServerRelease
void IRpcStubBuffer::DebugServerRelease(pv)
Indicate that an interface pointer returned previously from DebugServerQueryInte
rface() is no longer needed by the caller. In most implementations, DebugServerR
elease() is a completely empty no-op; see the description of DebugServerQueryInt
erface() for details.
9.7.6 IStdMarshalInfo
The IStdMarshalInfo interface returns the CLSID identifying the handler to be us
ed in the destination process during standard marshaling.
An object that uses COM s default implementation of IMarshal does not provide its
own proxy but, by implementing IStdMarshalInfo, can nevertheless specify a handl
er to be loaded in the client process. Such a handler would typically handle cer
tain requests in-process and use COM s default marshaling to delegate others back
to the original object.
To create an instance of an object in some client process, COM must first determ
ine whether the object uses default marshaling or its own implementation. If the
object uses default marshaling, COM then queries the object to determine whethe
r it uses a special handler or, simply, COM s default proxy. To get the CLSID of t
he handler to be loaded, COM queries the object for the IStdMarshalInfo interfac
e and then the IPersist interface. If neither interface is supported, a standard
handler is used.
9.7.6.1.1 When to Implement
If you are writing a server application that supports class emulation (that is,
if your server can manipulate objects of another type in response to the Activat
e As option in the Convert dialog box), you must implement the IStdMarshalInfo i
nterface in order to return the CLSID of the handler to be used for the object.
Note that your handler must aggregate the default handler.
9.7.6.1.2 When to Use
You typically don t call this interface yourself. COM queries for this interface w
hen performing standard marshaling.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IStdMarshalInfo Method Description
GetClassForHandler Obtains the class identifier of the object handler in th
e destination process.
See Also
IMarshal
9.7.6.2 IStdMarshalInfo::GetClassForHandler
Retrieves the CLSID of the object handler to be used in the destination process
during standard marshaling.
HRESULT GetClassForHandler(

DWORD dwDestContext, //Destination process


void * pvDestContext, //Reserved
CLSID * pClsid //Pointer to the CLSID
);
Parameters
dwDestContext
[in] Destination context, that is, the process in which the unmarshaling will be
done. The legal values for dwDestContext are taken from the enumeration MSHCTX.
For information on the MSHCTX enumeration, see the Data Structures section.
pvDestContext
[in] Reserved for future use; must be NULL.
pClsid
[out] Pointer to the handler s CLSID.
Return Values
This method supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and
E_UNEXPECTED, as well as the following:
S_OK
The CLSID was retrieved successfully.
Remarks
9.7.6.2.1.1.1 Notes to Implementers
Your implementation of IStdMarshalInfo::GetClassForHandler must return your own
CLSID. This allows an object created by a different server to behave as one your
server created.
9.8 Interface Remoting and Marshaling API Descriptions
9.8.2 CoGetMarshalSizeMax
Returns an upper bound on the number of bytes needed to marshal the specified in
terface pointer to the specified object.
STDAPI CoGetMarshalSizeMax(
ULONG *pulSize, //Pointer to the upper-bound value
REFIID riid, //Reference to the identifier of the interface
IUnknown * pUnk, //Pointer to the interface to be marshaled
DWORD dwDestContext, //Destination process
LPVOID pvDestContext, //Reserved for future use
DWORD mshlflags //Reason for marshaling
);
Parameters
pulSize
[out] Pointer to the upper-bound value on the size, in bytes, of the data packet
to be written to the marshaling stream; a value of zero means that the size of
the packet is unknown.
riid
[in] Reference to the identifier of the interface whose pointer is to be marshal
ed. This interface must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled. This interface must be derived fr
om the IUnknown interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Val
ues for dwDestContext come from the enumeration MSHCTX.
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back
to the client process¾the normal case¾or written to a global table, where it can be
retrieved by multiple clients. Values come from the enumeration MSHLFLAGS.
Return Values
This function supports the standard return value E_UNEXPECTED, as well as the fo
llowing:
S_OK
The upper bound was returned successfully.
CO_E_NOTINITIALIZED
The CoInitialize function was not called on the current thread before this funct
ion was called.
Remarks
This function performs the following tasks:
1. Queries the object for an IMarshal pointer or, if the object doe
s not implement IMarshal, gets a pointer to COM s standard marshaler.
2. Using whichever pointer is obtained in the preceding step, calls
IMarshal::GetMarshalSizeMax.
3. Adds to the value returned by the call to GetMarshalSizeMax the
size of the marshaling data header and, possibly, that of the proxy CLSID to obt
ain the maximum size in bytes of the amount of data to be written to the marshal
ing stream.
You do not explicitly call this function unless you are implementing IMarshal, i
n which case your marshaling stub should call this function to get the correct s
ize of the data packet to be marshaled.
The value returned by this method is guaranteed to be valid only as long as the
internal state of the object being marshaled does not change. Therefore, the act
ual marshaling should be done immediately after this function returns, or the st
ub runs the risk that the object, because of some change in state, might require
more memory to marshal than it originally indicated.
See Also
CoMarshalInterface, IMarshal::GetMarshalSizeMax
9.8.3 CoGetStandardMarshal
Creates a default, or standard, marshaling object in either the client process o
r the server process, depending on the caller, and returns a pointer to that obj
ect s IMarshal implementation.
STDAPI CoGetStandardMarshal(
REFIID riid, //Reference to the identifier of the interface
IUnknown * pUnk, //Pointer to the interface to be marshaled
DWORD dwDestContext, //Destination process
LPVOID pvDestContext, //Reserved for future use
DWORD mshlflags, //Reason for marshaling
LPMARSHAL * ppMarshal // Address of the output variable that receives
// the IMarshal interface pointer for the
// standard marshaler
);
Parameters
riid
[in] Reference to the identifier of the interface whose pointer is to be marshal
ed. This interface must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Val
ues for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling
can occur either in another apartment of the current process (MSHCTX_INPROC) or
in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag indicating whether the data to be marshaled is to be transmitted back
to the client process¾the normal case¾or written to a global table, where it can be
retrieved by multiple clients. Valid values come from the MSHLFLAGS enumeration.
ppMarshal
[out] IndirectAddress of IMarshal* pointer variable that receives the interface
pointer to the standard marshaler.
Return Values
This function supports the standard return values E_FAIL, E_OUTOFMEMORY and E_UN
EXPECTED, as well as the following:
S_OK
The IMarshal instance was returned successfully.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitializefunction was not called on the current thread b
efore this function was called.
Remarks
The CoGetStandardMarshal function creates a default, or standard, marshaling obj
ect in either the client process or the server process, as may be necessary, and
returns that object s IMarshal pointer to the caller. If you implement IMarshal,
you may want your implementation to call CoGetStandardMarshal as a way of delega
ting to COM s default implementation any destination contexts that you don t fully u
nderstand or want to handle. Otherwise, you can ignore this function, which COM
calls as part of its internal marshaling procedures.
When the COM library in the client process receives a marshaled interface pointe
r, it looks for a CLSID to be used in creating a proxy for the purposes of unmar
shaling the packet. If the packet does not contain a CLSID for the proxy, COM ca
lls CoGetStandardMarshal, passing a NULL pUnk value. This function creates a sta
ndard proxy in the client process and returns a pointer to that proxy s implementa
tion of IMarshal. COM uses this pointer to call CoUnmarshalInterface to retrieve
the pointer to the requested interface.
If your COM server application s implementation of IMarshal calls CoGetStandardMar
shal, you should pass both the IID of (riid), and a pointer to (pUnk), the inter
face being requested.
This function performs the following tasks:
1. Determines whether pUnk is NULL.
2. If pUnk is NULL, creates a standard interface proxy in the clien
t process for the specified riid and returns the proxy s IMarshal pointer.
3. If pUnk is not NULL, checks to see if a marshaler for the object
already exists, creates a new one if necessary, and returns the marshaler s IMars
hal pointer.
See Also
IMarshal
9.8.4 CoGetPSClsid
This function returns the CLSID of the DLL that implements the proxy and stub fo
r the specified interface.
WINOLEAPI CoGetPSClsid(
REFIID riid, // Interface whose proxy/stub CLSID is to be returned
CLSID *pclsid // Where to store returned proxy/stub CLSID
);
Parameters
riid
[in] The interface whose proxy/stub CLSID is to be returned.
pclsid
[out] Where to store the proxy/stub CLSID for the interface specified by riid.
Return Values
S_OK
The proxy/stub CLSID was successfully returned.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.
Remarks
The CoGetPSClsid function looks at the HKEY_CLASSES_ROOT\Interfaces\{ string for
m of riid }\ProxyStubClsid32 key in the registry to determine the CLSID of the D
LL to load in order to create the proxy and stub for the interface specified by
riid. This function also returns the CLSID for any interface IID registered by C
oRegisterPSClsid within the current process.
See Also
CoRegisterPSClsid
9.8.5 CoMarshalHresult
Marshals an HRESULT to the specified stream, from which it can be unmarshaled us
ing the CoUnmarshalHresult function.
STDAPI CoMarshalHresult(
IStream * pStm, //Pointer to the marshaling stream
HRESULT hresult //HRESULT to be marshaled
);
Parameters
pStm
[in] Pointer to the marshaling stream.
hresult
[in] HRESULT in the originating process.
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
The HRESULT was marshaled successfully.
STG_E_INVALIDPOINTER
Bad pointer passed in for pStm.
STG_E_MEDIUMFULL
The medium is full.
Remarks
An HRESULT is process-specific, so an HRESULT that is valid in one process might
not be valid in another. If you are writing your own implementation of IMarshal
and need to marshal an HRESULT from one process to another, either as a paramet
er or a return code, you must call this function. In other circumstances, you wi
ll have no need to call this function.
This function perfoms the following tasks:
1. Writes an HRESULT to a stream.
2. Returns an IStream pointer to that stream.
See Also
CoUnmarshalHresult, IStream
9.8.6 CoMarshalInterface
Writes into a stream the data required to initialize a proxy object in some clie
nt process. The COM library in the client process calls the CoUnmarshalInterface
function to extract the data and initialize the proxy. CoMarshalInterface can m
arshal only interfaces derived from IUnknown.
STDAPI CoMarshalInterface(
IStream *pStm, //Pointer to the stream used for marshaling
REFIID riid, //Reference to the identifier of the interface
IUnknown *pUnk, //Pointer to the interface to be marshaled
DWORD dwDestContext, //Destination context
void *pvDestContext, //Reserved for future use
DWORD mshlflags //Reason for marshaling
);
Parameters
pStm
[in] Pointer to the stream to be used during marshaling.
riid
[in] Reference to the identifier of the interface to be marshaled. This interfac
e must be derived from the IUnknown interface.
pUnk
[in] Pointer to the interface to be marshaled; can be NULL if the caller does no
t have a pointer to the desired interface. This interface must be derived from t
he IUnknown interface.
dwDestContext
[in] Destination context where the specified interface is to be unmarshaled. Val
ues for dwDestContext come from the enumeration MSHCTX. Currently, unmarshaling
can occur either in another apartment of the current process (MSHCTX_INPROC) or
in another process on the same computer as the current process (MSHCTX_LOCAL).
pvDestContext
[in] Reserved for future use; must be NULL.
mshlflags
[in] Flag specifying whether the data to be marshaled is to be transmitted back
to the client process¾the normal case¾or written to a global table, where it can be
retrieved by multiple clients. Values come from the MSHLFLAGS enumeration.
Return Values
This function supports the standard return values E_FAIL, E_OUTOFMEMORY, and E_U
NEXPECTED, as well as the following:
S_OK
The interface pointer was marshaled successfully.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread
before this function was called.
IStream errors
This function can also return any of the stream-access error values returned by
the IStream interface.
Remarks
The CoMarshalInterface function marshals the interface referred to by riid on th
e object whose IUnknown implementation is pointed to by pUnk. To do so, the CoMa
rshalInterface function performs the following tasks:
1. Queries the object for a pointer to the IMarshal interface. If t
he object does not implement IMarshal, meaning that it relies on COM to provide
marshaling support, CoMarshalInterface gets a pointer to COM s default implementat
ion of IMarshal.
2. Gets the CLSID of the object s proxy by calling IMarshal::GetUnmar
shalClass, using whichever IMarshal interface pointer has been returned.
3. Writes the CLSID of the proxy to the stream to be used for marsh
aling.
4. Marshals the interface pointer by calling IMarshal::MarshalInter
face.
If you are implementing existing COM interfaces or defining your own interfaces
using the Microsoft Interface Definition Language (MIDL), the MIDL-generated pro
xies and stubs call CoMarshalInterface for you. If you are writing your own prox
ies and stubs, your proxy code and stub code should each call CoMarshalInterface
to correctly marshal interface pointers. Calling IMarshal directly from your pr
oxy and stub code is not recommended.
If you are writing your own implementation of IMarshal, and your proxy needs acc
ess to a private object, you can include an interface pointer to that object as
part of the data you write to the stream. In such situations, if you want to use
COM s default marshaling implementation when passing the interface pointer, you c
an call CoMarshalInterface on the object to do so.
See Also
CoUnmarshalInterface, IMarshal::MarshalInterface
9.8.7 CoRegisterPSClsid
Enables a downloaded DLL to register its custom interfaces within its running pr
ocess so that the marshaling code will be able to marshal those interfaces.
WINOLEAPI CoRegisterPSCLsid(
REFIID riid, //Custom interface to be registered
REFCLSID rclsid //DLL containing the proxy/stub code for riid
);
Parameters
riid
[in] Points to the IID of the interface to be registered.
rclsid
[in] Points to the CLSID of the DLL that contains the proxy/stub code for the cu
stom interface specified by riid.
Return Values
S_OK
The custom interface was successfully registered.
E_INVALIDARG
One of the parameters is invalid.
E_OUTOFMEMORY
There is insufficient memory to complete this operation.
Remarks
Normally the code responsible for marshaling an interface pointer into the curre
nt running process reads the HKEY_CLASSES_ROOT\Interfaces section of the registr
y to obtain the CLSID of the DLL containing the ProxyStub code to be loaded. To
obtain the ProxyStub CLSIDs for an existing interface, the code calls the CoGetP
SClsid function.
In some cases, however, it may be desirable or necessary for an in-process handl
er or in-process server to make its custom interfaces available without writing
to the registry. A DLL downloaded across a network may not even have permission
to access the local registry, and because the code originated on another machine
, the user, for security purposes, may want to run it in a restricted environmen
t. Or a DLL may have custom interfaces that it uses to talk to a remote server a
nd may also include the ProxyStub code for those interfaces. In such cases, a DL
L needs an alternative way to register its interfaces. CoRegisterPSClsid, used i
n conjunction with CoRegisterClassObject, provides that alternative.
A DLL would normally call CoRegisterPSClsid as shown in the following code fragm
ent:
HRESULT RegisterMyCustomInterface(DWORD *pdwRegistrationKey)
{
HRESULT hr = CoRegisterClassObject(CLSID_MyProxyStubClsid,
pIPSFactoryBuffer,
CLSCTX_INPROC_SERVER,
REGCLS_MULTIPLEUSE
pdwRegistrationKey);
if(SUCCEEDED)(hr))
{
hr = CoRegisterPSClsid(IID_MyCustomInterface, CLSID_MyProxyStubClsid);
}
return hr;
}
See Also
CoGetPSClsid, CoRegisterClassObject
9.8.8 CoReleaseMarshalData
Destroys a previously marshaled data packet.
STDAPI CoReleaseMarshalData(
IStream * pStm //Pointer to stream containing data packet
);
Parameter
pStm
[in] Pointer to the stream that contains the data packet to be destroyed.
Return Values
This function supports the standard return values E_FAIL, E_INVALIDARG,
E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:
S_OK
The data packet was successfully destroyed.
STG_E_INVALIDPOINTER
An IStream error dealing with the pStm parameter.
CO_E_NOTINITIALIZED
The CoInitialize or OleInitialize function was not called on the current thread
before this function was called.
Remarks
The CoReleaseMarshalData function performs the following tasks:
1. The function reads a CLSID from the stream.
2. If COM s default marshaling implementation is being used, the func
tion gets an IMarshal pointer to an instance of the standard unmarshaler. If cus
tom marshaling is being used, the function creates a proxy by calling the CoCrea
teInstance function, passing the CLSID it read from the stream, and requesings a
n IMarshal interface pointer to the newly created proxy.
3. Using whichever IMarshal interface pointer it has acquired, the
function calls IMarshal::ReleaseMarshalData.
You typically do not call this function. The only situation in which you might n
eed to call this function is if you use custom marshaling (write and use your ow
n implementation of IMarshal). Examples of when CoReleaseMarshalData should be c
alled include the following situations:
· An attempt was made to unmarshal the data packet, but it failed.
· A marshaled data packet was removed from a global table.
As an analogy, the data packet can be thought of as a reference to the original
object, just as if it were another interface pointer being held on the object. L
ike a real interface pointer, that data packet must be released at some point. T
he use of IMarshal::ReleaseMarshalData to release data packets is analogous to t
he use of IUnknown::Release to release interface pointers.
Note that you do not need to call CoReleaseMarshalData after a successful call o
f the CoUnmarshalInterface function; that function releases the marshal data as
part of the processing that it does.
See Also
IMarshal::ReleaseMarshalData
9.8.9 CoUnmarshalHresult
Unmarshals an HRESULT type from the specified stream.
STDAPI CoUnmarshalHresult(
LPSTREAM pStm, //Pointer to stream used for unmarshaling
HRESULT * phresult //Pointer to the HRESULT
);
Parameters
pStm
[in] Pointer to the stream from which the HRESULT is to be unmarshaled.
phresult
[out] Pointer to the unmarshaled HRESULT.
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
The HRESULT was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.
Remarks
You do not explicitly call this function unless you are performing custom marsha
ling (that is, writing your own implementation of IMarshal), and your implementa
tion needs to unmarshal an HRESULT.
You must use CoUnmarshalHresult to unmarshal HRESULTs previously marshaled by a
call to the CoMarshalHresult function.
This function performs the following tasks:
1. Reads an HRESULT from a stream.
2. Returns the HRESULT.
See Also
CoMarshalHresult, IStream
9.8.10 CoUnmarshalInterface
Initializes a newly created proxy using data written into the stream by a previo
us call to the CoMarshalInterface function, and returns an interface pointer to
that proxy.
STDAPI CoUnmarshalInterface(
IStream * pStm, //Pointer to the stream
REFIID riid, //Reference to the identifier of the interface
void ** ppv //Address of output variable that receives the
// interface pointer requested in riid
);
Parameters
pStm
[in] Pointer to the stream from which the interface is to be unmarshaled.
riid
[in] Reference to the identifier of the interface to be unmarshaled.
ppv
[out] Address of pointer variable that receives the interface pointer requested
in riid. Upon successful return, *ppv contains the requested interface pointer f
or the unmarshaled interface.
Return Values
This function supports the standard return value E_FAIL, as well as the followin
g:
S_OK
The interface pointer was unmarshaled successfully.
STG_E_INVALIDPOINTER
pStm is an invalid pointer.
CO_E_NOTINITIALIZED
The CoInitialize function was not called on the current thread before this funct
ion was called.
CO_E_OBJNOTCONNECTED
The object application has been disconnected from the remoting system (for examp
le, as a result of a call to the CoDisconnectObject function).
REGDB_E_CLASSNOTREG
An error occurred reading the registration database.
E_NOINTERFACE
The final QueryInterface of this function for the requested interface returned E
_NOINTERFACE.
CoCreateInstance errors
An error occurred when creating the handler.
Remarks
The CoUnmarshalInterface function performs the following tasks:
1. Reads from the stream the CLSID to be used to create an instance
of the proxy.
2. Gets an IMarshal pointer to the proxy that is to do the unmarsha
ling. If the object uses COM s default marshaling implementation, the pointer thus
obtained is to an instance of the generic proxy object. If the marshaling is oc
curring between two threads in the same process, the pointer is to an instance o
f the in-process free threaded marshaler. If the object provides its own marshal
ing code, CoUnmarshalInterface calls the CoCreateInstance function, passing the
CLSID it read from the marshaling stream. CoCreateInstance creates an instance o
f the object s proxy and returns an IMarshal interface pointer to the proxy.
3. Using whichever IMarshal interface pointer it has acquired, the
function then calls IMarshal::UnmarshalInterface and, if appropriate, IMarshal::
ReleaseMarshalData.
The primary caller of this function is COM itself, from within interface proxies
or stubs that unmarshal an interface pointer. There are, however, some situatio
ns in which you might call CoUnmarshalInterface. For example, if you are impleme
nting a stub, your implementation would call CoUnmarshalInterface when the stub
receives an interface pointer as a parameter in a method call.
See Also
CoMarshalInterface, IMarshal::UnmarshalInterface
9.9 Interface Remoting and Marshaling Enumeration Definitions
9.9.1 EXTCONN
The EXTCONN enumeration specifies the type of external connection existing on an
embedded object. Currently, the only supported type is EXTCONN_STRONG, meaning
that the external connection is a link. This EXTCONN constant is used in the IEx
ternalConnection::AddConnection and IExternalConnection::ReleaseConnection metho
ds.
typedef enum tagEXTCONN
{
EXTCONN_STRONG = 0X0001,
EXTCONN_WEAK = 0X0002,
EXTCONN_CALLABLE = 0X0004
} EXTCONN;
Elements
EXTCONN_STRONG
If this value is specified, the external connection must keep the object alive u
ntil all strong external connections are cleared through IExternalConnection::Re
leaseConnection.
EXTCONN_WEAK
This value is currently not used.
EXTCONN_CALLABLE
This value is currently not used.
9.9.2 MSHCTX
The MSHCTX enumeration constants specify the destination context, which is the p
rocess in which the unmarshaling is to be done. These flags are used in the IMar
shal and IStdMarshalInfo interfaces and in the CoMarshalInterface and CoGetStand
ardMarshal functions.
MSHCTX is defined in WTYPES.IDL and in WTYPES.H]
typedef enum tagMSHCTX
{
MSHCTX_LOCAL = 0,
MSHCTX_NOSHAREDMEM = 1,
MSHCTX_DIFFERENTMACHINE = 2,
MSHCTX_INPROC = 3
} MSHCTX;
Elements
MSHCTX_LOCAL
The unmarshaling process is local and has shared memory access with the marshali
ng process.
MSHCTX_NOSHAREDMEM
The unmarshaling process does not have shared memory access with the marshaling
process.
MSHCTX_DIFFERENTMACHINE
The unmarshaling process is on a different machine. The marshaling code cannot a
ssume that a particular piece of application code is installed on that machine.
MSHCTX_INPROC
The unmarshaling will be done in another apartment in the same process. If your
object supports multiple threads, your custom marshaler can pass a direct pointe
r instead of creating a proxy object.
See Also
CoGetStandardMarshal, CoMarshalInterface, IMarshal, IStdMarshalInfo
9.9.3 MSHLFLAGS
The MSHLFLAGS enumeration constants determine why the marshaling is to be done.
These flags are used in the IMarshal interface and the CoMarshalInterface and Co
GetStandardMarshal functions.
MSHFLAGS is defined in WTYPES.IDL and WTYPES.H].
typedef enum tagMSHLFLAGS
{
MSHLFLAGS_NORMAL = 0,
MSHLFLAGS_TABLESTRONG = 1,
MSHLFLAGS_TABLEWEAK = 2
} MSHLFLAGS;
Elements
MSHLFLAGS_NORMAL
The marshaling is occurring because an interface pointer is being passed from on
e process to another. This is the normal case. The data packet produced by the m
arshaling process will be unmarshaled in the destination process. The marshaled
data packet can be unmarshaled just once, or not at all. If the receiver unmarsh
als the data packet successfully, the CoReleaseMarshalData function is automatic
ally called on the data packet as part of the unmarshaling process. If the recei
ver does not or cannot unmarshal the data packet, the sender must call the CoRel
easeMarshalData function on the data packet.
MSHLFLAGS_TABLESTRONG
The marshaling is occurring because the data packet is to be stored in a globall
y accessible table from which it can be unmarshaled one or more times, or not at
all. The presence of the data packet in the table counts as a strong reference
to the interface being marshaled, meaning that it is sufficient to keep the obje
ct alive. When the data packet is removed from the table, the table implementer
must call the CoReleaseMarshalData function on the data packet.
MSHLFLAGS_TABLESTRONG is used by the RegisterDragDrop function when registering
a window as a drop target. This keeps the window registered as a drop target no
matter how many times the end user drags across the window. The RevokeDragDrop f
unction calls CoReleaseMarshalData.
MSHLFLAGS_TABLEWEAK
The marshaling is occurring because the data packet is to be stored in a globall
y accessible table from which it can be unmarshaled one or more times, or not at
all. However, the presence of the data packet in the table acts as a weak refer
ence to the interface being marshaled, meaning that it is not sufficient to keep
the object alive. When the data packet is removed from the table, the table imp
lementer must call the CoReleaseMarshalData function on the data packet.
MSHLFLAGS_TABLEWEAK is typically used when registering an object in the Running
Object Table (ROT). This prevents the object s entry in the ROT from keeping the o
bject alive in the absence of any other connections. See IRunningObjectTable::Re
gister for more information.
See Also
CoGetStandardMarshal, CoMarshalInterface, CoReleaseMarshalData, IMarshal
10. Security in COM
To make it possible to implement an object that could perform privileged operati
ons without compromising security COM supports two main areas of security:
Launch Security
Call Security
Launch Security controls which objects a client is allowed to instantiate. Call
Security dictates how security operates at the call level between an established
connection from a client to a server. While anyone can get interface pointers f
rom the class table, they cannot use them if they do not have call permissions.
COM provides a default security model, but also defines call-level interfaces th
at external security providers can implement to control object security.
It is also possible to have a server run as a given user account, through settin
g the RunAs named-value. This can be used to restrict or enhance available opera
tions. The remainder of this section describes the capabilities of COM security
in greater detail.
10.1 Launch Security
Activation security controls which classes a client is allowed to launch and ret
rieve objects from. Launch security is automatically applied by the Service Cont
rol Manager (SCM) of a particular machine. Upon receipt of a request from a remo
te client to activate an object (as described in Instance Creation Helper Functi
ons), the SCM of the machine checks the request against activation security info
rmation stored within its registry.
There are two machine-wide secure settings in the registry, to which only machin
e administrators and the system have full access. All other users have only read
-access. These are EnableDCOM and DefaultLaunchPermission. The EnableDCOM allows
or disallows remote clients to launch class code and connect to objects for the
system, and DefaultLaunchPermission, as the name implies, sets the default Acce
ss Control List (ACL) of who has permission to classes on the system.
You can override the default for any given class by assigning the desired permis
sions to the LaunchPermission key.
On platforms which do not support launch security, such as Windows 95 and the Ma
cintosh, COM will never launch a process. Remote clients can still make connecti
ons to COM servers, however, as long as the process is already running, CoRegist
erClassObject has been called, and the EnableRemoteConnect registry key has been
set to Y . Also, remote connects are allowed on file moniker binds.
10.2 Call Security
COM provides two mechanisms to secure calls. The first is done automatically by
the COM infrastructure. If the application provides some setup information, COM
will make all the necessary checks to secure the application s objects. This autom
atic mechanism does security checking for the process, not for individual object
s or methods. The second is a set of functions and interfaces that applications
may use to do their own security checking, and provide more fine-grained securit
y. Furthermore, the two mechanisms are not exclusive: an application may ask COM
to perform automatic security checking and also perform its own.
COM call security services are divided into three categories:
General functions called by both clients and servers
Interfaces on client proxies and related helper functions
Server-side functions and call-context interfaces.
If you are using the default security values for a process for authentication an
d authorization, no security initialization call is necessary. If, however, you
want to set other values for that process, you would call CoInitializeSecurity.
This both initializes and registers these values. The values set in this call th
en become the default values for that process. The proxy interfaces allow the cl
ient to control the security on calls to individual interfaces.
The IClientSecurity interface is implemented locally to the client by the interf
ace remoting layer. The client calls its methods to control the security of indi
vidual interface proxies on the object prior to making a call on one of the inte
rfaces. Generally, clients using the default implementation instead call the hel
per functions that access that implementation and simplify the code: CoQueryProx
yBlanket, CoSetProxyBlanket, and CoCopyProxy. Calling IClientSecurity directly i
s slightly more efficient then calling the helper functions. IClientSecurity wor
ks with all authentication services (NTLMSSP, DCE, Kerberos). Some custom marsha
led objects might not support IClientSecurity.
The server APIs and interfaces allow the server to retrieve security information
about a call and to impersonate the caller. The IServerSecurity interface is im
plemented for all providers, but may be absent for some custom-marshaled interfa
ces. Helper functions are also available that rely on the IServerSecurity interf
ace implementation: CoQueryClientBlanket, CoImpersonateClient, and CoRevertToSel
f.
In a typical scenario, the client queries an existing object for IClientSecurity
, which is implemented locally by the interface remoting layer. The client uses
IClientSecurity to control the security of individual interface proxies on the o
bject prior to making a call on one of the interfaces. When a call arrives at th
e server, the server may call CoGetCallContext to retrieve an IServerSecurity in
terface. IServerSecurity allows the server to check the client s authentication an
d to impersonate the client, if needed. The IServerSecurity object is valid for
the duration of the call. CoInitializeSecurity allows the client to establish de
fault call security for the process, avoiding the use of IClientSecurity on indi
vidual proxies. CoInitializeSecurity allows a server to register automatic authe
ntication services for the process.
Implementations of QueryInterace must never check ACLs. COM requires that an obj
ect which supports a particular IID always return success when queried for that
IID. Aside from the requirement, checking ACLs on QueryInterface does not provid
e any real security. If client A legally has access to interface IFoo, A can han
d it directly to B without any calls back to the server. Additionally, COM cache
s interface pointers and will not call QueryInterface on the server every time a
client does a query. For more information on implementing QueryInterface, see R
ules for Implementing QueryInterface.
Machine administrators and the system only have full access to the portion of th
e registry that contains the default machine-wide call security settings. All ot
her uses have read access only. These named values are used for classes that do
not call CoInitializeSecurity, and are as follows:
DefaultAccessPermission
LegacyAuthenticationLevel
LegacyImpersonationLevel
LegacyMutualAuthentication
LegacySecureReferences
To set access to objects of a specific class, there is the single named?value, A
ccessPermission
10.3 Security Related Interface Descriptions
10.3.1 IClientSecurity
Gives the client control over the call-security of individual interfaces on a re
mote object.
All proxies generated by the COM MIDL compiler support the IClientSecurity inter
face automatically. If a call to QueryInterface for IClientSecurity fails, eithe
r the object is implemented in-process or it is remoted by a custom marshaler wh
ich does not support security (a custom marshaler may support security by offeri
ng the IClientSecurity interface to the client). The proxies passed as parameter
s to an IClientSecurity method must be from the same object as the IClientSecuri
ty interface. That is, each object has a distinct IClientSecurity interface: cal
ling IClientSecurity on one object and passing a proxy to another object will no
t work.
10.3.1.1.1.1.1 When to Implement
The system proxy manager provides an implementation to objects, so you would typ
ically not implement this interface.
If, however, you are defining objects that support custom marshaling, you may ch
oose to implement IClientSecurity on the objects custom proxy to maintain a consi
stent programming model for the objects client applications. You may also choose
to support this interface on in-process objects.
10.3.1.1.1.1.2 When to Use
Call the methods of this interface to examine or modify the security settings of
a particular connection to an out-of-process object. For example, you might tem
porarily establish a higher security level one with complex encryption only for
the period when sensitive information or data is being sent to the object. Alter
nately, you might establish different proxies to the same object with different
security levels and use them to support different clients that are calling your
object, or to support different operations within your application.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IClientSecurity Methods Description
IClientSecurity::QueryBlanket Retrieves authentication information.
IClientSecurity::SetBlanket Sets the authentication information that will be
used to make calls on the specified proxy.
IClientSecurity::CopyProxy Makes a copy of the specified proxy.
See Also
Security in COM

10.3.1.2 IClientSecurity::CopyProxy
Makes a private copy of the specified proxy.
HRESULT CopyProxy(
IUnknown * punkProxy , //IUnknown pointer to the proxy to copy
IUnknown ** ppunkCopy //Indirect IUnknown pointer to the copy
);
Parameter
punkProxy
[in] Points to the IUnknown interface on the proxy to be copied. May not be NULL
.
ppunkCopy
[out] On successful return, points to the location of the IUnknown pointer to th
e copy of the proxy. It may not be NULL.
Return Values
S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
Remarks
IClientSecurity::CopyProxy makes a private copy of the specified proxy for the c
alling client. Its authentication information may be changed through a call to I
ClientSecurity::SetBlanket without affecting any other clients of the original p
roxy. The copy has the default values for the authentication information. The co
py has one reference and must be released.
The helper function CoCopyProxy encapsulates a QueryInterface call on the proxy
for a pointer to IClientSecurity, and with that pointer calls IClientSecurity::C
opyProxy, and then releases the IClientSecurity pointer.
Local interfaces may not be copied. IUnknown and IClientSecurity are examples of
existing local interfaces.
Copies of the same proxy have a special relationship with respect to QueryInterf
ace. Given a proxy, a, of the IA interface of a remote object, suppose a copy of
a is created, called b. In this case, calling QueryInterface from the b proxy f
or IID_IA will not retrieve the IA interface on b, but the one on a, the origina
l proxy with the default security settings for the IA interface.
See Also
CoCopyProxy

10.3.1.3 IClientSecurity::QueryBlanket
Retrieves authentication information.
HRESULT QueryBlanket(
void* pProxy, //Location for the proxy to query
DWORD* pAuthnSvc, //Location for the current authentication service
DWORD* pAuthzSvc, //Location for the current authorization service
OLECHAR ** pServerPrincName, //Location for the current principal nam
e
DWORD * pAuthnLevel, //Location for the current authentication level
DWORD * pImpLevel, //Location for the current impersonation level
RPC_AUTH_IDENTITY_HANDLE ** ppAuthInfo, //Location for the value passed
to IClientSecurity::SetBlanket
DWORD ** pCapabilities //Location for flags indicating further capabili
ties of the proxy
);
Parameter
pProxy
[in] Pointer to the proxy to query. ????Where does the initial pointer to the prox
y come from, and what interface might it point to????
pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This
will be a single value taken from the list of RPC_C_AUTHN_xxx constants. May be
NULL, in which case the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This
will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. May be
NULL, in which case the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the
one called using CoTaskMemAlloc and must be freed by the caller using CoTaskMem
Free when they are done with it. May be NULL, in which case the principal name i
s not retrieved.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This w
ill be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. Ma
y be NULL, in which case the current authentication level is not retrieved.
pImpLevel
[out] Pointer to a DWORD value defining the current impersonation level. This wi
ll be a single value taken from the list of RPC_C_IMP_LEVEL_xxx constants. May b
e NULL, in which case the current authentication level is not retrieved.
ppAuthInfo
[out] Pointer to the pointer value passed to IClientSecurity::SetBlanket indicat
ing the identity of the client. Because this points to the value itself and is n
ot a copy, it should not be manipulated. May be NULL, in which case the informat
ion is not retrieved.
pCapabilities
[out] Pointer to a DWORD of flags indicating further capabilities of the proxy.
Currently, no flags are defined for this parameter and it will only return zero.
May be NULL, in which case the flags indicating further capabilities are not re
trieved.
Return Values
S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pServerPrincName out-parameter.
Remarks
IClientSecurity::QueryBlanket is called by the client to retrieve the authentica
tion information COM will use on calls made from the specified proxy. With a poi
nter to an interface on the proxy, the client would first call QueryInterface fo
r a pointer to IClientSecurity, then, with this pointer, would call IClientSecur
ity::QueryBlanket, followed by releasing the pointer. This sequence of calls is
encapsulated in the helper function CoQueryProxyBlanket.
In pProxy, you can pass any proxy, such as a proxy you get through a call to CoC
reateInstance, CoUnmarshalInterface, or just passing an interface pointer as a p
arameter. It can be any interface. You cannot pass a pointer to something that i
s not a proxy. Thus you can t pass a pointer to an interface that has the local ke
yword in its interface definition since no proxy is created for such an interfac
e. IUnknown is the exception.
See Also
CoQueryProxyBlanket

10.3.1.4 IClientSecurity::SetBlanket
Sets the authentication information that will be used to make calls on the speci
fied proxy.
HRESULT SetBlanket(
void * pProxy, //Indicates the proxy to set
DWORD dwAuthnSvc, //Authentication service to use
DWORD dwAuthzSvc, //Authorization service to use
WCHAR * pServerPrincName, //The server principal name to use with the auth
entication service
DWORD dwAuthnLevel, //The authentication level to use
DWORD dwImpLevel, //The impersonation level to use
RPC_AUTH_IDENTITY_HANDLE * pAuthInfo, //The identity of the client
DWORD dwCapabilities //Undefined capability flags
);
Parameter
pProxy
[in] Indicates the proxy to set.
dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants indicating
the authentication service to use. It may be RPC_C_AUTHN_NONE if no authenticati
on is required.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants indicating
the authorization service to use.
pServerPrincName
[in] Pointer to a WCHAR string that indicates the server principal name to use w
ith the authentication service. If you are using RPC_C_AUTHN_WINNT, the principa
l name must be NULL.
dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants indic
ating the authentication level to use.
dwImpLevel
[in] A single DWORD value from the list of RPC_C_IMP_LEVEL_xxx constants indicat
ing the impersonation level to use. Currently, only RPC_C_IMP_LEVEL_IMPERSONATE
and RPC_C_IMP_LEVEL_IDENTIFY are supported by NTLMSSP.
pAuthInfo
[in] Pointer to an RPC_AUTH_IDENTITY_HANDLE value that establishes the identity
of the client. It is authentication-service specific. Some authentication servic
es allow the application to pass in a different user name and password. COM keep
s a pointer to the memory passed in until COM is uninitialized or a new value is
set. If NULL is specified COM uses the current identity (the process token ). F
or NTLMSSP the structure is SEC_WINNT_AUTH_IDENTITY_W. The format of this struct
ure depends on the provider of the authentication service.
dwCapabilities
[in] A DWORD defining flags to establish indicating the further capabilities of
this proxy. Currently, no capability flags are defined.
The caller should specify EOAC_NONE. EOAC_MUTUAL_AUTH is defined and may be used
by other security providers, but is not supported by NTLMSSP. Thus, NTLMSSP wil
l accept this flag without generating an error but without providing mutual auth
entication.
Return Values
S_OK
Success, append the headers.
E_INVALIDARG
One or more arguments is invalid.
Remarks
IClientSecurity::SetBlanket sets the authentication information that will be use
d to make calls on the specified proxy. The values specified here override the v
alues chosen by automatic security. Calling this method changes the security val
ues for all other users of the specified proxy. Call IClientSecurity::CopyProxy
to make a private copy of the proxy.
By default, COM will choose the first available authentication service and autho
rization service available on both the client and server machines and the princi
pal name which the server registered for that authentication service. Currently,
COM will not try another authentication service if the first fails.
If pAuthInfo is NULL, it defaults to the current process token. dwAuthnLevel and
dwImpLevel default to the values specified to CoInitializeSecurity. If CoInitia
lizeSecurity is not called, the defaults are taken from the registry. The initia
l value for dwAuthnLevel on a proxy will be the higher of the value set on the c
lient s call to CoInitializeSecurity and the server s call to CoInitializeSecurity .
Security information cannot be set on local interfaces. For example, it is illeg
al to set security on the IClientSecurity interface. However, since that interfa
ce is supported locally, there is no need for security. IUnknown is a special ca
se. There are several cases. First, IUnknown cannot be copied. Thus all users of
an object get the same security. Second, SetBlanket can be used to set the secu
rity used for calls to QueryInterface. However, since QueryInterface is heavily
cached, the server might not see the call. Third, AddRef and Release always use
the security set with CoInitializeSecurity, never the values set with SetBlanket
.
See Also
CoSetProxyBlanket, CoQueryProxyBlanket, RPC_C_AUTHN_xxx, RPC_C_AUTHZ_xxx, RPC_C_
AUTHN_LEVEL_xxx, RPC_C_IMP_LEVEL_xxx
10.3.2 IServerSecurity
Used by a server to help identify the client and to manage impersonation of the
client. IServerSecurity:QueryBlanket and IServerSecurity::ImpersonateClient may
only be called before the ORPC call completes. The interface pointer must be rel
eased when it is no longer needed.
When a client calls a server, the server can call CoGetCallContext until the ser
ver sends the reply back to the client. The pointer to the instance of IServerS
ecurity returned by CoGetCallContext is automaticly deleted when the server send
s the reply back to the client.
When to Implement
The stub managment code in the system provides an implementation of IServerSecur
ity for objects by default as part of each incoming call, so typically you would
not implement this interface.
You may choose to implement IServerSecurity on the custom stubs of objects that
support custom marshaling to maintain a consistent programming model for their o
bjects.
When to Use
The methods of the IServerSecurity interface are called by the server/object to
examine or alter the security level of the connection between the caller and thi
s particular object. Its most common use is for impersonation (IServerSecurity::
ImpersonateClient and ::RevertToSelf), where the server impersonates the client
to test the privilege level of the calling client with an AccessCheck call. The
information obtained through IServerSecurity also allows an object to implement
its own security framework, perhaps not based on the Access Control Lists (ACLs)
that impersonation is geared toward. A different implementation could base its
security framework on the client name or other information available through a c
all to the QueryBlanket method.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IServerSecurity Methods Description
QueryBlanket Called by the server to find out about the client that invoked o
ne of its methods.
ImpersonateClient Allows a server to impersonate a client for the duration
of a call.
RevertToSelf Restores the authentication information on a thread to the proce
ss's identity.
IsImpersonating Indicates whether the server is currently impersonating the cli
ent.
See Also
Security in COM
10.3.2.1 IServerSecurity::QueryBlanket
Called by the server to find out about the client that invoked one of its method
s.
HRESULT QueryBlanket(
DWORD* pAuthnSvc, //Pointer to the current authentication service
DWORD* pAuthzSvc, //Pointer to the current authorization service
OLECHAR ** pServerPrincNam, //Pointer to the current principal name
DWORD * pAuthnLevel, //Pointer to the current authentication level
DWORD * pImpLevel, //Must be NULL
RPC_AUTHZ_HANDLE * pPrivs, //Pointer to string identifying client
DWORD ** pCapabilities //Pointer to flags indicating further capabiliti
es of the proxy
);
Parameter
pAuthnSvc
[out] Pointer to the current authentication service. This will be a single value
taken from the list of RPC_C_AUTHN_xxx constants. May be NULL, in which case th
e current authentication service is not returned.
pAuthzSvc
[out] Pointer to the current authorization service. This will be a single value
taken from the list of RPC_C_AUTHZ_xxx constants. May be NULL, in which case the
current authorization service is not returned.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the
callee using CoTaskMemAlloc, and must be freed by the caller using CoTaskMemFre
e when they are done with it. May be NULL, in which case the principal name is n
ot returned.
pAuthnLevel
[out] Pointer to the current authentication level. This will be a single value t
aken from the list of RPC_C_AUTHN_LEVEL_xxx constants. May be NULL, in which cas
e the current authorization level is not returned.
pImpLevel
[out] Must be NULL; the current authentication level is not supplied.
pPrivs
[out] Pointer to a string identifying the client. For NTLMSSP the string is of
the form domain\user. This is not a copy, and so should not be modified or freed
, and is not valid after the ORPC call completes.
pCapabilities
[out] Pointer to return flags indicating further capabilities of the call. Curre
ntly, no flags are defined for this parameter and it will only return EOAC_NONE.
May be NULL, in which case the flags indicating further capabilities are not re
turned.
Return Values
This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY,
as well as the following:
S_OK
Success.

Remarks
IServerSecurity::QueryBlanket is used by the server to find out about the client
that invoked one of its methods. To get a pointer to IServerSecurity for the cu
rrent call on the current thread, call CoGetCallContext, specifying IID_IServerS
ecurity. This interface pointer may only be used in the same apartment as the ca
ll for the duration of the call.
See Also
CoGetCallContext
10.3.2.2 IServerSecurity::ImpersonateClient
Allows a server to impersonate a client for the duration of a call.
HRESULT ImpersonateClient()
Return Values
This method supports the standard return value E_FAIL, as well as the following:
S_OK
Success.
Remarks
IServerSecurity::ImpersonateClient allows a server to impersonate a client for t
he duration of a call. What the server may do depends on the impersonation level
, specified through one of the RPC_C_IMP_LEVEL_xxx constants. The server may imp
ersonate the client on any secure call at identify, impersonate, or delegate lev
el. At identify level, the server may only find out the client name and perform
ACL checks; it may not access system objects as the client. At delegate level, t
he server may make off-machine calls while impersonating the client. The imperso
nation information only lasts until the end of the current method call. At that
time, IServerSecurity::RevertToSelf will automatically be called if necessary.
Traditionally, impersonation information is not nested - the last call to any Wi
n32 impersonation mechanism overrides any previous impersonation. However, in th
e apartment model, impersonation is maintained during nested calls. Thus if the
server A receives a call from B, impersonates, calls C, receives a call from D,
impersonates, reverts, and receives the reply from C, the impersonation will be
set back to B, not A.
Distributed COM currently does not support dynamic impersonation. The only way t
o change the client token associated with remote COM calls is to use IClientSecu
rity::SetBlanket on the proxy being called. Calling IServerSecurity::Impersonate
Client to impersonate your client and then making a remote call to another serve
r will not affect the token the second server sees when it impersonates on your
call.
See Also
CoImpersonateClient
10.3.2.3 IServerSecurity::RevertToSelf
Restores the authentication information on a thread to the process's identity.
HRESULT RevertToSelf()
Return Values
This method supports the standard return value E_FAIL, as well as the following:
S_OK
Success.
Remarks
IServerSecurity::RevertToSelf restores the authentication information and revert
s an impersonation on a thread to the process's identity. This method will only
revert impersonation changes made by IServerSecurity::ImpersonateClient. If the
thread token is modified by other means (through the SetThreadToken or RpcImpers
onateClient Win32 functions) the result of this function is undefined.
In the apartment model, CoRevertToSelf (IServerSecurity::RevertToSelf) affects o
nly the current method invocation. If there are nested method invocations, they
each may have their own impersonation and COM will correctly restore the imperso
nation before returning to them (regardless of whether or not CoRevertToSelf/ISe
rverSecurity::RevertToSelf was called).
See Also
CoRevertToSelf
10.3.2.4 IServerSecurity::IsImpersonating
Indicates whether IServerSecurity::ImpersonateClient has been called without a m
atching call to IServerSecurity::RevertToSelf.
BOOL IsImpersonating()
Return Values
TRUE
This thread has called IServerSecurity::ImpersonateClient and is currently imper
sonating the client of this call.
FALSE
This thread is not currently impersonating the client of this call.
See Also
IServerSecurity::RevertToSelf.
10.4 Security Related API Descriptions
10.4.1 CoCopyProxy
Makes a private copy of the specified proxy.
HRESULT CoCopyProxy(
IUnknown * punkProxy, //IUnknown pointer to the proxy to copy
IUnknown ** ppunkCopy //Address of output variable that receives
// the IUnknown interface pointer to the
//proxy copy
);
Parameter
punkProxy
[in] Points to the IUnknown interface on the proxy to be copied. May not be NULL
.
ppunkCopy
[out] Address of IUnknown* pointer variable that receives the interface pointer
to the copy of the proxy. It may not be NULL.
Return Values
S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
Remarks
CoCopyProxy makes a private copy of the specified proxy. Typically, this is call
ed when a client needs to change the authentication information of its proxy thr
ough a call to either CoSetClientBlanket or IClientSecurity::SetBlanket without
changing this information for other clients. CoSetClientBlanket affects all the
users of an instance of a proxy, so creating a private copy of the proxy through
a call to CoCopyProxy eliminates the problem.
This function encapsulates the following sequence of common calls (error handlin
g excluded):
pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
pcs->CopyProxy(punkProxy, ppunkCopy);
pcs->Release();
Local interfaces may not be copied. IUnknown and IClientSecurity are examples of
existing local interfaces.
Copies of the same proxy have a special relationship with respect to QueryInterf
ace. Given a proxy, a, of the IA interface of a remote object, suppose a copy of
a is created, called b. In this case, calling QueryInterface from the b proxy f
or IID_IA will not retrieve the IA interface on b, but the one on a, the origina
l proxy with the default security settings for the IA interface.
See Also
IClientSecurity::CopyProxy, Security in COM
10.4.2 CoGetCallContext
Retrieves the context of the current call on the current thread.
HRESULT CoGetCallContext(
REFIID riid, //Interface identifier
void ** ppv //Address of output variable that receives the
// interface pointer requested in riid
);
Parameters
riid
[in] Interface identifier (IID) of the call context that is being requested. If
you are using the default call context supported by standard marshaling, only II
D_IServerSecurity is available.
ppv
[out] Address of pointer variable that receives the interface pointer requested
in riid. Upon successful return, *ppv contains the requested interface pointer.
Return Values
S_OK
Success.
E_NOINTERFACE
The call context does not support the interface identified by riid.
Remarks
CoGetCallContext retrieves the context of the current call on the current thread
. The riid parameter specifies the interface on the context to be retrieved. Cur
rently, only IServerSecurity is available from the default call context supporte
d by standard marshaling.
This is one of the functions provided to give the server access to any contextua
l information of the caller and to encapsulate common sequences of security chec
king and caller impersonation.
See Also
IServerSecurity, Security in COM
10.4.3 CoImpersonateClient
Allows the server to impersonate the client of the current call for the duration
of the call.
HRESULT CoImpersonateClient( )
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
Remarks
Allows the server to impersonate the client of the current call for the duration
of the call. If you do not call CoRevertToSelf, COM reverts automatically for y
ou. This function will fail unless the object is being called with RPC_C_AUTHN_L
EVEL_CONNECT or higher authentication in effect (any authentication level except
RPC_C_AUTHN_LEVEL_NONE) This function encapsulates the following sequence of co
mmon calls (error handling excluded):
CoGetCallContext(IID_IServerSecurity, (void**)&pss);
pss->ImpersonateClient();
pss->Release();
This helper function encapsulates the process of getting a pointer to an instanc
e of IServerSecurity that contains data about the current call, calling its Impe
rsonateClient method, and then releasing the pointer.
See Also
IServerSecurity::ImpersonateClient, Security in COM
10.4.4 CoInitializeSecurity
Registers security and sets the default security values for the process. For leg
acy applications, COM automatically calls this function with values from the reg
istry. It is invoked once per process, rather than for each thread in the proces
s. If you set registry values and then call CoInitializeSecurity, the AppID regi
stry values will be ignored, and the CoInitializeSecurity values will be used.
HRESULT CoInitializeSecurity(
PSECURITY_DESCRIPTOR pVoid, //Points to security descriptor
DWORD cAuthSvc, //Count of entries in asAuthSvc
SOLE_AUTHENTICATION_SERVICE * asAuthSvc, //Array of names to register
void * pReserved1, //Reserved for future use
DWORD dwAuthnLevel, //The default authentication level for proxies
DWORD dwImpLevel, //The default impersonation level for proxies
RPC_AUTH_IDENTITY_HANDLE pAuthInfo, //Reserved; must be set to NULL
DWORD dwCapabilities, //Additional client and/or server-side capabilit
ies
void * pvReserved2 //Reserved for future use
);
Parameters
pVoid
[in] Defines the access permissions. If the capability flags (pCapabilities) are
not EOAC_APPID or EOAC_ACCESS_CONTROL, must be a pointer to a Win32 security de
scriptor. If EOAC_APPID is specified, must be a pointer to a GUID that specifies
the AppID of the process. In this case, all other parameters of the call are ig
nored, and registry values are used for security checks. If EOAC_ACCESS_CONTROL
is specified, must be a pointer to an IAccessControl interface, which will be us
ed to check access. See Remarks for more information. If NULL, no ACL checking w
ill be done. If not NULL, COM will check ACLs on new connections. If not NULL, d
wAuthnLevel cannot be RPC_C_AUTHN_LEVEL_NONE.
cAuthSvc
[in] Count of entries in asAuthSvc. Zero means register no services. A value of
-1 tells COM to choose which authentication services to register.
asAuthSvc
[in] Array of authentication/authorization/principal names to register. These va
lues are registered to allow incoming calls. After that they are ignored. The de
fault authentication/authorization/principal for each proxy will be negotiated r
egardless of whether these are set. For example, if the application registers RP
C_C_AUTHN_WINNT and receives and interface from a machine that only supports RPC
_C_AUTHN_DEC_PUBLIC, COM will choose RPC_C_AUTHN_DEC_PUBLIC if this machine supp
orts it.
pReserved1
[in] Reserved for future use; must be NULL.
dwAuthnLevel
[in] The default authentication level for proxies. On the server side, COM will
fail calls that arrive at a lower level. All calls to AddRef and Release are mad
e at this level.
dwImpLevel
[in] The default impersonation level for proxies. This value is not checked on t
he server side. AddRef and Release calls are made with this impersonation level
so even security aware apps should set this carefully. Setting IUnknown security
only affects calls to QueryInterface, not AddRef or Release.
pAuthInfo
[in] Reserved for future use; must be NULL.
dwCapabilities
[in] Additional client and/or server-side capabilitiesThese flags are described
in EOLE_AUTHENTICATION_CAPABILITIES. If EOLE_APPID is specified, pVoid must be a
pointer to a GUID specifying the AppID of the process. If EOLE_ACCESS_CONTROL i
s set, pVoid must be a pointer to the IAccessControl interface on an access cont
rol object. For more information, see the remarks.
pReserved2
[in] Reserved for future use; must be zero.
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
Remarks
The CoInitializeSecurity layer initializes the security layer and sets the speci
fied values as the security default.
If the application does not call CoInitializeSecurity, COM calls it automaticall
y the first time an interface is marshaled or unmarshaled, registering the syste
m default security. No default security packages will be registered till then.
The first parameter, pVoid, can be one of three types of value, depending on the
value of pCapabilities: a Win32 security descriptor, a pointer to a GUID specif
ying the AppID of the process, or an IAccessControl pointer.
If any capability flags other than EOAC_APPID or EOAC_ACCESS_CONTROL are called,
pVoid must be a pointer to a Win32 SECURITY_DESCRIPTOR. A NULL DACL will allow
calls from anyone. A DACL with no ACEs allows no access. For information on ACLs
and ACEs, refer to the Win32 Programmers Reference section Access Control Model
.
The owner and group of the SECURITY_DESCRIPTOR must be set applications should c
all AccessCheck (not IsValidSecurityDescriptor) to ensure that their security de
scriptor is correctly formed prior to calling CoInitializeSecurity.
If the application passes a NULL security descriptor, COM will construct one tha
t allows calls from the current user and local system. All new connections will
be audited. Distributed COM will copy the security descriptor.
If mutual authentication is enabled all calls will fail unless the server identi
ty is verified to match the principal name set on the proxy. Without mutual auth
entication, security only helps the server; the client has no idea who is handli
ng his call. While CoInitializeSecurity takes principal names as parameters, tha
t does not mean that the server can register any arbitrary name. The security pr
ovider verifies that the server has a right to use the names registered.
Secure references cause DCOM to make extra callbacks to insure that objects are
not released maliciously.
When the EOAC_APPID flag is set, CoInitializeSecurity looks for the authenticati
on level under the AppID. If the authentication level is not found, it looks for
the default authentication level. If the default authentication level is not fo
und, it generates a default authentication level of connect. If the authenticati
on level is not NONE, CoInitializeSecurity looks for the access permission value
under the AppID. If not found, it looks for the default access permission value
. If not found, it generates a default access permission. All the other security
settings are determined the same way as for a legacy application.
When the EOAC_APPID flag is set, all parameters to CoInitializeSecurity except p
Void are ignored. If pVoid is NULL, CoInitializeSecurity will look up the applic
ation .exe name in the registry and use the AppID stored there. Note that this g
ives the same security as if you did not call CoInitializeSecurity.
The IClientSecurity::SetBlanket method and CoSetProxyBlanket function return an
error if you set any of the following flags in the capabilities: EOAC_SECURE_REF
S, EOAC_ACCESS_CONTROL, or EOAC_APPID.
The CoInitializeSecurity function returns an error if both the EOAC_APPID and EO
AC_ACCESS_CONTROL flags are set.
When you set the cloaking capability through the EOAC_CLOAK capability flag, thi
s picks up the token only on the first call to the proxy; dynamic cloaking, in w
hich the token would be picked up on every call, is not supported.
See Also
RPC_C_IMP_LEVEL_xxx, RPC_C_AUTHN_LEVEL_xxx, Security in COM
10.4.5 CoQueryAuthenticationServices
Retrieves a list of the authentication services registered when the process call
ed CoInitializeSecurity.
HRESULT CoQueryAuthenticationServices(
DWORD * pcAuthSvc, //Pointer to the number of entries returned in the array
SOLE_AUTHENTICATION_SERVICE** prgAuthSvc //Pointer to an array of structu
res
);
Parameters
pcAuthSvc
[out] Pointer to return the number of entries returned in the rgAuthSvc array. M
ay not be NULL.
prgAuthSvc
[out] Pointer to an array of SOLE_AUTHENTICATION_SERVICE structures. The list is
allocated through a call to CoTaskMemAlloc. The caller must free the list when
finished with it by calling CoTaskMemFree.
Return Values
This function supports the standard return values E_INVALIDARG and
E_OUTOFMEMORY, as well as the following:
S_OK
Indicates success.
Remarks
CoQueryAuthenticationServices retrieves a list of the authentication services cu
rrently registered. If the process calls CoInitializeSecurity, these are the ser
vices registered through that call. If the application does not call it, CoIniti
alizeSecurity is called automatically by COM, registering the default security p
ackage, the first time an interface is marshaled or unmarshaled.
This function is primarily useful for custom marshalers, to determine which prin
cipal names an application can use.
Different authentication services support different levels of security. For exam
ple, NTLMSSP does not support delegation or mutual authentication while Kerberos
does. The application is responsible only for registering authentication servic
es that provide the features the application needs. This is the way to query whi
ch services have been registered with CoInitializeSecurity.
See Also
CoInitializeSecurity, SOLE_AUTHENTICATION_SERVICE structure, Security in COM
10.4.6 CoQueryClientBlanket
Called by the server to find out about the client that invoked the method execut
ing on the current thread.
HRESULT CoQueryClientBlanket(
DWORD* pAuthnSvc, //Pointer to the current authentication service
DWORD* pAuthzSvc, //Pointer to the current authorization service
OLECHAR ** pServerPrincName, //Pointer to the current principal name
DWORD * pAuthnLevel, //Pointer to the current authentication level

DWORD * pImpLevel, //Must be NULL


void ** ppPrivs, //Pointer to unicode string identifying client
DWORD ** pCapabilities //Pointer to flags indicating further capabiliti
es of the proxy
);
Parameters
pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This
will be a single value taken from the list of RPC_C_AUTHN_xxx constants. May be
NULL, in which case the current authentication service is not returned.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This
will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. May be
NULL, in which case the current authorization service is not returned.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the
callee using CoTaskMemAlloc and must be freed by the caller using CoTaskMemFree
when they are done with it. May be NULL, in which case the principal name is no
t returned.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This w
ill be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. Ma
y be NULL, in which case the current authentication level is not returned.
pImpLevel
[out] Pointer to a DWORD value defining the current impersonation level. This wil
l be a single value taken from the list of RPC_C_IMP_LEVEL_xxx constants. Must be
NULL;the current impersonation level is never returned.
pPrivs
[out] Pointer to a unicode string identifying the client. This string is not a c
opy, the user must not change it or free it. The string is not valid past the en
d of the call. For NTLMSSP the string is of the form domain\user.
pCapabilities
[out] Pointer to flags indicating further capabilities of the call. Currently, n
o flags are defined for this parameter and the value retrieved is EOAC_NONE. May
be NULL, in which case no value is retrieved. EOAC_MUTUAL_AUTH is defined but n
ot used by NTLMSSP. Third party security providers may return that flag.
Return Values
S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pServerPrincName out-parameter.
Remarks
CoQueryClientBlanket is called by the server to get security information about t
he client that invoked the method executing on the current thread. This function
encapsulates the following sequence of common calls (error handling excluded):
CoGetCallContext(IID_IServerSecurity, (void**)&pss);
pss->QueryBlanket(pAuthnSvc, pAuthzSvc, pServerPrincName,
pAuthnLevel, pImpLevel, pPrivs, pCapabilities);
pss->Release();
See Also
IServerSecurity::QueryBlanket, Security in COM
10.4.7 CoQueryProxyBlanket
Retrieves the authentication information the client uses to make calls on the sp
ecified proxy.
HRESULT CoQueryProxyBlanket(
void* pProxy, //Location for the proxy to query
DWORD* pAuthnSvc, //Location for the the current authorization service
DWORD* pAuthzSvc, //Location for the the current authorization service
OLECHAR ** pServerPrincName, //Location for the current principal nam
e
DWORD * pAuthnLevel, //Location for the current authentication level
DWORD * pImpLevel, //Location for the current impersonation level
RPC_AUTH_IDENTITY_HANDLE ** ppAuthInfo, //Location for the value passed
to IClientSecurity::SetBlanket
DWORD ** pCapabilities //Location for flags indicating further capabili
ties of the proxy
);
Parameters
pProxy
[in] Pointer to an interface on the proxy to query.
pAuthnSvc
[out] Pointer to a DWORD value defining the current authentication service. This
will be a single value taken from the list of RPC_C_AUTHN_xxx constants. May be
NULL, in which case the current authentication service is not retrieved.
pAuthzSvc
[out] Pointer to a DWORD value defining the current authorization service. This
will be a single value taken from the list of RPC_C_AUTHZ_xxx constants. May be
NULL, in which case the current authorization service is not retrieved.
pServerPrincName
[out] Pointer to the current principal name. The string will be allocated by the
one called using CoTaskMemAlloc and must be freed by the caller using CoTaskMem
Free when they are done with it. May be NULL, in which case the principal name i
s not retrieved.
pAuthnLevel
[out] Pointer to a DWORD value defining the current authentication level. This w
ill be a single value taken from the list of RPC_C_AUTHN_LEVEL_xxx constants. Ma
y be NULL, in which case the current authentication level is not retrieved.
pImpLevel
[out] Pointer to a DWORD value defining the current impersonation level. This wi
ll be a single value taken from the list of RPC_C_IMP_LEVEL_xxx constants. May b
e NULL, in which case the current authentication level is not retrieved.
ppAuthInfo
[out] Pointer to the pointer value passed to IClientSecurity::SetBlanket indicat
ing the identity of the client. Because this points to the value itself and is n
ot a copy, it should not be manipulated. May be NULL, in which case the informat
ion is not retrieved.
pCapabilities
[out] Pointer to a DWORD of flags indicating further capabilities of the proxy.
Currently, no flags are defined for this parameter and it will only return EOAC_
NONE. May be NULL, in which case the flags indicating further capabilities are n
ot retrieved.
Return Values
S_OK
Success.
E_INVALIDARG
One or more arguments are invalid.
E_OUTOFMEMORY
Insufficient memory to create the pasAuthnSvc out-parameter.
Remarks
CoQueryProxyBlanket is called by the client to retrieve the authentication infor
mation COM will use on calls made from the specified proxy. This function encaps
ulates the following sequence of common calls (error handling excluded):
pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
pcs->QueryBlanket(pProxy, pAuthnSvc, pAuthzSvc, pServerPrincName,
pAuthnLevel, pImpLevel, ppAuthInfo, pCapabilities);
pcs->Release();
This sequence calls QueryInterface on the proxy for IClientSecurity, and with th
e resulting pointer, calls IClientSecurity::QueryBlanket, and then releases the
pointer.
In pProxy, you can pass any proxy, such as a proxy you get through a call to CoC
reateInstance, CoUnmarshalInterface, or just passing an interface pointer as a p
arameter. It can be any interface. You cannot pass a pointer to something that i
s not a proxy. Thus you can t pass a pointer to an interface that has the local ke
yword in its interface definition since no proxy is created for such an interfac
e. IUnknown is the exception.
See Also
IClientSecurity::QueryBlanket, Security in COM
10.4.8 CoRevertToSelf
Restores the authentication information on a thread of execution to its previous
identity.
HRESULT CoRevertToSelf( )
Return Values
This function supports the standard return value E_INVALIDARG, as well as the fo
llowing:
S_OK
Indicates success.
Remarks
CoRevertToSelf restores the authentication information on a thread of execution
to its previous identity after a previous server call to CoImpersonateClient. Th
is is a helper function that encapsulates the following common sequence of calls
(error handling excluded):
CoGetCallContext(IID_IServerSecurity, (void**)&pss);
pss->RevertToSelf();
pss->Release();
See Also
IServerSecurity::RevertToSelf, CoGetCallContext, Security in COM
10.4.9 CoSetProxyBlanket
Sets the authentication information that will be used to make calls on the speci
fied proxy.
HRESULT CoSetProxyBlanket(
void * pProxy, //Indicates the proxy to set
DWORD dwAuthnSvc, //Authentication service to use
DWORD dwAuthzSvc, //Authorization service to use
WCHAR * pServerPrincName, //The server principal name to use with the auth
entication service
DWORD dwAuthnLevel, //The authentication level to use
DWORD dwImpLevel, //The impersonation level to use
RPC_AUTH_IDENTITY_HANDLE * pAuthInfo, //The identity of the client
DWORD dwCapabilities //Undefined capability flags
);
Parameter
pProxy
[in] Pointer to an interface on the proxy for which this authentication informat
ion is to be set.
dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants indicating
the authentication service to use. It may be RPC_C_AUTHN_NONE if no authenticati
on is required.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants indicating
the authorization service to use. If you are using the system default authentica
tion service, use RPC_C_AUTHZ_NONE.
pServerPrincName
[in] Points to a WCHAR string that indicates the server principal name to use wi
th the authentication service. If you are using RPC_C_AUTHN_WINNT, the principal
name must be NULL.
dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants indic
ating the authentication level to use.
dwImpLevel
[in] A single DWORD value from the list of RPC_C_IMP_LEVEL_xxx constants indicat
ing the impersonation level to use. Currently, only RPC_C_IMP_LEVEL_IMPERSONATE
and RPC_C_IMP_LEVEL_IDENTIFY are supported.
pAuthInfo
[in] Establishes the identity of the client. It is authentication service specif
ic. Some authentication services allow the application to pass in a different us
er name and password. COM keeps a pointer to the memory passed in until COM is u
ninitialized or a new value is set. If NULL is specified COM uses the current id
entity (the process token). For NTLMSSP the structure is SEC_WINNT_AUTH_IDENTITY
_W.
dwCapabilities
[in] Flags to establish indicating the further capabilities of this proxy. Curre
ntly, no capability flags are defined.
apability flags are defined in the EOLE_AUTHENTICATION_CAPABILITIES enumeration.
Only EOAC_NONE or EOAC_CLOAKING should be used with this call. EOAC_MUTUAL_AUTH
is accepted without error, but has no The caller should specify EOAC_NONE. EOAC
_MUTUAL_AUTH is defined and may be used by other security providers, but is not
supported by NTLMSSP. Thus, NTLMSSP will accept this flag without generating an
error but without providing mutual authenticationeffect. Other flags can be used
only with CoInitializeSecurity, and their use will generate an error.

Return Values
S_OK
Success, append the headers.
E_INVALIDARG
One or more arguments is invalid.
Remarks
Sets the authentication information that will be used to make calls on the speci
fied proxy. . The IClientSecurity::SetBlanket method and CoSetProxyBlanket funct
ion return an error if you set any of the following flags in the capabilities: E
OAC_SECURE_REFS, EOAC_ACCESS_CONTROL, or EOAC_APPID. This function encapsulates
the following sequence of common calls (error handling excluded):
pProxy->QueryInterface(IID_IClientSecurity, (void**)&pcs);
pcs->SetBlanket(pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName,
dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities);
pcs->Release();
See Also
IClientSecurity::SetBlanket, CoQueryClientBlanket, Security in COM
10.5 Security Related Structure Definitions
10.5.1 COAUTHINFO
Determines the authentication settings used while making a remote activation req
uest from the client scm to the server.
typedef struct _COAUTHINFO
{
DWORD dwAuthnSvc;
DWORD dwAuthzSvc;
[string] WCHAR * pwszServerPrincName;
DWORD dwAuthnLevel;
DWORD dwImpersonationLevel;
AUTH_IDENTITY * pAuthIdentityData;
DWORD dwCapabilities;
} COAUTHINFO;
Members
dwAuthnSvc
[in] A single DWORD value from the list of RPC_C_AUTHN_xxx constants indicating
the authentication service to use. It may be RPC_C_AUTHN_NONE if no authenticati
on is required.
dwAuthzSvc
[in] A single DWORD value from the list of RPC_C_AUTHZ_xxx constants indicating
the authorization service to use.
pwszServerPrincName
Pointer to a WCHAR string that indicates the server principal name to use with t
he authentication service. If you are using RPC_C_AUTHN_WINNT, the principal nam
e must be NULL.
dwAuthnLevel
[in] A single DWORD value from the list of RPC_C_AUTHN_LEVEL_xxx constants indic
ating the authentication level to use.
dwImpersonationLevel
[in] A single DWORD value from the list of RPC_C_IMP_LEVEL_xxx constants indicat
ing the impersonation level to use. Currently, only RPC_C_IMP_LEVEL_IMPERSONATE
and RPC_C_IMP_LEVEL_IDENTIFY are supported.
pAuthIdentityData
Pointer to an AUTH_IDENTITY structure that establishes the identity of the clien
t. It is authentication-service specific, as follows:
typedef struct _AUTH_IDENTITY
{
[size_is(UserLength+1)] USHORT * User;
ULONG UserLength;
[size_is(DomainLength+1)] USHORT * Domain;
ULONG DomainLength;
[size_is(PasswordLength+1)] USHORT * Password;
ULONG PasswordLength;
ULONG Flags;
} AUTH_IDENTITY;
dwCapabilities
[in] A DWORD defining flags to establish indicating the further capabilities of
this proxy. Currently, no capability flags are defined.
Remarks
The values of the COAUTHINFO structure determine the authentication settings use
d while making a remote activation request from the client's scm to the server's
scm. This structure is defined by default for NTLMSSP, and is described only f
or cases that need it to allow DCOM activations to work correctly with security
providers other than NTLMSSP, or to specify additional security information used
during remote activations for interoperability with alternate implementations o
f distributed COM
See Also
COSERVERINFO
10.5.2 COSERVERINFO
Identifies a remote machine resource to the new or enhanced activation functions
. The structure is defined as follows in the Wtypes.h header file:
typedef struct _COSERVERINFO
{
DWORD dwReserved1;
LPWSTR pwszName;
COAUTHINFO *pAuthInfo;
DWORD dwReserved2;
} COSERVERINFO;
Members
dwReserved1
Reserved for future use. Must be 0.
pszName
Pointer to the name of the machine to be used.
pAuthInfo
A non-zero value, which is a pointer to a COAUTHINFO structure, would only be us
ed when a security package other than NTLMSSP is being used.
dwReserved2
Reserved for future use. Must be 0.
Remarks
The COSERVERINFO structure is used primarily to identify a remote system in obje
ct creation functions. Machine resources are named using the naming scheme of th
e network transport. By default, all UNC ( \\server or server ) and DNS names ( server.c
om , www.foo.com , or 135.5.33.19 ) names are allowed.
If you are using the NTLMSSP security package, the default case, the pAuthinfo p
arameter should be set to zero. If you are a vendor supporting another security
package, refer to COAUTHINFO. The mechanism described there is intended to allow
DCOM activations to work correctly with security providers other than NTLMSSP,
or to specify additional security information used during remote activations for
interoperability with alternate implementations of DCOM. If pAuthInfo is set, t
hose values will be used to specify the authentication settings for the remote c
all. These settings will be passed to RpcBindingSetAuthInfoEx.
If the pAuthInfo field is not specified, any values in the AppID section of the
registry will be used to override the following default authentication settings:
dwAuthnSvc RPC_C_AUTHN_WINNT
dwAuthzSvc RPC_C_AUTHZ_NONE
pszServerPrincName NULL
dwAuthnLevel RPC_C_AUTHN_LEVEL_CONNECT
dwImpersonationLevel RPC_C_IMP_LEVEL_IMPERSONATE
pvAuthIdentityData NULL
dwCapabilities RPC_C_QOS_CAPABILITIES_DEFAULT
See Also
CLSCTX, CoGetClassObject, CoGetInstanceFromFile, CoGetInstanceFromIStorage, CoCr
eateInstanceEx, Locating a Remote Object
10.6 Security Related Enumeration Descriptions
10.6.1 RPC_C_AUTHN_XXX
These values, along with the RPC_C_AUTHZ_xxx values, are assigned to the SOLE_AU
THENICATION_SERVICE structure, which is retrieved by the CoQueryAuthenticationSe
rvices function, and passed in to the CoInitializeSecurity function.

Values
RPC_C_AUTHN_NONE
No authentication.
RPC_C_AUTHN_DCE_PRIVATE
DCE private key authentication.
RPC_C_AUTHN_DCE_PUBLIC
DCE public key authentication.
RPC_C_AUTHN_DEC_PUBLIC
DEC public key authentication (reserved for future use).
RPC_C_AUTHN_WINNT
NT LM SSP (NT Security Service).
RPC_C_AUTHN_DEFAULT
The system default authentication service.
See Also
CoInitializeSecurity, CoQueryAuthenticationServices
10.6.2 RPC_C_AUTH_LEVEL_XXX
Used in the security functions and interfaces to specify the authentication leve
l.
Values
RPC_C_AUTHN_LEVEL_NONE
Performs no authentication.
RPC_C_AUTHN_LEVEL_CONNECT
Authenticates only when the client establishes a relationship with the server. D
atagram transports always use RPC_AUTHN_LEVEL_PKT instead.
RPC_C_AUTHN_LEVEL_CALL
Authenticates only at the beginning of each remote procedure call when the serve
r receives the request. Datagram transports use RPC_C_AUTHN_LEVEL_PKT instead.
RPC_C_AUTHN_LEVEL_PKT
Authenticates that all data received is from the expected client.
RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
Authenticates and verifies that none of the data transferred between client and
server has been modified.
RPC_C_AUTHN_LEVEL_PKT_PRIVACY
Authenticates all previous levels and encrypts the argument value of each remote
procedure call.

See Also
IClientSecurity, IServerSecurity
10.6.3 RPC_C_AUTHZ_XXX
These values define what the server authorizes, and are used by methods of the I
ClientSecurity interface. Along with the RPC_C_AUTHN_xxx values, these are the v
alues assigned to the SOLE_AUTHENTICATION_SERVICE structure, which is retrieved
by the CoQueryAuthenticationServices function, and passed in to the CoInitialize
Security function.
Values
RPC_C_AUTHZ_NONE
Server performs no authorization.
RPC_C_AUTHZ_NAME
Server performs authorization based on the client's principal name.
RPC_C_AUTHZ_DCE
Server performs authorization checking using the client's DCE privilege attribut
e certificate (PAC) information, which is sent to the server with each remote pr
ocedure call made using the binding handle. Generally, access is checked against
DCE access control lists (ACLs).
See Also
SOLE_AUTHENTICATION_SERVICE
10.6.4 RPC_C_IMP_LEVEL_XXX
Used in the security functions and interfaces to specify the authentication leve
l.
Values
RPC_C_IMP_LEVEL_ANONYMOUS
(Not supported in this release.) The client is anonymous to the server. The serv
er process cannot obtain identification information about the client and it cann
ot impersonate the client.
RPC_C_IMP_LEVEL_IDENTIFY
The server can obtain the client's identity. The server can impersonate the clie
nt for ACL checking, but cannot access system objects as the client. This inform
ation is obtained when the connection is established, not on every call.
Note GetUserName will fail while impersonating at identify level. The workarou
nd is to impersonate, OpenThreadToken, revert, call GetTokenInformation, and fin
ally, call LookupAccountSid.
RPC_C_IMP_LEVEL_IMPERSONATE
The server process can impersonate the client's security context while acting on
behalf of the client. This information is obtained when the connection is estab
lished, not on every call.
RPC_C_IMP_LEVEL_DELEGATE
(Not supported in this release.) The server process can impersonate the client's
security context while acting on behalf of the client. The server process can a
lso make outgoing calls to other servers while acting on behalf of the client. T
his information is obtained when the connection is established, not on every cal
l.
Comments
See Also
CoInitializeSecurity
11. Error Handling
COM provides a rich mechanism for allowing objects to return error information t
o callers. The following interfaces are used:
· IErrorInfo Returns information from an error object.
· ICreateErrorInfo Sets error information.
· ISupportErrorInfo Identifies this object as supporting the IErrorInfo interface.
· Error handling functions.
This chapter covers the error handling interfaces.
11.1 Returning Error Information
To return error information
1. Implement the ISupportErrorInfo interface.
2. To create an instance of the generic error object, call the Crea
teErrorInfo function.
3. To set its contents, use the ICreateErrorInfo methods.
4. To associate the error object with the current logical thread, c
all the SetErrorInfo function.
The error handling interfaces create and manage an error object, which provides
information about the error. The error object is not the same as the object that
encountered the error. It is a separate object associated with the current thre
ad of execution.
11.2 Retrieving Error Information
To retrieve error information
1 Check whether the returned value represents an error that the object is
prepared to handle.
2 Call QueryInterface to get a pointer to the ISupportErrorInfo interface.
Then, call InterfaceSupportsErrorInfo to verify that the error was raised by th
e object that returned it and that the error object pertains to the current erro
r, and not to a previous call.
3 To get a pointer to the error object, call the GetErrorInfo function.
4 To retrieve information from the error object, use the IErrorInfo method
s.

If the object is not prepared to handle the error, but needs to propagate the er
ror information further down the call chain, it should simply pass the return va
lue to its caller. Because the GetErrorInfo function clears the error informatio
n and passes ownership of the error object to the caller, the function should be
called only by the object that handles the error.
11.3 Error Handling API Descriptions
11.3.1 IErrorInfo
The IErrorInfo interface provides detailed contextual error information.
Implemented by Used by Header filename Import library name
Oleaut32.dll
(32-bit systems)
Ole2disp.dll
(16-bit systems) Applications that receive rich information. Oleauto.
h
Dispatch.h Oleaut32.lib
Oledisp.lib
11.3.1.1 IErrorInfo::GetDescription
HRESULT GetDescription(
BSTR *pBstrDescription
);
Returns a textual description of the error.
Parameter
pBstrDescription
Pointer to a brief string that describes the error.
Return Value
The return value obtained from the returned HRESULT is:
Return value Meaning
S_OK Success.
Comments
The text is returned in the language specified by the locale identifier (LCID) t
hat was passed to IDispatch::Invoke for the method that encountered the error.
11.3.1.2 IErrorInfo::GetGUID
HRESULT GetGUID(
GUID *pGUID
);
Returns the globally unique identifier (GUID) of the interface that defined the
error.
Parameter
pGUID
Pointer to a GUID, or GUID_NULL, if the error was defined by the operating syste
m.
Return Value
The return value obtained from the returned HRESULT is:
Return value 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, IErrorInfo::GetGUID returns 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, an application c
an handle errors in an interface, independent of the class that implements the i
nterface.
11.3.1.3 IErrorInfo::GetHelpContext
HRESULT GetHelpContext(
DWORD *pdwHelpContext
);
Returns the Help context identifier (ID) for the error.
Parameter
pdwHelpContext
Pointer to the Help context ID for the error.
Return Value
The return value obtained from the returned HRESULT is:
Return value Meaning
S_OK Success.
Comments
IErrorInfo::GetHelpContext returns the Help context ID for the error. To find th
e Help file to which it applies, use IErrorInfo::GetHelpFile.
11.3.1.4 IErrorInfo::GetHelpFile
HRESULT GetHelpFile(
BSTR *pBstrHelpFile
);
Returns the path of the Help file that describes the error.
Parameter
pBstrHelpFile
Pointer to a string that contains the fully qualified path of the Help file.
Return Value
The return value obtained from the returned HRESULT is:
Return value Meaning
S_OK Success.
Comments
IErrorInfo::GetHelpFile returns the fully qualified path of the Help file that d
escribes the current error. IErrorInfo::GetHelpContext should be used to find th
e Help context ID for the error in the Help file.
11.3.1.5 IErrorInfo::GetSource
HRESULT GetSource(
BSTR *pBstrSource
);
Returns the language-dependent programmatic ID (ProgID) for the class or applica
tion that raised the error.
Parameter
pBstrSource
Pointer to a string containing a ProgID, in the form progname.objectname.
Return Value
The return value obtained from the returned HRESULT is:
Return value Meaning
S_OK Success.

Comments
Use IErrorInfo::GetSource to determine the class or application that is the sour
ce of the error. The language for the returned ProgID depends on the locale ID (
LCID) that was passed into the method at the time of invocation.
11.3.2 ICreateErrorInfo
The ICreateErrorInfo interface returns error information.
Implemented by Used by Header filename Import library name
Oleaut32.dll
(32-bit systems)
Oledisp.dll
(16-bit systems) Applications that return rich error information.
Oleauto.h
Dispatch.h Oleaut32.lib
Oledisp.lib
11.3.2.1 ICreateErrorInfo::SetDescription
HRESULT SetDescription(
LPCOLESTR *szDescription
);
Sets the textual description of the error.
Parameter
szDescription
A brief, zero-terminated string that describes the error.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Insufficient memory to complete the operation.
Comments
The text should be supplied in the language specified by the locale ID (LCID) th
at was passed to the method raising the error. For more information, see " LCID Attrib
ute " in Chapter 17, " Type Libraries and the Object Description Language. "
Example
hr = CreateErrorInfo(&pcerrinfo);
if (m_excepinfo.bstrDescription)
pcerrinfo->SetDescription(m_excepinfo.bstrDescription);
11.3.2.2 ICreateErrorInfo::SetGUID
HRESULT SetGUID(
REFGUID rguid
);
Sets the globally unique identifier (GUID) of the interface that defined the err
or.
Parameter
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 return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Insufficient memory to complete the operation.
Comments
ICreateErrorInfo::SetGUID sets the GUID of the interface that defined the error.
If the error was defined by the system, set ICreateErrorInfo::SetGUID to GUID_N
ULL.
This GUID does not necessarily represent the source of the error; however, the s
ource is the class or application that raised the error. Using the GUID, applica
tions can handle errors in an interface, independent of the class that implement
s the interface.
Example
hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetGUID(IID_IHello);
11.3.2.3 ICreateErrorInfo::SetHelpContext
HRESULT SetHelpContext(
DWORD dwHelpContext
);
Sets the Help context identifier (ID) for the error.
Parameter
dwHelpContext
The Help context ID for the error.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Insufficient memory to complete the operation.
Comments
ICreateErrorInfo::SetHelpContext sets the Help context ID for the error. To esta
blish the Help file to which it applies, use ICreateErrorInfo::SetHelpFile.
Example
hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetHelpContext(dwhelpcontext);
11.3.2.4 ICreateErrorInfo::SetHelpFile
HRESULT SetHelpFile(

LPCOLESTR szHelpFile
);
Sets the path of the Help file that describes the error.
Parameter
szHelpFile
The fully qualified path of the Help file that describes the error.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Insufficient memory to complete the operation.
Comments
ICreateErrorInfo::SetHelpFile sets the fully qualified path of the Help file tha
t describes the current error. Use ICreateErrorInfo::SetHelpContext to set the H
elp context ID for the error in the Help file.
Example
hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetHelpFile( " C:\myapp\myapp.hlp " );
11.3.2.5 ICreateErrorInfo::SetSource
HRESULT SetSource(
LPCOLESTR szSource
);
Sets the language-dependent programmatic identifier (ProgID) for the class or ap
plication that raised the error.
Parameter
szSource
A ProgID in the form progname.objectname.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Insufficient memory to complete the operation.

Comments
ICreateErrorInfo::SetSource should be used to identify the class or application
that is the source of the error. The language for the returned ProgID depends on
the locale identifier (LCID) that was passed to the method at the time of invoc
ation.
Example
hr = CreateErrorInfo(&pcerrinfo);
if (m_excepinfo.bstrSource)
pcerrinfo->SetSource(m_excepinfo.bstrSource);
11.3.3 ISupportErrorInfo
The ISupportErrorInfo interface ensures that error information can be propagated
up the call chain correctly. Automation objects that use the error handling int
erfaces must implement ISupportErrorInfo.

Implemented by Used by Header filename


Applications that return error information. Applications that retrieve error
information. Oleauto.h
(32-bit systems)
Dispatch.h
(16-bit systems)
11.3.3.1 ISupportErrorInfo::InterfaceSupportsErrorInfo
HRESULT InterfaceSupportsErrorInfo(
REFIID riid
);
Indicates whether or not an interface supports the IErrorInfo interface.
Parameter
riid
Pointer to an interface identifier (IID).
Return Value
The return value obtained from the returned HRESULT is one of the following:

Return value Meaning


S_OK Interface supports IErrorInfo.
S_FALSE Interface does not support IErrorInfo.
Comments
Objects that support the IErrorInfo interface must also implement this interface
.
Programs that receive an error return value should call QueryInterface to get a
pointer to the ISupportErrorInfo interface, and then call InterfaceSupportsError
Info with the riid of the interface that returned the return value. If Interface
SupportsErrorInfo returns S_FALSE, then the error object does not represent an e
rror returned from the caller, but from somewhere else. In this case, the error
object can be considered incorrect and should be discarded.
If ISupportErrorInfo returns S_OK, use the GetErrorInfo function to get a pointe
r to the error object.
Example
The following example implements the ISupportErrorInfo for the Lines sample. The
IErrorInfo implementation also supports the AddRef, Release, and QueryInterface
members inherited from the IUnknown interface.
CSupportErrorInfo::CSupportErrorInfo(IUnknown FAR* punkObject, REFIID riid)
{
m_punkObject = punkObject;
m_iid = riid;
}

STDMETHODIMP
CSupportErrorInfo::QueryInterface(REFIID iid, void FAR* FAR* ppv)
{
return m_punkObject->QueryInterface(iid, ppv);
}

STDMETHODIMP_(ULONG)
CSupportErrorInfo::AddRef(void)
{
return m_punkObject->AddRef();
}
STDMETHODIMP_(ULONG)
CSupportErrorInfo::Release(void)
{
return m_punkObject->Release();
}
STDMETHODIMP
CSupportErrorInfo::InterfaceSupportsErrorInfo(REFIID riid)
{
return (riid == m_iid) ? NOERROR : ResultFromScode(S_FALSE);
}
11.3.4 CreateErrorInfo
HRESULT CreateErrorInfo(
ICreateErrorInfo **pperrinfo
);
Creates an instance of a generic error object.
Parameter
pperrinfo
Pointer to a system-implemented generic error object.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Could not create the error object.
Comments
This function returns a pointer to a generic error object, which you can use wit
h QueryInterface on ICreateErrorInfo to set its contents. You can then pass the
resulting object to SetErrorInfo. The generic error object implements both ICrea
teErrorInfo and IErrorInfo.
Example
ICreateErrorInfo *perrinfo;
HRESULT hr;
hr = CreateErrorInfo(&pcerrinfo);
11.3.5 GetErrorInfo
HRESULT GetErrorInfo(dwReserved, pperrinfo)
DWORD dwReserved,
IErrorInfo **pperrinfo
);
Obtains the error information pointer set by the previous call to SetErrorInfo_o
a96_SetErrorInfo in the current logical thread.
Parameters
dwReserved
Reserved for future use. Must be zero.
pperrinfo
Pointer to a pointer to an error object.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 t
he current logical thread. It transfers ownership of the error object to the cal
ler, and clears the error state for the thread.
11.3.6 SetErrorInfo
HRESULT SetErrorInfo(
DWORD dwReserved,
IErrorInfo *perrinfo
);
Sets the error information object for the current thread of execution.
Parameters
dwReserved
Reserved for future use. Must be zero.
perrinfo
Pointer to an error object.
Return Value
The return value obtained from the returned HRESULT is:
Return value Meaning
S_OK Success.

Comments
This function releases the existing error information object, if one exists, and
sets the pointer to perrinfo. Use this function after creating an error object
that associates the object with the current thread of execution.
If the property or method that calls SetErrorInfo is called by DispInvoke, then
DispInvoke will fill the EXCEPINFO parameter with the values specified in the er
ror information object. DispInvoke will return DISP_E_EXCEPTION when the propert
y or method returns a failure return value for DispInvoke.
Virtual function table (VTBL) binding controllers that do not use IDispatch::Inv
oke can get the error information object by using GetErrorInfo. This allows an o
bject that supports a dual interface to use SetErrorInfo, regardless of whether
the client uses VTBL binding or IDispatch.
Example
ICreateErrorInfo *pcerrinfo;
IErrorInfo *perrinfo;
HRESULT hr;
hr = CreateErrorInfo(&pcerrinfo);
hr = pcerrinfo->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &perrinfo);
if (SUCCEEDED(hr))
{
SetErrorInfo(0, perrinfo);
perrinfo->Release();
}
pcerrinfo->Release();

11.3.7 HRESULT
The key type involved in COM error reporting is HRESULT. In addition, the COM Li
brary provides a few functions and macros to help applications of any kind deal
with error information. An HRESULT is a simple 32-bit value:
typedef LONG HRESULT;
An HRESULT is divided up into an internal structure that has four fields with th
e following format (numbers indicate bit positions):

S: (1 bit) Severity field:


0 Success. The function was successful; it behaved according to its proscr
ibed semantics.
1 Error. The function failed due to an error condition.
R: (2 bits) Reserved for future use; must be set to zero by present program
s generating HRESULTs; present code should not take action that relies on any pa
rticular bits being set or cleared this field.
Facility: (13 bits) Indicates which group of status codes this belongs to.
New facilities must be allocated by a central coordinating body since they need
to be universally unique. However, the need for new facility codes is very smal
l. Most cases can and should use FACILITY_ITF. See the section Use of FACILITY_IT
F below.
Code: (16 bits) Describes what actually took place, error or otherwise.
COM presently defines the following facility codes:
Facility Name Facility Value Description
FACILITY_NULL 0 Used for broadly applicable common status codes that hav
e no specific grouping. S_OK belongs to this facility, for example.
FACILITY_ITF 4 Used for by far the majority of result codes that are re
turned from an interface member function. Use of this facility indicates that th
e meaning of the error code is defined solely by the definition of the particula
r interface in question; an HRESULT with exactly the same 32-bit value returned
from another interface might have a different meaning
FACILITY_RPC 1 Used for errors that result from an underlying remote pr
ocedure call implementation. In general, this specification does not explicitly
document the RPC errors that can be returned from functions, though they neverth
eless can be returned in situations where the interface being used is in fact re
moted
FACILITY_DISPATCH 2 Used for IDispatch-interface-related status code
s.
FACILITY_STORAGE 3 Used for persistent-storage-related status codes
. Status codes whose code (lower 16 bits) value is in the range of DOS error cod
es (less than 256) have the same meaning as the corresponding DOS error.
FACILITY_WIN32 7 Used to provide a means of mapping an error code from a
function in the Win32 API into an HRESULT. The semantically significant part of
a Win32 error is 16 bits large.
FACILITY_WINDOWS 8 Used for additional error codes from Microsoft-d
efined interfaces.
FACILITY_CONTROL 10 Used for ActiveX Controls-related error values.

A particular HRESULT value by convention uses the following naming structure:


<Facility>_<Sev>_<Reason>
where <Facility> is either the facility name or some other distinguishing identi
fier, <Sev> is a single letter, one of the set { S, E } indicating the severity
(success or error), and <Reason> is a short identifier that describes the meanin
g of the code. Status codes from FACILITY_NULL omit the <Facility>_ prefix. For
example, the status code E_NOMEMORY is the general out-of memory error. All code
s have either S_ or E_ in them allowing quick visual determination if the code m
eans success or failure.
The general success HRESULT is named S_OK, meaning everything worked as per the func
tion specification. The value of this HRESULT is zero. In addition, as it is use
ful to have functions that can succeed but return Boolean results, the code S_FA
LSE is defined are success codes intended to mean function worked and the result
is false.
#define S_OK 0
#define S_FALSE 1
A list of presently-defined standard error codes and their semantics can be foun
d in Appendix A.
From a general interface design perspective, success status codes should be used f
or circumstances where the consequence of what happened in a method invocation is
most naturally understood and dealt with by client code by looking at the out-va
lues returned from the interface function: NULL pointers, etc. Error status codes
should in contrast be used in situations where the function has performed in a m
anner that would naturally require out of band processing in the client code, logi
c that is written to deal with situations in which the interface implementation
truly did not behave in a manner under which normal client code can make normal
forward progress. The distinction is an imprecise and subtle one, and indeed man
y existing interface definitions do not for historical reasons abide by this rea
soning. However, with this approach, it becomes feasible to implement automated
COM development tools that appropriately turn the error codes into exceptions as
was mentioned above.
Interface functions in general take the form:
HRESULT ISomeInteface::SomeFunction(ARG1_T arg1, ... , ARGN_T argn, RET_T * pret
);
Stylistically, what would otherwise be the return value is passed as an out-valu
e through the last argument of the function. COM development tools which map err
or returns into exceptions might also consider mapping the last argument of such
a function containing only one out-parameter into what the programmer sees as t
he return value of the method invocation.
The COM remoting infrastructure only supports reporting of RPC-induced errors (s
uch as communication failures) through interface member functions that return HR
ESULTs. For interface member functions of other return types (e.g.: void), such
errors are silently discarded. To do otherwise would, to say the least, signific
antly complicate local / remote transparency.
11.3.7.1 Use of FACILITY_ITF
The use of FACILITY_ITF deserves some special discussion with respect to interfa
ces defined in COM and interfaces that will be defined in the future. Where as s
tatus codes with other facilities (FACILITY_NULL, FACILITY_RPC, etc.) have unive
rsal meaning, status codes in FACILITY_ITF have their meaning completely determi
ned by the interface member function (or API function) from which they are retur
ned; the same 32-bit value in FACILITY_ITF returned from two different interface
functions may have completely different meanings.
The reasoning behind this distinction is as follows. For reasons of efficiency,
it is unreasonable to have the primary error code data type (HRESULT) be larger
than 32 bits in size. 32 bits is not large enough, unfortunately, to enable COM
to develop an allocation policy for error codes that will universally avoid conf
lict between codes allocated by different non-communicating programmers at diffe
rent times in different places (contrast, for instance, with what is done with I
IDs and CLSIDs). Therefore, COM structures the use of the 32 bit SCODE in such a
way so as to allow the a central coordinating body to define some universally d
efined error codes while at the same time allowing other programmers to define n
ew error codes without fear of conflict by limiting the places in which those fi
eld-defined error codes can be used. Thus:
1. Status codes in facilities other than FACILITY_ITF can only be defined b
y the central coordinating body.
2. Status codes in facility FACILITY_ITF are defined solely by the definer
of the interface or API by which said status code is returned. That is, in order
to avoid conflicting error codes, a human being needs to coordinate the assignm
ent of codes in this facility, and we state that he who defines the interface ge
ts to do the coordination.
COM itself defines a number of interfaces and APIs, and so COM defines many stat
us codes in FACILITY_ITF. By design, none of the COM-defined status codes in fac
t have the same value, even if returned by different interfaces, though it would
have been legal for COM to do otherwise.
Likewise, it is possible (though not required) for designers of COM interface su
ites to coordinate the error codes across the interfaces in that suite so as to
avoid duplication. The designers of the COM interface suite, for example, ensure
d such lack of duplication.
Thus, with regard to which errors can be returned by which interface functions,
it is the case that, in the extreme,
· It is legal that any COM-defined error code may in fact be returned by any COM-d
efined interface member function or API function. This includes errors presently
defined in FACILITY_ITF. Further, COM may in the future define new failure code
s (but not success codes) that may also be so ubiquitously returned.
Designers of interface suites may if they wish choose to provide similar
rules across the interfaces in their suites.
· Further, any error in FACILITY_RPC or other facility, even those errors not pres
ently defined, may be returned.
Clients must treat error codes that are unknown to them as synonymous with E_UNE
XPECTED, which in general should be and is presently a legal error return value
from each and every interface member function in all interfaces; interface desig
ners and implementors are responsible to insure that any newly defined error cod
es they should choose to invent or return will be such that that existing client
s with code treating generic cases as synonymous with E_UNEXPECTED this will hav
e reasonable behavior.
In short, if you know the function you invoked, you know as a client how to unam
biguously take action on any error code you receive. The interface implementor i
s responsible for maintaining your ability to do same.
Normally, of course, only a small subset of the COM-defined status codes will be
usefully returned by a given interface function or API, but the immediately pre
ceding statements are in fact the actual interoperability rules for the COM-defi
ned interfaces. This specification endeavors to point out which error codes are
particularly useful for each function, but code must be written to correctly han
dle the general rule.
The present document is, however, precise as to which success codes may legally
be returned.
Conversely, it is only legal to return a status code from the implementation of
an interface member function which has been sanctioned by the designer of that i
nterface as being legally returnable; otherwise, there is the possibility of con
flict between these returned code values and the codes in-fact sanctioned by the
interface designer. Pay particular attention to this when propagating errors fr
om internally called functions. Nevertheless, as noted above, callers of interfa
ces must to guard themselves from imprecise interface implementations by treatin
g any otherwise unknown returned error code (in contrast with success code) as s
ynonymous with E_UNEXPECTED: experience shows that programmers are notoriously l
ax in dealing with error handling. Further, given the third bullet point above,
this coding practice is required by clients of the COM-defined interfaces and AP
Is. Pragmatically speaking, however, this is little burden to programmers: norma
l practice is to handle a few special error codes specially, but treat the rest
generically.
All the COM-defined FACILITY_ITF codes will, in fact, have a code value which li
es in the region 0x0000 0x01FF. Thus, while it is indeed legal for the definer o
f a new function or interface to make use of any codes in FACILITY_ITF that he c
hooses in any way he sees fit, it is highly recommended that only code values in
the range 0x0200 0xFFFF be used, as this will reduce the possibility of acciden
tal confusion with any COM-defined errors. It is also highly recommended that de
signers of new functions and interfaces consider defining as legal that most if
not all of their functions can return the appropriate status codes defined by CO
M in facilities other than FACILITY_ITF. E_UNEXPECTED is a specific error code t
hat most if not all interface definers will wish to make universally legal.
11.3.8 IErrorLog
The IErrorLog interface is an abstraction for an error log that is used to commu
nicate detailed error information between a client and an object. The caller of
the single interface method, AddError, simply logs an error where the error is a
n EXCEPINFO structure related to a specific property. The implementer of the int
erface is responsible for handling the error in whatever way it desires.
IErrorLog is used in the protocol between a client that implements IPropertyBag
and an object that implements IPersistPropertyBag.
When to Implement
A container implements IErrorLog to provide a control with a means of logging er
rors when the control is loading its properties from the container-provided prop
erty bag.
When to Use
A control logs calls the single method in this interface to log any errors that
occur when it is loading its properties.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IErrorLog Method Description
AddError Logs an error, an EXCEPINFO structure, in the error log during t
he property load process for a named property.
See Also
IPersistPropertyBag, IPropertyBag

IErrorLog::AddError
Logs an error, an EXCEPINFO structure, in the error log during the property load
process for a named property.
HRESULT AddError(
LPCOLESTR pszPropName, //Pointer to the name of the property
// involved with the error
LPEXCEPINFO pException //Pointer to the caller-initialized
// EXCEPINFO structure describing the error
);
Parameters
pszPropName
[in] Pointer to the name of the property involved with the error. Cannot be NULL
.
pExcepInfo
[in] Pointer to the caller-initialized EXCEPINFO structure that describes the er
ror to log. Cannot be NULL.
Return Values
S_OK
The error was logged successfully.
E_FAIL
There was a problem logging the error.
E_OUTOFMEMORY
There was not enough memory to log the error.
E_POINTER
The address in pszPropName or pExceptInfo is not valid (such as NULL). The calle
r must supply both.
Remarks
E_NOTIMPL is not a valid return code as the method is the only one in the entire
interface.

12. Enumerators and Enumerator Interfaces


A frequent programming task is that of iterating through a sequence of items. Th
e COM interfaces are no exception: there are places in several interfaces descri
bed in this specification where a client of some object needs to iterate through
a sequence of items controlled by the object. COM supports such enumeration thr
ough the use of enumerator objects. Enumerators cleanly separate the caller s desire
to loop over a set of objects from the callee s knowledge of how to accomplish th
at function.
Enumerators are just a concept; there is no actual interface called IEnumerator
or IEnum or the like. This is due to the fact that the function signatures in an
enumerator interface must include the type of the things that the enumerator en
umerates. As a consequence, separate interfaces exist for each kind of thing tha
t can be enumerated. However, the difference in the type being enumerated is the
only difference between each of these interfaces; they are all used in fundamen
tally the same way. In other words, they are generic over the element type. This d
ocument describes the semantics of enumerators using a generic interface IEnum a
nd the C++ parameterized type syntax where ELT_T, which stands for ELemenT Type is
representative of the type involved in the enumeration:
[
object,
uuid(<IID_IEnum <ELT_T>>), // IID_IEnum<ELT_T>
pointer_default(unique)
]
interface IEnum<ELT_T> : IUnknown
{
HRESULT Next( [in] ULONG celt, [out] IUnknown **rgelt, [out] ULONG *pceltFet
ched );
HRESULT Skip( [in] ULONG celt );
HRESULT Reset( void );
HRESULT Clone( [out] IEnum<ELT_T>**ppenum );
}
A typical use of an enumerator is the following.
//Somewhere there s a type called String
typedef char * String;
//Interface defined using template syntax
typedef IEnum<char *> IEnumString;
...
interface IStringManager {
virtual IEnumString* EnumStrings(void) = 0;
};
...
void SomeFunc(IStringManager * pStringMan) {
char * psz;
IEnumString * penum;
penum=pStringMan->EnumStrings();
while (S_OK==penum->Next(1, &psz, NULL))
{
//Do something with the string in psz and free it
}
penum->Release();
return;
}
12.1 IEnum::Next
HRESULT IEnum::Next(celt, rgelt, pceltFetched)
Attempt to get the next celt items in the enumeration sequence, and return them
through the array pointed to by rgelt. If fewer than the requested number of ele
ments remain in the sequence, then just return the remaining ones; the actual nu
mber of elements returned is passed through *pceltFetched (unless it is NULL). I
f the requested celt elements are in fact returned, then return S_OK; otherwise
return S_FALSE. An error condition other than simply not that many elements left w
ill return an SCODE which is a failure code rather than one of these two success
values.
To clarify:
If S_OK is returned, then on exit the all celt elements requested are valid and
returned in rgelt.
If S_FALSE is returned, then on exit only the first *pceltFetched entries of rge
lt are valid. The contents of the remaining entries in the rgelt array are indet
erminate.
If an error value is returned, then on exit no entries in the rgelt array are va
lid; they are all in an indeterminate state.
Argument Type Description
celt ULONG The number of elements that are to be returned.
rgelt ELT_T* An array of size at least celt in which the next elements are to
be returned.
pceltFetched ULONG* May be NULL if celt is one. If non-NULL, then this is se
t with the number of elements actually returned in rgelt.
Return Value Meaning
S_OK Success. The requested number of elements were returned.
S_FALSE Success. Fewer than the requested number of elements were returned.
E_UNEXPECTED An unknown error occurred.
12.2 IEnum::Skip
HRESULT IEnum::Skip(celt)
Attempt to skip over the next celt elements in the enumeration sequence. Return
S_OK if this was accomplished, or S_FALSE if the end of the sequence was reached
first.
Argument Type Description
celt ULONG The number of elements that are to be skipped.
Return Value Meaning
S_OK Success. The requested number of elements were skipped.
S_FALSE Success. Some skipping was done, but the end of the sequence was hit bef
ore the requested number of elements could be skipped.
E_UNEXPECTED An unknown error occurred.
12.3 IEnum::Reset
HRESULT IEnum::Reset(void)
Reset the enumeration sequence back to the beginning.
Note that there is no intrinsic guarantee that exactly the same set of objects w
ill be enumerated the second time as was enumerated the first. Though clearly ve
ry desirable, whether this is the case or not is dependent on the collection bei
ng enumerated; some collections will simply find it too expensive to maintain th
is condition. Consider enumerating the files in a directory, for example, while
concurrent users may be making changes.
Return Value Meaning
S_OK Success. The enumeration was reset to its beginning.
E_UNEXPECTED An unknown error occurred.
12.4 IEnum::Clone
HRESULT IEnum::Clone(ppenum)
Return another enumerator which contains exactly the same enumeration state as t
his one. Using this function, a client can remember a particular point in the en
umeration sequence, then return to it at a later time. Notice that the enumerato
r returned is of the same actual interface as the one which is being cloned.
Caveats similar to the ones found in IEnum::Reset regarding enumerating the same
sequence twice apply here as well.
Argument Type Description
ppenum IEnum<ELT_T>** The place in which to return the clone enumerator.
Return Value Meaning
S_OK Success. The enumeration was reset to its beginning.
E_UNEXPECTED An unknown error occurred.
13. Connectable Objects
The COM technology known as Connectable Objects (also called connection points ) su
pports a generic ability for any object, called in this context a connectable obje
ct, to express these capabilities:
The existence of outgoing interfaces, such as event sets
The ability to enumerate the IIDs of the outgoing interfaces
The ability to connect and disconnect sinks to the object for those outgoing IIDs
The ability to enumerate the connections that exist to a particular outgoing int
erface.
Support for these capabilities involves four interfaces: IConnectionPointContai
ner, IEnumConnectionPoints, IConnectionPoint, and IEnumConnections. A connectabl
e object implements IConnectionPointContainer to indicate existence of outgoing i
nterfaces. Through this interface a client can enumerate connection points for
each outgoing IID (via an enumerator with IEnumConnectionPoints) and can obtain
an IConnectionPoint interface to a connection point for each IID. Through a con
nection point a client starts or terminates an advisory loop with the connectabl
e object and the client s own sink. The connection point can also enumerate the c
onnections it knows about through an enumerator with IEnumConnections.
13.1 Connectable Objects Interface Descriptions
13.1.1 IConnectionPoint
The ability to connect to a single outgoing interface (that is, for a unique IID
) is provided by a connection point sub-object that is conceptually owned by the c
onnectable object. The object is separate to avoid circular reference counting
problems. Through this interface the connection point allows callers to connect
a sink to the connectable object, to disconnect a sink, or to enumerate the exi
sting connections.
IDL:
[
uuid(B196B286-BAB4-101A-B69C-00AA00341D07)
, object, pointer_default(unique)
]
interface IConnectionPoint : IUnknown
{
HRESULT GetConnectionInterface([out] IID *pIID);
HRESULT GetConnectionPointContainer([out] IConnectionPointContainer **ppCPC)
;
HRESULT Advise([in] IUnknown *pUnk, [out] DWORD *pdwCookie);
HRESULT Unadvise([in] DWORD dwCookie);
HRESULT EnumConnections([out] IEnumConnections **ppEnum);
}

A connection point is allowed to stipulate how many connections (one or more) it


will allow in its implementation of Advise. A connection point that allows onl
y one interface can return E_NOTIMPL from EnumConnections.
13.1.1.1 IConnectionPoint::GetConnectionInterface
HRESULT IConnectionPoint::GetConnectionInterface([out] IID *pIID);
Returns the IID of the outgoing interface managed by this connection point. Thi
s is provided such that a client of IEnumConnectionPoints can determine the IID
of each connection point thus enumerated. The IID returned from this method mus
t enable the caller to access this same connection point through IConnectionPoin
tContainer::FindConnectionPoint.

Argument Type Description


pIID IID * [out] A pointer to the caller s variable to receive the IID of t
he outgoing interface managed by this connection point.
Return Value Meaning
S_OK Success.
E_POINTER The address in pIID is not valid (such as NULL)
E_UNEXPECTED An unknown error occurred.
Comments:
This function must be completely implemented in any connection point; therefore
E_NOTIMPL is not an acceptable return code.
13.1.1.2 IConnectionPoint::GetConnectionPointContainer
HRESULT IConnectionPoint::GetConnectionPointContainer([out] IConnectionPointCont
ainer **ppCPC);
Retrieves the IConnectionPointContainer interface pointer to the connectable obj
ect that conceptually owns this connection point. The caller becomes responsibl
e for the pointer on a successful return.

Argument Type Description


ppCPC IConnectionPointContainer * [out] A pointer to the caller s variable i
n which to return a pointer to the connectable object s IConnectionPointContainer
interface. The connection point will call IConnectionPointContainer::AddRef bef
ore returning and the caller must call IConnectionPoint::Release when it is done
using the pointer.
Return Value Meaning
S_OK Success.
E_POINTER The value in ppCPC is not valid (such as NULL)
E_UNEXPECTED An unknown error occurred.
Comments:
E_NOTIMPL is not an allowable return code.
13.1.1.3 IConnectionPoint::Advise
HRESULT IConnectionPoint::Advise([in] IUnknown *pUnk, [out] DWORD *pdwCookie);
Establishes an advisory connection between the connection point and the caller s s
ink object identified with pUnk. The connection point must call pUnk->QueryInte
rface(iid, ...) on this pointer in order to obtain the correct outgoing interfac
e pointer to call when events occur, where iid is the inherent outgoing interfac
e IID managed by the connection point (that is, the that when passed to IConnect
ionPointContainer::FindConnectionPoint would return an interface pointer to this
same connection point).
Upone successful return, the connection point provides a unique cookie in *pdwCook
ie that must be later passed to IConnectionPoint::Unadvise to terminate the conn
ection.
Argument Type Description
pUnk IUnknown * [in] The IUnknown pointer to the client s sink that wis
hes to receive calls for the outgoing interface managed by this connection point
. The connection point must query this pointer for the correct outgoing interfa
ce. If this query fails, this member returns CONNECT_E_CANNOTCONNECT.
pdwCookie DWORD * [out] A pointer to the caller s variable that is to rece
ive the connection cookie when connection is successful. This cookie must be uniq
ue for each connection to any given instance of a connection point.
Return Value Meaning
S_OK The connection has been established and *pdwCookie has the connection ke
y.
E_POINTER The value of pUnk or pdwCookie is not valid (NULL cannot be pass
ed for either argument)
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There was not enough memory to complete the operation, such as i
f the connection point failed to allocate memory in which to store the sink s inte
rface pointer.
CONNECT_E_ADVISELIMIT The connection point has already reached its limit of co
nnections and cannot accept any more.
CONNECT_E_CANNOTCONNECT The sink does not support the interface required by this
connection point.
13.1.1.4 IConnectionPoint::Unadvise
HRESULT IConnectionPoint::Unadvise([in] DWORD dwCookie);
Terminates an advisory connection previously established through IConnectionPoin
t::Advise. The dwCookie argument identifies the connection to terminate.

Argument Type Description


dwCookie DWORD [in] The connection cookie previously returned from ICo
nnectionPoint::Advise.
Return Value Meaning
S_OK The connection was successfully terminated.
E_UNEXPECTED An unknown error occurred.
CONNECT_E_NOCONNECTION dwCookie does not represent a value connection to this c
onnection point.
13.1.1.5 IConnectionPoint::EnumConnections
HRESULT IConnectionPoint::EnumConnections([out] IEnumConnections **ppEnum);
Creates an enumerator object for iteration through the connections that exist to
this connection point.
Argument Type Description
ppEnum IEnumConnections * [out] A pointer to the caller s variable to rece
ive the interface pointer of the newly created enumerator. The caller is respon
sible for releasing this pointer when it is no longer needed.
Return Value Meaning
S_OK Success.
E_POINTER The address in ppEnum is not valid (such as NULL)
E_NOTIMPL The connection point does not support enumeration.
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There was not enough memory to create the enumerator.
13.1.2 IConnectionPointContainer
When implemented on an object, makes the object connectable and expresses the exi
stence of outgoing interfaces on the object. Through this interface a client ma
y either locate a specific connection point for one IID or it can enumerate the co
nnections points that exist.
IDL:
[
uuid(B196B284-BAB4-101A-B69C-00AA00341D07)
, object, pointer_default(unique)
]
interface IConnectionPointContainer : IUnknown
{
HRESULT EnumConnectionPoints([out] IEnumConnectionPoints **ppEnum);
HRESULT FindConnectionPoint([in] REFIID riid
, [out] IConnectionPoint **ppCP);
}
13.1.2.1 IConnectionPointContainer::EnumConnectionPoints
HRESULT IConnectionPointContainer::EnumConnectionPoints([out] IEnumConnectionPoi
nts **ppEnum);
Creates an enumerator of all the connection points supported in the connectable
object, one connection point per IID. Since IEnumConnectionPoints enumerates IC
onnectionPoint* types, the caller must use IConnectionPoint::GetConnectionInterf
ace to determine the actual IID that the connection point supports.
The caller of this member must call (*ppEnum)->Release when the enumerator objec
t is no longer needed.
Argument Type Description
ppEnum IEnumConnectionPoints * [out] A pointer to the caller s variable that is
to receive the interface pointer to the enumerator. The caller is responsible
for releasing this pointer after this function returns successfully.
Return Value Meaning
S_OK The enumerator was created successfully.
E_UNEXPECTED An unknown error occurred.
E_POINTER The value passed in ppEnum is not valid (such as NULL).
E_OUTOFMEMORY There was not enough memory to create the enumerator object.

Comments:
E_NOTIMPL is specifically disallowed because outside of type information there w
ould be no other means through which a caller could find the IIDs of the outgoin
g interfaces.
13.1.2.2 IConnectionPointContainer::FindConnectionPoint
HRESULT FindConnectionPoint([in] REFIID riid , [out] IConnectionPoint **ppCP);
Asks the connectable object if it has a connection point for a particular IID, and
if so, returns the IConnectionPoint interface pointer to that connection point.
Upon successful return, the caller must call IConnectionPoint::Release when th
at connection point is no longer needed.
Note that this function is the QueryInterface equivalent for an object s outgoing
interfaces, where the outgoing interface is specified with riid and where the in
terface pointer returned is always that of a connection point.

Argument Type Description


riid REFIID [in] A reference to the outgoing interface IID whose connecti
on point is being requested.
ppCP IConnectionPoint ** [out] The address of the caller s variable that
is to receive the IConnectionPoint interface pointer to the connection point tha
t manages the outgoing interface identified with riid. This is set to NULL on f
ailure of the call; otherwise the caller must call IConnectionPoint::Release whe
n the connection point is no longer needed.
Return Value Meaning
S_OK The call succeeded and *ppCP has a valid interface pointer.
E_POINTER The address passed in ppCP is not valid (such as NULL)
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There was not enough memory to carry out the operation, such as
not being able to create a new connection point object.
CONNECT_E_NOCONNECTION This connectable object does not support the outgoing in
terface specified by riid.
Comments:
E_NOTIMPL is not allowed as a return code for this member. Any implementation of
IConnectionPointContainer must implement this method.
13.1.3 IEnumConnectionPoints
A connectable object can be asked to enumerate its supported connection points in
essence, it s outgoing interfaces through IConnectionPointContainer::EnumConnectionP
oints. The resulting enumerator returned from this member implements the interf
ace IEnumConnectionPoints through which a client can access all the individual c
onnection point sub-objects supported within the connectable object itself, wher
e each connection point, of course, implements IConnectionPoint.
Therefore IEnumConnectionPoints is a standard enumerator interface typed for ICo
nnectionPoint*.
IDL:
[
uuid(B196B285-BAB4-101A-B69C-00AA00341D07)
, object, pointer_default(unique)
]
interface IEnumConnectionPoints : IUnknown
{
HRESULT Next([in] ULONG cConnections
, [out, max_is(cConnections)] IConnectionPoint **rgpcn
, [out] ULONG *pcFetched);
HRESULT Skip([in] ULONG cConnections);
HRESULT Reset(void);
HRESULT Clone([out] IEnumConnectionPoints **ppEnum);
}
13.1.3.1 IEnumConnectionPoints::Next
HRESULT IEnumConnectionPoints::Next([in] ULONG cConnections , [out, max_is(cConn
ections)] IConnectionPoint **rgpcn, [out] ULONG *pcFetched);
Enumerates the next cConnections elements in the enumerator s list, returning them
in rgpcn along with the actual number of enumerated elements in pcFetched. The
caller is responsible for calling IConnectionPoint::Release through each pointe
r returned in rgpcn.

Argument Type Description


cConnections ULONG [in] Specfies the number of IConnectionPoint * values
to return in the array pointed to by rgpcn. This argument must be 1 if pcFetch
ed is NULL.
rgpcn IConnectionPoint ** [out] A pointer to a caller-allocated IConnect
ionPoint * array of size cConnections in which to return the enumerated connecti
on points. The caller is responsible for calling IConnectionPoint::Release thro
ugh each pointer enumerated into the array once this method returns successfully
. If cConnections is greater than one the caller must also pass a non-NULL poin
ter passed to pcFetched to know how many pointers to release.
pcFetched ULONG [out] A pointer to the variable to receive the actual
number of connection points enumerated in rgpcn. This argument can be NULL in w
hich case the cConnections argument must be 1.
Return Value Meaning
S_OK The requested number of elements has been returned and *pcFetched (if no
n-NULL) is set to cConnections if
S_FALSE The enumerator returned fewer elements than cConnections because there w
ere not that many elements left in the list.. In this case, unused elements in r
gpcn in the enumeration are not set to NULL and *pcFetched holds the number of v
alid entries, even if zero is returned.
E_POINTER The address in rgpcn is not valid (such as NULL)
E_INVALIDARG The value of cConnections is not 1 when pcFetched is NULL; or th
e value of cConnections is zero.
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There is not enough memory to enumerate the elements.
Comments:
E_NOTIMPL is not allowed as a return value. If an error value is returned, no e
ntries in the rgpcn array are valid on exit and require no release.
13.1.3.2 IEnumConnectionPoints::Skip
HRESULT IEnumConnectionPoints::Skip([in] ULONG cConnections); Instructs the enum
erator to skip the next cConnections elements in the enumeration such that the n
ext call to IEnumConnectionPoints::Next will not return those elements.

Argument Type Description


cConnections ULONG [in] Specifies the number of elements to skip in the enu
meration.
Return Value Meaning
S_OK The number of elements skipped is cConnections.
S_FALSE The enumerator skipped fewer than cConnections because there were not th
at many left in the list. The enumerator will, at this point, be positioned at
the end of the list such that subsequent calls to Next (without an intervening R
eset) will return zero elements.
E_INVALIDARG The value of cConnections is zero, which is not valid.
E_UNEXPECTED An unknown error occurred.
13.1.3.3 IEnumConnectionPoints::Reset
HRESULT IEnumConnectionPoints::Reset(void);
Instructs the enumerator to position itself back to the beginning of the list of
elements.

Argument Type Description


none
Return Value Meaning
S_OK The enumerator was successfully reset to the beginning of the list.
S_FALSE The enumerator was not reset to the beginning of the list.
E_UNEXPECTED An unknown error occurred.
Comments:
There is no guarantee that the same set of elements will be enumerated on each p
ass through the list: it depends on the collection being enumerated. It is too e
xpensive for some collections, such as files in a directory, to maintain this co
ndition.
13.1.3.4 IEnumConnectionPoints::Clone
HRESULT IEnumConnectionPoints::Clone([out] IEnumConnectionPoints **ppEnum);
Creates another connection point enumerator with the same state as the current e
numerator, which iterates over the same list. This makes it possible to record
a point in the enumeration sequence in order to return to that point at a later
time.

Argument Type Description


ppEnum IEnumConnectionPoints** [out] The address of the variable to receive t
he IEnumConnectionPoints interface pointer to the newly created enumerator. The
caller must release this new enumerator separately from the first enumerator.

Return Value Meaning


S_OK Clone creation succeeded.
E_NOTIMPL Cloning is not supported for this enumerator.
E_POINTER The address in ppEnum is not valid (such as NULL)
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There is not enough memory to create the clone enumerator.
13.1.4 IEnumConnections
Any individual connection point can support enumeration of its known connections
through IConnectionPoint::EnumConnections. The enumerator created by this func
tion implements the interface IEnumConnections which deals with the type CONNECT
DATA. Each CONNECTDATA structure contains the the IUnknown * of a connected sin
k and the dwCookie that was returned by IConnectionPoint::Advise when that sink
was connected. When enumerating connections through IEnumConnections, the enume
rator is responsible for calling IUnknown::AddRef through the pointer in each en
umerated structure, and the caller is responsible to later call IUnknown::Releas
e when those pointers are no longer needed.
IDL:
[
uuid(B196B287-BAB4-101A-B69C-00AA00341D07)
, object, pointer_default(unique)
]
interface IEnumConnections : IUnknown
{
typedef struct tagCONNECTDATA
{
IUnknown *pUnk;
DWORD dwCookie;
} CONNECTDATA;
typedef struct tagCONNECTDATA *PCONNECTDATA;
typedef struct tagCONNECTDATA *LPCONNECTDATA;
HRESULT Next([in] ULONG cConnections
, [out, max_is(cConnections)] CONNECTDATA *rgpcd
, [out] ULONG *pcFetched);
HRESULT Skip([in] ULONG cConnections);
HRESULT Reset(void);
HRESULT Clone([out] IEnumConnections **ppEnum);
}

13.1.4.1 IEnumConnections::Next
HRESULT IEnumConnections::Next([in] ULONG cConnections ,
[out, max_is(cConnections)] CONNECTDATA *rgpcd,
[out] ULONG *pcFetched);
Enumerates the next cConnections elements in the enumerator s list, returning them
in rgpcd along with the actual number of enumerated elements in pcFetched. The
caller is responsible for calling IUnknown::Release through each pUnk pointer r
eturned in the structure elements of rgpcd.

Argument Type Description


cConnections ULONG [in] Specfies the number of CONNECTDATA structures to
return in the array pointed to by rgpcd. This argument must be 1 if pcFetched
is NULL.
rgpcd CONNECTDATA * [out] A pointer to a caller-allocated CONNECTDATA arra
y of size cConnections in which to return the enumerated connections. The calle
r is responsible for calling CONNECTDATA.pUnk->Release for each element in the a
rray once this method returns successfully. If cConnections is greater than one
the caller must also pass a non-NULL pointer passed to pcFetched to know how ma
ny pointers to release.
pcFetched ULONG [out] A pointer to the variable to receive the actual
number of connections enumerated in rgpcd. This argument can be NULL in which c
ase the cConnections argument must be 1.
Return Value Meaning
S_OK The requested number of elements has been returned and *pcFetched (if no
n-NULL) is set to cConnections if
S_FALSE The enumerator returned fewer elements than cConnections because there w
ere not that many elements left in the list.. In this case, unused elements in r
gpcd in the enumeration are not set to NULL and *pcFetched holds the number of v
alid entries, even if zero is returned.
E_POINTER The address in rgpcd is not valid (such as NULL).
E_INVALIDARG The value of cConnections is not 1 when pcFetched is NULL; or th
e value of cConnections is zero.
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There is not enough memory to enumerate the elements.
Comments:
E_NOTIMPL is not allowed as a return value. If an error value is returned, no e
ntries in the rgpcn array are valid on exit and require no release.
13.1.4.2 IEnumConnections::Skip
HRESULT IEnumConnections::Skip([in] ULONG cConnections); Instructs the enumerato
r to skip the next cConnections elements in the enumeration such that the next c
all to IEnumConnections::Next will not return those elements.

Argument Type Description


cConnections ULONG [in] Specifies the number of elements to skip in the enu
meration.
Return Value Meaning
S_OK The number of elements skipped is cConnections.
S_FALSE The enumerator skipped fewer than cConnections because there were not th
at many left in the list. The enumerator will, at this point, be positioned at
the end of the list such that subsequent calls to Next (without an intervening R
eset) will return zero elements.
E_INVALIDARG The value in cConnections is zero which is not valid.
E_UNEXPECTED An unknown error occurred.
13.1.4.3 IEnumConnections::Reset
HRESULT IEnumConnections::Reset(void);
Instructs the enumerator to position itself back to the beginning of the list of
elements.

Argument Type Description


none
Return Value Meaning
S_OK The enumerator was successfully reset to the beginning of the list.
S_FALSE The enumerator was not reset to the beginning of the list.
E_UNEXPECTED An unknown error occurred.
Comments:
There is no guarantee that the same set of elements will be enumerated on each p
ass through the list: it depends on the collection being enumerated. It is too e
xpensive for some collections, such as files in a directory, to maintain this co
ndition.
13.1.4.4 IEnumConnections::Clone
HRESULT IEnumConnections::Clone([out] IEnumConnections **ppEnum);
Creates another connections enumerator with the same state as the current enumer
ator, which iterates over the same list. This makes it possible to record a poi
nt in the enumeration sequence in order to return to that point at a later time.

Argument Type Description


ppEnum IenumConnections** [out] The address of the variable to receive t
he IEnumConnections interface pointer to the newly created enumerator. The call
er must release this new enumerator separately from the first enumerator.

Return Value Meaning


S_OK Clone creation succeeded.
E_NOTIMPL Cloning is not supported for this enumerator.
E_POINTER The address in ppEnum is not valid (such as NULL)
E_UNEXPECTED An unknown error occurred.
E_OUTOFMEMORY There is not enough memory to create the clone enumerator.

13.2 Connectable Objects Structure Descriptions


13.2.1 CONNECTDATA
The CONNECTDATA structure is the type enumerated through the IEnumConnections::N
ext method. Each structure describes a connection that exists to a given connect
ion point.
typedef struct tagCONNECTDATA
{
IUnknown* pUnk
DWORD dwCookie;
} CONNECTDATA;
Members
pUnk
Pointer to the IUnknown interface on a connected advisory sink. The caller must
call IUnknown::Release using this pointer when the CONNECTDATA structure is no l
onger needed. The caller is responsible for calling Release for each CONNECTDATA
structure enumerated through IEnumConnections::Next.
dwCookie
Connection where this value is the same token that is returned originally from c
alls to IConnectionPoint::Advise. This token can be used to disconnect the sink
pointed to by a pUnk by passing dwCookie to IConnectionPoint::Unadvise.
See Also
IConnectionPoint, IEnumConnections
14. Persistent Storage
Structured storage provides the equivalent of a complete file system for storing
objects through the following elements:
· The IStorage, IStream, ILockBytes, and IRootStorage interfaces, along with a set
of related interfaces IPersistStorage, IPersist, IPersistFile, and IPersistStre
am.
· Helper APIs that facilitate your implementation of structured storage, as well a
s a second set of APIs for compound files, COM s implementation of the structured
storage interfaces. COM also provides a set of supporting structures and enumera
tion values used to organize parameters for interface methods and APIs.
· A set of access modes for regulating access to compound files.
14.1 Interfaces
Structured storage services are organized into three categories of interfaces. E
ach set represents a successive level of indirection, or abstraction, between a
compound file, the objects it contains, and the physical media on which these in
dividual components are stored.
The first category of interfaces consists of IStorage, IStream, and IRootStorage
. The first two interfaces define how objects are stored within a compound file.
These interfaces provide methods for opening storage elements, committing and r
everting changes, copying and moving elements, and reading and writing streams.
These interfaces do not understand the native data formats of the individual obj
ects and therefore have no methods for saving those objects to persistent storag
e. The IRootStorage interface has a single method for associating a compound doc
ument with an underlying file system name. Clients are responsible for implement
ing these interfaces on behalf of their compound files.
The second category of interfaces consists of the IPersist interfaces, which obj
ects implement to manage their persistent data. These interfaces provide methods
to read the data formats of individual objects and therefore know how to store
them. Objects are responsible for implementing these interfaces because clients
do not know the native data formats of their nested objects. These interfaces, h
owever, have no knowledge of specific physical storage media.
A third category consists of a single interface, ILockBytes, which provides meth
ods for writing files to specific physical media, such as a hard disk or tape dr
ive. COM provides an ILockBytes interface for the operating system s file system.
14.2 API Functions
Some of the API functions for structured storage are helper functions that perfo
rm a sequence of calls to other API functions and interface methods. You can use
these helper functions as short cuts.
Other API functions provide access to COM s implementation of structured storage,
Compound Files.
Finally, there are API functions for converting and emulating other objects. The
se functions provide services for one server application to work with data from
another application. The data can be converted to the native format of the serve
r application by reading the data from its original format but writing it in the
native format of the object application. Or the data can remain in its original
format while the server application both reads and writes the data in its origi
nal format.
14.3 Access Modes
In a world where multiple processes and users can access an object simultaneousl
y, mechanisms for controlling that access are essential. COM provides these mech
anisms by defining access modes for both storage and stream objects. The access
mode specified for a parent storage object is inherited by its children, though
you can place additional restrictions on the child storage or stream. A nested s
torage or stream object can be opened in the same mode or in a more restricted m
ode than that of its parent, but it cannot be opened in a less restricted mode t
han that of its parent.
You specify access modes by using the values listed in the STGM enumeration. The
se values serve as flags to be passed as arguments to methods in the IStorage in
terface and associated API functions. Typically, several flags are combined in t
he parameter grfMode, using a Boolean OR operation.
The flags fall into six groups: transaction flags, storage creation flags, tempo
rary creation flag, priority flag, access permission flags, and shared access fl
ags. Only one flag from each group can be specified at a time.
14.3.1 Transaction Flags
An object can be opened in either direct or transacted mode. When an object is o
pened in direct mode, changes are made immediately and permanently. When an obje
ct is opened in transacted mode, changes are buffered so they can be explicitly
committed or reverted once editing is complete. Committed changes are saved to t
he object while reverted changes are discarded. Direct mode is the default acces
s mode.
Transacted mode is not required on a parent storage object in order to use it on
a nested element. A transaction for a nested element, however, is nested within
the transaction for its parent storage object. Therefore, changes made to a chi
ld object cannot be committed until those made to the parent are committed, and
both are not committed until the root storage object (the top-level parent) is a
ctually written to disk. In other words, the changes move outward: inner objects
publish changes to the transactions of their immediate containers.
14.3.2 Storage Creation Flags
Storage creation flags specify what COM should do if an existing storage, stream
, or lockbytes object has the same name as a new storage or stream object that y
ou are creating. The default is to return an error message and not create the ne
w object. You can use only one of these flags in a given creation call.
14.3.3 Temporary Creation Flag
The temporary creation flag indicates that the underlying file is to be automati
cally destroyed when the root storage object is released. This capability is mos
t useful for creating temporary files.
14.3.4 Priority Flag
The priority flag opens a storage object in priority mode. When it opens an obje
ct, an application usually works from a snapshot copy because other applications
may also be using the object at the same time. When opening a storage object in
priority mode, however, the application has exclusive rights to commit changes
to the object.
Priority mode enables an application to read some streams from storage before op
ening the object in a mode that would require the system to make a snapshot copy
to be made. Since the application has exclusive access, it doesn t have to make a
snapshot copy of the object. When the application subsequently opens the object
in a mode where a snapshot copy is required, the application can exclude from t
he snapshot the streams it has already read, thereby reducing the overhead of op
ening the object.
Since other applications cannot commit changes to an object while it is open in
priority mode, applications should keep it in that mode for as short a time as p
ossible.
14.3.5 Access Permission Flags
Access permission flags specify the type of access a client application has to a
n open object: read, write, or read/write. Read permission enables an applicatio
n to read the contents of a stream but not to write changes to the stream. Write
permission enables an application to call a function that commits changes to an
object s storage but not to read the contents of the stream. Read/write permissio
n enables an application to either read the object s streams or write to the objec
t s storage.
14.3.6 Shared Access Flags
The shared access flags specify the degree of access other applications can have
to an object that your application is opening. You can deny read access, deny w
rite access, or deny all access. You can also specify explicitly that no type of
access be denied.
14.4 Storage Object Naming Conventions
Storage and stream objects are named according to a set of conventions.
The name of a root storage object is the actual name of the file in the underlyi
ng file system. It obeys the conventions and restrictions the file system impose
s. Filename strings passed to storage-related methods and functions are passed o
n, uninterpreted and unchanged, to the file system.
The name of a nested element contained within a storage object is managed by the
implementation of the particular storage object. All implementations of storage
objects must support nested element names 32 characters in length (including th
e NULL terminator), although some implementations might support longer names. Wh
ether the storage object does any case conversion is implementation-defined. As
a result, applications that define element names must choose names that are acce
ptable in either situation. The COM implementation of compound files supports na
mes up to 32 characters in length, and does not perform any case conversion.
14.5 Persistent Property Sets
While the kind of run-time properties that Automation and ActiveX Controls offer
are important, they do not directly address the need to store information with
objects persistently stored in the file system. These entities could include fil
es (structured, compound, etc.), directories, and summary catalogs. COM provides
both a standard serialized format for these persistent properties, and a set of
interfaces and functions that allow you to create and manipulate the property s
ets and their properties.
Persistent properties are stored as sets, and one or more sets may be associated
with a file system entity. These persistent property sets are intended to be us
ed to store data that is suited to being represented as a collection of fine-gra
ined values. They are not intended to be used as a large data base. They can be
used to store summary information about an object on the system, which can then
be accessed by any other object that understands how to interpret that property
set.
Previous versions of COM specified very little with respect to properties and th
eir usage, but did define a serialized format that allowed developers to store p
roperties and property sets in an IStorage instance. The property identifiers an
d semantics of a single property set, used for summary information about a docum
ent, was also defined. At that time, it was necessary to create and manipulate t
hat structure directly as a data stream. For information on the property set ser
ialized data format structure, refer to OLE Serialized Property Set Format.
Now, however, COM defines two primary interfaces to manage property sets:
· IPropertyStorage
· IPropertySetStorage
It is no longer necessary to deal with the serialized format directly when these
interfaces are implemented on an object that supports the IStorage interface (s
uch as compound files). Writing properties through IPropertySetStorage and IProp
ertyStorage creates data that exactly conforms to the COM property set format, a
s viewed through IStorage methods. The converse is also true¾properties written to
the COM property set format using IStorage are visible through IPropertySetStor
age and IPropertyStorage (although you cannot expect to write to IStream and hav
e the properties through IPropertyStorage immediately available, or vice versa).
The IPropertySetStorage interface defines methods that create and manage propert
y sets. The IPropertyStorage interface directly manipulates the properties withi
n a property set. By calling the methods of these interfaces, an application dev
eloper can manage whatever property sets are appropriate for a given file system
entity. Use of these interfaces provides one tuned reading and writing implemen
tation for properties, rather than having an implementation in each application,
which could suffer performance bottlenecks such as incessant seeking. You can i
mplement the interfaces to enhance performance, so properties can be read and wr
itten more quickly by, for example, more efficient caching. Furthermore, IProper
tyStorage and IPropertySetStorage make it possible to manipulate properties on e
ntities that do not support IStorage, although in general, most applications wil
l not do so.
14.5.1 Managing Property Sets
A persistent property set contains related pieces of information in the form of
properties. Each property set is identified with a FMTID, a GUID that allows pro
grams accessing the property set to identify the property set and, through this
identification, know how to interpret the properties it contains. Examples of pr
operty sets might be the character-formatting properties in a word processor or
the rendering attributes of an element in a drawing program.
COM defines the IPropertySetStorage interface to facilitate management of proper
ty sets. Through the methods of this interface, you can create a new property se
t, or open or delete an existing property set. In addition, it provides a method
that creates an enumerator and supplies a pointer to its IEnumSTATPROPSETSTG in
terface. You can call the methods of this interface to enumerate STATPROPSETSTG
structures on your object, which will provide information about all of the prope
rty sets on the object.
When you create or open an instance of IPropertyStorage, it is similar to openin
g an object that supports IStorage or IStream, because you need to specify the s
torage mode in which you are opening the interface. For IStorage, these include
the transaction mode, the read/write mode, and the sharing mode.
When you create a property set with a call to IPropertySetStorage::Create, you s
pecify whether the property set is to be simple or non-simple. A simple property
set contains types that can be fully written within the property set stream, wh
ich is intended to be limited, and can, in fact, not be larger than 256 Kbytes.
However, for those cases when you need to store a larger amount of information i
n the property set, you can specify that the property set be non-simple, allowin
g you to use one or more of the types that specify only a pointer to a storage o
r stream object. Also, if you need a transacted update, the property set must be
non-simple. There is, of course, a certain performance penalty for opening thes
e types, because it requires opening the stream or storage object to which you h
ave the pointer.
If your application uses compound files, you can use the COM-provided implementa
tion of these interfaces, which are implemented on the COM compound file storage
object.
Each property set consists primarily of a logically connected group of propertie
s, as described in the following section.
14.5.2 Managing Properties
Every property consists of a property identifier (unique within its property set
), a type tag that represents the type of a value, and the value itself. The typ
e tag describes the representation of the data in the value. In addition, a prop
erty may also be assigned a string name that can be used to identify the propert
y, rather than using the required numerical property identifier. To create and m
anage properties, COM defines the IPropertyStorage interface.
The IPropertyStorage interface includes methods to read and write arrays of eith
er properties themselves or just property names. The interface includes Commit a
nd Revert methods that are similar to IStorage methods of the same name. There a
re utility methods that allow you to set the CLSID of the property set, the time
s associated with the set, and get statistics about the property set. Finally, t
he Enum method creates an enumerator and returns a pointer to its IEnumSTATPROPS
TG interface. You can call the methods of this interface to enumerate STATPROPST
G structures on your object, which will provide information about all of the pro
perties in the current property set.
To illustrate how properties are represented, if a specific property in a proper
ty set holds an animal s scientific name, that name could be stored as a zero-term
inated string. Stored along with the name would be a type indicator to indicate
that the value is a zero-terminated string. These properties might have the foll
owing characteristics:
Property ID String Identifier Type Indicator Value Represented

02 PID_ANIMALNAME VT_LPWSTR Zero-terminated Unicode string


03 PID_LEGCOUNT VT_I2 WORD
Any application that recognizes the property set format (identifying it through
its FMTID) can look at the property with an identifier of PID_ANIMALNAME, determ
ine it is a zero-terminated string, and read and write the value. While the appl
ication can call IPropertyStorage::ReadMultiple to read any or all of a property
set (having first obtained a pointer), the application must know how to interpr
et the property set.
A property value is passed through property interfaces as an instance of the typ
e PROPVARIANT.
It is important to distinguish between these stored (persistent) properties, and
run-time properties. Value type constants have names beginning with VT_. The se
t of valid PROPVARIANTs is, however, not completely equivalent with the set of V
ARIANTs used in Automation and ActiveX Controls.
The only difference between the two structures is the allowable set of VT_ tags
in each. Where a certain property type can be used in both a VARIANT and a PROPV
ARIANT, the type tag (the VT_ value) always has an identical value. Further, for
a given VT_ value, the in-memory representation used in both VARIANTs and PROPV
ARIANTs is identical. Taken all together, this approach allows the type system t
o catch disallowed type tags, while at the same time, allowing a knowledgeable c
lient simply to do a pointer-cast when appropriate.
14.5.3 Using Property Sets
While the potential for uses of persistent property sets is not fully tapped, th
ere are currently two primary uses:
· Storing summary information with an object such as a document
· Transferring property data between objects
COM property sets were designed to store data that is suited to representation a
s a moderately sized collection of fine-grained values. Data sets that are too l
arge for this to be feasible should be broken into separate streams, storages, a
nd/or property sets. The COM property set data format was not meant to provide a
substitute for a database of many tiny objects.
This section discusses two ways to use property sets. The first describes an exa
mple of storing property sets within files to allow common access to the informa
tion in the property set, and describes the COM summary information property set s
tandard. The second is an example that shows how to transfer property sets betwe
en applications or COM objects as an effective means of communication.
The following section describes the Summary Information property set as an examp
le of property set definition.
14.5.3.1 The Summary Information Property Set
COM defines a standard common property set for storing summary information about
documents. The Summary Information property set must be stored in an IStream in
stance off of the root storage object; it is not valid to store the property set
in the Contents stream of a named IStorage instance.
For example, to create an ANSI simple property set, you would call IPropertySetS
torage::Create to create the property set, specifying PROPSETFLAG_ANSI (simple i
s the default mode), then write to it with a call to IPropertyStorage::WriteMult
iple. To read the property set, you would call IPropertyStorage::ReadMultiple.
All shared property sets are identified by a stream or storage name with the pre
fix \005 (or 0x05) to show it is a property set shareable among applications, and
the Summary Information property set is no exception. The name of the stream tha
t contains the Summary Information property set is:
"\005SummaryInformation"
The FMTID for the 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 F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9
The following table shows the string property names for the Summary Information
property set, along with the respective property identifiers and VT type indicat
ors.
Property Name Property ID String Property ID VT 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 (UTC)
Last Printed PID_LASTPRINTED 0x0000000B VT_FILETIME (UTC)
Create Time/Date* PID_CREATE_DTM 0x0000000C VT_FILETIME (UTC)
Last saved Time/Date* PID_LASTSAVE_DTM 0x0000000D VT_FILETIME (UTC
)
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.

14.5.3.1.1 Guidelines for Implementing the Summary Information Property Set


The following guidelines pertain to implementing the Summary Information propert
y set described in the preceding section:
· PID_TEMPLATE refers to an external document containing formatting and styling in
formation. The means by which the template is located is implementation-defined.
· PID_LASTAUTHOR is the name stored in User Information by the application. For ex
ample, 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 th
is document.
· Each of the date/time values must be stored in Universal Coordinated Time (UTC).
· PID_CREATE_DTM is a read-only property; this property should be set when a docum
ent is created, but should not be subsequently changed.
· For PID_THUMBNAIL, applications should store data in CF_DIB or CF_METAFILEPICT f
ormat. CF_METAFILEPICT is recommended.
· PID_SECURITY is the suggested security level for the document. By noting the sec
urity level on the document, an application other than the originator of the doc
ument can adjust its user interface to the properties appropriately. An applicat
ion should not display any information about a password-protected document or al
low modifications to enforced read-only or locked-for-annotations documents. App
lications 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 4
Locked for annotations 8
14.5.4 Property Set Implementations
COM provides a compound file implementation of the property set interfaces, alon
g with three helper functions. These implementations are available through COM c
ompound file storage objects, to which you can get a pointer through the functio
ns StgCreateDocfile, to create a new compound file storage object, and StgOpenSt
orage, to open one that currently exists. The following section describes some p
erformance characteristics of these implementations. For more information on spe
cific interfaces and how to get a pointer to these interfaces, refer to the foll
owing in the reference section:
· IPropertySetStorage -- Compound File Implementation
· IPropertyStorage -- Compound File Implementation
· IEnumSTATPROPSTG -- Compound File Implementation
· IEnumSTATPROPSETSTG -- Compound File Implementation
In addition, there are three helper functions, designed to aid in dealing with p
ropvariants:
· PropVariantClear
· FreePropVariantArray
· PropVariantCopy
14.5.4.1 Performance Characteristics
A call to the COM compound file implementation of IPropertySetStorage interface
to create a property set causes either a stream or storage to be created through
a call to IStorage::CreateStream or IStorage::CreateStorage. A default property
set is created in memory, but not flushed to disk. When there is a call to IPro
pertyStorage::WriteMultiple, it operates within the buffer.
When a property set is opened, IStorage::OpenStream or IStorage::OpenStorage is
used. The entire property set stream is read into contiguous memory. IPropertySt
orage::ReadMultiple operations then operate by reading the memory buffer. Theref
ore, the first access is expensive in terms of time (because of disk reads) but
subsequent accesses are very efficient. Writes may be slightly more expensive be
cause SetSize operations on the underlying stream may be required to guarantee t
hat disk space is available if data is added.
No guarantees are made as to whether IPropertyStorage::WriteMultiple will flush
updates. In general, the client should assume that IPropertyStorage::WriteMultip
le only updates the in memory buffer. To flush data, IPropertyStorage::Commit or
IPropertyStorage::Release (last release) should be called.
This design means that WriteMultiple may succeed but the data is not actually pe
rsistently written.
Note
This size of the property set stream may not exceed 256K bytes.
14.5.5 Using Implemented Property Sets
Since the property set stream is read into memory in its entirety before a singl
e property can be read or written, it is strongly recommended that property sets
be kept small. Small is somewhere under 32K of data. This should not present too
much of a problem because typically, in-line properties will be small items such a
s descriptive strings, keywords, timestamps, counts, author names, GUIDs, CLSIDs
, etc.
For the storage of larger chunks of data, or where the total size of a set of re
lated properties far exceeds the recommended amount, the use of non-simple types
such as VT_STREAM and VT_STORAGE are strongly recommended. These are not stored
inside the property set stream, so do not significantly affect the initial over
head of the first accessing/writing of a property. There is some effect because
the property set stream contains the name of the sibling stream- or storage-valu
ed property and this takes a small amount of time to process.
14.5.6 IPropertySetStorage Implementation Considerations
Several issues arise when considering how to provide an implementation of the IP
ropertySetStorage interface that reads and writes the COM property set format. T
he following sections describe these considerations.
14.5.7 Names in IStorage
This is the most complex interoperability issue. In the IPropertySetStorage inte
rface, property sets are identified with FMTIDs, but in IStorage, they are named
with strings with a maximum length of 32 characters.
To accomplish this mapping, the first task is to establish a mapping between FMT
IDs and strings. Converting in the one direction, you have a FMTID, and need a c
orresponding string name. First, check whether the FMTID is one of a fixed set o
f well-known values, and use the corresponding well-known string name if so:

FMTID String Name Semantic


F29F85E0-4FF9-1068-AB91-08002B27B3D9 \005SummaryInformation COM summary info
rmation
D5CDD502-2E9C-101B-9397-08002B2CF9AE
D5CDD505-2E9C-101B-9397-08002B2CF9AE
\005DocumentSummaryInformation Office document summary information and
user-defined properties.
Note
The DocumentSummaryInformation property set is special in that it contains two s
ections. Multiple sections are not permitted in any other property set. This pr
operty set is described in more detail in Appendix B, OLE Serialized Property Se
t Format.
The first was defined as part of OLE; the second one was defined by Microsoft Of
fice.
Otherwise, algorithmically form a string name in the following way. First, conve
rt the FMTID to little-endian byte order if necessary. Then, take the 128 bits o
f the FMTID and consider them as one long bit string by concatenating each of th
e bytes together. The first bit of the 128 bit value is the least significant bi
t of the first byte in memory of the FMTID; the last bit of the 128 bit value is
the most significant bit of the last byte in memory of the FMTID. Extend these
128 bits to 130 bits by adding two zero bits to the end. Next, chop the 130 bits
into groups of five bits; there will be 26 such groups. Consider each group as
an integer with reversed bit precedence. For example, the first of the 128 bits
is the least significant bit of the first group of five bits; the fifth of the
128 bits is the most significant bit of the first group. Map each of these inte
gers as an index into the array of thirty-two characters:
ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
This yields a sequence of 26 Unicode characters that uses only uppercase charact
ers and numerals. Note that no two characters in this range compare equally in a
case-insensitive manner in any locale. The final string is the concatenation of
the string \005 onto the front of these 26 characters, for a total length of 27 c
haracters.
The following code illustrates one way to map from FMTID to property string:
#define CBIT_BYTE 8
#define CBIT_CHARMASK 5
#define CCH_MAP (1 << CBIT_CHARMASK) // 32
#define CHARMASK (CCH_MAP - 1) // 0x1f
CHAR awcMap[CCH_MAP + 1] = "abcdefghijklmnopqrstuvwxyz012345";
WCHAR MapChar(ULONG i) {
return((WCHAR) awcMap[i & CHARMASK]);
}
VOID GuidToPropertyStringName(GUID *pguid, WCHAR awcname[]) {
BYTE *pb = (BYTE *) pguid;
BYTE *pbEnd = pb + sizeof(*pguid);
ULONG cbitRemain = CBIT_BYTE;
WCHAR *pwc = awcname;
*pwc++ = ((WCHAR) 0x0005);
while (pb < pbEnd) {
ULONG i = *pb >> (CBIT_BYTE - cbitRemain);
if (cbitRemain >= CBIT_CHARMASK) {
*pwc = MapChar(i);
if (cbitRemain == CBIT_BYTE && *pwc >= L'a' && *pwc <= L'z') {
*pwc += (WCHAR) (L'A' - L'a');
}
pwc++;
cbitRemain -= CBIT_CHARMASK;
if (cbitRemain == 0) {
pb++;
cbitRemain = CBIT_BYTE;
}
}
else {
if (++pb < pbEnd) {
i |= *pb << cbitRemain;
}
*pwc++ = MapChar(i);
cbitRemain += CBIT_BYTE - CBIT_CHARMASK;
}
}
*pwc = L'\0';
}
Converters of property string names to GUIDs should accept lowercase letters as
synonymous with their upper case counterparts. The following example shows one
way to map from the property string to a FMTID:
ULONG
PropertySetNameToGuid(
IN ULONG cwcname,
IN WCHAR const awcname[],
OUT GUID *pguid)
{
ULONG Status = ERROR_INVALID_PARAMETER;
WCHAR const *pwc = awcname;
if (pwc[0] == WC_PROPSET0)
{
//Note: cwcname includes the WC_PROPSET0, and
//sizeof(wsz ) includes the trailing L \0 , but
//the comparison excludes both the leading
//WC_PROPSET0 and the trailing L \0 .
if (cwcname == sizeof(wszSummary)/sizeof(WCHAR) &&
wcsnicmp(&pwc[1], wszSummary, cwcname - 1) == 0)
{
*pguid = guidSummary;
return(NO_ERROR);
}
if (cwcname == CWC_PROPSET)
{
ULONG cbit;
BYTE *pb = (BYTE *) pguid - 1;
ZeroMemory(pguid, sizeof(*pguid));
for (cbit = 0; cbit < CBIT_GUID; cbit +=
CBIT_CHARMASK)
{
ULONG cbitUsed = cibt % CBIT_BYTE;
ULONG cbitStored;
WCHAR wc;

if (cbitUsed == 0)
{
pb++;
}
wc = *++pwc - L A ; //assume uppercase
if (wc > CALPHACHARS)
{
wc += (WCHAR) (L A - L a ) //try lowercase
if (wc > CALPHACHARS)
{
wc += L a - L 0 + CALPHACHARS; //must
be a digit
if (wc > CHARMASK)
{
goto fail; //invalid character
}
}
}
*pb |= (BYTE) (wc << cbitUsed);
cbitStored = min(CBIT_BYTE - cbitUsed,
CBIT_CHARMASK);
//If the translated bits wouldn t fit in the
current byte
if (cbitStored < CBIT_CHARMASK)
{
wc >>= CBIT_BYTE - cbitUsed;
if (cbit + cbitStored == CBIT_GUID)
{
if (wc !+0)
{
goto fail; //extra bits
}
break;
}
pb++;
*pb |= (BYTE) wc;
}
}
Status = NO_ERROR
}
}
fail:
return(Status);
}
When attempting to open an existing property set (in IPropertySetStorage::Open)
the (root) FMTID in hand is converted to a string as depicted above. If an eleme
nt of the IStorage of that name exists, it is used. Otherwise, the open fails.
When creating a new property set, the above mapping determines the string name u
sed.
14.5.8 Storage vs Stream for a Property Set
To provide applications the control they need to fully interoperate through the
IPropertySetStorage interface with the COM property set, the programmer must con
trol whether a property set is stored in a storage or a stream. This is provided
through the presence or absence of the PROPSETFLAG_NONSIMPLE flag in IPropertyS
etStorage::Create.
14.5.9 Setting the CLSID of the Property Set
IPropertyStorage::SetClass, when invoked on a property stored in a compound file
, will set the CLSID of the storage object through a call to IStorage::SetClass
in addition to setting the class tag value stored in the COM property set. This
provides a consistency and uniformity that creates better interaction with some
tools.
14.5.10 Synchronization Points
When property sets are supported on the same object as is IStorage, it is import
ant to be aware of synchronization points between the base storage and the prope
rty storage. The property set storage holds the property set stream in an intern
al buffer until that buffer is commited through the IPropertyStorage::Commit met
hod. This is true whether IPropertyStorage was opened in transacted mode or dire
ct mode.
14.5.11 Code pages: Unicode strings, Macintosh, etc.
Another consideration is how Unicode property names are stored in the property I
D 0 (the property name dictionary), which is not specified per se to use Unicode
strings.
This is straightforward. Unicode officially has a code page value of 1200. To st
ore Unicode values in the property name dictionary, use a code page value of 120
0 for the whole property set (in property ID 1, of course), specified by the abs
ence of the PROPSETFLAG_ANSI flag in IPropertySetStorage::Create. Note that this
has the side effect of storing all string values in the property set in Unicode
. In all code pages, the count found at the start of a VT_LPSTR s a byte count,
not a character count. This is necessary to provide for smooth interoperability
with down-level clients.
The compound file implementation of IPropertySetStorage creates all new property
sets completely either in Unicode (code page 1200) or in the current system ANS
I code page. This is controlled by the absence or presence of the PROPSETFLAG_AN
SI flag in the grfFlags parameter of IPropertySetStorage::Create.
It is recommended that property sets be created or opened as Unicode, by not set
ting the PROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage:
:Create. It is also recommended that you avoid using VT_LPSTR values, and use VT
_LPWSTR values instead. When the property set code page is Unicode, VT_LPSTR str
ing values are converted to Unicode when stored, and back to multibyte string va
lues when retrieved. When the code page of the property set is not Unicode, prop
erty names, VT_BSTR strings, and non-simple property values are converted to mul
tibyte strings when stored, and converted back to Unicode when retrieved. If the
property set code page is Unicode, VT_LPSTR string values are converted to Unic
ode when stored, and back to multibyte string values when retrieved.
The setting of the PROPSETFLAG_ANSI flag as reported through a call to IProperty
Storage::Stat simply reflects whether the underlying code page is not Unicode or
is Unicode. Note, though, property ID 1 can be explicitly read to learn the cod
e page.
Property ID 1 is accessible through IPropertyStorage::ReadMultiple. However, it
is read-only, and may not be updated with WriteMultiple. Further, it may not be
deleted with DeleteMultiple.
14.5.12 Dictionary
IPropertyStorage::WritePropertyNames is implemented using the property ID 0 dict
ionary as described above. Property ID 0 is not accessible through IPropertyStor
age::ReadMultiple or ::WriteMultiple.
14.5.13 Extensions
Property set extensions as defined in the original COM property set format have
been removed and are not supported, except for the User Defined Properties secti
on in the Document Summary Information property set, described in more detail in
Appendix C, OLE Serialized Property Set Format.
14.6 Persistent Storage Interface Descriptions
14.6.1 IEnumSTATPROPSETSTG
The IEnumSTATPROPSETSTG interface is used to iterate through an array of STATPRO
PSETSTG structures, which contain statistical information about the property set
s managed by the current instance of IPropertySetStorage. IEnumSTATPROPSETSTG ha
s the same methods as all enumerator interfaces: Next, Skip, Reset, and Clone. F
or general information on these methods, refer to IEnumXXXX.
The implementation defines the order in which the property sets are enumerated.
Property sets that are present when the enumerator is created, and are not remov
ed during the enumeration, will be enumerated only once. Property sets added or
deleted while the enumeration is in progress may or may not be enumerated, but,
if enumerated, will not be enumerated more than once.
For information on how the COM compound document implementation of IEnumSTATPROP
SETSTG:Next supplies members of the STATPROPSETSTG structure, refer to IEnumSTAT
PROPSETSTG--Compound File Implementation.
14.6.1.1.1 When to Implement
IEnumSTATPROPSETSTG is implemented to enumerate the property sets supported by t
he current property set storage object. If you are using the compound file imple
mentation of the storage object, a pointer to which is available through a call
to StgCreateDocfile, IEnumSTATPROPSETSTG is implemented on that object, and a po
inter is returned through a call to IPropertySetStorage::Enum. If you are doing
a custom implementation of IPropertySetStorage, you need to implement IEnumSTATP
ROPSETSTG to fill in a caller-allocated array of STATPROPSETSTG structures, each
of which contains information about the nested elements in the storage object.
14.6.1.1.2 When to Use
Call IPropertySetStorage::Enum to return a pointer to IEnumSTATPROPSETSTG, the m
ethods of which can then be called to enumerate STATPROSETSTG structures so the
application can manage its property sets.
The prototypes of the methods are as follows:
HRESULT Next(
ULONG celt,
STATPROPSETSTG * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumSTATPROPSETSTG ** ppenum
);
See Also
IPropertyStorage::Enum

14.6.2 IEnumSTATPROPSETSTG-Compound File Implementation


The compound file implementation of IEnumSTATPROPSETSTG interface is used to enu
merate through an array of STATPROPSETSTG structures, which contain statistical
information about the properties managed by the compound file implementation of
IPropertySetStorage, which is associated with a current compound file storage ob
ject.
14.6.2.1.1 When to Use
Call the methods of IEnumSTATPROPSETSTG to enumerate STATPROPSETSTG structures,
each of which provides information about one of the property sets associated wit
h the compound file storage object.
Remarks
IEnumSTATPROPSETSTG::Next
Gets the next one or more STATPROPSETSTG structures (the number is specified by
the celt parameter). The STATPROPSETSTG elements provided through a call to the
compound file implementation of IEnumSTATPROPSETSTG::Next follow these rules:
· If IEnumSTATPROPSETSTG::Next cannot provide STATPROPSETSTG.fmtid, zeros are writ
ten to that member. This occurs when the property set does not have a predefined
name (such as \005SummaryInformation) and is not a legal value.
· The DocumentSummaryInformation property set is special, in that it may have two
property set sections. This property set is described in the section titled The
DocumentSummaryInformation Property Set. The second section is referred to as th
e User-Defined Properties. Each section is identified with a unique Format ID. W
hen IPropertySetStorage::Enum is used to enumerate property sets, the User-Defin
ed Property Set will not be enumerated.

Note
If you always create a property set using IPropertySetStorage::Create, then, sin
ce a Character GUID is created for the storage name, IEnumSTATPROPSETSTG::Next wil
l return a nonzero, valid format identifier for the property set[STATPROPSETSTG.
fmtid].
· The STATPROPSETSTG.grfFlags member does not necessarily reflect whether the prop
erty set is ANSI or not. If PROPSETFLAG_ANSI is set, the property set is definit
ely ANSI. If PROPSETFLAG_ANSI is clear, the property set could be either Unicode
or non-Unicode, because it is not possible to tell whether it is ANSI without o
pening it.
· The STATPROPSETSTG.grfFlags member does reflect whether the property set is simp
le or not, so the setting of the PROPSETFLAG_NONSIMPLE flag is always valid.
· If IEnumSTATPROPSETSTG::Next cannot provide STATPROPSETSTG.clsid, it is set to a
ll zeroes (CLSID_NULL). In the COM compound file implementation, this occurs whe
n the property set is simple (the PROPSETFLAG_NONSIMPLE flag is not set), or is
non-simple but the CLSID was not explicitly set. For non-simple property sets, t
he CLSID that is received is the one that is maintained by the underlying IStora
ge.
· If IEnumSTATPROPSETSTG::Next cannot provide the time fields [ctime, mtime, atime
], each non-supported time will be set to zeroes. In the COM compound file imple
mentation, getting these values depends on retrieving them from the underlying I
Storage implementation.
IEnumSTATPROPSETSTG::Skip
Skips the number of elements specified in celt. Returns S_OK if the specified nu
mber of elements are skipped, returns S_FALSE if fewer elements than requested a
re skipped. In any other case, returns the appropriate error.
IEnumSTATPROPSETSTG::Reset
Sets the cursor to the beginning of the enumeration. If successful, returns S_OK
, otherwise, returns STG_E_INVALIDHANDLE.
IEnumSTATPROPSETSTG::Clone
Copies the current enumeration state of this enumerator.

14.6.3 IEnumSTATPROPSTG
The IEnumSTATPROPSTG interface is used to iterate through an array of STATPROPST
G structures, which contain statistical information about an open property stora
ge containing a property set. IEnumSTATPROPSTG has the same methods as all enume
rator interfaces: Next, Skip, Reset, and Clone. For general information on these
methods, refer to IEnumXXXX.
The implementation defines the order in which the properties in the set are enum
erated. Properties that are present when the enumerator is created, and are not
removed during the enumeration, will be enumerated only once. Properties added o
r deleted while the enumeration is in progress may or may not be enumerated, but
they will never be enumerated more than once.
Properties with property ID 0 (dictionary), property ID 1 (codepage indicator),
or property ID greater than or equal to 0x80000000 are not enumerated.
Enumeration of a non-simple property does not necessarily indicate that the prop
erty can be read successfully through a call to IPropertyStorage::ReadMultiple.
This is because the performance overhead of checking existence of the indirect s
tream or storage is prohibitive during property enumeration. A client of this in
terface should code accordingly.
14.6.3.1.1 When to Implement
Implement IEnumSTATPROPSTG to enumerate the properties within a property set. If
you are using the compound file implementation of the storage object, a pointer
to which is available through a call to StgCreateDocfile, you can then query fo
r a pointer to IPropertySetStorage. After calling one of its methods either to o
pen or create a property set, you can get a pointer to IEnumSTATPROPSTG through
a call to IPropertyStorage::Enum. If you are doing a custom implementation of IP
ropertyStorage, you also need to implement IEnumSTATPROPSTG to fill in a caller-
allocated array of STATPROPSTG structures. Each STATPROPSTG structure contains i
nformation about a simple property.
14.6.3.1.2 When to Use
Applications that support storage objects and persistent properties within those
objects call IPropertyStorage::Enum to return a pointer to IEnumSTATPROPSTG to
enumerate the properties in the current property set.
The prototypes of the methods are as follows:
HRESULT Next(

ULONG celt,
STATPROPSTG * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumSTATPROPSTG ** ppenum
);
See Also
IPropertyStorage::Enum

14.6.4 IEnumSTATPROPSTG-Compound File Implementation


The compound file implementation of the IEnumSTATPROPSTG interface is used to en
umerate through properties, resulting in STATPROPSTG structures, which contain s
tatistical information about the properties managed by the compound file impleme
ntation of IPropertyStorage, which is associated with a current compound file st
orage object.
The constructor in the COM implementation of IEnumSTATPROPSTG creates a class th
at reads the entire property set, and creates a static array which can be shared
when IEnumSTATPROPSTG::Clone is called.
14.6.4.1.1 When to Use
Call the compound file implementation of IEnumSTATPROPSTG to enumerate the STATP
ROPSTG structures that contain information about the properties within the curre
nt property set. When you are using the compound file implementation of the prop
erty storage interfaces, call IPropertyStorage::Enum to return a pointer to IEnu
mSTATPROPSTG to manage the property storage object and the elements within it.
Remarks
IEnumSTATPROPSTG::Next
Gets the next one or more STATPROPSTG structures (the number is specified by the
celt parameter). Returns S_OK if successful.
IEnumSTATPROPSTG::Skip
Skips the number of elements specified in celt. The next element to be enumerate
d through a call to Next then becomes the element after the ones skipped. Return
s S_OK if celt elements were skipped; returns S_FALSE if fewer than celt element
s were skipped.
IEnumSTATPROPSTG::Reset
Sets the cursor to the beginning of the enumeration. If successful, returns S_OK
, otherwise, returns STG_E_INVALIDHANDLE.
IEnumSTATPROPSTG::Clone
Uses the constructor for the IEnumSTATPROPSTG to create a copy of the array. Bec
ause the class that constructs the static array actually contains the object, th
is function mainly adds to the reference count.
See Also
STATPROPSTG, IPropertyStorage::Enum

14.6.5 IEnumSTATSTG
The IEnumSTATSTG interface is used to enumerate through an array of STATSTG stru
ctures, which contain statistical information about an open storage, stream, or
byte array object. IEnumSTATSTG has the same methods as all enumerator interface
s: Next, Skip, Reset, and Clone. For general information on these methods, refer
to IEnumXXXX.
14.6.5.1.1 When to Implement
IEnumSTATSTG is implemented to enumerate the elements of a storage object. If yo
u are using the compound file implementation of the storage object, a pointer to
which is available through a call to StgCreateDocfile, IEnumSTATSTG is implemen
ted on that object, and a pointer is returned through a call to IStorage::EnumEl
ements. If you are doing a custom implementation of a storage object, you need t
o implement IEnumSTATSTG to fill in a caller-allocated array of STATSTG structur
es, each of which contains information about the nested elements in the storage
object.
14.6.5.1.2 When to Use
Containers call methods that return a pointer to IEnumSTATSTG so the container c
an manage its storage object and the elements within it. Calls to the IStorage::
EnumElements method supplies a pointer to IEnumSTATDATA. The caller allocates an
array of STATSTG structures and the IEnumSTATSTG methods fill in each structure
with the statistics about one of the nested elements in the storage object. If
present, the lpszName member of the STATSTG structure requires additional memory
allocations through the IMalloc interface, and the caller is responsible for fr
eeing this memory, if it is allocated, by calling the IMalloc::Free method. If t
he lpszName member is NULL, no memory is allocated, and, therefore, no memory ne
eds to be freed.
The prototypes of the methods are as follows:
HRESULT Next(
ULONG celt,
STATSTG * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(

IEnumSTATSTG ** ppenum
);
See Also
CoGetMalloc, IEnumXXXX, IStorage::EnumElements, STATSTG

e ILockBytes interface is implemented on a byte array object that is backed by s


ome physical storage, such as a disk file, global memory, or a database. It is u
sed by an COM compound file storage object to give its root storage access to th
e physical device, while isolating the root storage from the details of accessin
g the physical storage.
14.6.6.1.1 When to Implement
Most applications will not implement the ILockBytes interface because COM provid
es implementations for the two most common situations:
File-based implementation If you call StgCreateDocfile function to create a comp
ound file storage object, it contains an implementation of ILockBytes that is as
sociated with a byte array stored in a physical disk file. The compound file sto
rage object calls the ILockBytes methods¾you do not call them directly in this imp
lementation.
Memory-based implementation COM also provides a byte array object based on globa
l memory that supports an implementation of ILockBytes. You can get a pointer th
rough a call to the CreateILockBytesOnHGlobal function). Then, to create a compo
und file storage object on top of that byte array object, call the StgCreateDocf
ileOnILockBytes function. The compound file storage object calls the ILockBytes
methods you do not call them directly in this implementation.
There are situations in which it would be useful for an application to provide i
ts own ILockBytes implementation. For example, a database application could impl
ement ILockBytes to create a byte array object backed by the storage of its rela
tional tables. However, it is strongly recommended that you use the COM-provided
implementations. For a discussion of the advantages of using the COM implementa
tions rather than creating your own, see the StgCreateDocfileOnILockBytes API fu
nction, which creates a compound file storage object on top of a caller-provided
byte array object.
If you choose to implement your own ILockBytes interface, you should consider pr
oviding custom marshaling by implementing the IMarshal interface as part of your
byte array object. The reason for this is that when the COM-provided implementa
tions of IStorage and IStream are marshaled to another process, their ILockBytes
interface pointers are also marshaled to the other process. The default marshal
ing mechanism creates a proxy byte array object (on which is the ILockBytes inte
rface) that transmits method calls back to the original byte array object. Custo
m marshaling can improve efficiency by creating a remote byte array object that
can access the byte array directly.
14.6.6.1.2 When to Use
The ILockBytes methods are called by the COM implementations of IStorage and ISt
ream on the compound file object. Unless you are implementing IStorage and IStre
am, you would not need to call ILockBytes methods directly. If you write your ow
n ILockBytes implementation, you can use the StgCreateDocfileOnILockBytes functi
on to create a compound file storage object backed by your implementation of ILo
ckBytes.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.

ILockBytes Methods Description


ReadAt Reads a specified number of bytes starting at a specified offset from th
e beginning of the byte array.
WriteAt Writes a specified number of bytes to a specified location in the byte a
rray.
Flush Ensures that any internal buffers maintained by the byte array object ar
e written out to the backing storage.
SetSize Changes the size of the byte array.
LockRegion Restricts access to a specified range of bytes in the byte array
.
UnlockRegion Removes the access restriction on a range of bytes previously re
stricted with ILockBytes::LockRegion.
Stat Retrieves a STATSTG structure for this byte array object.

14.6.6.2 ILockBytes::Flush
Ensures that any internal buffers maintained by the ILockBytes implementation ar
e written out to the underlying physical storage.
HRESULT Flush(void);
Return Values
S_OK
The flush operation was successful.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_MEDIUMFULL
The flush operation is not completed because there is no space left on the stora
ge device.
E_FAIL
General failure writing data.
STG_E_TOOMANYFILESOPEN
Under certain circumstances, Flush does a dump-and-close to flush, which can lea
d to a return value of STG_E_TOOMANYFILESOPEN if no file handles are available.
STG_E_INVALIDHANDLE
An underlying file has been prematurely closed, or the correct floppy disk has b
een replaced by an invalid one.
Remarks
ILockBytes::Flush flushes internal buffers to the underlying storage device.
The COM-provided implementation of compound files calls this method during a tra
nsacted commit operation to provide a two-phase commit process that protects aga
inst loss of data.
See Also
IStorage::Commit, ILockBytes¾File-Based Implementation, ILockBytes¾Global Memory Imp
lementation

14.6.6.3 ILockBytes::LockRegion
Restricts access to a specified range of bytes in the byte array.
HRESULT LockRegion(
ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of
the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType //Specifies the type of restriction on accessing the spe
cified range
);
Parameters
libOffset
[in]Specifies the byte offset for the beginning of the range.
cb
[in]Specifies, in bytes, the length of the range to be restricted.
dwLockType
[in]Specifies the type of restrictions being requested on accessing the range. T
his parameter uses one of the values from the LOCKTYPE enumeration.
Return Values
S_OK
The specified range of bytes was locked.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not su
pported.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_INVALIDHANDLE
An underlying file has been prematurely closed, or the correct floppy disk has b
een replaced by an invalid one.
Remarks
ILockBytes::LockRegion restricts access to the specified range of bytes. Once a
region is locked, attempts by others to gain access to the restricted range must
fail with the STG_E_ACCESSDENIED error.
The byte range can extend past the current end of the byte array. Locking beyond
the end of an array is useful as a method of communication between different in
stances of the byte array object without changing data that is actually part of
the byte array. For example, an implementation of ILockBytes for compound files
could rely on locking past the current end of the array as a means of access con
trol, using specific locked regions to indicate permissions currently granted.
The dwLockType parameter specifies one of three types of locking, using values f
rom the LOCKTYPE enumeration. The types are as follows: locking to exclude other
writers, locking to exclude other readers or writers, and locking that allows o
nly one requestor to obtain a lock on the given range. This third type of lockin
g is usually an alias for one of the other two lock types, and permits an Implem
enter to add other behavior as well. A given byte array might support either of
the first two types, or both.
To determine the lock types supported by a particular ILockBytes implementation,
you can examine the grfLocksSupported member of the STATSTG structure returned
by a call to ILockBytes::Stat.
Any region locked with ILockBytes::LockRegion must later be explicitly unlocked
by calling ILockBytes::UnlockRegion with exactly the same values for the libOffs
et, cb, and dwLockType parameters. The region must be unlocked before the stream
is released. Two adjacent regions cannot be locked separately and then unlocked
with a single unlock call.
14.6.6.3.1.1.1 Notes to Callers
Since the type of locking supported is optional and can vary in different implem
entations of ILockBytes, you must provide code to deal with the STG_E_INVALIDFUN
CTION error.
14.6.6.3.1.1.2 Notes to Implementers
Support for this method depends on how the storage object built on top of the IL
ockBytes implementation is used. If you know that only one storage object at any
given time can be opened on the storage device that underlies the byte array, t
hen your ILockBytes implementation does not need to support locking. However, if
multiple simultaneous openings of a storage object are possible, then region lo
cking is needed to coordinate them.
A LockRegion implementation can choose to support all, some, or none of the lock
types. For unsupported lock types, the implementation should return STG_E_INVAL
IDFUNCTION.
See Also
ILockBytes::Stat, ILockBytes::UnlockRegion, IStream::LockRegion, LOCKTYPE, ILock
Bytes File-Based Implementation, ILockBytes Global Memory Implementation

14.6.6.4 ILockBytes::ReadAt
Reads a specified number of bytes starting at a specified offset from the beginn
ing of the byte array object.
HRESULT ReadAt(
ULARGE_INTEGER ulOffset, //Specifies the starting point for reading data
void *pv, //Points to the buffer into which the data is read
ULONG cb, //Specifies the number of bytes to read
ULONG *pcbRead //Pointer to location that contains actual number of byt
es read
);
Parameters
ulOffset
[in]Specifies the starting point from the beginning of the byte array for readin
g data.
pv
[in]Points to the buffer into which the byte array is read.
cb
[in]Specifies the number of bytes of data to attempt to read from the byte array
.
pcbRead
[out]Pointer to a location where this method writes the actual number of bytes r
ead from the byte array. You can set this pointer to NULL to indicate that you a
re not interested in this value. In this case, this method does not provide the
actual number of bytes read.
Return Values
S_OK
Indicates that the specified number of bytes were read, or the maximum number of
bytes were read up to the end of the byte array.
E_FAIL
Data could not be read from the byte array.
E_PENDING
Asynchronous Storage only: Part or all of the data to be read is currently unava
ilable.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_READFAULT
The number of bytes to be read does not equal the number of bytes that were acut
ally read.
Remarks
ILockBytes::ReadAt reads bytes from the byte array object. It reports the number
of bytes that were actually read. This value may be less than the number of byt
es requested if an error occurs or if the end of the byte array is reached durin
g the read.
It is not an error to read less than the specified number of bytes if the operat
ion encounters the end of the byte array. Note that this is the same end-of-file
behavior as found in MS-DOS FAT file system files.
See Also
ILockBytes::WriteAt, ILockBytes File-Based Implementation, ILockBytes Global Memory
Implementation

14.6.6.5 ILockBytes::SetSize
Changes the size of the byte array.
HRESULT SetSize(
ULARGE_INTEGER cb //Specifies the new size of the byte array in bytes
);
Parameter
cb
[in]Specifies the new size of the byte array as a number of bytes.
Return Values
S_OK
The size of the byte array was successfully changed.
STG_E_ACCESSDENIED
The caller does not have permission to access the byte array.
STG_E_MEDIUMFULL
The byte array size is not changed because there is no space left on the storage
device.
Remarks
ILockBytes::SetSize changes the size of the byte array. If the cb parameter is l
arger than the current byte array, the byte array is extended to the indicated s
ize by filling the intervening space with bytes of undefined value, as does ILoc
kBytes::WriteAt, if the seek pointer is past the current end-of-stream.
If the cb parameter is smaller than the current byte array, the byte array is tr
uncated to the indicated size.
14.6.6.5.1.1.1 Notes to Callers
Callers cannot rely on STG_E_MEDIUMFULL being returned at the appropriate time b
ecause of cache buffering in the operating system or network. However, callers m
ust be able to deal with this return code because some ILockBytes implementation
s might support it.
See Also
ILockBytes::ReadAt, ILockBytes::WriteAt, ILockBytes File-Based Implementation, ILo
ckBytes Global Memory Implementation

14.6.6.6 ILockBytes::Stat
Retrieves a STATSTG structure containing information for this byte array object.
HRESULT Stat(
STATSTG *pstatstg, //Location for STATSTG structure
DWORD grfStatFlag //Values taken from the STATFLAG enumeration
);
Parameters
pstatstg
[out]Points to a STATSTG structure in which this method places information about
this byte array object. The pointer is NULL if an error occurs.
grfStatFlag
[in]Specifies whether this method should supply the pwcsName member of the STATS
TG structure through values taken from the STATFLAG enumeration. If the STATFLAG
_NONAME is specified, the pwcsName member of STATSTG is not supplied, thus savin
g a memory allocation operation. The other possible value, STATFLAG_DEFAULT, ind
icates that all STATSTG members be supplied.
Return Values
S_OK
The STATSTG structure was successfully returned at the specified location.
E_OUTOFMEMORY
The STATSTG structure was not returned due to a lack of memory for the name fiel
d in the structure.
STG_E_ACCESSDENIED
The STATSTG structure was not returned because the caller did not have access to
the byte array.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned, due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPOINTER
The value for the pStatStg parameter is not valid.
Remarks
ILockBytes::Stat should supply information about the byte array object in a STAT
STG structure.
See Also
STATFLAG, STATSTG, ILockBytes File-Based Implementation, ILockBytes Global Memory Im
plementation

14.6.6.7 ILockBytes::UnlockRegion
Removes the access restriction on a previously locked range of bytes.
HRESULT UnlockRegion(
ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of
the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType //Specifies the access restriction previously placed on
the range
);
Parameters
libOffset
[in]Specifies the byte offset for the beginning of the range.
cb
[in]Specifies, in bytes, the length of the range that is restricted.
dwLockType
[in]Specifies the type of access restrictions previously placed on the range. Th
is parameter uses a value from the LOCKTYPE enumeration.
Return Values
S_OK
The byte range was unlocked.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not su
pported.
STG_E_LOCKVIOLATION
The requested unlock cannot be granted.
Remarks
ILockBytes::UnlockRegion unlocks a region previously locked with a call to ILock
Bytes::LockRegion. Each region locked must be explicitly unlocked, using the sam
e values for the libOffset, cb, and dwLockType parameters as in the matching cal
ls to ILockBytes::LockRegion. Two adjacent regions cannot be locked separately a
nd then unlocked with a single unlock call.
See Also
ILockBytes::LockRegion, LOCKTYPE, ILockBytes File-Based Implementation, ILockBytes G
lobal Memory Implementation

14.6.6.8 ILockBytes::WriteAt
Writes the specified number of bytes starting at a specified offset from the beg
inning of the byte array.
HRESULT WriteAt(
ULARGE_INTEGER ulOffset, //Specifies the starting point for writing data
void const *pv, //Points to the buffer containing the data to be written
ULONG cb, //Specifies the number of bytes to write
ULONG *pcbWritten //Pointer to location that contains actual number of byt
es written
);
Parameters
ulOffset
[in]Specifies the starting point from the beginning of the byte array for the da
ta to be written.
pv
[in]Points to the buffer containing the data to be written.
cb
[in]Specifies the number of bytes of data to attempt to write into the byte arra
y.
pcbRead
[out]Pointer to a location where this method specifies the actual number of byte
s written to the byte array. You can set this pointer to NULL to indicate that y
ou are not interested in this value. In this case, this method does not provide
the actual number of bytes written.
Return Values
S_OK
Indicates that the specified number of bytes were written.
E_FAIL
A general failure occurred during the write.
E_PENDING
Asynchronous Storage only: Part or all of the data to be written is currently un
available.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for writing this byte array.
STG_E_WRITEFAULT
The number of bytes to be written does not equal the number of bytes that were a
cutally written.
STG_E_MEDIUMFULL
The write operation was not completed because there is no space left on the stor
age device. The actual number of bytes written is still returned in pcbWritten.
Remarks
ILockBytes::WriteAt writes the specified data at the specified location in the b
yte array. The number of bytes actually written must always be returned in pcbWr
itten, even if an error is returned. If the byte count is zero bytes, the write
operation has no effect.
If ulOffset is past the end of the byte array and cb is greater than zero, ILock
Bytes::WriteAt increases the size of the byte array. The fill bytes written to t
he byte array are not initialized to any particular value.
See Also
ILockBytes::ReadAt, ILockBytes::SetSize, ILockBytes File-Based Implementation, ILo
ckBytes Global Memory Implementation

14.6.7 ILockBytes - File-Based Implementation


Implemented on a byte array object underlying an COM compound file storage objec
t, and designed to read and write directly to a disk file.
14.6.7.1.1 When to Use
Methods of ILockBytes are called from the compound file implementations of IStor
age and IStream on the compound file storage object created through a call to St
gCreateDocfile, so you do not need to call them directly.
Remarks
ILockBytes::ReadAt
This method queries the wrapped pointer for the requested interface.
ILockBytes::WriteAt
This method queries the wrapped pointer for the requested interface.
ILockBytes::Flush
This method queries the wrapped pointer for the requested interface.
ILockBytes::SetSize
This method queries the wrapped pointer for the requested interface.
ILockBytes::LockRegion
The dwLockTypes parameter is set to LOCK_ONLYONCE OR LOCK_EXCLUSIVE, which will
allow or restrict access to locked regions.
ILockBytes::UnlockRegion
This method unlocks the region locked by ILockBytes::LockRegion.
ILockBytes::Stat
The COM-provided IStorage::Stat implementation calls the ILockBytes::Stat method
to retrieve information about the byte array object. If there is no reasonable
name for the byte array, the COM-provided ILockBytes::Stat method returns NULL i
n the pwcsName field of the STATSTG structure.
See Also
ILockBytes, IStorage, IStream

14.6.8 ILockBytes - Global Memory Implementation


Implemented on a byte array object underlying an COM compound file storage objec
t, and designed to read and write directly to global memory.
14.6.8.1.1 When to Use
Methods of ILockBytes are called from the compound file implementations of IStor
age and IStream on the compound file storage object created through a call to St
gCreateDocfile.
Remarks
ILockBytes::ReadAt
This method queries the wrapped pointer for the requested interface.
ILockBytes::WriteAt
This method queries the wrapped pointer for the requested interface.
ILockBytes::Flush
This method queries the wrapped pointer for the requested interface.
ILockBytes::SetSize
This method queries the wrapped pointer for the requested interface.
ILockBytes::LockRegion
This implementation does not support locking, so dwLocksType is set to zero. It
is the caller s responsibility to ensure accesses are valid and mutually exclusive
.
ILockBytes::UnlockRegion
This implementation does not support locking.
ILockBytes::Stat
The COM-provided IStorage::Stat implementation calls the ILockBytes::Stat method
to retrieve information about the byte array object. If there is no reasonable
name for the byte array, the COM-provided ILockBytes::Stat method returns NULL i
n the pwcsName field of the STATSTG structure.
See Also
ILockBytes, IStorage, IStream

14.6.9 IPersist
The IPersist interface defines the single method GetClassID, which is designed t
o supply the CLSID of an object that can be stored persistently in the system. A
call to this method can allow the object to specify which object handler to use
in the client process, as it is used in the COM default implementation of marsh
aling.
IPersist is the base interface for three other interfaces: IPersistStorage, IPer
sistStream, and IPersistFile. Each of these interfaces, therefore, includes the
GetClassID method, and the appropriate one of these three interfaces is implemen
ted on objects that can be serialized to a storage, a stream, or a file. The met
hods of these interfaces allow the state of these objects to be saved for later
instantiations, and load the object using the saved state. Typically, the persis
tence interfaces are implemented by an embedded or linked object, and are called
by the container application or the default object handler
14.6.9.1.1 When to Implement
You must implement the single method of IPersist in implementing any one of the
other persistence interfaces: IPersistStorage, IPersistStream, or IPersistFile.
Typically, for example, you would implement IPersistStorage on an embedded objec
t, IPersistFile on a linked object, and IPersistStream on a new moniker class, a
lthough their uses are not limited to these objects. You could implement IPersis
t in a situation where all that is required is to obtain the CLSID of a persiste
nt object, as it is used in marshaling.
14.6.9.1.2 When to Use
The single method of IPersist is rarely called directly in application code. It
is called by the default object handler to get the CLSID of an embedded object,
or an object to be marshaled. A container application, for example, would probab
ly not call the GetClassID method directly unless it provided object handlers fo
r specific classes of objects.
Methods in Vtable Order

IUnknown Methods Description


QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the component object.

14.6.9.2 IPersist::GetClassID
Retrieves the class identifier (CLSID) of an object. The CLSID is a unique value
that identifies the code that can manipulate the persistent data.
HRESULT GetClassID(
CLSID *pClassID //Pointer to CLSID of object
);
Parameter
pClassID
[out]Points to the location of the CLSID on return. The CLSID is a globally uniq
ue identifier (GUID) that uniquely represents an object class that defines the c
ode that can manipulate the object s data.
Return Values
S_OK
The CLSID was successfully retrieved.
E_FAIL
The CLSID could not be retrieved.
Remarks
The GetClassID method retrieves the class identifier (CLSID) for an object, used
in later operations to load object-specific code into the caller s context.
14.6.9.2.1.1.1 Notes to Callers
A container application might call this method to retrieve the original CLSID of
an object that it is treating as a different class. Such a call would be necess
ary if a user performed an editing operation that required the object to be save
d. If the container were to save it using the treat-as CLSID, the original appli
cation would no longer be able to edit the object. Typically, in this case, the
container calls the OleSave helper function, which performs all the necessary st
eps. For this reason, most container applications have no need to call this meth
od directly.
The exception would be a container that provides an object handler for certain o
bjects. In particular, a container application should not get an object s CLSID an
d then use it to retrieve class specific information from the registry.
14.6.9.2.1.1.2 Notes to Implementers
Typically, implementations of this method simply supply a constant CLSID for an
object. If, however, the object s TreatAs registry key has been set by an applicat
ion that supports emulation (and so is treating the object as one of a different
class), a call to IPersist::GetClassID must supply the CLSID specified in the T
reatAs key. For more information on emulation, refer to CoTreatAsClass.
When an object is in the running state, the default handler calls an implementat
ion of IPersist::GetClassID that delegates the call to the implementation in the
object. When the object is not running, the default handler instead calls the R
eadClassStg function to read the CLSID that is saved in the object s storage.
If you are writing a custom object handler for your object, you might want to si
mply delegate this method to the default handler implementation (see OleCreateDe
faultHandler).
See Also
IPersistStorage, IPersistStream, IPersistFile, ReadClassStg

14.6.10 IPersistFile
The IPersistFile interface provides methods that permit an object to be loaded f
rom or saved to a disk file, rather than a storage object or stream. Because the
information needed to open a file varies greatly from one application to anothe
r, the implementation of IPersistFile::Load on the object must also open its dis
k file.
The IPersistFile interface inherits its definition from IPersist, so all impleme
ntations must also include the GetClassID method of IPersist.
14.6.10.1.1 When to Implement
Implement IPersistFile when you want to read or write information from a separat
e file, which could be of any file format.
This interface should be implemented on any objects that support linking through
a file moniker, including the following:
· Any object that supports links to its files or to pseudo-objects within its file
s
· A container application that supports links to objects within its compound file
Typically, you implement the IPersistFile interface as part of an aggregate obje
ct that includes other interfaces that are appropriate for the type of moniker b
inding that is supported.
For example, in either of the cases mentioned above, the moniker for the linked
object can be a composite moniker. In the first case, a composite moniker identi
fies the pseudo-object contained within the file. In the second case, a composit
e moniker identifies the embedded object contained within the compound file. In
either case of composite monikers, you must implement the IPersistFile interface
as part of the same object on which the IOleItemContainer interface is implemen
ted. Then, when the application for the linked object is run, COM queries for th
e IOleItemContainer interface to locate the embedded object or the pseudo-object
contained in the file.
As another example, if the moniker is a simple file moniker (i.e., the link is t
o the entire file), COM queries for the interface that the initiator of the bind
operation requested. Typically, this is one of the compound document interfaces
, such as IOleObject, IDataObject, or IPersistStorage.
14.6.10.1.2 When to Use
Call methods in the IPersistFile interface to load or save a linked object in a
specified file.
When IPersistFile is implemented on an object that supports linking through a fi
le moniker and the application for the linked object is run, COM calls IPersistF
ile::Load. Once the file is loaded, COM calls IPersistFile::QueryInterface to ge
t another interface pointer to the loaded object. The IPersistFile interface is
typically part of an aggregate object that offers other interfaces.
In this case, the only IPersistFile method that COM calls is the Load method to
load a file linked to a container, running the application associated with the f
ile. It would also be unusual for applications to call other methods in this cas
e, which support saving an object to a file. Generally, it is left to the end us
er and the application for the linked object to decide when to save the object.
This differs from the situation for an embedded object, in which the container a
pplication uses the IPersistStorage interface to provide the storage and to tell
the object when to save itself.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the component object.

IPersistFile Methods Description


IsDirty Checks an object for changes since it was last saved to its current file
.
Load Opens the specified file and initializes an object from the file content
s.
Save Saves the object into the specified file.
SaveCompleted Notifies the object that it can revert from NoScribble mode to N
ormal mode.
GetCurFile Gets the current name of the file associated with the object.

14.6.10.2 IPersistFile::GetCurFile
Retrieves either the absolute path to the object s current working file or, if the
re is no current working file, the object s default filename prompt.
HRESULT GetCurFile(
LPOLESTR *ppszFileName //Pointer to the path for the current file or th
e default save prompt
);
Parameter
ppszFileName
[out]Points to the location of a pointer to a zero-terminated string containing
the path for the current file or the default filename prompt (such as *.txt). If
an error occurs, ppszFileName is set to NULL.
Return Values
S_OK
A valid absolute path was successfully returned.
S_FALSE
The default save prompt was returned.
E_OUTOFMEMORY
The operation failed due to insufficient memory.
E_FAIL
The operation failed due to some reason other than insufficient memory.
Remarks
This method returns the current filename or the default save prompt for the obje
ct.
This method allocates memory for the string returned in the ppszFileName paramet
er using the IMalloc::Alloc method. The caller is responsible for calling the IM
alloc::Free method to free the string. Both the caller and this method use the C
OM task allocator provided by a call to CoGetMalloc.
The LPOLESTR type indicates a wide character string (two bytes per character); o
therwise, the string has one byte per character.
The filename returned in ppszFileName is the name specified in a call to IPersis
tFile::Load when the document was loaded; or in IPersistFile::SaveCompleted if t
he document was saved to a different file.
If the object does not have a current working file, it should supply the default
filename prompt that it would display in a Save As dialog. For example, the defau
lt save prompt for a word processor object could be:
*.txt
14.6.10.2.1.1.1 Notes to Callers
COM does not call the IPersistFile::GetCurFile method. Applications would not ca
ll this method unless they are also calling the save methods of this interface.
In saving the object, you can call this method before calling IPersistFile:Save
to determine whether the object has an associated file. If this method returns S
_OK, you can then call IPersistFile::Save with a NULL filename and a TRUE value
for the fRemember parameter to tell the object to save itself to its current fil
e. If this method returns S_FALSE, you can use the save prompt returned in the p
pszFileName parameter to ask the end user to provide a filename. Then, you can c
all IPersistFile::Save with the filename that the user entered to perform a Save
As operation.
See Also
IPersistFile::Load, IPersistFile::Save, IPersistFile::SaveCompleted

14.6.10.3 IPersistFile::IsDirty
Checks an object for changes since it was last saved to its current file.
HRESULT IsDirty(void);
Return Values
S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.
Remarks
This method checks whether an object has changed since it was last saved. Call i
t to determine whether an object should be saved before closing it. The dirty fl
ag for an object is conditionally cleared in the IPersistFile::Save method.
14.6.10.3.1.1.1 Notes to Callers
COM does not call IPersistFile::IsDirty. Applications would not call it unless t
hey are also saving an object to a file.
You should treat any error return codes as an indication that the object has cha
nged. Unless this method explicitly returns S_FALSE, assume that the object must
be saved.
14.6.10.3.1.1.2 Notes to Implementers
An object with no contained objects simply checks its dirty flag to return the a
ppropriate result.
A container with one or more contained objects must maintain an internal dirty f
lag that is set when any of its contained objects has changed since it was last
saved. To do this, the container should maintain an advise sink by implementing
the IAdviseSink interface. Then, the container can register each link or embeddi
ng for data change notifications with a call to IDataObject::DAdvise. Then, the
container can set its internal dirty flag when it receives an IAdviseSink::OnDat
aChange notification. If the container does not register for data change notific
ations, the IPersistFile::IsDirty implementation would call IPersistStorage::IsD
irty for each of its contained objects to determine whether they have changed.
The container can clear its dirty flag whenever it is saved, as long as the file
to which the object is saved is the current working file after the save. Theref
ore, the dirty flag would be cleared after a successful Save or Save As operation, b
ut not after a Save A Copy As . . . operation.
See Also
IAdviseSink::OnDataChange, IDataObject::DAdvise, IPersistStorage::IsDirty

14.6.10.4 IPersistFile::Load
Opens the specified file and initializes an object from the file contents.
HRESULT Load(
LPCOLESTR pszFileName, //Pointer to absolute path of the file to open
DWORD dwMode //Specifies the access mode from the STGM enumeration
);
Parameters
pszFileName
[in]Points to a zero-terminated string containing the absolute path of the file
to open.
dwMode
[in]Specifies some combination of the values from the STGM enumeration to indica
te the access mode to use when opening the file. IPersistFile::Load can treat th
is value as a suggestion, adding more restrictive permissions if necessary. If d
wMode is zero, the implementation should open the file using whatever default pe
rmissions are used when a user opens the file.
Return Values
S_OK
The object was successfully loaded.
E_OUTOFMEMORY
The object could not be loaded due to a lack of memory.
E_FAIL
The object could not be loaded for some reason other than a lack of memory.
IPersistFile::Load STG_E_* error codes.
Remarks
IPersistFile::Load loads the object from the specified file. This method is for
initialization only and does not show the object to the end user. It is not equi
valent to what occurs when an end user selects the File Open command.
14.6.10.4.1.1.1 Notes to Callers
The BindToObject method in file monikers calls this method to load an object dur
ing a moniker binding operation (when a linked object is run). Typically, applic
ations do not call this method directly.
14.6.10.4.1.1.2 Notes to Implementers
Because the information needed to open a file varies greatly from one applicatio
n to another, the object on which this method is implemented must also open the
file specified by the pszFileName parameter. This differs from the IPersistStora
ge::Load and IPersistStream::Load, in which the caller opens the storage or stre
am and then passes an open storage or stream pointer to the loaded object.
For an application that normally uses COM compound files, your IPersistFile::Loa
d implementation can simply call the StgOpenStorage function to open the storage
object in the specified file. Then, you can proceed with normal initialization.
Applications that do not use storage objects can perform normal file-opening pr
ocedures.
When the object has been loaded, your implementation should register the object
in the Running Object Table (see IRunningObjectTable::Register).
See Also
IRunningObjectTable::Register, StgOpenStorage
14.6.10.5 IPersistFile::Save
Saves a copy of the object into the specified file.
HRESULT Save(
LPCOLESTR pszFileName, //Pointer to absolute path of the file where the
object is saved
BOOL fRemember //Specifies whether the file is to be the current workin
g file or not
);
Parameters
pszFileName
[in]Points to a zero-terminated string containing the absolute path of the file
to which the object should be saved. If pszFileName is NULL, the object should s
ave its data to the current file, if there is one.
fRemember
[in]Indicates whether the pszFileName parameter is to be used as the current wor
king file. If TRUE, pszFileName becomes the current file and the object should c
lear its dirty flag after the save. If FALSE, this save operation is a Save A Cop
y As ... operation. In this case, the current file is unchanged and the object sh
ould not clear its dirty flag. If pszFileName is NULL, the implementation should
ignore the fRemember flag.
Return Values
S_OK
The object was successfully saved.
E_FAIL
The file was not saved.
IPersistFile::Save STG_E_* errors.
Remarks
This method can be called to save an object to the specified file in one of thre
e ways:
Save
Call IPersistFile::GetCurFile first to determine whether the object has an assoc
iated filename. If so, call IPersistFile::Save specifying NULL for the pszFileNa
me parameter in this method to indicate that the object should be saved to its c
urrent file. Then call IPersistFile::SaveCompleted to indicate completion.
Save As
Call IPersistFile::Save specifying TRUE in the fRemember parameter and a non-NUL
L value, indicating the name of the new file the object is to be saved to, for t
he pszFileName parameter . Then call IPersistFile::SaveCompleted to indicate com
pletion.
Save a Copy As
Call IPersistFile::Save specifying FALSE in the fRemember parameter and a non-NU
LL value, indicating the name of the new file the object is to be copied to, for
the pszFileName parameter.
The implementer must detect which type of save operation the caller is requestin
g. If the pszFileName parameter is NULL, a Save is being requested. If the pszFile
Name parameter is not NULL, use the value of the fRemember parameter to distingu
ish between a Save As and a Save a Copy As .
In Save or Save As operations, IPersistFile::Save clears the internal dirty flag aft
er the save and sends IAdviseSink::OnSave notifications to any advisory connecti
ons. Also, in these operations, the object is in NoScribble mode until it receiv
es an IPersistFile::SaveCompleted call. In NoScribble mode, the object must not
write to the file.
In the Save As scenario, the implementation should also send IAdviseSink::OnRename
notifications to any advisory .
In the Save a Copy As scenario, the implementation does not clear the internal dir
ty flag after the save.
14.6.10.5.1.1.1 Notes to Callers
COM does not call IPersistFile::Save. Typically, applications would not call it
unless they are saving an object to a file directly, which is generally left to
the end-user.
See Also
IPersistFile::GetCurFile, IPersistFile::SaveCompleted

14.6.10.6 IPersistFile::SaveCompleted
Notifies the object that it can write to its file. It does this by notifying the
object that it can revert from NoScribble mode (in which it must not write to i
ts file), to Normal mode (in which it can). The component enters NoScribble mode
when it receives an IPersistFile::Save call.
HRESULT SaveCompleted(
LPCOLESTR pszFileName //Pointer to absolute path of the file where the
object was saved
);
Parameter
pszFileName
[in]Points to the absolute path of the file where the object was previously save
d.
Return Value
S_OK
Returned in all cases.
Remarks
IPersistFile::SaveCompleted is called when a call to IPersistFile::Save is compl
eted, and the file that was saved is now the current working file (having been s
aved with Save or Save As operations). The call to Save puts the object into NoScrib
ble mode so it cannot write to its file. When SaveCompleted is called, the objec
t reverts to Normal mode, in which it is free to write to its file.
14.6.10.6.1.1.1 Notes to Callers
COM does not call the IPersistFile::SaveCompleted method. Typically, application
s would not call it unless they are saving objects directly to files, an operati
on which is generally left to the end-user.
See Also
IPersistFile::Save

14.6.11 IPersistMemory
The IPersistMemory interface operates exactly as IPersistStreamInit, except that
it allows the caller to provide a fixed-size memory block (identified with a vo
id *) as opposed to IPersistStreamInit which involves an arbitrarily expandable
IStream.
The cbSize argument to the Load and Save methods indicate the amount of memory a
ccessible through pvMem.
The IsDirty, GetSizeMax, and InitNew methods are semantically and syntactically
identical to those in IPersistStreamInit. Only Load and Save differ.
14.6.11.1.1 When to Implement
An object implements this interface to save itself in memory.
14.6.11.1.2 When to Use
A container calls the methods of this interface to instruct an object to save an
d load itself in memory.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the component object.

IPersistMemory Methods Description


IsDirty Checks the object for changes since it was last saved.
Load Initializes an object from the memory block where it was previously save
d.
Save Saves an object into the specified memory block and indicates whether th
e object should reset its dirty flag.
GetSizeMax Returns the size in bytes of the memory block needed to save the
object.
InitNew Initializes an object to a default state.

14.6.11.2 IPersistMemory::GetSizeMax
Returns the size in bytes of the memory block needed to save the object.
HRESULT GetSizeMax(
ULARGE_INTEGER* pcbSize //Pointer to size of memory needed to save objec
t
);
Parameter
pcbSize
[out]Pointer to a 64-bit unsigned integer value indicating the size in bytes of
the memory needed to save this object.
Return Value
S_OK
The size was successfully returned.
Remarks
This method returns the size needed to save an object. You can call this method
to determine the size and set the necessary buffers before calling the IPersistM
emory::Save method.
14.6.11.2.1.1.1 Notes to Implementers
The GetSizeMax implementation must return a conservative estimate of the necessa
ry size because the IPersistMemory::Save method uses a fixed size memory block.
See Also
IPersistMemory::Save

14.6.11.3 IPersistMemory::InitNew
Initializes the object to a default state. This method is called instead of IPer
sistMemory::Load.
HRESULT InitNew(void);
Return Values
S_OK
The object successfully initialized itself.
E_NOTIMPL
The object requires no default initialization. This error code is allowed becaus
e an object may choose to implement IPersistMemory simply for orthogonality or i
n anticipation of a future need for this method.
E_UNEXPECTED
This method was called after the object was already initialized with IPersistMem
ory::Load. Only one initialization is allowed per instance.
E_OUTOFMEMORY
There was not enough memory for the object to initialize itself.
14.6.11.3.1.1.1 Notes to Implementers
If the object has already been initialized with Load, then this method must retu
rn E_UNEXPECTED.
See Also
IPersistMemory::Load

14.6.11.4 IPersistMemory::IsDirty
Checks the object for changes since it was last saved.
HRESULT IsDirty(void);
Return Values
S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.
Remarks
This method checks whether an object has changed since it was last saved so you
can avoid losing information in objects that have not yet been saved. The dirty
flag for an object is conditionally cleared in the IPersistMemory::Save method.
14.6.11.4.1.1.1 Notes to Callers
You should treat any error return codes as an indication that the object has cha
nged. In other words, unless this method explicitly returns S_FALSE, you must as
sume that the object needs to be saved.
See Also
IPersistMemory::Save

14.6.11.5 IPersistMemory::Load
Instructs the object to load its persistent data from the memory pointed to by p
vMem where cbSize indicates the amount of memory at pvMem. The object must not r
ead past the address (BYTE*)((BYTE *)pvMem+cbSize).
HRESULT Load(
void* pvMem, //Pointer to the stream from which the object should be
loaded
ULONG cbSize //Amount of memory from which the object can read its da
ta
);
Parameters
pvMem
[in] Pointer to the address in memory from which the object can read up to cbSiz
e bytes of its data.
cbSize
[in] The amount of memory available at pvMem from which the object can read its
data.
Return Values
S_OK
The object successfully loaded its data.
E_UNEXPECTED
This method was called after the object was already initialized with IPersistMem
ory::Load. Only one initialization is allowed per instance.
E_POINTER
The pointer in pvMem is NULL.
Remarks
Any object that implements IPersistMemory has some information to load persisten
tly, therefore E_NOTIMPL is not a valid return code.
See Also
IPersistMemory::InitNew
14.6.11.6 IPersistMemory::Save
Instructs the object to save its persistent data to the memory pointed to by pvM
em where cbSize indicates the amount of memory available at pvMem. The object mu
st not write past the address (BYTE*)((BYTE *)pvMem+cbSize). The fClearDirty fla
g determines whether the object is to clear its dirty state after the save is co
mplete.
HRESULT Save(
void* pvMem, //Pointer to the stream where the object is to be saved
BOOL fClearDirty, //Specifies whether to clear the dirty flag
ULONG cbSize //Amount of memory to which the object can write its dat
a
);
Parameters
pvMem
[in] Pointer to the memory in which the object should save up to cbSize bytes of
its data.
fClearDirty
[in] A flag indicating whether the object should clear its dirty state on return
from Save or leave that state as-is.
cbSize
[in] The amount of memory available at pvMem to which the object can write its d
ata.
Return Values
S_OK
The object successfully initialized itself.
E_UNEXPECTED
This method was called before the object was initialized with IPersistMemory::In
itNew or IPersistMemory::Load.
E_INVALIDARG
The number of bytes indicated by cbSize is too small to allow the object to save
itself completely.
E_POINTER
The pointer in pvMem is NULL.
Remarks
Any object that implements IPersistMemory has some information to save persisten
tly, therefore E_NOTIMPL is not a valid return code.
The caller should ideally allocate as many bytes as the object returns from IPer
sistMemory::GetSizeMax.
See Also
IPersistMemory::InitNew, IPersistMemory::Load

14.6.12 IPersistPropertyBag
The IPersistPropertyBag interface works in conjunction with IPropertyBag and IEr
rorLog to define an individual property-based persistence mechanism. Whereas a m
echanism like IPersistStream gives an object an IStream in which to store its bi
nary data, IPersistPropertyBag provides an object with an IPropertyBag interface
through which it can save and load individual properties. The implementer of IP
ropertyBag can then save those properties in whatever way it chooses, such as na
me/value pairs in a text file. Errors encountered in the process (on either side
) are recorded in an error log through IErrorLog. This error reporting mechanism
work on a per-property basis instead of an all properties as a whole basis thro
ugh just the return value of IPersist*::Load or IPersist*::Save.
The basic mechanism is that a container tells the object to save or load its pro
perties through IPersistPropertyBag. For each property, the object calls the con
tainer s IPropertyBag interface passed to the IPersistPropertyBag methods. IProper
tyBag::Write saves a property in whatever place the container wants to put it, a
nd IPropertyBag::Read retrieves a property.
This protocol is essentially a means of sequentially communicating individual pr
operty values from the object to the container, which is useful for doing save-a
s-text operations and the like. The object gives the container the choice of the
format in which each property is saved, while retaining itself the decision as
to which properties are saved or loaded.
14.6.12.1.1 When to Implement
An object implements this interface to enable saving its properties persistently
.
14.6.12.1.2 When to Use
A container calls the methods on this interface to instruct an object to load an
d save its properties to the supplied property bag.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.

IPersist Method Description


GetClassID Returns the class identifier (CLSID) for the component object.

IPersistPropertyBag Methods Description


InitNew Called by the container when the control is initialized to initialize th
e property bag.
Load Called by the container to load the control s properties.
Save Called by the container to save the object s properties.
See Also
IErrorLog, IPropertyBag

14.6.12.2 IPersistPropertyBag::InitNew
Called by the container when the control is initialized to initialize the proper
ty bag.
HRESULT InitNew(void);
Return Values
S_OK
The object successfully initialized itself. This should be returned even if the
object doesn t do anything in the method.
CO_E_ALREADYINITIALISED
The object has already been initialized.
E_OUTOFMEMORY
The storage object was not initialized due to a lack of memory.
E_UNEXPECTED
The storage object was not initialized due to some reason other than a lack of m
emory.
Remarks
This method informs the object that it is being initialized as a newly created o
bject.
E_NOTIMPL should not be returned use S_OK when the object has nothing to do in the
method.
See Also
IPersistPropertyBag::Load

14.6.12.3 IPersistPropertyBag::Load
Called by the container to load the control s properties.
HRESULT Load(
IPropertyBag* pPropBag, //Pointer to caller s property bag
IErrorLog* pErrorLog //Pointer to error log
);
Parameters
pPropBag
[in] Pointer to the caller s IPropertyBag interface bag that the control uses to r
ead its properties. Cannot be NULL.
pErrorLog
[in] Pointer to the caller s IErrorLog interface in which the object stores any er
rors that occur during initialization. Can be NULL in which case the caller is n
ot interested in errors.
Return Values
S_OK
The object successfully initialized itself.
E_UNEXPECTED
This method was called after IPersistPropertyBag::InitNew has already been calle
d. They two initialization methods are mutually exclusive.
E_OUTOFMEMORY
The properties were not loaded due to a lack of memory.
E_POINTER
The address in pPropBag is not valid (such as NULL) and therefore the object can
not initialize itself.
E_FAIL
The object was unable to retrieve a critical property that is necessary for the
object s successful operation. The object was therefore unable to initialize itsel
f completely.
Remarks
This method instructs the object to initialize itself using the properties avail
able in the property bag, notifying the provided error log object when errors oc
cur. All property storage must take place within this method call as the object
cannot hold the IPropertyBag pointer.
E_NOTIMPL is not a valid return code as any object implementing this interface m
ust support the entire functionality of the interface.
See Also
IPersistPropertyBag::InitNew

14.6.12.4 IPersistPropertyBag::Save
Called by the container to save the object s properties.
HRESULT Save(
IPropertyBag* pPropBag, //Pointer to the caller s property bag
BOOL fClearDirty, //Specifies whether to clear the dirty flag
BOOL fSaveAllProperties //Specifies whether to save all properties or ju
st those that have changed
);
Parameters
pPropBag
[in] Pointer to the caller s IPropertyBag interface through which the object can w
rite properties. Cannot be NULL.
fClearDirty
[in] A flag indicating whether the object should clear its dirty flag when savin
g is complete. TRUE means clear the flag, FALSE means leave the flag unaffected.
FALSE is used when the caller wishes to do a Save Copy As type of operation.
fSaveAllProperties
[in] A flag indicating whether the object should save all its properties (TRUE)
or only those that have changed since the last save or initialization (FALSE).
Return Values
S_OK
The object successfully saved the requested properties itself.
E_FAIL
There was a problem saving one of the properties. The object can choose to fail
only if a necessary property could not be saved, meaning that the object can ass
ume default property values if a given property is not seen through IPersistProp
ertyBag::Load at some later time.
E_POINTER
The address in pPropBag is not valid (such as NULL) and therefore the object can
not initialize itself.
STG_E_MEDIUMFULL
The object was not saved because of a lack of space on the disk.
Remarks
This method instructs the object to save its properties to the specified propert
y bag, optionally clearing the object s dirty flag. The caller can request that th
e object save all properties or that the object save only those that are known t
o have changed.
E_NOTIMPL is not a valid return code as any object implementing this interface m
ust support the entire functionality of the interface.
See Also
IPersistPropertyBag::InitNew, IPersistPropertyBag::Load

14.6.13 IPersistStorage
The IPersistStorage interface defines methods that enable a container applicatio
n to pass a storage object to one of its contained objects and to load and save
the storage object. This interface supports the structured storage model, in whi
ch each contained object has its own storage that is nested within the container s
storage.
The IPersistStorage contract inherits its definition from IPersist, so all imple
mentations must also include the GetClassID method of IPersist.
14.6.13.1.1 When to Implement
Any object that can be embedded in a container must implement the IPersistStorag
e interface. This interface is one of the primary interfaces for a compound docu
ment object. Embeddable objects must also implement the IOleObjectand IDataObjec
t interfaces.
The COM default handler for embedded objects provides an implementation of the I
PersistStorage interface that is used when the object is in the loaded state. Si
milarly, the COM default link handler provides an IPersistStorage implementation
that manages storage for a linked object. These default handlers both interact
with the COM default cache implementation, which has its own IPersistStorage imp
lementation.
If you are providing a custom embedding or link handler for your objects, the ha
ndler must include an implementation of IPersistStorage. You can delegate calls
to the default handler so you can take advantage of the default cache implementa
tion.
14.6.13.1.2 When to Use
When an COM container creates a new object, loads an existing object from storag
e, or inserts a new object in a clipboard or a drag-and-drop operation, the cont
ainer uses the IPersistStorage interface to initialize the object and put it in
the loaded or running state. When an object is loaded or running, an COM contain
er calls other IPersistStorage methods to instruct the object to perform various
save operations or to release its storage.
Typically, applications use helper functions such as OleLoad or OleCreate, rathe
r than calling the IPersistStorage::Load or IPersistStorage::InitNew methods dir
ectly. Similarly, applications typically call the OleSave helper function rather
than calling IPersistStorage::Save directly.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the object on which it
is implemented.

IPersistStorage Methods Description


IsDirty Indicates whether the object has changed since it was last saved to its
current storage.
InitNew Initializes a new storage object.
Load Initializes an object from its existing storage.
Save Saves an object, and any nested objects that it contains, into the speci
fied storage object. The object enters NoScribble mode.
SaveCompleted Notifies the object that it can revert from NoScribble or HandsO
ff mode, in which in must not write to its storage object, to Normal mode in whi
ch it can.
HandsOffStorage Instructs the object to release all storage objects that have be
en passed to it by its container and to enter HandsOffAfterSave or HandsOffFromN
ormal mode.
See Also
IDataObject

14.6.13.2 IPersistStorage::HandsOffStorage
Instructs the object to release all storage objects that have been passed to it
by its container and to enter HandsOff mode, in which the object cannot do anyth
ing and the only operation that works is a close operation.
HRESULT HandsOffStorage(void);
Return Value
S_OK
The object has successfully entered HandsOff mode.
Remarks
This method causes an object to release any storage objects that it is holding a
nd to enter the HandsOff mode until a subsequent IPersistStorage::SaveCompleted
call. In HandsOff mode, the object cannot do anything and the only operation tha
t works is a close operation.
A container application typically calls this method during a full save or low-me
mory full save operation to force the object to release all pointers to its curr
ent storage. In these scenarios, the HandsOffStorage call comes after a call to
either OleSave or IPersistStorage::Save, putting the object in HandsOffAfterSave
mode. Calling this method is necessary so the container application can delete
the current file as part of a full save, or so it can call the IRootStorage::Swi
tchToFile method as part of a low-memory save.
A container application also calls this method when an object is in Normal mode
to put the object in HandsOffFromNormal mode.
While the component object is in either HandsOffAfterSave or HandsOffFromNormal
mode, most operations on the object will fail. Thus, the container should restor
e the object to Normal mode as soon as possible. The container application does
this by calling the IPersistStorage::SaveCompleted method, which passes a storag
e pointer back to the component object for the new storage object.
14.6.13.2.1.1.1 Notes to Implementers
This method must release all pointers to the current storage object, including p
ointers to any nested streams and storages. If the object contains nested object
s, the container application must recursively call this method for any nested ob
jects that are loaded or running.
See Also
IPersistStorage::Save, IPersistStorage::SaveCompleted, IRootStorage::SwitchToFil
e

14.6.13.3 IPersistStorage::InitNew
Initializes a new object, providing a pointer to the storage to be used for the
object.
HRESULT InitNew(
IStorage *pStg //Points to the new storage object
);
Parameter
pStg
[in]IStorage pointer to the new storage object to be initialized. The container
creates a nested storage object in its storage object (see IStorage::CreateStora
ge). Then, the container calls the WriteClassStg function to initialize the new
storage object with the object class identifier (CLSID).
Return Values
S_OK
The new storage object was successfully initialized.
CO_E_ALREADYINITIALIZED
The object has already been initialized by a previous call to either the IPersis
tStorage::Load method or the IPersistStorage::InitNew method.
E_OUTOFMEMORY
The storage object was not initialized due to a lack of memory.
E_FAIL
The storage object was not initialized for some reason other than a lack of memo
ry.
Remarks
A container application can call this method when it needs to initialize a new o
bject, for example, with an InsertObject command.
An object that supports the IPersistStorage interface must have access to a vali
d storage object at all times while it is running. This includes the time just a
fter the object has been created but before it has been made persistent. The obj
ect s container must provide the object with a valid IStorage pointer to the stora
ge during this time through the call to IPersistStorage::InitNew. Depending on t
he container s state, a temporary file might have to be created for this purpose.
If the object wants to retain the IStorage instance, it must call IUnknown::AddR
ef to increment its reference count.
After the call to IPersistStorage::InitNew, the object is in either the loaded o
r running state. For example, if the object class has an in-process server, the
object will be in the running state. However, if the object uses the default han
dler, the container s call to InitNew only invokes the handler s implementation whic
h does not run the object. Later if the container runs the object, the handler c
alls the IPersistStorage::InitNew method for the object.
14.6.13.3.1.1.1 Notes to Callers
Rather than calling IPersistStorage::InitNew directly, you typically call the Ol
eCreatehelper function which does the following:
1. Calls the CoCreateInstance function to create an instance of the
object class
2. Queries the new instance for the IPersistStorage interface
3. Calls the IPersistStorage::InitNew method to initialize the obje
ct
The container application should cache the IPersistStorage pointer to the object
for use in later operations on the object.
14.6.13.3.1.1.2 Notes to Implementers
An implementation of IPersistStorage::InitNew should initialize the object to it
s default state, taking the following steps:
1. Pre-open and cache the pointers to any streams or storages that
the object will need to save itself to this storage.
2. Call IPersistStorage::AddRef and cache the storage pointer that
is passed in.
3. Call the WriteFmtUserTypeStg function to write the native clipbo
ard format and user type string for the object to the storage object.
4. Set the dirty flag for the object.
The first two steps are particularly important for ensuring that the object can
save itself in low memory situations. Pre-opening and holding onto pointers to t
he stream and storage interfaces guarantee that a save operation to this storage
will not fail due to insufficient memory.
Your implementation of this method should return the CO_E_ALREADYINITIALIZED err
or code if it receives a call to either the IPersistStorage::InitNew method or t
he IPersistStorage::Load method after it is already initialized.
See Also
IPersistStorage::Load, WriteFmtUserTypeStg

14.6.13.4 IPersistStorage::IsDirty
Indicates whether the object has changed since it was last saved to its current
storage.
HRESULT IsDirty(void);
Return Values
S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.
Remarks
This method checks whether an object has changed since it was last saved so you
can save it before closing it. The dirty flag for an object is conditionally cle
ared in the IPersistStorage::Save method.
For example, you could optimize a File:Save operation by calling the IPersistSto
rage::IsDirty method for each object and then calling the IPersistStorage::Save
method only for those objects that are dirty.
14.6.13.4.1.1.1 Notes to Callers
You should treat any error return codes as an indication that the object has cha
nged. In other words, unless this method explicitly returns S_FALSE, you must as
sume that the object needs to be saved.
14.6.13.4.1.1.2 Notes to Implementers
A container with one or more contained objects must maintain an internal dirty f
lag that is set whenever any of its contained objects are dirty.
See Also
IAdviseSink::OnDataChange, IDataObject::DAdvise, IPersistStorage::Save

14.6.13.5 IPersistStorage::Load
Loads an object from its existing storage.
HRESULT Load(
IStorage *pStg //Pointer to existing storage for the object
);
Parameter
pStg
[in]IStorage pointer to the existing storage from which the object is to be load
ed.
Return Values
S_OK
The object was successfully loaded.
CO_E_ALREADYINITIALIZED
The object has already been initialized by a previous call to the IPersistStorag
e::Load method or the IPersistStorage::InitNew method.
E_OUTOFMEMORY
The object was not loaded due to lack of memory.
E_FAIL
The object was not loaded due to some reason besides a lack of memory.
Remarks
This method initializes an object from an existing storage. The object is placed
in the loaded state if this method is called by the container application. If c
alled by the default handler, this method places the object in the running state
.
Either the default handler or the object itself can hold onto the IStorage point
er while the object is loaded or running.
14.6.13.5.1.1.1 Notes to Callers
Rather than calling IPersistStorage::Load directly, you typically call the OleLo
ad helper function which does the following:
1. Create an uninitialized instance of the object class
2. Query the new instance for the IPersistStorage interface
3. Call IPersistStorage::Load to initialize the object from the exi
sting storage
You also call this method indirectly when you call the OleCreateFromData functio
n or the OleCreateFromFile function to insert an object into a compound file (as
in a drag-and-drop or clipboard paste operation).
The container should cache the IPersistStorage pointer for use in later operatio
ns on the object.
14.6.13.5.1.1.2 Notes to Implementers
Your implementation should perform the following steps to load an object:
1. Open the object s streams in the storage object, and read the nece
ssary data into the object s internal data structures.
2. Clear the object s dirty flag.
3. Call the IPersistStorage::AddRef method and cache the passed in
storage pointer.
4. Keep open and cache the pointers to any streams or storages that
the object will need to save itself to this storage.
5. Perform any other default initialization required for the object
.
Steps 3 and 4 are particularly important for ensuring that the object can save i
tself in low memory situations. Holding onto pointers to the storage and stream
interfaces guarantees that a save operation to this storage will not fail due to
insufficient memory.
Your implementation of this method should return the CO_E_ALREADYINITIALIZED err
or code if it receives a call to either the IPersistStorage::InitNew method or t
he IPersistStorage::Load method after it is already initialized.
See Also
GetConvertStg, IPersistStorage::InitNew, ReadFmtUserTypeStg, SetConvertStg, Writ
eFmtUserTypeStg

14.6.13.6 IPersistStorage::Save
Saves an object, and any nested objects that it contains, into the specified sto
rage. The object is placed in NoScribble mode, and it must not write to the spec
ified storage until it receives a call to its IPersistStorage::SaveCompleted met
hod.
HRESULT Save(
IStorage *pStgSave, //Pointer to storage object
BOOL fSameAsLoad //Indicates whether the specified storage object is the
current one
);
Parameters
pStgSave
[in]IStorage pointer to the storage into which the object is to be saved.
fSameAsLoad
[in]Indicates whether the specified storage is the current one, which was passed
to the object by one of the following calls:
· IPersistStorage::InitNew when it was created.
· IPersistStorage::Load when it was loaded.
· IPersistStorage::SaveCompleted when it was saved to a storage different from its
current storage.
This parameter is set to FALSE when performing a Save As or Save A Copy To opera
tion or when performing a full save. In the latter case, this method saves to a
temporary file, deletes the original file, and renames the temporary file.
This parameter is set to TRUE to perform a full save in a low-memory situation o
r to perform a fast incremental save in which only the dirty components are save
d.
Return Values
S_OK
The object was successfully saved.
STG_E_MEDIUMFULL
The object was not saved because of a lack of space on the disk.
E_FAIL
The object could not be saved due to errors other than a lack of disk space.
Remarks
This method saves an object, and any nested objects it contains, into the specif
ied storage. It also places the object into NoScribble mode. Thus, the object ca
nnot write to its storage until a subsequent call to the IPersistStorage::SaveCo
mpleted method returns the object to Normal mode.
If the storage object is the same as the one it was loaded or created from, the
save operation may be able to write incremental changes to the storage object. O
therwise, a full save must be done.
This method recursively calls the IPersistStorage::Save method, the OleSave func
tion, or the IStorage::CopyTo method to save its nested objects.
This method does not call the IStorage::Commit method. Nor does it write the CLS
ID to the storage object. Both of these tasks are the responsibilities of the ca
ller.
14.6.13.6.1.1.1 Notes to Callers
Rather than calling IPersistStorage::Save directly, you typically call the OleSa
ve helper function which performs the following steps:
1. Call the WriteClassStg function to write the class identifier fo
r the object to the storage.
2. Call the IPersistStorage::Save method.
3. If needed, call the IStorage::Commit method on the storage objec
t.
Then, a container application performs any other operations necessary to complet
e the save and calls the SaveCompleted method for each object.
If an embedded object passes the IPersistStorage::Save method to its nested obje
cts, it must receive a call to its IPersistStorage::SaveCompleted method before
calling this method for its nested objects.
See Also
IPersistStorage::InitNew, IPersistStorage::Load, IPersistStorage::SaveCompleted,
IStorage::Commit, IStorage::CopyTo, WriteClassStg, WriteFmtUserTypeStg

14.6.13.7 IPersistStorage::SaveCompleted
Notifies the object that it can revert from NoScribble or HandsOff mode, in whic
h it must not write to its storage object, to Normal mode, in which it can. The
object enters NoScribble mode when it receives an IPersistStorage::Save call.
HRESULT SaveCompleted(
IStorage *pStgNew //Pointer to the current storage object
);
Parameter
pStgNew
[in]IStorage pointer to the new storage object, if different from the storage ob
ject prior to saving. This pointer can be NULL if the current storage object doe
s not change during the save operation. If the object is in HandsOff mode, this
parameter must be non-NULL.
Return Values
S_OK
The object was successfully returned to Normal mode.
E_OUTOFMEMORY
The object remained in HandsOff mode or NoScribble mode due to a lack of memory.
Typically, this error occurs when the object is not able to open the necessary
streams and storage objects in pStgNew.
E_INVALIDARG
The pStgNew parameter is not valid. Typically, this error occurs if pStgNew is N
ULL when the object is in HandsOff mode.
E_UNEXPECTED
The object is in Normal mode, and there was no previous call to IPersistStorage:
:Save or IPersistStorage::HandsOffStorage.
Remarks
This method notifies an object that it can revert to Normal mode and can once ag
ain write to its storage object. The object exits NoScribble mode or HandsOff mo
de.
If the object is reverting from HandsOff mode, the pStgNew parameter must be non
-NULL. In HandsOffFromNormal mode, this parameter is the new storage object that
replaces the one that was revoked by the IPersistStorage::HandsOffStorage metho
d. The data in the storage object is a copy of the data from the revoked storage
object. In HandsOffAfterSave mode, the data is the same as the data that was mo
st recently saved. It is not the same as the data in the revoked storage object.
If the object is reverting from NoScribble mode, the pStgNew parameter can be NU
LL or non-NULL. If NULL, the object once again has access to its storage object.
If it is not NULL, the component object should simulate receiving a call to its
IPersistStorage::HandsOffStorage method. If the component object cannot simulat
e this call, its container must be prepared to actually call the IPersistStorage
::HandsOffStorage method.
The IPersistStorage::SaveCompleted method must recursively call any nested objec
ts that are loaded or running.
If this method returns an error code, the object is not returned to Normal mode.
Thus, the container object can attempt different save strategies.
See Also
IAdviseSink::OnSave, IPersistStorage::HandsOffStorage, IPersistStorage::Save, IR
ootStorage::SwitchToFile

14.6.14 IPersistStream
The IPersistStream interface provides methods for saving and loading objects tha
t use a simple serial stream for their storage needs. The IPersistStream interfa
ce inherits its definition from the IPersist interface, and so the includes the
GetClassID method of IPersist.
One way in which it is used is to support COM moniker implementations. Each of t
he COM-provided moniker interfaces provides an IPersistStream implementation thr
ough which the moniker saves or loads itself. An instance of the COM generic com
posite moniker class calls the IPersistStream methods of its component monikers
to load or save the components in the proper sequence in a single stream.
COM containers with embedded and linked component objects do not use this interf
ace; they use the IPersistStorage interface instead.
14.6.14.1.1 When to Implement
Implement the IPersistStream interface on objects that are to be saved to a simp
le stream. Some objects of this type are monikers and some COM controls, althoug
h generally, controls use the IPersistStreamInit interface, which has the same m
ethods as IPersistStream, with one added method, IPersistStreamInit::InitNew. Th
e IMoniker interface is derived from the IPersistStream interface, so you must i
mplement the IPersistStream interface if you are implementing a new moniker clas
s.
14.6.14.1.2 When to Use
Call methods of IPersistStream from a container application to save or load obje
cts that are contained in a simple stream. When used to save or load monikers, t
ypical applications do not call the methods directly, but allow the default link
handler to make the calls to save and load the monikers that identify the link
source. These monikers are stored in a stream in the storage for the linked obje
ct. If you are writing a custom link handler for your class of objects, you woul
d call the methods of IPersistStream to implement the link handler.
Methods in Vtable Order

IUnknown Methods Description


QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IPersist Method Description
GetClassID Returns the class identifier (CLSID) for the component object.

IPersistStream Methods Description


IsDirty Checks the object for changes since it was last saved.
Load Initializes an object from the stream where it was previously saved.
Save Saves an object into the specified stream and indicates whether the obje
ct should reset its dirty flag.
GetSizeMax Return the size in bytes of the stream needed to save the object
.
See Also
IMoniker

14.6.14.2 IPersistStream::GetSizeMax
Returns the size in bytes of the stream needed to save the object.
HRESULT GetSizeMax(
ULARGE_INTEGER *pcbSize //Pointer to size of stream needed to save objec
t
);
Parameter
pcbSize
[out]Points to a 64-bit unsigned integer value indicating the size in bytes of t
he stream needed to save this object.
Return Value
S_OK
The size was successfully returned.
Remarks
This method returns the size needed to save an object. You can call this method
to determine the size and set the necessary buffers before calling the IPersistS
tream::Save method.
14.6.14.2.1.1.1 Notes to Implementers
The GetSizeMax implementation should return a conservative estimate of the neces
sary size because the caller might call the IPersistStream::Save method with a n
on-growable stream.
See Also
IPersistStream::Save

14.6.14.3 IPersistStream::IsDirty
Checks the object for changes since it was last saved.
HRESULT IsDirty(void);
Return Values
S_OK
The object has changed since it was last saved.
S_FALSE
The object has not changed since the last save.
Remarks
This method checks whether an object has changed since it was last saved so you
can avoid losing information in objects that have not yet been saved. The dirty
flag for an object is conditionally cleared in the IPersistStream::Save method.
14.6.14.3.1.1.1 Notes to Callers
You should treat any error return codes as an indication that the object has cha
nged. In other words, unless this method explicitly returns S_FALSE, you must as
sume that the object needs to be saved.
Note that the COM-provided implementations of the IPersistStream::IsDirty method
in the COM-provided moniker interfaces always return S_FALSE because their inte
rnal state never changes.
See Also
IPersistStream::Save

14.6.14.4 IPersistStream::Load
Initializes an object from the stream where it was previously saved.
HRESULT Load(
IStream *pStm //Pointer to the stream from which the object should be
loaded
);
Parameter
pStm
[in]IStream pointer to the stream from which the object should be loaded.
Return Values
S_OK
The object was successfully loaded.
E_OUTOFMEMORY
The object was not loaded due to a lack of memory.
E_FAIL
The object was not loaded due to some reason other than a lack of memory.
Remarks
This method loads an object from its associated stream. The seek pointer is set
as it was in the most recent IPersistStream::Save method. This method can seek a
nd read from the stream, but cannot write to it.
On exit, the seek pointer must be in the same position it was in on entry, immed
iately past the end of the data.
14.6.14.4.1.1.1 Notes to Callers
Rather than calling IPersistStream::Load directly, you typically call the OleLoa
dFromStream function does the following:
1. Calls the ReadClassStm function to get the class identifier from
the stream.
2. Calls the CoCreateInstance function to create an instance of the
object.
3. Queries the instance for IPersistStream.
4. Calls IPersistStream::Load.
The OleLoadFromStream function assumes that objects are stored in the stream wit
h a class identifier followed by the object data. This storage pattern is used b
y the generic, composite-moniker implementation provided by COM.
If the objects are not stored using this pattern, you must call the methods sepa
rately yourself.
See Also
CoCreateInstance, ReadClassStm

14.6.14.5 IPersistStream::Save
Saves an object to the specified stream.
HRESULT Save(
IStream *pStm, //Pointer to the stream where the object is to be saved
BOOL fClearDirty //Specifies whether to clear the dirty flag
);
Parameters
pStm
[in]IStream pointer to the stream into which the object should be saved.
fClearDirty
[in]Indicates whether to clear the dirty flag after the save is complete. If TRU
E, the flag should be cleared. If FALSE, the flag should be left unchanged.
Return Values
S_OK
The object was successfully saved to the stream.
STG_E_CANTSAVE
The object could not save itself to the stream. This error could indicate, for e
xample, that the object contains another object that is not serializable to a st
ream or that an IStream::Write call returned STG_E_CANTSAVE.
STG_E_MEDIUMFULL
The object could not be saved because there is no space left on the storage devi
ce.
Remarks
IPersistStream::Save saves an object into the specified stream and indicates whe
ther the object should reset its dirty flag.
The seek pointer is positioned at the location in the stream at which the object
should begin writing its data. The object calls the IStream::Write method to wr
ite its data.
On exit, the seek pointer must be positioned immediately past the object data. T
he position of the seek pointer is undefined if an error returns.
14.6.14.5.1.1.1 Notes to Callers
Rather than calling IPersistStream::Save directly, you typically call the OleSav
eToStream helper function which does the following:
1. Calls IPersistStream::GetClassID to get the object s CLSID.
2. Calls the WriteClassStm function to write the object s CLSID to th
e stream.
3. Calls IPersistStream::Save.
If you call these methods directly, you can write other data into the stream aft
er the CLSID before calling IPersistStream::Save.
The COM-provided implementation of IPersistStream follows this same pattern.
14.6.14.5.1.1.2 Notes to Implementers
The IPersistStream::Save method does not write the CLSID to the stream. The call
er is responsible for writing the CLSID.
The IPersistStream::Save method can read from, write to, and seek in the stream;
but it must not seek to a location in the stream before that of the seek pointe
r on entry.
See Also
IPersist::GetClassID, IStream::Write

14.6.15 IPersistStreamInit
The IPersistStreamInit interface is defined as a replacement for IPersistStream
in order to add an initialization method, InitNew. This interface is not derived
from IPersistStream; it is mutually exclusive with IPersistStream. An object ch
ooses to support only one of the two interfaces, based on whether it requires th
e InitNew method. Otherwise, the signatures and semantics of the other methods a
re the same as the corresponding methods of IPersistStream, except as described
below.
14.6.15.1.1 When to Implement
Implement this interface on any object that needs to support initialized stream-
based persistence, regardless of whatever else the object does. The presence of
the InitNew method requires some changes to other methods that are common to IPe
rsistStream, as noted in the method descriptions.
14.6.15.1.2 When to Use
Use this interface to initialize a stream-based object and to save that object t
o a stream.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPersistStreamInit Methods Description
IsDirty Checks the object for changes since it was last saved.
Load Initializes an object from the stream where it was previously saved.
Save Saves an object into the specified stream and indicates whether the obje
ct should reset its dirty flag.
GetSizeMax Return the size in bytes of the stream needed to save the object
.
InitNew Initializes an object to a default state.
See Also
IPersistStream

14.6.15.2 IPersistStreamInit::GetSizeMax
Same as IPersistStream::GetSizeMax.
HRESULT GetSizeMax(
ULARGE_INTEGER* pcbSize //Receives a pointer to the size of the stream n
eeded to save object
);
14.6.15.3 IPersistStreamInit::InitNew
Initializes the object to a default state. This method is called instead of IPer
sistStreamInit::Load.
HRESULT InitNew(void);
Return Values
This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED,
as well as the following:
S_OK
The object successfully initialized itself.
E_NOTIMPL
The object requires no default initialization. This error code is allowed becaus
e an object may choose to implement IPersistStreamInit simply for orthogonality
or in anticipation of a future need for this method.
Remarks
14.6.15.3.1.1.1 Notes to Implementers
If the object has already been initialized with Load, then this method must retu
rn E_UNEXPECTED.
See Also
IPersistStreamInit::Load

14.6.15.4 IPersistStreamInit::IsDirty
Same as IPersistStream::IsDirty.
HRESULT IsDirty(void);
14.6.15.5 IPersistStreamInit::Load
Same as IPersistStream::Load.
HRESULT Load(
LPSTREAM pStm //Pointer to the stream from which the object should be
loaded
);
Remarks
14.6.15.5.1.1.1 Notes to Implementers
If the object has already been initialized with InitNew, then this method must r
eturn E_UNEXPECTED.

14.6.15.6 IPersistStreamInit::Save
Same as IPersistStream::Save.
HRESULT Save(
LPSTREAM pStm , //Pointer to the stream where the object is to be saved
BOOL fClearDirty //Specifies whether to clear the dirty flag
);

14.6.16 IPropertyBag
The IPropertyBag interface provides an object with a property bag in which the o
bject can persistently save its properties.
When a client wishes to have exact control over how individually named propertie
s of an object are saved, it would attempt to use an object s IPersistPropertyBag
interface as a persistence mechanism. In that case the client supplies a propert
y bag to the object in the form of an IPropertyBag interface.
When the object wishes to read a property in IPersistPropertyBag::Load it will c
all IPropertyBag::Read. When the object is saving properties in IPersistProperty
Bag::Save it will call IPropertyBag::Write. Each property is described with a na
me in pszPropName whose value is exchanged in a VARIANT. This information allows
a client to save the property values as text, for instance, which is the primar
y reason why a client might choose to support IPersistPropertyBag.
The client records errors that occur during reading into the supplied error log.
14.6.16.1.1 When to Implement
A container implements this interface to provide its object with a way to store
their properties persistently.
14.6.16.1.2 When to Use
An object calls the methods on this interface to read and write its properties i
nto the container provided property bag.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPropertyBag Methods Description
Read Called by the control to read a property from the storage provided by th
e container.
Write Called by the control to write each property in turn to the storage prov
ided by the container.
See Also
IErrorLog, IPersistPropertyBag

14.6.16.2 IPropertyBag::Read
Called by the control to read a property from the storage provided by the contai
ner.
HRESULT Read(
LPCOLESTR pszPropName, //Pointer to the property to be read
VARIANT* pVar, //Pointer to the VARIANT to receive the property value
IErrorLog* pErrorLog //Pointer to the caller s error log
);
Parameters
pszPropName
[in] Pointer to the name of the property to read. Cannot be NULL.
pVar
[in, out] Pointer to the caller-initialized VARIANT that is to receive the prope
rty value on output. The method must set both type and value fields in the VARIA
NT before returning. If the caller initialized the pVar->vt field on entry, the
property bag should attempt to coerce the value it knows into this type. If the
caller sets pVar->vt to VT_EMPTY, the property bag can use a convenient type tha
t is consistent with the data. For example, if the property is a string, the pro
perty bag cannot use VT_I4.
pErrorLog
[in] Pointer to the caller s IErrorLog interface in which the property bag stores
any errors that occur during reads. Can be NULL in which case the caller is not
interested in errors.
Return Values
S_OK
The property was read successfully. The caller becomes responsible for any alloc
ations that are contained in the VARIANT in pVar.
E_POINTER
The address in pszPropName is not valid (such as NULL).
E_INVALIDARG
The property named with pszPropName does not exist in the property bag.
E_FAIL
The property bag was unable to read the specified property, such as if the calle
r specified a data type to which the property bag could not coerce the known val
ue. If the caller supplied an error log, a more descriptive error was sent there
.
Remarks
This method asks the property bag to read the property named with pszPropName in
to the caller-initialized VARIANT in pVar. Errors that occur are logged in the e
rror log pointed to by pErrorLog.
When pVar->vt specifies another object pointer (VT_UNKNOWN or VT_DISPATCH) then
the property bag is responsible for creating and initializing the object describ
ed by pszPropName. The action taken by the property bag depends on the value of
pvar->punkVal or pvar->vdispVal. For example, if pvar->punkVal is non-NULL, the
property bag initializes the existing object using the value of the pointer, usu
ally querying for a persistence interface and calling the Load method. However,
if pVar->punkVal is NULL, then the property bag creates a new object and loads i
t as appropriate.
E_NOTIMPL is not a valid return code since any object implementing this interfac
e must support the entire functionality of the interface.
See Also
IPropertyBag::Write

14.6.16.3 IPropertyBag::Write
Called by the control to write each property in turn to the storage provided by
the container.
HRESULT Write(
LPCOLESTR pszPropName, //Points to the property to be written
VARIANT* pVar //Points to the VARIANT containing the property value an
d type
);
Parameters
pszPropName
[in] Pointer to the name of the property to write. Cannot be NULL.
pVar
[in] Pointer to the caller-initialized VARIANT that holds the property value to
save. The caller owns this VARIANT and is responsible for all allocations therei
n. That is, the property bag itself does not attempt to free data in the VARIANT
.
Return Values
S_OK
The property bag successfully saved the requested property.
E_FAIL
There was a problem writing the property. It is possible that the property bag d
oes not understand how to save a particular VARIANT type.
E_POINTER
The address in pszPropName or pVar is not valid (such as NULL). The caller must
supply both.
Remarks
This method asks the property bag to save the property named with pszPropName us
ing the type and value in the caller-initialized VARIANT in pVar. In some cases
the caller may be asking the property bag to save another object, that is, when
pVar->vt is VT_UNKNOWN. In such cases, the property bag queries this object poin
ter for some persistence interface, like IPersistStream or even IPersistProperty
Bag again and has that object save its data as well. Usually, this results in th
e property bag having some byte array for this object which can be saved as enco
ded text (hex string, MIME, etc.). When the property bag is later used to reinit
ialize a control, the client that owns the property bag must recreate the object
when the caller asks for it, initializing that object with the previously saved
bits.
This allows very efficient persistence operations for large BLOB properties like
a picture, where the owner of the property bag itself directly asks the picture
object (managed as a property in the control being saved) to save into a specif
ic location. This avoids potential extra copy operations that would be involved
with other property-based persistence mechanisms.
E_NOTIMPL is not a valid return code as any object implementing this interface m
ust support the entire functionality of the interface.
See Also
IPropertyBag::Read

14.6.17 IPropertySetStorage
Creates, opens, deletes, and enumerates property set storages that support insta
nces of the IPropertyStorage interface. The IPropertyStorage interface manages a
single property set in a property storage subobject; the IPropertySetStorage in
terface manages the storage of groups of such property sets. IPropertySetStorage
can be supported by any file system entity, and is currently implemented in the
COM compound file object.
The IPropertySetStorage and IPropertyStorage interfaces provide a uniform way to
create and manage property sets, whether or not these sets reside in a storage
object that supports IStorage. When called through an object supporting IStorage
(such as structured and compound files and directories) or IStream, the propert
y sets created conform to the COM property set format, described in detail in Ap
pendix C of the COM Programming Guide. Similarly, properties written using IStor
age to the COM property set format are visible through IPropertySetStorage and I
PropertyStorage. IPropertyStorage does not support extensions to the COM seriali
zed property set format or multiple sections, because you can get equivalent fun
ctionality as simply by creating new sets or by adding new properties to existin
g property sets.
IPropertySetStorage methods identify property sets through a GUID called a forma
t identifier (FMTID). The FMTID for a property set identifies the set of propert
y identifiers in the property set, their meaning, and any constraints on the val
ues. The format identifier of a property set should also provide the means to ma
nipulate that property set. Only one instance of a given FMTID may exist at a ti
me within a single property storage.
14.6.17.1.1 When to Implement
Implement IPropertySetStorage to store persistent properties in the file system.
If you are using the COM compound files implementation, you can use the impleme
ntation on the compound file object created through a call to StgCreateDocfile o
r StgOpenStorage. Once you have a pointer to any of the interface implementation
s (such as IStorage) on this object, you can call QueryInterface to get a pointe
r to the IPropertySetStorage interface implementation.
14.6.17.1.2 When to Use
Call IPropertySetStorage methods to create, open, or delete one or more property
sets, or to enumerate the property sets contained in this property set storage.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPropertySetStorage Methods Description
Create Creates a new property set.
Open Opens a previously created property set.
Delete Deletes an existing property set.
Enum Creates and retrieves a pointer to an object that can be used to enumera
te property sets.
See Also
IPropertyStorage, IEnumSTATPROPSETSTG, STATPROPSETSTG, PROPVARIANT

14.6.17.2 IPropertySetStorage::Create
Creates and opens a new property set in the property set storage object.
HRESULT Create(
REFFMTID fmtid, //Format identifier of the property set to be created
CLSID * pclsid, //Pointer to initial CLSID for this property set
DWORD grfFlags, //PROPSETFLAG values
DWORD grfMode, //Storage mode of new property set
IPropertyStorage** ppPropStg //Indirect pointer to property storage s
ub-object
);
Parameters
fmtid
[in] Format identifier of the property set to be created.
pclsid
[in] Pointer to the initial CLSID for this property set. May be NULL, in which c
ase it is set to all zeroes.
grfFlags
[in] Values from the PROPSETFLAG enumeration.
grfMode
[in] Access mode in which the newly created property set is to be opened, taken
from certain values of the STGM enumeration, as described in the Remarks.
ppPropStg
[out] Indirect pointer to the IPropertyStorage interface on the new property sto
rage sub-object.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The property set was created.
STG_E_FILEALREADYEXISTS
A property set of the indicated name already exists, and STGM_CREATE was not spe
cified.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
A parameter is invalid.
Remarks
IPropertySetStorage::Create creates and opens a new property set sub-object (sup
porting the IPropertyStorage interface) contained in this property set storage o
bject. The property set automatically contains code page and locale ID propertie
s. These are set to the current system default, and the current user default, re
spectively.
The grfFlags parameter is a combination of values taken from the enumeration PRO
PSETFLAG.
The grfMode parameter specifies the access mode in which the newly created set i
s to be opened. Values for this parameter are as in the like-named parameter to
IPropertySetStorage::Open, with the addition of the following values:
Value Meaning
STGM_FAILIFTHERE If another property set with the specified fmtid already
exists, the call fails. This is the default action; that is, unless STGM_CREATE
is specified, STGM_FAILIFTHERE is implied.
STGM_CREATE If another property set with the specified fmtid already exists,
it is removed and replaced with this new one.
STGM_DIRECT Open the property set without an additional level of transaction
nesting. This is the default (the behavior if neither STGM_DIRECT nor STGM_TRAN
SACTED is specified).
STGM_TRANSACTED Open the property set with an additional level of transaction ne
sting (beyond the transaction, if any, on this property set storage). This is po
ssible only when you specify PROPSETFLAG_NONSIMPLE in the grfFlags parameter. Ch
anges in the property set must be committed with IPropertyStorage::Commit before
they are visible to the transaction on this property set storage.
STGM_READ Read access is desired on the property set. Read permission is r
equired on the property set storage.
STGM_WRITE Write access is desired on the property set. Write permission is
not required on the property set storage; however, such write permission is req
uired for changes in the storage to be committed.
STGM_READWRITE Read-write access is desired on the property set. Note that this
flag is not the binary OR of the values STGM_READ and STGM_WRITE.
STGM_SHARE_EXCLUSIVE Prevents others from subsequently opening the property s
et either in STGM_READ or STGM_WRITE mode.
Note
The only access mode supported by Create is STGM_SHARE_EXCLUSIVE. To use the res
ulting property set in an access mode other than STGM_SHARE_EXCLUSIVE, the calle
r should close the stream and then re-open it with a call to IPropertySetStorage
::Open.
See Also
IPropertySetStorage::Open

14.6.17.3 IPropertySetStorage::Delete
Deletes one of the property sets contained in the property set storage object.
HRESULT Delete(

REFFMTID fmtid //Format identifier of the property set to be deleted.


);
Parameters
fmtid
[in] Format identifier of the property set to be deleted.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The property set was successfully deleted.
STG_E_FILENOTFOUND
The specified property set does not exist.
STG_E_ACCESSDENIED
The requested access to the property set storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid.
Remarks
IPropertySetStorage::Delete deletes the property set specified by its format ide
ntifier. Specifying a property set that does not exist returns an error. Open su
bstorages and streams (opened through one of the storage- or stream-valued prope
rties) are put into the reverted state.

14.6.17.4 IPropertySetStorage::Enum
Creates an enumerator object which contains information on the property sets sto
red in this property set storage. On return, this method supplies a pointer to t
he IEnumSTATPROPSETSTG pointer on the enumerator object.
HRESULT Enum(
IEnumSTATPROPSETSTG**ppenum //Indirect pointer to the new enumerator
);
Parameters
ppenum
[out] Indirect pointer to the IEnumSTATPROPSETSTG on the newly created enumerati
on object.
Return Values
S_OK
The enumerator object was successfully created.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
Remarks
IPropertySetStorage::Enum creates an enumerator object that can be used to itera
te through STATPROPSETSTG structures. These sometimes provide information on the
property sets managed by IPropertySetStorage. This method, on return, supplies
a pointer to the IEnumSTATPROPSETSTG interface on this enumerator object on retu
rn.
See Also
IEnumSTATPROPSETSTG, IEnumSTATPROPSETSTG -- Compound File Implementation

14.6.17.5 IPropertySetStorage::Open
Opens a property set contained in the property set storage object.
HRESULT Open(
REFFMTID fmtid, //The format identifier of the property set to be opened
DWORD grfMode, //Storage mode in which property set is to be opened
IPropertyStorage** ppPropStg //Indirect pointer to property storage o
bject
);
Parameters
fmtid
[in] Format identifier of the property set to be opened.
grfMode
[in] Access mode in which the newly created property set is to be opened. These
flags are taken from the STGM enumeration. Flags that may be used and their mean
ings in the context of this method are described in the Remarks.
ppPropStg
[in] Indirect pointer to the IPropertyStorage interface on the requested propert
y storage sub-object.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Success.
STG_E_FILENOTFOUND
A property set of the indicated name does not exist.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied, or the prop
erty set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
A parameter is invalid.
Remarks
The mode in which the property set is to be opened is specified in the parameter
grfMode. These flags are taken from the STGM enumeration, but, for this method,
legal values and their meanings are as follows (only certain combinations of th
ese flag values are legal).
Value Meaning
STGM_DIRECT Open the property set without an additional level of transaction
nesting. This is the default (the behavior if neither STGM_DIRECT nor STGM_TRAN
SACTED is specified).
STGM_TRANSACTED Open the property set with an additional level of transaction ne
sting (beyond the transaction, if any, on this property set storage object). Tra
nsacted mode is available only on non-simple property sets, because they use an
IStorage with a contents stream. Changes in the property set must be committed w
ith a call to IPropertyStorage::Commit before they are visible to the transactio
n on this property set storage.
STGM_READ Open the property set with read access. Read permission is requi
red on the property set storage.
STGM_WRITE Open the property set with write access. Write permission is not
required on the IPropertySetStorage; however, such write permission is required
for changes in the storage to be committed.
STGM_READWRITE Open the property set with read-write access. Note that this fla
g is not the binary OR of the values STGM_READ and STGM_WRITE.
STGM_SHARE_DENY_NONE Subsequent openings of the property set are not denied r
ead or write access. Not available in compound file implementation.
STGM_SHARE_DENY_READ Subsequent openings of the property set in are denied re
ad access. Not available in compound file implementation.
STGM_SHARE_DENY_WRITE Subsequent openings of the property set are denied write
access. This value is typically used to prevent making unnecessary copies of an
object opened by multiple users. If this value is not specified, a snapshot is
made, whether there are subsequent openings or not. Thus, you can improve perfor
mance by specifying this value. Not available in compound file implementation.
STGM_SHARE_EXCLUSIVE The combination of STGM_SHARE_DENY_READ and STGM_SHARE_D
ENY_WRITE.
14.6.18 IPropertySetStorage-Compound File Implementation
The COM compound file storage object implementation includes an implementation o
f both IPropertyStorage, the interface that manages a single persistent property
set, and IPropertySetStorage, the interface that manages groups of persistent p
roperty sets.
To get a pointer to the compound file implementation of IPropertySetStorage, fir
st call StgCreateDocfile to create a new compound file object or StgOpenStorage
to open a previously created compound file. Both functions supply a pointer to t
he object s IStorage interface. When you want to deal with persistent property set
s, call IStorage::QueryInterface for the IPropertySetStorage interface, specifyi
ng the header-defined name for the interface identifier IID_IPropertySetStorage.
14.6.18.1.1.1.1 When to Use
Call the methods of IPropertySetStorage to create, open, or delete property sets
in the current compound file property set storage. There is also a method that
supplies a pointer to an enumerator that can be used to enumerate the property s
ets in the storage.
Remarks
IPropertySetStorage::Create
Creates a new property set in the current compound file storage and, on return,
supplies an indirect pointer to the IPropertyStorage compound file implementatio
n. In this implementation, property sets may be transacted only if PROPSETFLAG_N
ONSIMPLE is specified.
IPropertySetStorage::Open
Opens an existing property set in the current property storage. On return, it su
pplies an indirect pointer to the compound file implementation of IPropertyStora
ge.
IPropertySetStorage::Delete
Deletes a property set in this property storage.
IPropertySetStorage::Enum
Creates an object that can be used to enumerate STATPROPSETSTG structures. Each
STATPROPSETSTG structure provides information about a single property set. The i
mplementation calls the constructor for IEnumSTATPROPSETSTG, which, in turn, use
s the pointer to the IStorage interface to create a STATSTG enumerator, which is
then used over the actual storage to get the information about the property set
s.
Note
The DocumentSummaryInformation property set is special, in that it may have two
property set sections. This property set is described in the OLE Programmer s Refe
rence, in the section titled The DocumentSummaryInformation Property Set. The se
cond section is referred to as the User-Defined Properties. Each section is iden
tified with a unique Format ID, for example FMTID_DocumentSummaryInformation and
FMTID_UserDefinedProperties.
When IPropertySetStorage::Create is called to create the User-Defined Property S
et, the first section is created automatically. Thus once FMTID_UserDefinedPrope
rties is created, FMTID_DocumentSummaryInformation need not be created, but can
be opened with a call to IPropertySetStorage::Open. Note that creating the first
section does not automatically create the second section. It is not possible to
open both sections simultaneously.
When IPropertySetStorage::Create is called to create the User-Defined Property S
et, the first section is created automatically. Thus once FMTID_UserDefinedPrope
rties is created, FMTID_DocumentSummaryInformation need not be created, but can
be opened with a call to IPropertySetStorage::Open. Note that creating the first
section does not automatically create the second section. It is not possible to
open both sections simultaneously.
Alternately, when IPropertySetStorage::Delete is called to delete the first sect
ion, both sections are deleted. That is, calling IPropertySetStorage::Delete wit
h FMTID_DocumentSummaryInformation, causes both that section and the FMTID_UserD
efinedProperties section to be deleted. Note that deleting the second section do
es not automatically delete the first section.
Finally, when IPropertySetStorage::Enum is used to enumerate property sets, the
User-Defined Property Set will not be enumerated.
See Also
IPropertyStorage, IPropertySetStorage - Compound File Implementation, STATPROPSE
TSTG structure, PROPSETFLAG enumeration, IStorage::EnumElements
14.6.19 IPropertySetStorage-Standalone Implementation
The system-provided, standalone implementation of IPropertySetStorage includes a
n implementation of both IPropertyStorage, the interface that reads and writes p
roperties in a property set storage, and IPropertySetStorage, the interface that
creates and opens property sets in a storage. The IEnumSTATPROPSTG and IEnumSTA
TPROPSETSTG interfaces are also provided in the standalone implementation.
To use the standalone implementation of IPropertySetStorage, you first obtain a
pointer to the system-provided, standalone implementation and associate the syst
em-provided implementation with your storage object. To get a pointer to the sta
ndalone implementation of IPropertySetStorage, call the StgCreatePropSetStg func
tion and provide the pStorage parameter specifying the storage object that will
contain the property set. This function supplies you with a pointer to the new I
PropertySetStorage interface for the specified storage object.
The standalone implementation of IPropertySetStorage creates property sets on an
y storage or stream object, not just on compound file storages and streams. The
standalone implementation does not depend on compound files and can be used with
any implementation of structured storages. See the section IPropertySetStorage-
Compound File Implementation in the Object Services section of the Platform SDK
for more information on the compound file implementation of this interface.
14.6.19.1.1.1.1 When to Use
Call the methods of IPropertySetStorage to create, open, and delete property set
s in any structured storage. There is also a method that supplies a pointer to t
he IEnumSTATPROPSETSTG enumerator that can be used to enumerate the property set
s in the storage.
The standalone implementation also provides the StgCreatePropStg and the StgOpen
PropStg helper functions in addition to the Create and Open methods to create an
d open property sets. These two functions add support for the PROPSETFLAG_UNBUFF
ERED value so you can directly write changes to the property set instead of buff
ering them in a cache. See the PROPSETFLAG enumeration for more information on u
sing this value.
Remarks
The standalone implementation of IPropertySetStorage supports the following meth
ods:
IPropertySetStorage::Create
Creates a new property set in the storage and returns a pointer to the IProperty
Storage interface on the property set.
If you plan to use the PROPSETFLAG_UNBUFFERED value, use the StgCreatePropStg fu
nction instead to create and open the new property set and to obtain a pointer t
o the standalone implementation for the IPropertyStorage interface on the proper
ty set.
IPropertySetStorage::Open
Opens an existing property set in the storage and returns a pointer to the IProp
ertyStorage interface on the property set.
If you plan to use the PROPSETFLAG_UNBUFFERED value, use the StgOpenPropStg func
tion instead to obtain a pointer to the standalone implementation of IPropertySt
orage on the specified property set.
IPropertySetStorage::Delete
Deletes a property set in this property set storage.
IPropertySetStorage::Enum
Creates an object that can be used to enumerate STATPROPSETSTG structures. Each
STATPROPSETSTG structure provides information about a single property set.
Note
The DocumentSummaryInformation property set is special, in that it may have two
property set sections. This property set is described in the Object Services top
ic of the Platform SDK in the section titled "The DocumentSummaryInformation Pro
perty Set." The second property set is referred to as the User-Defined Propertie
s. Each section is identified with a unique Format ID, for example FMTID_Documen
tSummaryInformation and FMTID_UserDefinedProperties.
When IPropertySetStorage::Create is called to create the User-Defined Property S
et, the first section is created automatically. Thus once FMTID_UserDefinedPrope
rties is created, FMTID_DocumentSummaryInformation need not be created, but can
be opened with a call to IPropertySetStorage::Open. Note that creating the first
section does not automatically create the second section. It is not possible to
open both sections simultaneously.
Alternately, when IPropertySetStorage::Delete is called to delete the first sect
ion, both sections are deleted. That is, calling IPropertySetStorage::Delete wit
h FMTID_DocumentSummaryInformation, causes both that section and the FMTID_UserD
efinedProperties section to be deleted. Note that deleting the second section do
es not automatically delete the first section.
Finally, when IPropertySetStorage::Enum is used to enumerate property sets, the
User-Defined Property Set isl not enumerated.
Programming Information
Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertyStorage, IPropertySetStorage - Compound File Implementation, IPropertyS
torage-Standalone Implementation, STATPROPSETSTG, PROPSETFLAG, IStorage::EnumEle
ments, STGM, StgCreatePropStg, StgOpenPropStg, StgCreatePropSetStg

14.6.20 IPropertyStorage
Manages the persistent properties of a single property set. Persistent propertie
s consist of information that can be stored persistently in a property set, such
as the summary information associated with a file. This contrasts with run-time
properties associated with Controls and Automation, which can be used to affect
system behavior. Use the methods of the IPropertySetStorage interface to create
or open a persistent property set. An IPropertySetStorage instance can manage z
ero or more IPropertyStorage instances.
Each property within a property set is identified by a property identifier, a fo
ur-byte ULONG value unique to that set. You can also assign a string name to a p
roperty through the IPropertyStorage interface.
Property identifiers are different from the dispatch identifiers used in Automat
ion dispid property name tags. One difference is that the general-purpose use of
property identifier values zero and one is prohibited in IPropertyStorage, whil
e no such restriction exists in IDispatch. In addition, while there is significa
nt overlap in the data types for property values that may be used in IPropertySt
orage and IDispatch, the sets are not identical. Persistent property data types
used in IPropertyStorage methods are defined in the PROPVARIANT structure.
14.6.20.1.1.1.1 When to Implement
Implement IPropertyStorage when you want to store properties in the file system.
If you are using the COM compound files implementation, the compound file objec
t created through a call to StgCreateDocfile includes an implementation of IProp
ertySetStorage, which allows access to the implementation of IPropertyStorage. O
nce you have a pointer to any of the interface implementations (such as IStorage
) on this object, you can call QueryInterface to get a pointer to the IPropertyS
etStorage interface implementation, and then call either the Open or Create meth
od, as appropriate to obtain a pointer to the IPropertyStorage interface managin
g the specified property set.
14.6.20.1.1.1.2 When to Use
Use IPropertyStorage to create and manage properties that are stored in a given
property set.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPropertyStorage Methods Description
ReadMultiple Reads property values in a property set.
WriteMultiple Writes property values in a property set.
DeleteMultiple Deletes properties in a property set.
ReadPropertyNames Gets corresponding string names for given property ident
ifiers.
WritePropertyNames Creates or changes string names corresponding to given p
roperty identifiers.
DeletePropertyNames Deletes string names for given property identifiers.
SetClass Assigns a CLSID to the property set.
Commit As in IStorage::Commit, flushes or commits changes to the property stora
ge object.
Revert When the property storage is opened in transacted mode, discards all cha
nges since the last commit.
Enum Creates and gets a pointer to an enumerator for properties within this s
et.
Stat Receives statistics about this property set.
SetTimes Sets modification, creation, and access times for the property s
et.
See Also
IPropertySetStorage, IEnumSTATPROPSTG, IEnumSTATPROPSETSTG, STATPROPSTG, STATPRO
PSETSTG, PROPVARIANT

14.6.20.2 IPropertyStorage::Commit
Saves any changes made to a property storage object to the parent storage object
.
HRESULT Commit(

DWORD grfCommitFlags //Flags specifying conditions for the commit


);
Parameters
grfCommitFlags
[in] Flags specifying the conditions under which the commit is to be performed.
Specific flags and their meanings are described in the following Remarks section
.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The changes were saved successfully.
STG_E_NOTCURRENT
STGC_ONLYIFCURRENT was specified, but the optimistic concurrency control failed.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
One or more flags specified in grfCommitFlags is invalid.
Remarks
As in IStorage::Commit, ensures that any changes made to a property storage obje
ct are reflected in the parent storage.
In direct mode in the compound file implementation, this call causes any changes
currently buffered up in memory to be flushed to the underlying property stream
. In the compound file implementation for non-simple property sets, IStorage::Co
mmit is also called on the underlying substorage object with the passed grfCommi
tFlags parameter.
In transacted mode, this method causes the changes to be permanently reflected i
n the persistent image of the storage object. The changes that are committed mus
t have been made to this property set since it was opened or since the last comm
it on this opening of the property set. One could think of the action of committ
ing as publishing the changes that this level currently knows about one more lay
er outwards. Of course, this is still subject to any outer level transaction tha
t may be present on the object in which this property set is contained. Write pe
rmission must be specified when the property set is opened (through IPropertySet
Storage) on the property set opening for the commit operation to succeed.
If the commit operation fails for any reason, the state of the property storage
object is as it was before the commit.
This call has no effect on existing storage- or stream-valued properties opened
from this property storage, but it does commit them.
Valid values for the grfCommitFlags parameter are as follows:
Value Meaning
STGC_DEFAULT Commit per the usual transaction semantics. Last writer wins. Th
is flag may not be specified with other flag values.
STGC_ONLYIFCURRENT Commit the changes only if the current persistent conten
ts of the property set are the ones on which the changes about to be committed a
re based. That is, do not commit changes if the contents of the property set hav
e been changed by a commit from another opening of the property set. The error S
TG_E_NOTCURRENT is returned if the commit does not succeed for this reason.
STGC_OVERWRITE Only useful when committing a transaction which has no further o
uter nesting level of transactioning, though legal in all cases. Indicates that
the caller is willing to take some risk of data corruption at the expense of a d
ecreased usage of disk on the destination volume. This flag is potentially usefu
l in low disk space scenarios, though should be used only with caution.
See Also
IPropertyStorage::ReadMultiple, IStorage::Commit

14.6.20.3 IPropertyStorage::DeleteMultiple
Deletes as many of the indicated properties as exist in this property set.
HRESULT DeleteMultiple(
ULONG cpspec, //Count of properties to be deleted
PROPSPEC const rgpspec[] //Array of properties to be deleted
);
Parameters
cpspec
[in] Count of properties being deleted. May legally be zero, though this is a no
-op, deleting no properties.
rgpspec[]
[in] Properties to be deleted. A mixture of property identifiers and string-name
d properties is permitted. There may be duplicates, and there is no requirement
that properties be specified in any order.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
All of the specified properties that exist in the property set have been deleted
.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No properti
es were deleted.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. Some properties may no
t have been deleted.
STG_E_INVALIDPARAMETER
At least one of the parameters is invalid, as when one of the PROPSPECs contains
an illegal ulKind value. Some properties may not have been deleted.
STG_E_INVALIDPOINTER
May be returned when at least one of the pointers passed in is invalid. Some pro
perties may not have been written. More frequently, an invalid pointer will inst
ead result in an access violation.
Remarks
IPropertyStorage::DeleteMultiple must delete as many of the indicated properties
as are in the current property set. If a deletion of a stream- or storage-value
d property occurs while that property is open, the deletion will succeed and pla
ce the previously returned IStream or IStorage pointer in the reverted state.

14.6.20.4 IPropertyStorage::DeletePropertyNames
Deletes specified string names from the current property set.
HRESULT DeletePropertyNames(
ULONG cpropid, //Size of the rgpropid array
PROPID const rgpropid[] //Property identifiers for which string names ar
e to be deleted
);
Parameters
cpropid
[in] The size on input of the array rgpropid. If 0, no property names are delete
d.
rgpropid[]
[in] Property identifiers for which string names are to be deleted.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Success. The names of all of the indicated properties that exist in this set hav
e been deleted.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No property
names were deleted.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. Some property names ma
y not have been deleted.
STG_E_INVALIDPARAMETER
At least one of the parameters is invalid. Some property names may not have been
deleted.
Remarks
For each property identifier in rgpropid, IPropertyStorage::DeletePropertyNames
removes the corresponding name-to-property identifier mapping, if any. An attemp
t to delete the name of a property that either does not exist or does not presen
tly have a string name associated with it is silently ignored. This method has n
o effect on the properties themselves.
Note
All the stored string property names can be deleted by deleting property identif
ier zero, but cpropid must be equal to 1 for this to not be an invalid parameter
error.
See Also
IPropertyStorage::ReadPropertyNames
14.6.20.5 IPropertyStorage::Enum
Creates an enumerator object designed to enumerate data of type STATPROPSTG, whi
ch contains information on the current property set. On return, this method supp
lies a pointer to the IEnumSTATPROPSTG pointer on this object.
HRESULT Enum(
IEnumSTATPROPSTG ** ppenum //Indirect pointer to new enumerator
);
Parameters
ppenum
[out] Indirect pointer to the IEnumSTATPROPSTG interface on the new enumeration
object.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
A pointer to the enumerator has been retrieved.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid.
STG_E_READFAULT
Error reading storage.
Remarks
IPropertyStorage::Enum creates an enumeration object that can be used to iterate
STATPROPSTG structures. On return, this method supplies a pointer to an instanc
e of IEnumSTATPROPSTG interface on this objects whose methods you can call to ob
tain information on the current property set.
See Also
IEnumSTATPROPSTG, IEnumSTATPROPSTG -- Compound File Implementation

14.6.20.6 IPropertyStorage::ReadMultiple
Reads specified properties from the current property set.
HRESULT ReadMultiple(
ULONG cpspec, //Count of properties being read.
PROPSPEC const rgpspec[], //Array of the properties to be read
PROPVARIANT rgvar[] //Array of PROPVARIANTs containing the property values o
n return
);
Parameters
cpspec
[in] Count of properties specified in the rgpspec array. May legally be zero, th
ough this is a no-op, reading no properties.
rgpspec[]
[in] The properties to be read in the PROPSPEC structures. Properties can be spe
cified either by property identifier or by optional string name. It is not neces
sary to specify properties in any particular order in the array. The array can c
ontain duplicate properties, resulting in duplicate property values on return fo
r simple properties. Non-simple properties should return access denied on an att
empt to open them a second time. The array can contain a mixture of property ide
ntifiers and string identifiers.
rgvar[]
[in, out] Caller-allocated array of PROPVARIANTs that, on return, contains the v
alues of the properties specified by rgpspec. The array must be able to receive
at least cpspec PROPVARIANTs. The caller does not need to initialize these PROPV
ARIANTs in any particular way; the implementation must fill in all field members
correctly on return. If there is no other appropriate value, the implementation
must set the vt member of each PROPVARIANT to VT_EMPTY.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Success. At least some of the requested properties were retrieved.
S_FALSE
All the property names or identifiers had valid syntax, but none of them exist i
n this property set. Accordingly, no properties were retrieved., and each PROPVA
RIANT structure is set to VT_EMPTY.
STG_E_ACCESSDENIED
The requested access to the property set has been denied, or, when one or more o
f the properties is a stream or storage object, access to that substorage or sub
stream has been denied. (The storage or stream may already be open). No properti
es were retrieved.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. No properties were ret
rieved.
STG_E_INVALIDPARAMETER
At least one of the parameters is invalid, such as when one of the PROPSPECs con
tains an illegal ulKind value. No properties were retrieved.
STG_E_INVALIDPOINTER
At least one of the pointers passed in is invalid. No properties were retrieved.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from Ansi.
Remarks
IPropertyStorage::ReadMultiple reads as many of the properties specified in the
rgpspec array as are found in the property set. As long as any of the properties
requested is read, a request to retrieve a property that does not exist is not
an error. Instead, this must cause VT_EMPTY to be written for that property to t
he rgvar[] array on return. When none of the requested properties exist, the met
hod should return S_FALSE, and set VT_EMPTY in each PROPVARIANT. If any other er
ror is returned, no property values are retrieved, and the caller need not worry
about releasing them.
The rgpspec parameter is an array of PROPSPEC structures, which specify for each
property either its property identifier or, if one is assigned, a string identi
fier. You can map a string to a property identifier by calling IPropertyStorage:
:WritePropertyNames. The use of property identifiers is, however, likely to be s
ignificantly more efficient than the use of strings.
Properties that are requested by string name (PRSPEC_LPWSTR) are mapped case-ins
ensitively to property identifiers as they are specified in the current property
set (and according to the current system locale).
All propvariants, except for those that are pointers to streams and storages, ar
e called simple propvariants. These simple propvariants receive data by value, s
o a call to IPropertyStorage::ReadMultiple supplies a copy of the data that the
caller then owns. To create or update these properties, call IPropertyStorage::W
riteMultiple.
In contrast, the variant types VT_STREAM, VT_STREAMEDOBJECT, VT_STORAGE, and VT_
STOREDOBJECT are non-simple properties, because rather than supplying a value, t
he method retrieves a pointer to the indicated interface, from which the data ca
n then be read. These types permit the storage of large amounts of information t
hrough a single property. There are several issues that arise in using non-simpl
e properties.
To create these properties, as for the other properties, call IPropertyStorage::
WriteMultiple. Rather than calling the same method to update, however, it is mor
e efficient to first call IPropertyStorage::ReadMultiple to get the interface po
inter to the stream or storage, then write data using the IStream or IStorage me
thods. A stream or storage opened through a property is always opened in direct
mode, so an additional level of nested transaction is not introduced. There may,
however, still be a transaction on the property set as a whole, depending on ho
w it was opened or created through IPropertySetStorage. Further, the access and
share mode tags specified when the property set is opened or created, are passed
to property-based streams or storages.
The lifetimes of property-based stream or storage pointers, although theoretical
ly independent of their associated IPropertyStorage and IPropertySetStorage poin
ters, in fact, effectively depend on them. The data visible through the stream o
r storage is related to the transaction on the property storage object from whic
h it is retrieved, just as for a storage object (supporting IStorage) with conta
ined stream and storage sub-objects. If the transaction on the parent object is
aborted, existing IStream and IStorage pointers subordinate to that object enter
a zombie state. Because IPropertyStorage is the only interface on the property st
orage object, the useful lifetime of the contained IStream and IStorage pointers
is bounded by the lifetime of the IPropertyStorage interface.
The implementation must also deal with the situation where the same stream- or s
torage-valued property is requested multiple times through the same IPropertySto
rage interface instance. For example, in the COM compound file implementation, t
he open will succeed or fail depending on whether or not the property is already
open.
Another issue is multiple opens in transacted mode. The result depends on the is
olation level that was specified through a call to IPropertySetStorage methods,
(either the Open or Create method, through the STGM flags) at the time that the
property storage was opened .
If the call to open the property set specifies read-write access, IStorage- and
IStream-valued properties are always opened with read-write access. Data can the
n be written through these interfaces, changing the value of the property, which
is the most efficient way to update these properties. The property value itself
does not have an additional level of transaction nesting, so changes are scoped
under the transaction (if any) on the property storage object.
See Also
IPropertySetStorage, IPropertyStorage::WriteMultiple, IPropertyStorage::WritePro
pertyNames

14.6.20.7 IPropertyStorage::ReadPropertyNames
Retrieves any existing string names for the specified property identifiers.
HRESULT ReadPropertyNames(
ULONG cpropid, //Number of elements in rgpropid
PROPID const rgpropid[], //Property identifiers for which names are to be
retrieved.
LPWSTR rglpwstrName[] //Array of returned string names
);
Parameters
cpropid
[in] Number of elements on input of the array rgpropid. May legally be zero, tho
ugh this is a no-op, reading no property names.
rgpropid[]
[in] Array of property identifiers for which names are to be retrieved.
rglpwstrName[]
[in, out] Caller-allocated array of size cpropid of LPWSTRs. On return, the impl
ementation fills in this array. A given entry contains either the corresponding
string name of a property identifier or NULL if the property identifier has no s
tring name.
Each LPWSTR member of the array should be freed using CoTaskMemFree.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
One or more string names were retrieved and all members of rglpwstrName are vali
d (either NULL or a valid LPWSTR).
S_FALSE
No string names were retrieved because none of the requested property identifier
s have string names presently associated with them in this property storage obje
ct (this result does not address whether the given property identifiers presentl
y exist in the set).
STG_E_INVALIDHEADER
The property name dictionary was not found.
STG_E_READFAULT
Error reading the storage.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No string n
ames were retrieved.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. No string names were r
etrieved.
STG_E_INVALIDPARAMETER
A parameter is invalid. No string names were retrieved.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from Ansi.
Remarks
For each property identifier in the list of property identifiers supplied in the
rgpropid array, IPropertyStorage::ReadPropertyNames retrieves the corresponding
string name, if there is one. String names are created either by specifying the
names in calls to IPropertyStorage::WriteMultiple when you are creating the pro
perty, or through a call to IPropertyStorage::WritePropertyNames. In any case, t
he string name is optional; all properties must have a property identifier.
String names mapped to property identifiers must be unique within the set.
See Also
IPropertyStorage::WritePropertyNames, IPropertyStorage::WriteMultiple

14.6.20.8 IPropertyStorage::Revert
Discards all changes to the property set it was opened or changes were last comm
itted. Has no effect on a direct-mode property set.
HRESULT Revert();
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Success.
Remarks
For transacted-mode property sets, discards all changes that have been made in t
his property set since set was opened or the time it was last committed (dependi
ng on which is later). After this operation, any existing storage- or stream-val
ued properties that have been opened from the property set being reverted are in
valid and can no longer be used. The error STG_E_REVERTED will be returned on al
l calls except Release using these streams or storages.
For direct-mode property sets, this request is ignored and returns S_OK.
See Also
IPropertyStorage::Commit

14.6.20.9 IPropertyStorage::Stat
Retrieves information about the current open property set.
HRESULT Stat(
STATPROPSTG * pstatpsstg //Pointer to a filled-in STATPROPSETSTG structur
e
);
Parameters
pstatpsstg
[out] Pointer to a STATPROPSETSTG structure, which contains statistics about the
current open property set.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Statistics were successfully obtained.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid.
Remarks
IPropertyStorage::Stat fills in and returns a pointer to a STATPROPSETSTG struct
ure, containing statistics about the current property set. STATPROPSETSTG fields
have the following meanings:
Field Meaning
fmtid The FMTID of this property set, specified when the property set was init
ially created.
clsid The CLSID of this property set, specified when the property set was init
ially created and possibly modified thereafter with IpropertyStorage::SetClass.
If not set, the value will be CLSID_NULL.
grfFlags The flag values this set was created with. For details, see Ipro
pertySetStorage::Create.
mtime The time in UTC (FILETIME) at which this property set was last modified.
Not all IPropertyStorage implementations maintain modification times on propert
y sets; those who do not will return zero for this value.
ctime The time in UTC (FILETIME) at which this property set was created. Not a
ll IPropertyStorage implementations maintain creation times on property sets; th
ose that do not will set this value to 0.
atime The time in UTC (FILETIME) at which this property set was last accessed.
Not all IPropertyStorage implementations maintain last access times on property
sets; those that do not will set this value to 0.
See Also
STATPROPSETSTG structure, IPropertySetStorage::Enum, FILETIME structure

14.6.20.10 IPropertyStorage::SetClass
Assigns a new CLSID to the current property storage object, and persistently sto
res the CLSID with the object.
HRESULT SetClass(
REFCLSID clsid //New CLSID for the property set
);
Parameters
clsid
[in] New CLSID to be associated with the property set.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The CLSID has been assigned.
STG_E_ACCESSDENIED
The requested access to the IPropertyStorage interface has been denied. The CLSI
D was not assigned.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. The CLSID was not assi
gned.
STG_E_INVALIDPARAMETER
The parameter is invalid. The CLSID was not assigned.
Remarks
Assigns a CLSID to the current property storage object. The CLSID has no relatio
nship to the stored property identifiers. Assigning a CLSID allows a piece of co
de to be associated with a given instance of a property set; such code, for exam
ple, might manage the user interface. Different CLSIDs can be associated with di
fferent property set instances that have the same FMTID.
If the property set is created with NULL specified as the IPropertySetStorage::C
reate pclsid parameter, the CLSID is set to all zeroes.
The current CLSID on a property storage object can be retrieved with a call to I
PropertyStorage::Stat. The initial value for the CLSID can be specified at the t
ime that the storage is created with a call to IPropertySetStorage::Create.
Setting the CLSID on a non-simple property set (one that can legally contain sto
rage- or stream-valued properties, as described in IPropertySetStorage::Create)
also sets the CLSID on the underlying sub-storage.
See Also
IPropertySetStorage::Create, IPropertyStorage::Stat

14.6.20.11 IPropertyStorage::SetTimes
Sets the modification, access, and creation times of this property set, if suppo
rted by the implementation. Not all implementations support all these time value
s.
HRESULT SetTimes(
FILETIME const * pctime, //New creation time for the property set
FILETIME const * patime, //New access time for the property set
FILETIME const * pmtime //New modification time for the property set
);
Parameters
pctime
[in] Pointer to the new creation time for the property set. May be NULL, indicat
ing that this time is not to be modified by this call.
patime
[in] Pointer to the new access time for the property set. May be NULL, indicatin
g that this time is not to be modified by this call.
pmtime
[in] Pointer to the new modification time for the property set. May be NULL, ind
icating that this time is not to be modified by this call.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
All the requested times have been successfully updated.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied; no times ha
ve been updated.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation.
STG_E_INVALIDPARAMETER
The parameter is invalid. This error is returned if an attempt is made to set a
time value which is not supported by this implementation.
Remarks
Sets the modification, access, and creation times of the current open property s
et, if supported by the implementation (not all implementations support all thes
e time values). Unsupported timestamps are always reported as zero, enabling the
caller to test for support. A call to IPropertyStorage::Stat supplies (among ot
her information) timestamp information.
Notice that this functionality is provided as an IPropertyStorage method on a pr
operty storage object that is already open, in contrast to being provided as a m
ethod in IPropertySetStorage. Normally, when the SetTimes method is not explicit
ly called, the access and modification times are updated as a side effect of rea
ding and writing the property set. When SetTimes is used, the latest specified t
imes supersede either default times or time values specified in previous calls t
o SetTimes.
See Also
IPropertyStorage::Stat, FILETIME structure

14.6.20.12 IPropertyStorage::WriteMultiple
Writes a specified group of properties to the current property set. If a propert
y with a specified name already exists, it is replaced, even when the old and ne
w types for the property value are different. If a property of a given name or p
roperty identifier does not exist, it is created.
HRESULT WriteMultiple(
ULONG cpspec, //The number of properties being set.
PROPSPEC const rgpspec[], //Property specifiers
PROPVARIANT const rgvar[], //Array of PROPVARIANT values
PROPID propidNameFirst //Minimum value for property identifiers when th
ey must be allocated
);
Parameters
cpspec
[in] The number of properties being set. May legally be zero, though this is a n
o-op, writing no properties.
rgpspec[]
[in] Array of the specifiers to which properties are to be set. These are in no
particular order, and may legally contain duplicates (the last specified is to t
ake effect). A mixture of property identifiers and string names is permitted.
rgvar[]
[in] An array (of size cpspec) of PROPVARIANTs that contain the property values
to be written. The array must be of the size specified by cpspec.
propidNameFirst
[in] Specifies the minimum value for the property identifiers the method must as
sign if the rgpspec parameter specifies string-named properties for which no pro
perty identifiers currently exist. If all string-named properties specified alre
ady exist in this set, and thus already have property identifiers, this value is
ignored. When not ignored, this value must be at least two (property identifier
s 0and 1 are reserved for special uses) and less than 0x80000000 (property ident
ifier values beyond that are reserved for special use).
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from Ansi.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
All of the indicated properties were successfully written.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No properti
es have been written. The property set was opened in STGM_READ mode.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. Some properties may or
may not have been written.
STG_E_INVALIDPARAMETER
At least one of the parameters is invalid. Some properties may not have been wri
tten. This error would be returned in several situations, for example: 1) rgvar
may be NULL; 2) a stream- or storage-valued property is present in rgpspec but t
he property set was created without PROPSETFLAG_NONSIMPLE; 3) one or more proper
ty variant types may be invalid; 4) one of the PROPSPECs contains an illegal ulK
ind value.
STG_E_INVALIDPOINTER
May be returned when at least one of the pointers passed in is invalid. Some pro
perties may or may not have been written. More frequently, an invalid pointer wi
ll instead result in an access violation.
STG_E_WRITEFAULT
Error writing the storage.
STG_E_REVERTED
The property set was reverted. For example, if the property set is deleted while
open (by using IPropertySetStorage::Delete) this status would be returned.
STG_E_MEDIUMFULL
The disk is full. Some properties may or may not have been written.
STG_E_PROPSETMISMATCHED
An attempt was made to write a non-simple (stream- or storage-valued) property t
o a simple property set.
Remarks
If a specified property already exists, it s value is replaced with the new one, e
ven when the old and new types for the property value are different. If you spec
ify a property identifier that does not exist, that property is created. If a st
ring name is supplied for a property which does not exist, the method will alloc
ate a property identifier for that property, and the name will be added to the d
ictionary.
When allocating a property identifier, the implementation can choose any value n
ot currently in use in the property set for a property identifier, as long as it
is not 0 or 1 or greater than 0x80000000, all of which are reserved values. The
propidNameFirst parameter establishes a minimum value for property identifiers
within the set, and must be greater than 1 and less than 0x80000000.
If there is an attempt to write a property that already exists with an invalid p
arameter, the method should return STG_E_INVALIDPARAMETER; if the property does
not exist, it should not be created. This behavior facilitates the use of a Read
Multiple update WriteMultiple sequence to update a group of properties without r
equiring that the calling code ensure that all the requested properties in the c
all to ReadMultiple were retrieved.
It is recommended that property sets be created as Unicode, by not setting the P
ROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage::Create. I
t is also recommended that you avoid using VT_LPSTR values, and use VT_LPWSTR va
lues instead. When the property set code page is Unicode, VT_LPSTR string values
are converted to Unicode when stored, and back to multibyte string values when
retrieved. When the code page of the property set is not Unicode, property names
, VT_BSTR strings, and non-simple property values are converted to multibyte str
ings when stored, and converted back to Unicode when retrieved, all using the cu
rrent system ANSI code page.
To create stream or storage object as a property in a nonsimple property set, ca
ll IPropertyStorage::WriteMultiple. While you would also call this method to upd
ate simple properties, it is not an efficient way to update stream and storage o
bjects in a property set. This is because updating one of these properties throu
gh a call to WriteMultiple creates in the property storage object a copy of the
passed-in data, and the IStorage or IStream pointers are not retained beyond the
duration of this call. It is usually more efficient to update stream or storage
objects by first calling IPropertyStorage::ReadMultiple to get the interface po
inter to the stream or storage, then writing data through the IStream or IStorag
e methods.
A stream or storage opened through a property is always opened in direct mode, s
o an additional level of nested transaction is not introduced. There is still li
kely to be a transaction on the property set as a whole. Further, a property-bas
ed stream or storage is opened in read-write mode, if possible, given the mode o
n the property set; otherwise, read mode is used.
When the copy is made, the underlying CopyTo operation on VT_STREAM properties o
perates on the current seek position of the source. The seek position is destroy
ed on failure, but on success it is at EOF.
If a stream or storage property does not exist, passing an IStream or IStorage p
ointer with a value of NULL creates an empty stream or storage property value. I
f a stream or storage property is already open from a call to ReadMultiple, a NU
LL value must cause the WriteMultiple operation to truncate it and return S_OK,
placing the previously returned stream- and storage-valued pointers into the rev
erted state (as happens in the compound file implementation.)
Storage- and stream-valued properties always manifest themselves to downlevel cl
ients as sibling streams or storages to the stream containing the main contents
of the property set¾they are never stored directly in-line in the property set. Th
is allows smooth interoperability and control when down-level clients interact w
ith up-level clients. Thus, from a downlevel perspective, property sets containi
ng IStream or IStorage valued properties are always stored in a storage object,
not a stream. The specific name of the sibling used is completely under the cont
rol of the IPropertyStorage implementation, as long as the name is from the non-
reserved part of the IStorage name space. See Appendix C of the OLE Programmer s G
uide for a discussion of the serialized property set format for further details.
As is described there, the string name is stored in the same format as a VT_BST
R. Refer also to the earlier discussion in this method of multibyte to Unicode c
onversions for property names.
If the WriteMultiple method returns an error when writing stream- or storage-val
ued properties (indirect properties), the amount of data actually written is und
efined. If the caller requires consistency of the property set and its indirect
properties when writing stream- and/or storage-valued properties, use of transac
ted mode is advised.
If an implicit deletion of a stream- or storage-valued property occurs while tha
t property is open, (as, for example, when a VT_I4 is written over a VT_STREAM),
the deletion will succeed and place the previously returned IStream pointer in
the reverted state.
See Also
IPropertySetStorage::Create, IPropertyStorage::ReadMultiple
14.6.20.13 IPropertyStorage::WritePropertyNames
Assigns string names to a specified array of property IDs in the current propert
y set.
HRESULT WritePropertyNames(
ULONG cpropid, //Size on input of the array rgpropid
PROPID const rgpropid[], //Property identifiers for which names are to be
set
LPWSTR const rglpwstrName[] //New names of the corresponding propert
y identifiers
);
Parameters
cpropid
[in] Size on input of the array rgpropid. May legally be zero, though this is a
no-op, writing no property names.
rgpropid[]
[in] Array of the property identifiers for which names are to be set.
rglpwstrName[]
[in] Array of new names to be assigned to the corresponding property identifiers
in the rgpropid array. These names may not exceed 255 characters (not including
the NULL terminator).
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
Success. All of the indicated string names were successfully set.
STG_E_INVALIDNAME
At least one of the indicated property identifier values does not exist in this
property set. No names were set.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied. No property
names have been changed in the storage.
STG_E_INSUFFICIENTMEMORY
There is not sufficient memory to perform this operation. Some names may not hav
e been set.
STG_E_INVALIDPARAMETER
A parameter is invalid. Some names may not have been set.
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
There was a failed attempt to translate a Unicode string to or from Ansi.
Remarks
IPropertyStorage::WritePropertyNames assigns string names to property identifier
s passed to the method in the rgpropid array. It associates each string name in
the rglpwstrName array with the respective property identifier in rgpropid. It i
s explicitly valid to define a name for a property identifier that is not curren
tly present in the property storage object.
It is also valid to change the mapping for an existing string name (determined b
y a case-insensitive match). That is, you can use the WritePropertyNames method
to map an existing name to a new property identifier, or to map a new name to a
property identifier that already has a name in the dictionary. In either case, t
he original mapping is deleted. Property names must be unique (as are property i
dentifiers) within the property set.
The storage of string property names preserves the case. String property names a
re limited in length to 128 characters. Property names that begin with the binar
y Unicode characters 0x0001 through 0x001F are reserved for future use.
See Also
IPropertyStorage::ReadPropertyNames, IPropertyStorage::ReadMultiple, IPropertySt
orage::WriteMultiple

14.6.21 IPropertyStorage-Compound File Implementation


The COM implementation of the Structured Storage architecture is called compound
files. Storage objects as implemented in compound files include an implementati
on of both IPropertyStorage, the interface that manages a single persistent prop
erty set, and IPropertySetStorage, the interface that manages groups of persiste
nt property sets.
To get a pointer to the compound file implementation of IPropertyStorage, first
call StgCreateDocfile to create a new compound file object or StgOpenStorage, to
open a previously created compound file. Both functions supply a pointer to the
object s IStorage interface. When you want to deal with persistent property sets,
call QueryInterface for the IPropertySetStorage interface, specifying the heade
r-defined name for the interface identifier IID_IPropertySetStorage. Calling eit
her the Create or Open method of that interface, you get a pointer to the IPrope
rtyStorage interface, which you can use to call any of its methods.
14.6.21.1.1.1.1 When to Use
Use IPropertyStorage to manage properties within a single property set. Its meth
ods support reading, writing, and deleting both properties and the optional stri
ng names that can be associated with property identifiers. Other methods support
the standard commit and revert storage operations. There is also a method that
allows you to set times associated with the property storage, and another that p
ermits the assignment of a CLSID that can be used to associate other code, such
as user interface code, with the property set. Calling the Enum method supplies
a pointer to the compound file implementation of IEnumSTATPROPSTG, which allows
you to enumerate the properties in the set.
Remarks
The compound file implementation of IPropertyStorage caches open property sets i
n memory in order to improve performance. As a result, changes to a property set
are not written to the compound file until the Commit or Release (last referenc
e) methods are called.
IPropertyStorage::ReadMultiple
Reads the properties specified in the rgpspec array and supplies the values of a
ll valid properties in the rgvar array of PROPVARIANTs. In the COM compound file
implementation, duplicate property identifiers that refer to stream- or storage
-types result in multiple calls to IStorage::OpenStream or IStorage::OpenStorage
and the success or failure of ReadMultiple depends on the underlying storage im
plementation s ability to share opens. Because in a compound file STGM_SHARE_EXCLU
SIVE is forced, multiple opens will fail. Opening the same storage object more t
han once from the same parent storage is not supported. The STGM_SHARE_EXCLUSIVE
flag must be specified.
In addition, to ensure thread-safe operation if the same stream- or storage-valu
ed property is requested multiple times through the same IPropertyStorage pointe
r in the COM compound file implementation, the open will succeed or fail dependi
ng on whether or not the property is already open and on whether the underlying
file system handles multiple opens of a stream or storage. Thus, the ReadMultipl
e operation on a stream- or storage-valued property always results in a call to
IStorage::OpenStream, or IStorage::OpenStorage, passing the access (STGM_READWRI
TE, etc.) and share flags (STGM_SHARE_EXCLUSIVE, etc) specified when the origina
l property set was opened or created.
If the method fails, the values written to rgvar[] are undefined. If some stream
- or storage-valued properties are opened successfully but an error occurs befor
e execution is complete, these should be released before the method returns.
IPropertyStorage::WriteMultiple
Writes the properties specified in the rgpspec[] array, assigning them the PROPV
ARIANT tags and values specified in rgvar[]. Properties that already exist are a
ssigned the specified PROPVARIANT values, and properties that do not currently e
xist are created.
IPropertyStorage::DeleteMultiple
Deletes the properties specified in the rgpspec[].
IPropertyStorage::ReadPropertyNames
Reads existing string names associated with the property identifiers specified i
n the rgpropid[] array.
IPropertyStorage::WritePropertyNames
Assigns string names specified in the rglpwstrName array to property identifiers
specified in the rgpropid array.
IPropertyStorage::DeletePropertyNames
Deletes the string names of the property identifiers specified in the rgpropid a
rray by writing NULL to the property name.
IPropertyStorage::SetClass
Sets the CLSID field of the property set stream. In this implementation, setting
the CLSID on a non-simple property set (one that can legally contain storage- o
r stream-valued properties, as described in IPropertySetStorage::Create) also se
ts the CLSID on the underlying sub-storage so that it can be obtained through a
call to IStorage::Stat.
IPropertyStorage::Commit
For both simple and non-simple property sets, flushes the memory image to the di
sk subsystem. In addition, for non-simple transacted-mode property sets, this me
thod performs a commit (as in IStorage::Commit) on the property set.
IPropertyStorage::Revert
For non-simple property sets only, calls the underlying storage's Revert method
and re-opens the 'contents' stream. For simple property sets, returns E_OK.
IPropertyStorage::Enum
Constructs an instance of IEnumSTATPROPSTG, the methods of which can be called t
o enumerate the STATPROPSTG structures that provide information about each of th
e properties in the set. This implementation creates an array into which the ent
ire property set is read and which can be shared when IEnumSTATPROPSTG::Clone is
called.
IPropertyStorage::Stat
Fills in the fields of a STATPROPSETSTG structure, which contains information ab
out the property set as a whole. On return, supplies a pointer to the structure.
For non-simple storage sets, this implementation calls IStorage::Stat (or IStre
am::Stat) to get the times from the underlying storage or stream. For simple sto
rage sets, no times are maintained.
IPropertyStorage::SetTimes
For non-simple property sets only, sets the times supported by the underlying st
orage. The compound file storage implementation supports all three: modification
, access, and creation. This implementation of SetTimes calls the IStorage::SetE
lementTimes method of the underlying storage to retrieve these times.
See Also
IPropertyStorage, IStorage::SetElementTimes
14.6.22 IPropertyStorage-Standalone Implementation
The system-provided, standalone implementation of IPropertySetStorage includes a
n implementation of IPropertyStorage, the interface that reads and writes proper
ties in a property set storage. The IPropertySetStorage interface creates and op
ens property sets in a storage. The IEnumSTATPROPSTG and IEnumSTATPROPSETSTG int
erfaces are also provided in the standalone implementation.
To get a pointer to the standalone implementation of IPropertyStorage, call the
StgCreatePropStg function if you are creating a new property set or StgOpenPropS
tg if you want to obtain the interface pointer on an existing property set (or c
all the Create or Open methods of the IPropertySetStorage standalone implementat
ion).
The standalone implementation of IPropertyStorage creates property sets on any s
torage or stream object, not just on compound file storages and streams. The sta
ndalone implementation does not depend on compound files and can be used with an
y implementation of structured storages. See the section IPropertyStorage-Compou
nd File Implementation in the Object Services section of the Platform SDK for mo
re information on the compound file implementation of this interface.
14.6.22.1.1.1.1 When to Use
Use IPropertyStorage to manage properties within a single property set. Its meth
ods support reading, writing, and deleting both properties and the optional stri
ng names that can be associated with property identifiers. Other methods support
the standard commit and revert storage operations. There is also a method that
sets times associated with the property storage, and another that permits the as
signment of a CLSID that can be used to associate other code, such as user inter
face code, with the property set. The Enum method supplies a pointer to the stan
dalone implementation of IEnumSTATPROPSTG, which enumerates the properties in th
e set.
Remarks
There are some differences between the standalone implementation of the property
set interfaces and the compound file implementation. In the compound file imple
mentation of stream, storage, property set storage, and property storage objects
, the various interfaces are able to coordinate with one another because they ar
e part of a common implementation. In the standalone implementation, the interfa
ce implementations are distinct from one another.
As a result, the compound-file implementation handles concurrency issues and syn
chronizes the property set object with the storage or stream object. With the st
andalone implementation, the client is responsible for handling concurrency and
synchronization issues between the storage or stream object and the property set
. A client can meet these requirements by following two simple rules. First, nev
er manipulate a property set using its stream or storage interfaces while a prop
erty storage object is opened on it. And, second, always Commit a property stora
ge object before calling Commit, CopyTo, or MoveElementTo on an ancestor storage
object. Specifically, the following items require client attention:
· In the compound file implementation, a single mechanism provides concurrency pro
tection for the storage object and its associated property set objects. However,
in the standalone implementation, the storage object implementation is separate
from the property set implementation and each provides its own concurrency mech
anisms. Thus, in the standalone implementation, the client is responsible for ma
intaining concurrency protection between the two implementations through a mutua
l exclusion mechanism.
· In the compound file implementation, changes to property sets are buffered in a
property set cache. Then, when the IStorage::Commit method is called on the stor
age object, the compound files implementation automatically flushes the property
set changes from the property set buffer before the storage object is committed
. Thus, the property set changes are made visible as part of the transaction bei
ng committed.
In the standalone implementation, the client must explicitly flush the p
roperty set buffer by calling IPropertyStorage::Commit before calling the IStora
ge:Commit method on the storage. Alternately, the client can use the new PROPSET
FLAG_UNBUFFERED value in the standalone implementation to write directly to the
property set instead of caching changes to the property set's internal buffer. I
f PROPSETFLAG_UNBUFFERED is used, the client's responsibilities are automaticall
y met. The compound file implementation does not support the PROPSETFLAG_UNBUFFE
RED value. See the PROPSETFLAG enumeration for more information on using this va
lue.
· As with transacted storages, the compound file implementation updates the proper
ty set by flushing its internal buffer prior to executing a call to IStorage::Co
pyTo or IStorage::MoveElementTo. Thus, changes to the property set are reflected
in the copied or moved storage element.
In the standalone implementation, the client must explicitly flush the p
roperty set buffer by calling IPropertyStorage::Commit before calling IStorage::
CopyTo or IStorage::MoveElementTo. Alternately, the client can use the new PROPS
ETFLAG_UNBUFFERED to write directly to the property set instead of caching chang
es to the property set buffer. See the PROPSETFLAG enumeration for more informat
ion on using this value.
The standalone implementation of IPropertyStorage supports the following methods
:
IPropertyStorage::ReadMultiple
Reads the properties specified in the rgpspec array and supplies the values of a
ll valid properties in the rgvar array of PROPVARIANTs.
In the system-provided, standalone implementation, duplicate property identifier
s that refer to stream- or storage-types result in multiple calls to IStorage::O
penStream or IStorage::OpenStorage and the success or failure of ReadMultiple de
pends on the underlying storage implementation s ability to share open storages.
In addition, to ensure thread-safe operation if the same stream- or storage-valu
ed property is requested multiple times through the same IPropertyStorage pointe
r, the open will succeed or fail depending on whether or not the property is alr
eady open and on whether the underlying file system handles multiple opens of a
stream or storage. Thus, the ReadMultiple operation on a stream- or storage-valu
ed property always results in a call to IStorage::OpenStream, or IStorage::OpenS
torage, passing the access (STGM_READWRITE, for example) and share values (STGM_
SHARE_EXCLUSIVE, for example) specified when the property set was originally ope
ned or created.
If the method fails, the values written to rgvar[] are undefined. If some stream
- or storage-valued properties are opened successfully but an error occurs befor
e execution is complete, these properties should be released before the method r
eturns.
IPropertyStorage::WriteMultiple
Writes the properties specified in the rgpspec[] array, assigning them the PROPV
ARIANT tags and values specified in rgvar[]. Properties that already exist are a
ssigned the specified PROPVARIANT values, and properties that do not currently e
xist are created.
IPropertyStorage::DeleteMultiple
Deletes the properties specified in the rgpspec[].
IPropertyStorage::ReadPropertyNames
Reads existing string names associated with the property identifiers specified i
n the rgpropid[] array.
IPropertyStorage::WritePropertyNames
Assigns string names specified in the rglpwstrName array to property identifiers
specified in the rgpropid array.
IPropertyStorage::DeletePropertyNames
Deletes the string names of the property identifiers specified in the rgpropid a
rray by writing NULL to the property name.
IPropertyStorage::SetClass
Sets the CLSID field of the property set stream. In the standalone implementatio
n, setting the CLSID on a non-simple property set (one that can contain storage-
or stream-valued properties, as described in IPropertySetStorage::Create) also
sets the CLSID on the underlying sub-storage so it can be obtained through a cal
l to IStorage::Stat.
IPropertyStorage::Commit
For both simple and non-simple property sets, flushes the memory image to the di
sk subsystem. In addition, for non-simple transacted-mode property sets, this me
thod calls IStorage::Commit on the property set.
IPropertyStorage::Revert
For non-simple property sets only, calls the underlying storage's Revert method
and re-opens the 'contents' stream. For simple property sets, only returns E_OK.
IPropertyStorage::Enum
Creates an enumerator object that implements IEnumSTATPROPSTG, the methods of wh
ich can be called to enumerate the STATPROPSTG structures that provide informati
on about each of the properties in the set.
This implementation creates an array into which the entire property set is read
and which can be shared when IEnumSTATPROPSTG::Clone is called.
IPropertyStorage::Stat
Fills in the fields of a STATPROPSETSTG structure, which contains information ab
out the property set as a whole. On return, supplies a pointer to the structure.
For non-simple storage sets, this implementation calls IStorage::Stat (or IStrea
m::Stat) to get the information from the underlying storage or stream.
IPropertyStorage::SetTimes
For non-simple property sets only, sets the times supported by the underlying st
orage. This implementation of SetTimes calls the IStorage::SetElementTimes metho
d of the underlying storage to modify the times. It supports the times supported
by the underlying method which can be modification time, access time, or creati
on time.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertySetStorage-Standalone Implementation, IPropertyStorage, IStorage::SetEl
ementTimes, StgOpenPropStg, StgCreatePropStg, StgCreatePropSetStg

14.6.23 IRootStorage
The IRootStorage interface contains a single method that switches a storage obje
ct to a different underlying file and saves the storage object to that file. The
save operation occurs even with low memory conditions and uncommitted changes t
o the storage object. A subsequent call to IStorage::Commit is guaranteed to not
consume any additional memory.
14.6.23.1.1 When to Implement
Storage objects that are based on a file should implement IRootStorage in additi
on to the IStorage interface. For storage objects that are not file-based, this
interface is not necessary.
COM provides an implementation of a storage object, including the IRootStorage i
nterface, as part of its compound file implementation.
14.6.23.1.2 When to Use
The primary use for the IRootStorage interface is to save a storage object to a
file during low memory conditions. Typically, the container application calls th
e IRootStorage interface to switch to a new file.
If you have an IStorage pointer to a compound file object, you can call IStorage
::QueryInterface with IID_IRootStorage to obtain a pointer to the IRootStorage i
nterface.
Methods in Vtable Order
IUnknown Methods Description

QueryInterface Returns pointers to supported interfaces.


AddRef Increments the reference count.
Release Decrements the reference count.
IrootStorage Method Description
SwitchToFile Copy the file underlying this root storage object, then associat
e this storage with the copied file.
See Also
IStorage, StgCreateDocfile
14.6.24 IRootStorage::SwitchToFile
Copies the current file associated with the storage object to a new file. The ne
w file is then used for the storage object and any uncommitted changes.
HRESULT SwitchToFile(
LPOLESTR pszFile //Filename for the new file
);
Parameter
pszFile
Specifies the filename for the new file. It cannot be the name of an existing fi
le. If NULL, this method creates a temporary file with a unique name, and you ca
n call IStorage::Stat to retrieve the name of the temporary file.
Return Values
S_OK
The file was successfully copied.
STG_E_MEDIUMFULL
The file was not copied because of insufficient space on the storage device.
STG_E_ACCESSDENIED
The file was not copied because the caller does not have permission to access st
orage device.
STG_E_INVALIDPOINTER
The file was not copied because the pszFile pointer is invalid.
STG_E_FILEALREADYEXISTS
The file was not copied because the new filename (pszFile) points to an existing
file.
Remarks
The IRootStorage::SwitchToFile method copies the file associated with the storag
e object. A COM container calls SwitchToFile to perform a full save on a file in
a low-memory situation. Typically, this is done only after a normal full save o
peration (i.e., save to temporary file, delete original file, rename temporary f
ile) has failed with an E_OUTOFMEMORY error.
It is illegal to call SwitchToFile if the storage object or anything contained w
ithin it has been marshalled to another process. As a consequence, before callin
g SwitchToFile, the container must call the IPersistStorage::HandsOffStorage met
hod for any element within the storage object that is loaded or running. The Han
dsOffStorage method forces the element to release its storage pointers and enter
the hands-off storage mode. The container must also release all pointers to str
eams or storages that are contained in this root storage. After the full save op
eration is completed, the container returns the contained elements to normal sto
rage mode.
14.6.24.1.1 Notes to Implementers
If you are implementing your own storage objects, the IRootStorage methods (incl
uding QueryInterface, AddRef, and Release) must not consume additional memory or
file handles.
See Also
IPersistStorage::HandsOffStorage, IPersistStorage::SaveCompleted, IStorage::Comm
it, IStorage::Stat
14.6.25 IRootStorage - Compound File Implementation
COM s compound file implementation of IRootStorage provides a way to support savin
g files in low-memory or low disk-space situations. For information on how this
interface behaves, see IRootStorage.
14.6.25.1.1 When to Use
Use the system-supplied implementation of IRootStorage only to support saving fi
les under low memory conditions.
Remarks
It is possible to call COM s implementation of IRootStorage::SwitchToFile to do a
normal Save As operation to
another file. Applications that do so, however, may not be compatible with futur
e generations of COM storage. To avoid this possibility, applications performain
g a Save As operation should manually create the second docfile and invoke
IStorage::CopyTo. IRootStorage::SwitchToFile should beused only in emergency (lo
w memory or disk space) situations.
See Also
IRootStorage; IRootStorage::SwitchToFile
14.6.26 IStorage
The IStorage interface supports the creation and management of structured storag
e objects. Structured storage allows hierarchical storage of information within
a single file, and is often referred to as a file system within a file . Elements o
f a structured storage object are storages and streams. Storages are analogous t
o directories, and streams are analogous to files. Within a structured storage t
here will be a primary storage object that may contain substorages, possibly nes
ted, and streams. Storages provide the structure of the object, and streams cont
ain the data, which is manipulated through the IStream interface.
The IStorage interface provides methods for creating and managing the root stora
ge object, child storage objects, and stream objects. These methods can create,
open, enumerate, move, copy, rename, or delete the elements in the storage objec
t.
An application must release its IStorage pointers when it is done with the stora
ge object to deallocate memory used. There are also methods for changing the dat
e and time of an element.
There are a number of different modes in which a storage object and its elements
can be opened, determined by setting values from the STGM enumeration. One aspe
ct of this is how changes are committed. You can set direct mode, in which in wh
ich changes to an object are immediately written to it, or transacted mode, in w
hich changes are written to a buffer until explicitly committed. The IStorage in
terface provides methods for committing changes and reverting to the last-commit
ted version. Other storage modes set, for example, a stream can be opened in rea
d only mode or read/write mode. For more information, refer to the STGM enumerat
ion.
Other methods provide a means to gain access to information about a storage obje
ct and its elements through the STATSTG structure.
14.6.26.1 When to Implement
Generally, you would not implement this interface unless you were defining a new
storage scheme for your system. COM provides a compound file implementation of
the IStorage interface that supports transacted access. COM provides a set of he
lper APIs to facilitate using the compound file implementation of storage object
s. Refer to IStorage - Compound File Implementation.
14.6.26.2 When to Use
Call the methods of IStorage to manage substorages or streams within the current
storage. This management includes creating, opening, or destroying sub-storages
or streams, as well as managing aspects such as time stamps, names, etc. You ca
n also commit changes or revert to previous version for storages opened in trans
acted mode. The methods of IStorage do not include means to read and write data¾th
is is reserved for IStream, which manages the actual data. While the IStorage an
d IStream interfaces are used to manipulate the storage object and its elements,
the IPersistStorage interface contains methods that are called to serialize the
storage object and its elements to a disk file.
Methods VTable Order
IUnknown Methods Description

QueryInterface Returns pointers to supported interfaces.


AddRef Increments the reference count.
Release Decrements the reference count.
IStorage Methods Description
CreateStream Creates and opens a stream object with the specified name contai
ned in this storage object.
OpenStream Opens an existing stream object within this storage object using
the specified access permissions in grfMode.
CreateStorage Creates and opens a new storage object within this storage objec
t.
OpenStorage Opens an existing storage object with the specified name accordi
ng to the specified access mode.
CopyTo Copies the entire contents of this open storage object into another stor
age object. The layout of the destination storage object may differ.
MoveElementTo Copies or moves a substorage or stream from this storage object
to another storage object.
Commit Reflects changes for a transacted storage object to the parent level.
Revert Discards all changes that have been made to to the storage object since
the last commit operation.
EnumElements Returns an enumerator object that can be used to enumerate the s
torage and stream objects contained within this storage object.
DestroyElement Removes the specified storage or stream from this storage object
.
RenameElement Renames the specified storage or stream in this storage object.
SetElementTimes Sets the modification, access, and creation times of the indicat
ed storage element, if supported by the underlying file system.
SetClass Assigns the specified CLSID to this storage object.
SetStateBits Stores up to 32 bits of state information in this storage object
.
Stat Returns the STATSTG structure for this open storage object.
14.6.26.3 IStorage::Commit
Ensures that any changes made to a storage object open in transacted mode are re
flected in the parent storage; for a root storage, reflects the changes in the a
ctual device, for example, a file on disk. For a root storage object opened in d
irect mode, this method has no effect except to flush all memory buffers to the
disk. For non-root storage objects in direct mode, this method has no effect.
HRESULT Commit(
DWORD grfCommitFlags //Specifies how changes are to be committed
);
Parameter
grfCommitFlags
[in] Controls how the changes are committed to the storage object. See the STGC
enumeration for a definition of these values.
Return Values
S_OK
Changes to the storage object were successfully committed to the parent level.
E_PENDING
Asynchronous Storage only: Part or all of the data to be committed is currently
unavailable.
STG_E_INVALIDFLAG
The value for the grfCommitFlags parameter is not valid.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_NOTCURRENT
Another open instance of the storage object has committed changes. Thus, the cur
rent commit operation may overwrite previous changes.
STG_E_MEDIUMFULL
No space left on device to commit.
STG_E_TOOMANYOPENFILES
The commit operation could not be completed because there are too many open file
s.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
Remarks
IStorage::Commit makes permanent changes to a storage object that is in transact
ed mode, in which changes are accumulated in a buffer, and not reflected in the
storage object until there is a call to this method. The alternative is to open
an object in direct mode, in which changes are immediately reflected in the stor
age object and so does not require a commit operation. Calling this method on a
storage opened in direct mode has no effect, unless it is a root storage, in whi
ch case it ensures that changes in memory buffers are written to the underlying
storage device.
The commit operation publishes the current changes in this storage object and it
s children to the next level up in the storage hierarchy. To undo current change
s before committing them, call IStorage::Revert to roll back to the last-committ
ed version.
Calling IStorage::Commit has no effect on currently-opened nested elements of th
is storage object. They are still valid and can be used. However, the IStorage::
Commit method does not automatically commit changes to these nested elements. Th
e commit operation publishes only known changes to the next higher level of the
storage hierarchy. Thus, transactions to nested levels must be committed to this
storage object before they can be committed to higher levels.
In commit operations, you need to take steps to ensure that data is protected du
ring the commit process:
· When committing changes to root storage objects, the caller must check the retur
n value to determine whether the operation has been completed successfully, and
if not, that the old committed contents of the IStorage are still intact and can
be restored.
· If this storage object was opened with some of its items excluded, then the call
er is responsible for rewriting them before calling commit. Write mode is requir
ed on the storage opening for the commit to succeed.
· Unless prohibiting multiple simultaneous writers on the same storage object, an
application calling this method should specify at least STGC_ONLYIFCURRENT in th
e grfCommitFlags parameter to prevent the changes made by one writer from inadve
rtently overwriting the changes made by another.
See Also
IStorage - Compound File Implementation, STGC, IStorage::Revert
14.6.26.4 IStorage::CopyTo
Copies the entire contents of an open storage object to another storage object.
HRESULT CopyTo(
DWORD ciidExclude, //Number of elements in rgiidExclude
IID const * rgiidExclude, //Array of interface identifiers (IIDs)
SNB snbExclude, //Points to a block of stream names in the storage objec
t
IStorage * pstgDest //Points to destination storage object
);
Parameters
ciidExclude
[in] The number of elements in the array pointed to by rgiidExclude. If rgiidExc
lude is NULL, then ciidExclude is ignored.
rgiidExclude
[in] An array of interface identifiers that either the caller knows about and do
es not want to be copied or that the storage object does not support but whose
state the caller will later explicitly copy. The array can include IStorage, ind
icating that only stream objects are to be copied, and IStream, indicating that
only storage objects are to be copied. An array length of zero indicates that on
ly the state exposed by the IStorage object is to be copied; all other interface
s on the object are to be ignored. Passing NULL indicates that all interfaces on
the object are to be copied.
snbExclude
[in] A string name block (refer to SNB) that specifies a block of storage or str
eam objects that are not to be copied to the destination. These elements are not
created at the destination. If IID_IStorage is in the rgiidExclude array, this
parameter is ignored. This parameter may be NULL.
pstgDest
[in] Points to the open storage object into which this storage object is to be c
opied. The destination storage object can be a different implementation of the I
Storage interface from the source storage object. Thus, IStorage::CopyTo can onl
y use publicly available methods of the destination storage object. If pstgDest
is open in transacted mode, it can be reverted by calling its IStorage::Revert m
ethod.
Return Values
S_OK
The storage object was successfully copied.
E_PENDING
Asynchronous Storage only: Part or all of the data to be copied is currently una
vailable.
STG_E_ACCESSDENIED
The destination storage object is a child of the source storage object.
STG_E_INSUFFICIENTMEMORY
The copy was not completed due to a lack of memory.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_TOOMANYOPENFILES
The copy was not completed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_MEDIUMFULL
The copy was not completed because the storage medium is full.
Remarks
This method merges elements contained in the source storage object with those al
ready present in the destination. The layout of the destination storage object m
ay differ from the source storage object.
The copy process is recursive, invoking IStorage::CopyTo and IStream::CopyTo on
the elements nested inside the source.
When copying a stream on top of an existing stream with the same name, the exist
ing stream is first removed and then replaced with the source stream. When copy
ing a storage on top of an existing storage with the same name, the existing sto
rage is not removed. As a result,, after the copy operation, the destination ISt
orage contains older elements, unless they were replaced by newer ones with the
same names.
A storage object may expose interfaces other than IStorage, including IRootStora
ge, IPropertyStorage, or IPropertySetStorage. The rgiidExclude parameter provid
es a way to exclude any or all of these additional interfaces from the copy oper
ation.
A caller with a newer or more efficient copy of an existing substorage or stream
object may want to exclude the current versions of these objects from the copy
operation. The snbExclude and rgiidExclude parameters provide two different ways
of excluding a storage objects existing storages or streams.
14.6.26.4.1 Note to Callers
The most common way to use this method is to copy everything possible from the s
ource to the destination, as in most Full Save and SaveAs operations. The follow
ing example illustrates this call:
pstg->CopyTo(0, Null, Null, pstgDest)
See Also
IStorage - Compound File Implementation, IStorage::MoveElementTo, IStorage::Reve
rt
14.6.26.5 IStorage::CreateStorage
Creates and opens a new storage object nested within this storage object.
HRESULT CreateStorage(
const WCHAR * pwcsName, //Points to the name of the new storage object
DWORD grfMode, //Access mode for the new storage object
DWORD reserved1, //Reserved; must be zero
DWORD reserved2, //Reserved; must be zero
IStorage ** ppstg //Points to new storage object
);
Parameters
pwcsName
[in] Points to a wide character string that contains the name of the newly creat
ed storage object. This name can be used later to reopen the storage object.
grfMode
[in] Specifies the access mode to use when opening the newly created storage obj
ect. See the STGM enumeration values for descriptions of the possible values.
reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be zero.
ppstg
[out] When successful, points to the location of the IStorage pointer to the new
ly-created storage object. This parameter is set to NULL if an error occurs.
Return Values
S_OK
The storage object was created successfully.
E_PENDING
Asynchronous Storage only: Part or all of the necessary data is currently unavai
lable.
STG_E_ACCESSDENIED
Insufficient permissions to create storage object.
STG_E_FILEALREADYEXISTS
The name specified for the storage object already exists in the storage object a
nd the grfmode flag includes the flag STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
The storage object was not created due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The storage object was not created because there are too many open files.
STG_S_CONVERTED
The existing stream with the specified name was replaced with a new storage obje
ct containing a single stream called CONTENTS. The new storage object will be ad
ded.
Remarks
If a storage with the name specified in the pwcsName parameter already exists wi
thin the parent storage object, and the grfMode parameter includes the STGM_CREA
TE flag, the existing storage is replaced by the new one. If the grfMode paramet
er includes the STGM_CONVERT flag, the existing element is converted to a stream
object named CONTENTS and the new storage object is created containing the CONT
ENTS stream object. The destruction of the old element and the creation of the n
ew storage object are both subject to the transaction mode on the parent storage
object.
If a storage object with the same name already exists and grfMode is set to STGM
_FAILIFTHERE, this method fails with the return value STG_E_FILEALREADYEXISTS.
See Also
IStorage - Compound File Implementation, IStorage::OpenStorage
14.6.26.6 IStorage::CreateStream
Creates and opens a stream object with the specified name contained in this stor
age object. All elements within a storage object both streams and other storage
objects are kept in the same name space.
HRESULT CreateStream(
const WCHAR * pwcsName, //Points to the name of the new stream
DWORD grfMode, //Access mode for the new stream
DWORD reserved1, //Reserved; must be zero
DWORD reserved2, //Reserved; must be zero
IStream ** ppstm //Points to new stream object
);
Parameters
pwcsName
[in] Points to a wide character string that contains the name of the newly creat
ed stream. This name can be used later to open or reopen the stream.
grfMode
[in] Specifies the access mode to use when opening the newly created stream. See
the STGM enumeration values for descriptions of the possible values.
reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be zero.
ppstm
[out] On return, points to the location of the new IStream interface pointer. Th
is is only valid if the operation is successful. When an error occurs, this para
meter is set to NULL.
Return Values
S_OK
The new stream was successfully created
E_PENDING
Asynchronous Storage only: Part or all of the necessary data is currently unavai
lable.
STG_E_ACCESSDENIED
Insufficient permissions to create stream.
STG_E_FILEALREADYEXISTS
The name specified for the stream already exists in the storage object and the g
rfmode flag includes the flag STGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
The stream was not created due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported. For example, if thi
s method is called without the STGM_SHARE_EXCLUSIVE flag.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the stream object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The stream was not created because there are too many open files.
Remarks
If a stream with the name specified in the pwcsName parameter already exists and
the grfMode parameter includes the STGM_CREATE flag, the existing stream is rep
laced by a newly created one. Both the destruction of the old stream and the cre
ation of the new stream object are subject to the transaction mode on the parent
storage object.
If the stream already exists and grfMode is set to STGM_FAILIFTHERE, this method
fails with the return value STG_E_FILEALREADYEXISTS.
See Also
IStorage - Compound File Implementation, IStorage::OpenStream, IStream
14.6.26.7 IStorage::DestroyElement
Removes the specified storage or stream from this storage object.
HRESULT DestroyElement(
wchar * pwcsName //Points to the name of the element to be removed
);
Parameter
pwcsName
[in] Points to a wide character string that contains the name of the storage or
stream to be removed.
Return Values
S_OK
The element was successfully removed.
E_PENDING
Asynchronous Storage only: Part or all of the element s data is currently unavaila
ble.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for removing the element.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The element was not removed due to a lack of memory.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the element was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The element was not removed because there are too many open files.
Remarks
The DestroyElement method deletes a substorage or stream from the current storag
e object. After a successful call to DestroyElement, any open instance of the de
stroyed element from the parent storage becomes invalid.
If a storage object is opened in transacted mode, destruction of an element requ
ires that the call to DestroyElement be followed by a call to IStorage::Commit.
See Also
IStorage - Compound File Implementation
14.6.26.8 IStorage::EnumElements
Retrieves a pointer to an enumerator object that can be used to enumerate the st
orage and stream objects contained within this storage object.
HRESULT EnumElements(
DWORD reserved1, //Reserved; must be zero
void * reserved2, //Reserved; must be NULL
DWORD reserved3, //Reserved; must be zero
IEnumSTATSTG ** ppenum //Indirect pointer to IEnumSTATSTG
);
Parameters
reserved1
[in] Reserved for future use; must be zero.
reserved2
[in] Reserved for future use; must be NULL.
reserved3
[in] Reserved for future use; must be zero.
ppenum
[out] When successful, points to the location of an IEnumSTATSTG pointer to new
enumerator object.
Return Values
S_OK
The enumerator object was successfully returned.
E_PENDING
Asynchronous Storage only: Part or all of the element s data is currently unavaila
ble.
STG_E_INSUFFICIENTMEMORY
The enumerator object could not be created due to lack of memory.
STG_E_INVALIDPARAMETER
One of the parameters was not valid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
Remarks
The enumerator object returned by this method implements the IEnumSTATSTG interf
ace, one of the standard enumerator interfaces that contain the Next, Reset, Clo
ne, and Skip methods. IEnumSTATSTG enumerates the data stored in an array of STA
TSTG structures.
The storage object must be open in read mode to allow the enumeration of its ele
ments.
The order in which the elements are enumerated and whether the enumerator is a s
napshot or always reflects the current state of the storage object, and depends
on the IStorage implementation.
See Also
IStorage - Compound File Implementation, IEnumXXXX, IEnumSTATSTG, STATSTG
14.6.26.9 IStorage::MoveElementTo
Copies or moves a substorage or stream from this storage object to another stora
ge object.
HRESULT MoveElementTo(
const WCHAR * pwcsName, //Name of the element to be moved
IStorage * pstgDest, //Points to destination storage object
LPWSTR pwcsNewName, //Points to new name of element in destination
DWORD grfFlags //Specifies a copy or a move
);
Parameters
pwcsName
[in] Points to a wide character string that contains the name of the element in
this storage object to be moved or copied.
pstgDest
[in] IStorage pointer to the destination storage object.
pwcsNewName
[in] Points to a wide character string that contains the new name for the elemen
t in its new storage object.
grfFlags
[in] Specifies whether the operation should be a move (STGMOVE_MOVE) or a copy (
STGMOVE_COPY). See the STGMOVE enumeration.
Return Values
S_OK
The storage object was successfully copied or moved.
E_PENDING
Asynchronous Storage only: Part or all of the element s data is currently unavaila
ble.
STG_E_ACCESSDENIED
The destination storage object is a child of the source storage object.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_FILEALREADYEXISTS
The specified file already exists.
STG_E_INSUFFICIENTMEMORY
The copy or move was not completed due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfFlags parameter is not valid.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The copy or move was not completed because there are too many open files.
Remarks
The IStorage::MoveElementTo method is typically the same as invoking the IStorag
e::CopyTo method on the indicated element and then removing the source element.
In this case, the MoveElementTo method uses only the publicly available function
s of the destination storage object to carry out the move.
If the source and destination storage objects have special knowledge about each
other s implementation (they could, for example, be different instances of the sam
e implementation), this method can be implemented more efficiently.
Before calling this method, the element to be moved must be closed, and the dest
ination storage must be open.
See Also
IStorage - Compound File Implementation, STGMOVE, IStorage::CopyTo
14.6.26.10 IStorage::OpenStorage
Opens an existing storage object with the specified name in the specified access
mode.
HRESULT OpenStorage(
const WCHAR * pwcsName, //Points to the name of the storage object to op
en
IStorage * pstgPriority, //Points to previous opening of the storage obje
ct
DWORD grfMode, //Access mode for the new storage object
SNB snbExclude, //Points to a block of stream names in the storage objec
t
DWORD reserved, //Reserved; must be zero
IStorage ** ppstg //Points to opened storage object
);
Parameters
pwcsName
[in] Points to a wide character string that contains the name of the storage obj
ect to open. It is ignored if pstgPriority is non-NULL.
pstgPriority
[in] If the pstgPriority parameter is not NULL, it is an IStorage pointer to a p
revious opening of an element of the storage object, usually one that was opened
in priority mode. The storage object should be closed and re-opened according t
o grfMode. When the IStorage::OpenStorage method returns, pstgPriority is no lon
ger valid. Use the value supplied in the ppstg parameter. If the pstgPriority pa
rameter is NULL, it is ignored.
grfMode
[in] Specifies the access mode to use when opening the storage object. See the S
TGM enumeration values for descriptions of the possible values. Whatever other m
odes you may choose, you must at least specify STGM_SHARE_EXCLUSIVE when calling
this method.
snbExclude
[in] Must be NULL. A non-NULL value will return STG_E_INVALIDPARAMETER.
reserved
[in] Reserved for future use; must be zero.
ppstg
[out] When the operation is successful, points to the location of an IStorage po
inter to the opened storage object. This parameter is set to NULL if an error oc
curs.
Return Values
S_OK
The storage object was opened successfully.
E_PENDING
Asynchronous Storage only: Part or all of the storage s data is currently unavaila
ble.
STG_E_ACCESSDENIED
Insufficient permissions to open storage object.
STG_E_FILENOTFOUND
The storage object with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The storage object was not opened due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the storage object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The storage object was not created because there are too many open files.
STG_S_CONVERTED
The existing stream with the specified name was replaced with a new storage obje
ct containing a single stream called CONTENTS. In direct mode, the new storage i
s immediately written to disk. In transacted mode, the new storage is written to
a temporary storage in memory and later written to disk when it is committed.
Remarks
Storage objects can be opened with STGM_DELETEONRELEASE, in which case the objec
t is destroyed when it receives its final release. This is useful for creating t
emporary storage objects.
See Also
IStorage - Compound File Implementation, IStorage::CreateStorage
14.6.26.11 IStorage::OpenStream
Opens an existing stream object within this storage object in the specified acce
ss mode.
HRESULT OpenStream(
const WCHAR * pwcsName, //Points to name of stream to open
void * reserved1, //Reserved; must be NULL
DWORD grfMode, //Access mode for the new stream
DWORD reserved2, //Reserved; must be zero
IStream ** ppstm //Indirect pointer to opened stream object
);
Parameters
pwcsName
[in] Points to a wide character string that contains the name of the stream to o
pen.
reserved1
[in] Reserved for future use; must be NULL.
grfMode
[in] Specifies the access mode to be assigned to the open stream. See the STGM e
numeration values for descriptions of the possible values. . Whatever other mode
s you may choose, you must at least specify STGM_SHARE_EXCLUSIVE when calling th
is method.
reserved2
[in] Reserved for future use; must be zero.
ppstm
[out] On successful return, points to the location of an IStream pointer to the
newly-opened stream object. This parameter is set to NULL if an error occurs.
Return Values
S_OK
The stream was successfully opened.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le.
STG_E_ACCESSDENIED
Insufficient permissions to open stream.
STG_E_FILENOTFOUND
The stream with specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The stream was not opened due to a lack of memory.
STG_E_INVALIDFLAG
The value specified for the grfMode flag is not a valid STGM enumeration value.
STG_E_INVALIDFUNCTION
The specified combination of grfMode flags is not supported. For example, if thi
s method is called without the STGM_SHARE_EXCLUSIVE flag.
STG_E_INVALIDNAME
Invalid value for pwcsName.
STG_E_INVALIDPOINTER
The pointer specified for the stream object was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The stream was not opened because there are too many open files.
Remarks
IStorage::OpenStream opens an existing stream object within this storage object
in the access mode specified in grfMode. There are restrictions on the permissio
ns that can be given in grfMode. For example, the permissions on this storage ob
ject restrict the permissions on its streams. In general, access restrictions on
streams should be stricter than those on their parent storages. Compound-file s
treams must be opened with STGM_SHARE_EXCLUSIVE.
See Also
IStorage - Compound File Implementation, IStorage::CreateStream, IStream
14.6.26.12 IStorage::RenameElement
Renames the specified substorage or stream in this storage object.
HRESULT RenameElement(
const WCHAR * pwcsOldName, //Points to the name of the element to be change
d
const WCHAR * pwcsNewName //Points to the new name for the specified eleme
nt
);
Parameters
pwcsOldName
[in] Points to a wide character string that contains the name of the substorage
or stream to be changed.
pwcsNewName
[in] Points to a wide character string that contains the new name for the specif
ied sustorage or stream.
Return Values
S_OK
The element was successfully renamed.
E_PENDING
Asynchronous Storage only: Part or all of the element s data is currently unavaila
ble.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for renaming the element.
STG_E_FILENOTFOUND
The element with the specified old name does not exist.
STG_E_FILEALREADYEXISTS
The element specified by the new name already exists.
STG_E_INSUFFICIENTMEMORY
The element was not renamed due to a lack of memory.
STG_E_INVALIDNAME
Invalid value for one of the names.
STG_E_INVALIDPOINTER
The pointer specified for the element was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
STG_E_TOOMANYOPENFILES
The element was not renamed because there are too many open files.
Remarks
IStorage::RenameElement renames the specified substorage or stream in this stora
ge object. An element in a storage object cannot be renamed while it is open. Th
e rename operation is subject to committing the changes if the storage is open i
n transacted mode.
The IStorage::RenameElement method is not guaranteed to work in low memory with
storage objects open in transacted mode. It may work in direct mode.
See Also
IStorage - Compound File Implementation
14.6.26.13 IStorage::Revert
Discards all changes that have been made to the storage object since the last co
mmit.
HRESULT Revert(void);
Return Values
S_OK
The revert operation was successful.
E_PENDING
Asynchronous Storage only: Part or all of the storage s data is currently unavaila
ble. For more information see Asynchronous Storage.
STG_E_INSUFFICIENTMEMORY
The revert operation could not be completed due to a lack of memory.
STG_E_TOOMANYOPENFILES
The revert operation could not be completed because there are too many open file
s.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
Remarks
For storage objects opened in transacted mode, the IStorage::Revert method disca
rds any uncommitted changes to this storage object or changes that have been com
mitted to this storage object from nested elements.
After this method returns, any existing elements (substorages or streams) that w
ere opened from the reverted storage object are invalid and can no longer be use
d. Specifying these reverted elements in any call except IStorage::Release retur
ns the error STG_E_REVERTED
This method has no effect on storage objects opened in direct mode.
See Also
IStorage - Compound File Implementation, IStorage::Commit
14.6.26.14 IStorage::SetClass
Assigns the specified CLSID to this storage object.
HRESULT SetClass(
REFCLSID clsid //Class identifier to be assigned to the storage object
);
Parameter
clsid
[in] The class identifier (CLSID) that is to be associated with the storage obje
ct.
Return Values
S_OK
The CLSID was successfully assigned.
E_PENDING
Asynchronous Storage only: Part or all of the storage s data is currently unavaila
ble. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for assigning a class identifier
to the storage object.
STG_E_MEDIUMFULL
Not enough space was left on device to complete the operation.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
Remarks
When first created, a storage object has an associated CLSID of CLSID_NULL. Call
this method to assign a CLSID to the storage object.
Call the IStorage::Stat method to retrieve the current CLSID of a storage object
.
See Also
IStorage - Compound File Implementation, IStorage::Stat
14.6.26.15 IStorage::SetElementTimes
Sets the modification, access, and creation times of the specified storage eleme
nt, if supported by the underlying file system.
HRESULT SetElementTimes(
const WCHAR * pwcsName, //Points to name of element to be changed
FILETIME const * pctime, //New creation time for element, or NULL
FILETIME const * patime, //New access time for element, or NULL
FILETIME const * pmtime //New modification time for element, or NULL
);
Parameters
pwcsName
[in] The name of the storage object element whose times are to be modified. If N
ULL, the time is set on the root storage rather than one of its elements.
pctime
[in] Either the new creation time for the element or NULL if the creation time i
s not to be modified.
patime
[in] Either the new access time for the element or NULL if the access time is no
t to be modified.
pmtime
[in] Either the new modification time for the element or NULL if the modificatio
n time is not to be modified.
Return Values
S_OK
The time values were successfully set.
E_PENDING
Asynchronous Storage only: Part or all of the element s data is currently unavaila
ble. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for changing the element.
STG_E_FILENOTFOUND
The element with the specified name does not exist.
STG_E_INSUFFICIENTMEMORY
The element was not changed due to a lack of memory.
STG_E_INVALIDNAME
Invalid value for the element name.
STG_E_INVALIDPOINTER
The pointer specified for the element was invalid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
STG_E_TOOMANYOPENFILES
The element was not changed because there are too many open files.
STG_E_REVERTED
The storage object has been invalidated by a revert operation above it in the tr
ansaction tree.
Remarks
This method sets time statistics for the specified storage element within this s
torage object.
Not all file systems support all of the time values. This method sets those time
s that are supported and ignores the rest. Each of the time value parameters can
be NULL; indicating that no modification should occur.
Call the IStorage::Stat method to retrieve these time values.
See Also
IStorage - Compound File Implementation, IStorage::Stat
14.6.26.16 IStorage::SetStateBits
Stores up to 32 bits of state information in this storage object.
HRESULT SetStateBits(
DWORD grfStateBits, //Specifies new values of bits
DWORD grfMask //Specifies mask that indicates which bits are significa
nt
);
Parameters
grfStateBits
[in] Specifies the new values of the bits to set. No legal values are defined fo
r these bits; they are all reserved for future use and must not be used by appli
cations.
grfMask
[in] A binary mask indicating which bits in grfStateBits are significant in this
call.
Return Values
S_OK
The state information was successfully set.
E_PENDING
Asynchronous Storage only: Part or all of the storage s data is currently unavaila
ble. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for changing this storage object
.
STG_E_INVALIDFLAG
The value for the grfStateBits or grfMask parameters are not valid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
Remarks
This interface is reserved for future use. The values for the state bits are not
currently defined.
See Also
IStorage - Compound File Implementation, IStorage::Stat
14.6.26.17 IStorage::Stat
Retrieves the STATSTG structure for this open storage object.
HRESULT Stat(
STATSTG * pstatstg, //Location for STATSTG structure
DWORD grfStatFlag //Values taken from the STATFLAG enumeration
);
Parameters
pstatstg
[out] On return, points to a STATSTG structure where this method places informat
ion about the open storage object. This parameter is NULL if an error occurs.
grfStatFlag
[in] Specifies that some of the fields in the STATSTG structure are not returned
, thus saving a memory allocation operation. Values are taken from the STATFLAG
enumeration.
Return Values
S_OK
The STATSTG structure was successfully returned at the specified location.
E_PENDING
Asynchronous Storage only: Part or all of the storage s data is currently unavaila
ble. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for accessing statistics for thi
s storage object.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPARAMETER
One of the parameters was invalid.
Remarks
IStorage::Stat retrieves the STATSTG structure for the current storage. This str
ucture contains statistical information about the storage. IStorage::EnumElement
s creates an enumerator object with the IEnumSTATSTG interface, though which you
can enumerate the substorages and streams of a storage through the STATSTG stru
cture of each.
See Also
IStorage - Compound File Implementation, STATFLAG, STATSTG, IEnumSTATSTG, IStora
ge::SetClass, IStorage::SetElementTimes, IStorage::SetStateBits

14.6.27 IStorage - Compound File Implementation


The compound file implementation of IStorage allows you to create and manage sub
storages and streams within a storage object residing in a compound file object.
To create a compound file object and get an IStorage pointer, call the API func
tion StgCreateDocfile. To open an existing compound file object and get its root
IStorage pointer, call StgOpenStorage.
14.6.27.1.1 When to Use
Most applications use this implementation to to create and manage storages and s
treams.
Remarks
IStorage::CreateStream
Creates and opens a stream object with the specified name contained in this stor
age object. The COM-provided compound file implementation of the IStorage::Creat
eStream method does not support the following behaviors:
· The STGM_DELETEONRELEASE flag is not supported.
· Transacted mode is not supported for stream objects.
· Opening the same stream more than once from the same storage is not supported. T
he STGM_SHARE_EXCLUSIVE flag must be specified.
IStorage::OpenStream
Opens an existing stream object within in this storage object using the specifie
d access modes specified in the grfMode parameter. The COM-provided compound fil
e implementation of the IStorage::OpenStream method does not support the followi
ng behavior:
· The STGM_DELETEONRELEASE flag is not supported.
· Transacted mode is not supported for stream objects.
· Opening the same stream more than once from the same storage is not supported. T
he STGM_SHARE_EXCLUSIVE flag must be specified.
IStorage::CreateStorage
The COM-provided compound file implementation of the IStorage::CreateStorage met
hod does not support the STGM_DELETEONRELEASE flag. Specifying this flag causes
the method to return STG_E_INVALIDFLAG.
IStorage::OpenStorage
Opens an existing storage object with the specified name in the specified access
mode. The COM-provided compound file implementation of the IStorage::OpenStorag
e method does not support the following behavior:
· The STGM_PRIORITY flag is not supported for non-root storages.
· Opening the same storage object more than once from the same parent storage is n
ot supported. The STGM_SHARE_EXCLUSIVE flag must be specified.
· The STGM_DELETEONRELEASE flag is not supported. If this flag is specified, the f
unction returns STG_E_INVALIDFUNCTION.
IStorage::CopyTo
Copies only the substorages and streams of this open storage object into another
storage object. The rgiidExclude parameter can be set to IID_IStream to copy on
ly substorages, or to IID_IStorage to copy only streams.
IStorage::MoveElementTo
Copies or moves a substorage or stream from this storage object to another stora
ge object.
IStorage::Commit
Ensures that any changes made to a storage object open in transacted mode are re
flected in the parent storage; for a root storage, reflects the changes in the a
ctual device, for example, a file on disk. For a root storage object opened in d
irect mode, this method has no effect except to flush all memory buffers to the
disk. For non-root storage objects in direct mode, this method has no effect.
The COM-provided compound files implementation uses a two phase commit process u
nless STGC_OVERWRITE is specified in the grfCommitFlags parameter. This two-phas
e process ensures the robustness of data in case the commit operation fails. Fir
st, all new data is written to unused space in the underlying file. If necessary
, new space is allocated to the file. Once this step has been successfully compl
eted, a table in the file is updated using a single sector write to indicate tha
t the new data is to be used in place of the old. The old data becomes free spac
e to be used at the next commit. Thus, the old data is available and can be rest
ored in case an error occurs when committing changes. If STGC_OVERWRITE is speci
fied, a single phase commit operation is used.
IStorage::Revert
Discards all changes that have been made to the storage object since the last co
mmit.
IStorage::EnumElements
Creates and retrieves a pointer to an enumerator object that can be used to enum
erate the storage and stream objects contained within this storage object.The CO
M-provided compound file implementation takes a snapshot.
IStorage::DestroyElement
Removes the specified element (substorage or stream) from this storage object.
IStorage::RenameElement
Renames the specified substorage or stream in this storage object.
IStorage::SetElementTimes
Sets the modification, access, and creation times of the specified storage eleme
nt. The COM-provided compound file implementation maintains modification and cha
nge times for internal storage objects. For root storage objects, whatever is su
pported by the underlying file system (or ILockBytes) is supported. The compound
file implementation does not maintain any time stamps for internal streams. Uns
upported time stamps are reported as zero, enabling the caller to test for suppo
rt.
IStorage::SetClass
Assigns the specified CLSID to this storage object.
IStorage::SetStateBits
Stores up to 32 bits of state information in this storage object. The state set
by this method is for external use only. The COM-provided compound file implemen
tation does not perform any action based on the state.
IStorage::Stat
Retrieves the STATSTG structure for this open storage object.
See Also
IStorage, IStream, StgCreateDocfile, StgOpenStorage, ILockBytes, IRootStorage
14.6.28 IStream
The IStream interface supports reading and writing data to stream objects. Strea
m objects contain the data in a structured storage object, where storages provid
e the structure. Simple data can be written directly to a stream, but most frequ
ently, streams are elements nested within a storage object. They are similar to
standard files.
The IStream interface defines methods similar to the MS-DOS FAT file functions.
For example, each stream object has its own access rights and a seek pointer. Th
e main difference between a stream object and a DOS file is that streams are not
opened using a file handle, but through an IStream interface pointer.
The methods in this interface present your object s data as a contiguous sequence
of bytes that you can read or write. There are also methods for committing and r
everting changes on streams open in transacted mode and methods for restricting
access to a range of bytes in the stream.
Streams can remain open for long periods of time without consuming file system r
esources. The IStream::Release method is similar to a close function on a file.
Once released, the stream object is no longer valid and cannot be used.
Clients of asynchronous monikers can choose between a data-pull or data-push mod
el for driving an asynchronous IMoniker::BindToStorage operation and for receivi
ng asynchronous notifications.The table below compares the behavior of asynchro
nous IStream::Read and IStream::Seek calls returned in IBindStatusCallback::OnDa
taAvailable in these two download models:
IStream method call Behavior in data-pull model Behavior in data-push mo
del
IStream::Read is called to read partial data, (i.e. not all the available data)
Returns S_OK. The client must continue to read all available data before returni
ng from IBindStatusCallback::OnDataAvailable or else the bind operation is block
ed. (i.e. read until S_FALSE or E_PENDING is returned) Returns S_OK. Even if th
e client returns from IBindStatusCallback::OnDataAvailable at this point the bin
d operation continues and IBindStatusCallback::OnDataAvailable will be called a
gain repeatedly until the binding finishes.
IStream::Read is called to read all the available data Returns E_PENDING if the
bind operation has not completed, and IBindStatusCallback::OnDataAvailable will
be called again when more data is available. Same as data-pull model.
IStream::Read is called to read all the available data and the bind operation is
over (end-of-file) Returns S_FALSE. There will be a subsequent call to IBin
dStatusCallback::OnDataAvailable with the grfBSC flag set to BSCF_LASTDATANOTIFI
CATION. Same as data-pull model.
IStream::Seek is called IStream::Seek doesn t work in data-pull model IStream:
:Seek doesn't work in data-push model.
For general information on this topic, see Asynchronous Monikers and Data-Pull-M
odel versus Data Push-Model for more specific information. Also, see Managing Me
mory Allocation for details on COM s rules for managing memory.
14.6.28.1.1 When to Implement
Implement IStream on a container or object application when you require function
ality not provided by the COM compound file implementation. The specification of
IStream defines more functionality that the COM implementation supports. In add
ition, if you are creating a stream object that is larger than the heap in your
machine s memory and you are using a global memory handle, the compound file imple
mentation calls GlobalRealloc internally whenever it needs more memory, which ca
n be extremely inefficient. In this case, the preferred solution is to implement
an IStream that uses memory allocated by VirtualAlloc instead of GlobalAlloc. T
his can reserve a large chunk of virtual address space and then commit memory wi
thin that address space as required. No data copying occurs and memory is commit
ted only as it is needed. For more information, refer to IStream - Compound File
Implementation.
14.6.28.1.2 When to Use
Call the methods of the IStream interface from a container or application to rea
d and write the data for an object. Since stream objects can be marshaled to oth
er processes, applications can share the data in storage objects without having
to use global memory.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IStream Methods Description
Read Reads a specified number of bytes from the stream object into memory sta
rting at the current seek pointer.
Write Writes a specified number from bytes into the stream object starting at
the current seek pointer.
Seek Changes the seek pointer to a new location relative to the beginning of
the stream, the end of the stream, or the current seek pointer.
SetSize Changes the size of the stream object.
CopyTo Copies a specified number of bytes from the current seek pointer in the
stream to the current seek pointer in another stream.
Commit Ensures that any changes made to a stream object open in transacted mode
are reflected in the parent storage object.
Revert Discards all changes that have been made to a transacted stream since th
e last IStream::Commit call.
LockRegion Restricts access to a specified range of bytes in the stream. Su
pporting this functionality is optional since some file systems do not provide i
t.
UnlockRegion Removes the access restriction on a range of bytes previously re
stricted with IStream::LockRegion.
Stat Retrieves the STATSTG structure for this stream.
Clone Creates a new stream object that references the same bytes as the origin
al stream but provides a separate seek pointer to those bytes.
14.6.28.2 IStream::Clone
Creates a new stream object with its own seek pointer that references the same b
ytes as the original stream.
HRESULT Clone(
IStream ** ppstm //Points to location for pointer to the new stream objec
t
);
Parameter
ppstm
[out] When successful, points to the location of an IStream pointer to the new s
tream object. If an error occurs, this parameter is NULL.
Return Values
S_OK
The stream was successfully cloned.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_INSUFFICIENT_MEMORY
The stream was not cloned due to a lack of memory.
STG_E_INVALIDPOINTER
The ppStm pointer is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
This method creates a new stream object for accessing the same bytes but using a
separate seek pointer. The new stream object sees the same data as the source s
tream object. Changes written to one object are immediately visible in the other
. Range locking is shared between the stream objects.
The initial setting of the seek pointer in the cloned stream instance is the sam
e as the current setting of the seek pointer in the original stream at the time
of the clone operation.
See Also
IStream - Compound File Implementation, IStream::CopyTo
14.6.28.3 IStream::Commit
Ensures that any changes made to a stream object open in transacted mode are ref
lected in the parent storage. If the stream object is open in direct mode, IStre
am::Commit has no effect other than flushing all memory buffers to the next leve
l storage object. The COM compound file implementation of streams does not suppo
rt opening streams in transacted mode.
HRESULT Commit(
DWORD grfCommitFlags //Specifies how changes are committed
);
Parameter
grfCommitFlags
[in] Controls how the changes for the stream object are committed. See the STGC
enumeration for a definition of these values.
Return Values
S_OK
Changes to the stream object were successfully committed to the parent level.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_MEDIUMFULL
The commit operation failed due to lack of space on the storage device.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
This method ensures that changes to a stream object opened in transacted mode ar
e reflected in the parent storage. Changes that have been made to the stream sin
ce it was opened or last committed are reflected to the parent storage object. I
f the parent is opened in transacted mode, the parent may still revert at a late
r time rolling back the changes to this stream object. The compound file impleme
ntation does not support opening streams in transacted mode, so this method has
very little effect other than to flush memory buffers. For more information, ref
er to IStream - Compound File Implementation.
If the stream is open in direct mode, this method ensures that any memory buffer
s have been flushed out to the underlying storage object. This is much like a fl
ush in traditional file systems.
The IStream::Commit method is useful on a direct mode stream when the implementa
tion of the IStream interface is a wrapper for underlying file system APIs. In t
his case, IStream::Commit would be connected to the file system s flush call.
See Also
IStream - Compound File Implementation, IStorage::Commit
14.6.28.4 IStream::CopyTo
Copies a specified number of bytes from the current seek pointer in the stream t
o the current seek pointer in another stream.
HRESULT CopyTo(
IStream * pstm, //Points to the destination stream
ULARGE_INTEGER cb, //Specifies the number of bytes to copy
ULARGE_INTEGER * pcbRead, //Pointer to the actual number of bytes read fro
m the source
ULARGE_INTEGER * pcbWritten //Pointer to the actual number of bytes written
to the destination
);
Parameters
pstm
[in] Points to the destination stream. The stream pointed to by pstm can be a ne
w stream or a clone of the source stream.
cb
[in] Specifies the number of bytes to copy from the source stream.
pcbRead
[out] Pointer to the location where this method writes the actual number of byte
s read from the source. You can set this pointer to NULL to indicate that you ar
e not interested in this value. In this case, this method does not provide the a
ctual number of bytes read.
pcbWritten
[out] Pointer to the location where this method writes the actual number of byte
s written to the destination. You can set this pointer to NULL to indicate that
you are not interested in this value. In this case, this method does not provide
the actual number of bytes written.
Return Values
S_OK
The stream object was successfully copied.
E_PENDING
Asynchronous Storage only: Part or all of the data to be copied is currently una
vailable. For more information see Asynchronous Storage.
STG_E_INVALIDPOINTER
The value of one of the pointer parameters is not valid.
STG_E_MEDIUMFULL
The stream is not copied because there is no space left on the storage device.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
This method copies the specified bytes from one stream to another. The seek poin
ter in each stream instance is adjusted for the number of bytes read or written.
This method is equivalent to reading cb bytes into memory using IStream::Read a
nd then immediately writing them to the destination stream using IStream::Write,
although IStream::CopyTo will be more efficient.
The destination stream can be a clone of the source stream created by calling th
e IStream::Clone method.
If IStream::CopyTo returns an error, you cannot assume that the seek pointers ar
e valid for either the source or destination. Additionally, the values of pcbRea
d and pcbWritten are not meaningful even though they are returned.
If IStream::CopyTo returns successfully, the actual number of bytes read and wri
tten are the same.
To copy the remainder of the source from the current seek pointer, specify the m
aximum large integer value for the cb parameter. If the seek pointer is the begi
nning of the stream, this technique copies the entire stream.
See Also
IStream - Compound File Implementation, IStream::Read, IStream::Write, IStream::
Clone
14.6.28.5 IStream::LockRegion
Restricts access to a specified range of bytes in the stream. Supporting this fu
nctionality is optional since some file systems do not provide it.
HRESULT LockRegion(
ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of
the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType //Specifies the restriction on accessing the specified r
ange
);
Parameters
libOffset
[in] Integer that specifies the byte offset for the beginning of the range.
cb
[in] Integer that specifies the length of the range, in bytes, to be restricted.
dwLockType
[in] Specifies the restrictions being requested on accessing the range.
Return Values
S_OK
The specified range of bytes was locked.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not su
pported.
STG_E_LOCKVIOLATION
Requested lock is supported, but cannot be granted because of an existing lock.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
The byte range can extend past the current end of the stream. Locking beyond the
end of a stream is useful as a method of communication between different instan
ces of the stream without changing data that is actually part of the stream.
Three types of locking can be supported: locking to exclude other writers, locki
ng to exclude other readers or writers, and locking that allows only one request
or to obtain a lock on the given range, which is usually an alias for one of the
other two lock types. A given stream instance might support either of the first
two types, or both. The lock type is specified by dwLockType, using a value fro
m the LOCKTYPE enumeration.
Any region locked with IStream::LockRegion must later be explicitly unlocked by
calling IStream::UnlockRegion with exactly the same values for the libOffset, cb
, and dwLockType parameters. The region must be unlocked before the stream is re
leased. Two adjacent regions cannot be locked separately and then unlocked with
a single unlock call.
14.6.28.5.1 Notes to Callers
Since the type of locking supported is optional and can vary in different implem
entations of IStream, you must provide code to deal with the STG_E_INVALIDFUNCTI
ON error.
This method has no effect in the compound file implementation, because the imple
mentation does not support range locking.
14.6.28.5.2 Notes to Implementers
Support for this method is optional for implementations of stream objects since
it may not be supported by the underlying file system. The type of locking suppo
rted is also optional. The STG_E_INVALIDFUNCTION error is returned if the reques
ted type of locking is not supported.
See Also
IStream - Compound File Implementation, LOCKTYPE, IStream::UnlockRegion
14.6.28.6 IStream::Read
Reads a specified number of bytes from the stream object into memory starting at
the current seek pointer.
HRESULT Read(
void * pv, //Pointer to buffer into which the stream is read
ULONG cb, //Specifies the number of bytes to read
ULONG * pcbRead //Pointer to location that contains actual number of byt
es read
);
Parameters
pv
[in] Points to the buffer into which the stream is read. If an error occurs, thi
s value is NULL.
cb
[in] Specifies the number of bytes of data to attempt to read from the stream ob
ject.
pcbRead
[out] Pointer to a location where this method writes the actual number of bytes
read from the stream object. You can set this pointer to NULL to indicate that y
ou are not interested in this value. In this case, this method does not provide
the actual number of bytes read.
Return Values
S_OK
Data was successfully read from the stream object.
S_FALSE
The data could not be read from the stream object.
E_PENDING
Asynchronous Storage only: Part or all of the data to be read is currently unava
ilable. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for reading this stream object.
STG_E_INVALIDPOINTER
One of the pointer values is invalid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
This method reads bytes from this stream object into memory. The stream object m
ust be opened in STGM_READ mode. This method adjusts the seek pointer by the act
ual number of bytes read.
The number of bytes actually read is returned in the pcbRead parameter.
14.6.28.6.1 Notes to Callers
The actual number of bytes read can be fewer than the number of bytes requested
if an error occurs or if the end of the stream is reached during the read operat
ion.
Some implementations might return an error if the end of the stream is reached d
uring the read. You must be prepared to deal with the error return or S_OK retur
n values on end of stream reads.
See Also
IStream - Compound File Implementation, STGMOVE, IStorage::OpenStream, IStream::
Write
14.6.28.7 IStream::Revert
Discards all changes that have been made to a transacted stream since the last I
Stream::Commit call. On streams open in direct mode and streams using the COM co
mpound file implementation of IStream::Revert, this method has no effect.
HRESULT Revert(void);
Return Values
S_OK
The stream was successfully reverted to its previous version.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
Remarks
This method discards changes made to a transacted stream since the last commit o
peration.
See Also
IStream - Compound File Implementation, IStream::Commit
14.6.28.8 IStream::Seek
Changes the seek pointer to a new location relative to the beginning of the stre
am, to the end of the stream, or to the current seek pointer.
HRESULT Seek(
LARGE_INTEGER dlibMove, //Offset relative to dwOrigin
DWORD dwOrigin, //Specifies the origin for the offset
ULARGE_INTEGER * plibNewPosition //Pointer to location containing new see
k pointer
);
Parameters
dlibMove
[in] 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 signed
.
dwOrigin
[in] Specifies the origin for the displacement specified in dlibMove. The origin
can be the beginning of the file, the current seek pointer, or the end of the f
ile. See the STREAM_SEEK enumeration for the values.
plibNewPosition
[out] Pointer to the location where this method writes the value of the new seek
pointer from the beginning of the stream. You can set this pointer to NULL to i
ndicate that you are not interested in this value. In this case, this method doe
s not provide the new seek pointer.
Return Values
S_OK
The seek pointer has been successfully adjusted.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_INVALIDPOINTER
The value of the plibNewPosition parameter is not valid.
STG_E_INVALIDFUNCTION
The value of the dwOrigin parameter is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
IStream::Seek changes the seek pointer so subsequent reads and writes can take p
lace at a different location in the stream object. It is an error to seek before
the beginning of the stream. It is not, however, an error to seek past the end
of the stream. Seeking past the end of the stream is useful for subsequent write
s, as the stream will at that time be extended to the seek position immediately
before the write is done.
You can also use this method to obtain the current value of the seek pointer by
calling this method with the dwOrigin parameter set to STREAM_SEEK_CUR and the d
libMove parameter set to 0 so the seek pointer is not changed. The current seek
pointer is returned in the plibNewPosition parameter.
See Also
IStream - Compound File Implementation, STREAM_SEEK, IStream::Read, IStream::Wri
te
14.6.28.9 IStream::SetSize
Changes the size of the stream object.
HRESULT SetSize(
ULARGE_INTEGER libNewSize //Specifies the new size of the stream object
);
Parameter
libNewSize
[in] Specifies the new size of the stream as a number of bytes.
Return Values
S_OK
The size of the stream object was successfully changed.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_MEDIUMFULL
The stream size is not changed because there is no space left on the storage dev
ice.
STG_E_INVALIDFUNCTION
The value of the libNewSize parameter is not valid. Since streams cannot be grea
ter than 232 bytes in the COM-provided implementation, the high DWORD of libNewS
ize must be 0. If it is nonzero, this parameter is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
IStream::SetSize changes the size of the stream object. Call this method to prea
llocate space for the stream. If the libNewSize parameters larger than the curre
nt stream size, the stream is extended to the indicated size by filling the inte
rvening space with bytes of undefined value. This operation is similar to the IS
tream::Write method if the seek pointer is past the current end-of-stream.
If the libNewSize parameter is smaller than the current stream, then the stream
is truncated to the indicated size.
The seek pointer is not affected by the change in stream size.
Calling IStream::SetSize can be an effective way of trying to obtain a large ch
unk of contiguous space.
See Also
IStream - Compound File Implementation, IStream::Write
14.6.28.10 IStream::Stat
Retrieves the STATSTG structure for this stream.
HRESULT Stat(
STATSTG * pstatstg, //Location for STATSTG structure
DWORD grfStatFlag //Values taken from the STATFLAG enumeration
);
Parameters
pstatstg
[out] Points to a STATSTG structure where this method places information about t
his stream object. This pointer is NULL if an error occurs.
grfStatFlag
[in] Specifies that this method does not return some of the fields in the STATST
G structure, thus saving a memory allocation operation. Values are taken from th
e STATFLAG enumeration.
Return Value
S_OK
The STATSTG structure was successfully returned at the specified location.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for accessing statistics for thi
s storage object.
STG_E_INSUFFICIENTMEMORY
The STATSTG structure was not returned due to a lack of memory.
STG_E_INVALIDFLAG
The value for the grfStateFlag parameter is not valid.
STG_E_INVALIDPOINTER
The pStatStg pointer is not valid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
IStream::Stat retrieves a pointer to the STATSTG structure that contains informa
tion about this open stream. When this stream is within a structured storage and
IStorage::EnumElements is called, it creates an enumerator object with the IEnu
mSTATSTG interface on it, which can be called to enumerate the storages and stre
ams through the STATSTG structures associated with each of them.
See Also
IStream - Compound File Implementation, STATFLAG, STATSTG
14.6.28.11 IStream::UnlockRegion
Removes the access restriction on a range of bytes previously restricted with IS
tream::LockRegion.
HRESULT UnlockRegion(
ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of
the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType //Specifies the access restriction previously placed on
the range
);
Parameters
libOffset
[in] Specifies the byte offset for the beginning of the range.
cb
[in] Specifies, in bytes, the length of the range to be restricted.
dwLockType
[in] Specifies the access restrictions previously placed on the range.
Return Values
S_OK
The byte range was unlocked.
E_PENDING
Asynchronous Storage only: Part or all of the stream s data is currently unavailab
le. For more information see Asynchronous Storage.
STG_E_INVALIDFUNCTION
Locking is not supported at all or the specific type of lock requested is not su
pported.
STG_E_LOCKVIOLATION
The requested unlock cannot be granted.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
Remarks
IStream::UnlockRegion unlocks a region previously locked with the IStream::LockR
egion method. Locked regions must later be explicitly unlocked by calling IStrea
m::UnlockRegion with exactly the same values for the libOffset, cb, and dwLockTy
pe parameters. The region must be unlocked before the stream is released. Two ad
jacent regions cannot be locked separately and then unlocked with a single unloc
k call.
See Also
IStream - Compound File Implementation, LOCKTYPE, IStream::LockRegion
14.6.28.12 IStream::Write
Writes a specified number from bytes into the stream object starting at the curr
ent seek pointer.
HRESULT Write(
void const* pv, //Pointer to buffer from which stream is written
ULONG cb, //Specifies the number of bytes to write
ULONG * pcbWritten //Specifies the actual number of bytes written
);
Parameters
pv
[in] Points to the buffer from which the stream should be written.
cb
[in] The number of bytes of data to attempt to write into the stream.
pcbWritten
[out] Pointer to a location where this method writes the actual number of bytes
written to the stream object. The caller can set this pointer to NULL, in which
case, this method does not provide the actual number of bytes written.
Return Values
S_OK
The data was successfully written into the stream object.
E_PENDING
Asynchronous Storage only: Part or all of the data to be written is currently un
available. For more information see Asynchronous Storage.
STG_E_MEDIUMFULL
The write operation was not completed because there is no space left on the stor
age device.
STG_E_ACCESSDENIED
The caller does not have sufficient permissions for writing this stream object.
STG_E_CANTSAVE
Data cannot be written for reasons other than no access or space.
STG_E_INVALIDPOINTER
One of the pointer values is invalid.
STG_E_REVERTED
The object has been invalidated by a revert operation above it in the transactio
n tree.
STG_E_WRITEFAULT
The write operation was not completed due to a disk error.
Remarks
IStream::Write writes the specified data to a stream object. The seek pointer is
adjusted for the number of bytes actually written. The number of bytes actually
written is returned in the pcbWrite parameter. If the byte count is zero bytes,
the write operation has no effect.
If the seek pointer is currently past the end of the stream and the byte count i
s non-zero, this method increases the size of the stream to the seek pointer and
writes the specified bytes starting at the seek pointer. The fill bytes written
to the stream are not initialized to any particular value. This is the same as
the end-of-file behavior in the MS-DOS FAT file system.
With a zero byte count and a seek pointer past the end of the stream, this metho
d does not create the fill bytes to increase the stream to the seek pointer. In
this case, you must call the IStream::SetSize method to increase the size of the
stream and write the fill bytes.
The pcbWrite parameter can have a value even if an error occurs.
In the COM-provided implementation, stream objects are not sparse. Any fill byte
s are eventually allocated on the disk and assigned to the stream.
See Also
IStream - Compound File Implementation

14.6.29 IStream - Compound File Implementation


The IStream interface supports reading and writing data to stream objects. Strea
m objects contain the data in a structured storage object, where storages provid
e the structure. Simple data can be written directly to a stream, but most frequ
ently, streams are elements nested within a storage object. They are similar to
standard files.
The specification of IStream defines more functionality that the COM implementat
ion supports. For example, the IStream interface defines streams up to 264 bytes
in length requiring a 64-bit seek pointer. However, the COM implementation only
supports streams up to 232 bytes in length and read and write operations are al
ways limited to 232 bytes at a time. The COM implementation also does not suppor
t stream transactioning or region locking.
When you want to create a simple stream based on global memory, you can get an I
Stream pointer by calling the API function CreateStreamOnHGlobal. To get an IStr
eam pointer within a compound file object, call either StgCreateDocfile or StgOp
enStorage. These functions retrieve an IStorage pointer, with which you can then
call CreateStream/OpenStream for an IStream pointer. In either case, the same I
Stream implementation code is used.
14.6.29.1.1 When to Use
Call the methods of IStream to read and write data to a stream.
Since stream objects can be marshaled to other processes, applications can share
the data in storage objects without having to use global memory. In the COM com
pound file implementation of stream objects, the custom marshaling facilities in
COM create a remote version of the original object in the new process when the
two processes have shared memory access. Thus, the remote version does not need
to communicate with the original process to carry out its functions.
The remote version of the stream object shares the same seek pointer as the orig
inal stream. If you do not want to share the seek pointer, you should use the IS
tream::Clone method to provide a copy of the stream object for the remote proces
s.
Note
If you are creating a stream object that is larger than the heap in your machine s
memory and you are using an HGLOBAL, the stream object calls GlobalRealloc inte
rnally whenever it needs more memory. Because GlobalRealloc always copies data f
rom the source to the destination, increasing a stream object from 20M to 25M, f
or example, consumes immense amounts of time. This is due to the size of the inc
rements copied and is worsened if there is less than 45M of memory on the machin
e 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 a
ddress space and then commit memory within that address space as required. No da
ta copying occurs and memory is committed only as it is needed.
Another alternative is to call the IStream::SetSize method on the stream object
to increase the memory allocation in advance. This is not, however, as efficient
as using VirtualAlloc as described above.
Remarks
IStream::Read
Reads a specified number of bytes from the stream object into memory starting at
the current seek pointer. This implementation returns S_OK if the end of the st
ream was reached during the read. (This is the same as the end of file behavior fo
und in the MS-DOS FAT file system.
IStream::Write
Writes a specified number from bytes into the stream object starting at the curr
ent seek pointer. In this implementation, stream objects are not sparse. Any fil
l bytes are eventually allocated on the disk and assigned to the stream.
IStream::Seek
Changes the seek pointer to a new location relative to the beginning of the stre
am, to the end of the stream, or to the current seek pointer.
IStream::SetSize
Changes the size of the stream object. In this implementation, there is no guara
ntee that the space allocated will be contiguous
IStream::CopyTo
Copies a specified number of bytes from the current seek pointer in the stream t
o the current seek pointer in another stream.
IStream::Commit
The compound file implementation of IStream supports opening streams only in dir
ect mode, not transacted mode. Therefore, the method has no effect when called o
ther than to flush all memory buffers to the next storage level.
In this implementation, it does not matter if you commit changes to streams, you
need only commit changes for storage objects.
IStream::Revert
This implementation does not support transacted streams, so a call to this metho
d has no effect.
IStream::LockRegion
Range-locking is not supported by this implementation, so a call to this method
has no effect.
IStream::UnlockRegion
Removes the access restriction on a range of bytes previously restricted with IS
tream::LockRegion.
IStream::Stat
Retrieves the STATSTG structure for this stream
IStream::Clone
Creates a new stream object with its own seek pointer that references the same b
ytes as the original stream.
See Also
IStream, IStorage, CreateStreamOnHGlobal, StgCreateDocfile, StgOpenStorage
14.7 Persistent Storage API Descriptions
14.7.1 FreePropVariantArray
Calls PropVariantClear on each of the PROPVARIANTs in the rgvar array to zero th
e value of each of the members of the array.
HRESULT FreePropVariantArray(
ULONG cVariant, //Count of elements in the structure
PROPVARIANT* rgvar[] //Pointer to the PROPVARIANT structure
);
Parameters
cVariant
[in] Count of elements in the PROPVARIANT array (rgvar).
rgvar
[in] Pointer to an initialized array of PROPVARIANT structures for which any dea
llocatable elements are to be freed. On exit, all zeroes are written to the PROP
VARIANT (thus tagging them as VT_EMPTY).
Return Values
S_OK
The variant types are recognized and all items that can be freed have been freed
.
STG_E_INVALID_PARAMETER
One or more PROPVARIANTs has an unknown type.
Remarks
FreePropVariantArray calls PropVariantClear on an array of PROPVARIANTs to clear
all the valid members. All valid PROPVARIANTS are freed. If any of the PROPVARI
ANTs contain illegal VT-types, valid members are freed and the function returns
STG_E_INVALIDPARAMETER.
Passing NULL for rgvar is legal, and produces a return code of S_OK.
See Also
PropVariantClear
14.7.2 PropStgNameToFmtId
Converts a property set's storage or stream name to its format identifier.
HRESULT PropStgNameToFmtId(
const LPOLESTR oszName, //Pointer to a string containing the property se
t name
FMTID* pfmtid //Pointer to the format identifier of the property set
);
Parameters
oszName
[in] Pointer to a string containing the stream name of a simple property set or
the storage name of a nonsimple property set.
pfmtid
[out] Pointer to a FMTID variable that receives the format identifier of the pro
perty set specified by oszName.
Return Values
This function supports the standard return value E_INVALIDARG as well as the fol
lowing:
S_OK
The format identifier of the property set was successfully returned.
STG_E_INVALIDNAME
The oszName parameter was invalid.
Remarks
ProgStgNameToFmtId maps a simple property set's stream name or a non-simple prop
erty set's storage name to its format identifier.
This function is useful in creating or opening a property set using the PROPSETF
LAG_UNBUFFERED value with the StgCreatePropStg and StgOpenPropStg functions. See
the PROPSETFLAG enumeration for more information on the use of PROPSETFLAG_UNB
UFFERED.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
PROPSETFLAG, StgCreatePropStg, StgOpenPropStg

14.7.3 PropVariantClear
Frees all elements that can be freed in a given PROPVARIANT structure.
HRESULT PropVariantClear(
PROPVARIANT* pvarg //Pointer to a PROPVARIANT structure
);
Parameters
pvarg
[in] Pointer to an initialized PROPVARIANT structure for which any deallocatable
elements are to be freed. On return, all zeroes are written to the PROPVARIANT.
Return Values
S_OK
The VT types are recognized and all items that can be freed have been freed.
STG_E_INVALID_PARAMETER
The variant has an unknown VT type.
Remarks
At any level of indirection, a NULL pointer is ignored. For example, in a VT_CF
PROPVARIANT, the pvarg¾pclipdata¾pClipData could be NULL. In this case, the pvarg¾pcli
pdata¾pClipData pointer would be ignored, but the pvarg¾pclipdata pointer would be
freed.
On return, this function writes zeroes to the specified PROPVARIANT, so the VT-t
ype is VT_EMPTY.
Passing NULL as the pvarg parameter produces a return code of S_OK.
See Also
FreePropVariantArray

14.7.4 PropVariantCopy
Copies the contents of a PROPVARIANT structure to another.
HRESULT PropVariantCopy(
PROPVARIANT * pDest //Pointer to uninitialized PROPVARIANT that is filled on
return
PROPVARIANT *pvarg //PROPVARIANT to be copied
);
Parameters
pDest
[in, out] Pointer to an uninitialized PROPVARIANT structure that receives the co
py.
pvarg
[in] Pointer to the PROPVARIANT to be copied.
Return Values
S_OK
The copy was successfully completed.
STG_E_INVALID_PARAMETER
The variant has an unknown type.
Remarks
Copies a PROPVARIANT by value so the original pvarg and new pDest may be freed i
ndependently with calls to PropVariantClear. For non-simple PROPVARIANT types su
ch as VT_STREAM, VT_STORAGE, etc, which require a subobject, the copy is made by
reference. The pointer is copied and AddRef is called on it. It is illegal to p
ass NULL for either pDest or pvarg.
14.7.5 ReadClassStg
Reads the CLSID previously written to a storage object with the WriteClassStg.
WINOLEAPI ReadClassStg(
IStorage * pStg, //Pointer to the storage object containing the CLSID
CLSID * pclsid //Pointer to return the CLSID
);
Parameters
pStg
[in] Pointer to the IStorage interface on the storage object containing the CLSI
D to be retrieved.
pclsid
[out] Pointer to where the CLSID is written. May return CLSID_NULL.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The CLSID was returned successfully.
This function also returns any of the error values returned by the IStorage::Sta
t method.
Remarks
This function is simply a helper function that calls the IStorage::Stat method a
nd retrieves the CLSID previously written to the storage object with a call to W
riteClassStg from the STATSTG structure.
See Also
WriteClassStg, IStorage::Stat, STATSTG structure.

14.7.6 ReadClassStm
Reads the CLSID previously written to a stream object with the WriteClassStm met
hod.
WINOLEAPI ReadClassStm(
IStream * pStm, //Pointer to the stream holding the CLSID
CLSID * pclsid //Pointer to where the CLSID is to be written
);
Parameters
pStm
[in] Pointer to the IStream interface on the stream object containing the CLSID
to be read. This CLSID must have been previously written to the stream object us
ing WriteClassStm.
pclsid
[out] Pointer to where the CLSID is to be written.
Return Values
S_OK
The CLSID was successfully retrieved.
STG_E_READFAULT
End of file was reached.
This function also returns any of the error values returned by the IStream::Read
method.
Remarks
Most applications do not call the ReadClassStm method directly. COM calls it bef
ore making a call to an object s IPersistStream::Load implementation.
See Also
WriteClassStm, ReadClassStg, WriteClassStg

14.7.7 ReadFmtUserTypeStg
Returns the clipboard format and user type previously saved with the WriteFmtUse
rTypeStg function.
WINOLEAPI ReadFmtUserTypeStg(
IStorage * pStg, //Pointer to storage object holding the values
CLIPFORMAT * pcf, //Pointer to return the clipboard format
LPWSTR * lplpszUserType //Address of output variable that receives a
// pointer to the requested user type string
);
Parameters
pStg
[in] Pointer to the IStorage interface on the storage object from which the info
rmation is to be read.
pcf
[out] Pointer to where the clipboard format is to be written on return. It can b
e NULL, indicating the format is of no interest to the caller.
lplpszUserType
[out] Address of LPWSTR pointer variable that receives a pointer to the user typ
e string. The caller can specify NULL for this parameter, indicating that the us
er type is of no interest . This function allocates memory for the string. The c
aller is responsible for freeing the memory with CoTaskMemFree.
Return Values
This function supports the standard return values E_FAIL, E_INVALIDARG, and E_OU
TOFMEMORY, as well as the following:
S_OK
The requested information was read successfully.
This function also returns any of the error values returned by the IStream::Read
method.
Remarks
This function returns the clipboard format and the user type string from the spe
cified storage object. The WriteClassStg function must have been called before c
alling the ReadFmtUserTypeStg function.
See Also
CoTaskMemFree, WriteFmtUserTypeStg

14.7.8 SetConvertStg
Sets the convert bit in a storage object to indicate that the object is to be co
nverted to a new class when it is opened. The setting can be retrieved with a ca
ll to the GetConvertStg function.
WINOLEAPI SetConvertStg(
IStorage * pStg, //Points to storage object where the conversion bit is t
o be set
BOOL fConvert //Indicates whether an object is to be converted
);
Parameters
pStg
IStorage pointer to the storage object in which to set the conversion bit.
fConvert
If TRUE, sets the conversion bit for the object to indicate the object is to be
converted when opened. If FALSE, clears the conversion bit.
Return Values
S_OK
Indicates the object s conversion bit was set successfully.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
E_OUTOFMEMORY
Indicates the conversion bit was not set due to a lack of memory.
E_INVALIDARG
Indicates one or more arguments are invalid.
E_UNEXPECTED
Indicates an unexpected error occurred.
See the IStorage::CreateStream, IStorage::OpenStreamISequentialStream::Read, and
ISequentialStream::Write methods for possible storage and stream access errors.
Remarks
The SetConvertStg function determines the status of the convert bit in a contain
ed object. It is called by both the container application and the server in the
process of converting an object from one class to another. When a user specifies
through a Convert To dialogue (which the container produces with a call to the
OleUIConvert function) that an object is to be converted, the container must tak
e the following steps:
1. Unload the object if it is currently loaded.
2. Call WriteClassStg to write the new CLSID to the object storage.
3. Call WriteFmtUserTypeStg to write the new user type name and the
existing main format to the storage.
4. Call SetConvertStg with the fConvert parameter set to TRUE to in
dicate that the object has been tagged for conversion to a new class the next ti
me it is loaded.
5. Just before the object is loaded, call OleDoAutoConvert to handl
e any needed object conversion, unless you call OleLoad, which calls it internal
ly.
When an object is initialized from a storage object and the server is the destin
ation of a Convert To operation, the object server should do the following:
1. Call the GetConvertStg function to retrieve the value of the con
version bit.
2. If the bit is set, the server reads the data out of the object a
ccording to the format associated with the new CLSID.
3. When the object is asked to save itself, the object should call
WriteFmtUserType() using the normal native format and user type of the object.
4. The object should then call SetConvertStg with the fConvert para
meter set to FALSE to reset the object s conversion bit.
See Also
GetConvertStg

14.7.9 StgCreateDocfile
Creates a new compound file storage object using the COM-provided compound file
implementation for the IStorage interface.
WINOLEAPI StgCreateDocfile(
const WCHAR * pwcsName, //Points to path of compound file to create
DWORD grfMode, //Specifies the access mode for opening the storage obje
ct
DWORD reserved, //Reserved; must be zero
IStorage ** ppstgOpen //Points to location for returning the new stora
ge object
);
Parameters
pwcsName
[in] Points to the path of the compound file to create. It is passed uninterpret
ed to the file system. This can be a relative name or NULL. If NULL, a temporary
compound file is allocated with a unique name.
grfMode
[in] Specifies the access mode to use when opening the new storage object. For m
ore information, see the STGM enumeration. If the caller specifies transacted mo
de together with STGM_CREATE or STGM_CONVERT, the overwrite or conversion takes
place at the time the storage object is opened and therefore is not revertible.
reserved
[in] Reserved for future use; must be zero.
ppstgOpen
[out] Points to the location of the IStorage pointer to the new storage object.
Return Values
S_OK
Indicates the compound file was successfully created.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates the compound file already exists and grfMode is set to STGM_FAILIFTHER
E.
STG_S_CONVERTED
Indicates the specified file was successfully converted to Storage format.
STG_E_INSUFFICIENTMEMORY
Indicates the compound file was not created due to a lack of memory.
STG_E_INVALIDNAME
Indicates bad name in the pwcsName parameter.
STG_E_INVALIDPOINTER
Indicates bad pointer in the pwcsName parameter or the ppStgOpen parameter.
STG_E_INVALIDFLAG
Indicates bad flag combination in the grfMode pointer.
STG_E_TOOMANYOPENFILES
Indicates the compound file was not created due to a lack of file handles.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
See also any file system errors for other error return values.
Remarks
The StgCreateDocfile function creates a new storage object using the COM-provide
d, compound-file implementation for the IStorage interface. The name of the open
compound file can be retrieved by calling the IStorage::Stat method.
StgCreateDocfile creates the file if it does not exist. If it does exist, the us
e of the STGM_CREATE, STGM_CONVERT, and STGM_FAILIFTHERE flags in the grfMode pa
rameter indicate how to proceed. See the STGM enumeration for more information o
n these values.
If the compound file is opened in transacted mode (the grfMode parameter specifi
es 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 proce
ss lacks write access to the existing file (because of access control in the fil
e system), the grfMode parameter can only specify STGM_READ and not STGM_WRITE o
r STGM_READWRITE. The resulting new open compound file can still be written to,
but a subsequent commit operation will fail (in transacted mode, write permissio
ns are enforced at commit time).
Specifying STGM_SIMPLE provides a much faster implementation of a compound file
object in a limited, but frequently-used case. This can be used by applications
that require a compound file implementation with multiple streams and no storage
s. The simple mode does not support all of the methods on IStorage. For more inf
ormation, refer to the STGM enumeration.
If the grfMode parameter specifies STGM_TRANSACTED and no file yet exists with t
he name specified by the pwcsName parameter, the file is created immediately. In
an access-controlled file system, the caller must have write permissions in the
file system directory in which the compound file is created. If STGM_TRANSACTED
is not specified, and STGM_CREATE is specified, an existing file with the same
name is destroyed before creating the new file.
StgCreateDocfile can be used to create a temporary compound file by passing a NU
LL value for the pwcsName parameter. However, these files are temporary only in
the sense that they have a system-provided unique name likely one that is meanin
gless to the user. The caller is responsible for deleting the temporary file whe
n finished with it, unless STGM_DELETEONRELEASE was specified for the grfMode pa
rameter.
See Also
StgCreateDocFileOnILockBytes

14.7.10 StgCreateDocfileOnILockBytes
Creates and opens a new compound file storage object on top of a byte array obje
ct provided by the caller. The storage object supports the COM-provided, compoun
d-file implementation for the IStorage interface.
WINOLEAPI StgCreateDocfileOnILockBytes(
ILockBytes * plkbyt, //Points to the ILockBytes interface on the byte
array object
DWORD grfMode, //Specifies the access mode
DWORD reserved, //Reserved; must be zero
IStorage ** ppstgOpen //Points to location for returning the new stora
ge object
);
Parameters
plkbyt
[in] Points to the ILockBytes interface on the underlying byte array object on w
hich to create a compound file.
grfMode
[in] Specifies the access mode to use when opening the new compound file. For mo
re information, see the STGM enumeration.
reserved
[in] Reserved for future use; must be zero.
ppstgOpen
[out] Points to the location of the IStorage pointer on the new storage object.
This function can also return any file system errors or Win32 errors wrapped in
an HRESULT
Return Values
S_OK
Indicates the compound file was successfully created.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates the compound file already exists and the grfMode parameter is set to S
TGM_FAILIFTHERE.
STG_E_INSUFFICIENTMEMORY
Indicates the storage object was not created due to a lack of memory.
STG_E_INVALIDPOINTER
Indicates a bad pointer was in the pLkbyt parameter or the ppStgOpen parameter.
STG_E_INVALIDFLAG
Indicates a bad flag combination was in the grfMode parameter.
STG_E_TOOMANYOPENFILES
Indicates the storage object was not created due to a lack of file handles.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_S_CONVERTED
Indicates the compound file was successfully converted. The original byte array
object was successfully converted to IStorage format.
This function can also return any file system errors, or Win32 errors wrapped in
an HRESULT, or ILockBytes interface error return values.
Remarks
The StgCreateDocfileOnILockBytes function creates a storage object on top of a b
yte array object using the COM-provided, compound-file implementation of the ISt
orage interface. StgCreateDocfileOnILockBytes can be used to store a document in
a relational database. The byte array (indicated by the pLkbyt parameter, which
points to the ILockBytes interface on the object) is used for the underlying st
orage in place of a disk file.
Except for specifying a programmer-provided byte-array object, StgCreateDocfileO
nILockBytes is similar to the StgCreateDocfile function. For more information, r
efer to StgCreateDocfile.
The newly created compound file is opened according to the access modes in the g
rfMode parameter. For conversion purposes, the file is always considered to alre
ady exist. As a result, it is not useful to use the STGM_FAILIFTHERE value, beca
use it always causes an error to be returned. However, STGM_CREATE and STGM_CONV
ERT are both still useful.
The ability to build a compound file on top of a byte array object is provided t
o support having the data (underneath an IStorage and IStream tree structure) li
ve in a non-persistent space. Given this capability, there is nothing preventing
a document that is stored in a file from using this facility. For example, a co
ntainer might do this to minimize the impact on its file format caused by adopti
ng COM. However, it is recommended that COM documents adopt the IStorage interfa
ce for their own outer-level storage. This has the following advantages:
· The storage structure of the document is the same as its storage structure when
it is an embedded object, reducing the number of cases the application needs to
handle.
· One can write tools to access the COM embeddings and links within the document w
ithout special knowledge of the document s file format. An example of such a tool
is a copy utility that copies all the documents included in a container containi
ng linked objects. A copy utility like this needs access to the contained links
to determine the extent of files to be copied.
· The IStorage implementation addresses the problem of how to commit the changes t
o the file. An application using the ILockBytes interface must handle these issu
es itself.
· Future file systems will likely implement the IStorage and IStream interfaces 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 ISto
rage interface as their outer level containment structure would get an automatic
efficiency gain by having the layering flattened when files are saved on the ne
w file system.
See Also
StgCreateDocfile

14.7.11 StgCreatePropSetStg
Creates a property set storage object from a specified storage object. The prope
rty set storage object supplies the system-provided, standalone implementation o
f the IPropertySetStorage interface.
HRESULT StgCreatePropSetStg(
IStorage* pStorage, //Pointer to a storage object
DWORD dwReserved, //Reserved; must be zero
IPropertySetStorage** ppPropSetStg //Address of output variable that receiv
es
// IPropertySetStorage interface pointer
);
Parameter
pStorage
[in] Pointer to the storage object that contains one or more property sets.
dwReserved
Reserved for future use; must be zero.
ppPropSetStg
[out] Address of IPropertySetStorage pointervariable that receives the interface
pointer to the property set storage object.
Return Values
This function supports the standard return value E_INVALIDARG as well as the fol
lowing:
S_OK
The property set storage object was successfully created.
Remarks
StgCreatePropSetStg calls IUnknown::AddRef on the storage object specified by pS
torage. The caller is responsible for releasing the object when it is no longer
needed.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertySetStorage-Standalone Implementation

14.7.12 StgCreatePropStg
Creates and opens a property set in a specified storage or stream object. The pr
operty set supplies the system-provided, standalone implementation of the IPrope
rtyStorage interface.
HRESULT StgCreatePropStg(
IUnknown* pUnk, //Interface pointer for object containing the new proper
ty set
REFFMTID fmtid, //Format identifier of the property set to be created
const CLSID* pclsid, //Initial CLSID for the new property set
DWORD grfFlags, //PROPSETFLAG values
DWORD dwReserved, //Reserved; must be zero
IPropertyStorage** ppPropStg //Address of output that receives
// the IPropertyStorage interface pointer
);
Parameters
pUnk
[in] Pointer to the IUnknown interface on the storage or stream object that stor
es the new property set.
fmtid
[in] Format identifier of the property set to be created.
pclsid
[in] Pointer to the initial CLSID for this property set. May be NULL, in which c
ase pclsid is set to all zeroes.
grfFlags
[in] Values from the PROPSETFLAG enumeration that determine how the property set
is created and opened.
dwReserved
[in] Reserved; must be zero.
ppPropStg
[out] Address of theIPropertyStorage pointer variable that receives the interfac
e pointer to the new property set.
Return Values
This function supports the standard return values E_INVALIDARG and E_UNEXPECTED,
as well as the following:
S_OK
The property set was created.
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied.
STG_E_INVALIDHEADER
The property set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFLAG
A flag in the grfMode or grfFlags parameter was invalid.
STG_E_INVALIDPARAMETER
A parameter is invalid.
STG_E_MEDIUMFULL
The storage object could not be increased in size to hold the default property s
et.
Remarks
StgCreatePropStg creates and opens a new property set which supplies the system-
provided, standalone implementation of the IPropertyStorage interface. The new p
roperty set is contained in the storage or stream object specified by pUnk. The
value of the grfFlags parameter indicates whether pUnk specifies a storage or st
ream object. For example, if PROPSETFLAG_NONSIMPLE is set, then pUnk can be quer
ied for an IStorage interface on a storage object.
In either case, this function calls pUnk->AddRef for the storage or stream objec
t containing the property set. It is the responsibility of the caller to release
the object when it is no longer needed.
This function is similar to the IPropertySetStorage::Create method. However, Stg
CreatePropStg adds the pUnk parameter and supports the PROPSETFLAG_UNBUFFERED va
lue for the grfFlags parameter. Use this function instead of the Create method i
f you have an IStorage interface that does not support the IPropertySetStorage i
nterface, or if you want to use the PROPSETFLAG_UNBUFFERED value. See the PROPSE
TFLAG enumeration for more information on using this PROPSETFLAG_UNBUFFERED.
The property set automatically contains code page and locale ID properties. Thes
e are set to the current system default and the current user default, respective
ly.
The grfFlags parameter is a combination of values taken from the enumeration PRO
PSETFLAG. The new enumeration value PROPSETFLAG_UNBUFFERED is supported. See the
PROPSETFLAG enumeration for more information on the use of this value.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertySetStorage, IPropertySetStorage-Standalone Implementation, IPropertySto
rage, IPropertyStorage-Standalone Implementation, PROPSETFLAG, StgOpenPropStg, S
tgCreatePropSetStg

14.7.13 StgIsStorageFile
Indicates whether a particular disk file contains a storage object.
WINOLEAPI StgIsStorageFile(
const WCHAR * pwcsName //Points to a pathname of the file to check
);
Parameter
pwcsName
[in] Points to the name of the disk file to be examined. The pwcsName parameter
is passed uninterpreted to the underlying file system.
Return Values
S_OK
Indicates the file contains a storage object.
S_FALSE
Indicates the file does not contain a storage object.
STG_E_INVALIDFILENAME
Indicates a bad filename was passed in the pwcsName parameter.
STG_E_FILENOTFOUND
Indicates the file was not found.
This function can also return any file system errors orWin32 errors wrapped in a
n HRESULT.
Remarks
At the beginning of the disk file underlying a storage object is a signature dis
tinguishing a storage object from other file formats. The StgIsStorageFile funct
ion is useful to applications whose documents use a disk file format that might
or might not use storage objects.
If a root compound file has been created in transacted mode but not yet committe
d, this method still return S_OK.
See Also
StgIsStorageILockBytes

14.7.14 StgIsStorageILockBytes
Indicates whether the specified byte array contains a storage object.
WINOLEAPI StgIsStorageILockBytes(
ILockBytes * plkbyt //ILockBytes pointer to the byte array to be examined
);
Parameter
plkbyt
ILockBytes pointer to the byte array to be examined.
Return Values
S_OK
Indicates the specified byte array contains a storage object.
S_FALSE
Indicates the specified byte array does not contain a storage object.
.This function can also return any file system errors, or Win32 errors wrapped i
n an HRESULT, or .
ILockBytes interface error return values.
Remarks
At the beginning of the byte array underlying a storage object is a signature di
stinguishing a storage object (supporting the IStorage interface) from other fil
e formats. The StgIsStorageILockBytes function is useful to applications whose d
ocuments use a byte array (a bye array object supports the ILockBytes interface)
that might or might not use storage objects.
See Also
StgIsStorageFile, ILockBytes

14.7.15 StgOpenPropStg
Opens a specified property set in a specified storage or stream object. The prop
erty set supplies the system-provided, standalone implementation of the IPropert
yStorage interface.
HRESULT StgOpenPropStg(
IUnknown* pUnk, //Interface pointer for object containing the requested
property set
REFFMTID fmtid, //Format identifier of the property set to be opened
DWORD grfFlags, //PROPSETFLAG values
DWORD dwReserved, //Reserved; must be zero
IPropertyStorage** ppPropStg //Address of output variable that receiv
es the
// IPropertyStorage interface pointer
);
Parameters
pUnk
[in] Interface pointer for IUnknown interface on the storage or stream object th
at contains the requested property set object.
fmtid
[in] Format identifier of the property set to be opened.
grfFlags
[in] Values from the PROPSETFLAG enumeration.
dwReserved
[in] Reserved for future use; must be zero.
ppPropStg
[out] Address of IPropertyStorage pointer variable that receives the interface p
ointer to the requested property set.
Return Values
This function supports the standard return values E_INVALIDARG and E_UNEXPECTED,
as well as the following:
S_OK
The property set was opened.
STG_E_FILENOTFOUND
A property set was not found in the specified object (pUnk).
STG_E_ACCESSDENIED
The requested access to the property storage object has been denied, or the prop
erty set is corrupted.
STG_E_INVALIDHEADER
The property set is corrupted.
STG_E_INSUFFICIENTMEMORY
There is not enough memory to perform this operation.
STG_E_INVALIDFLAG
A flag in the grfMode or grfFlags parameter was invalid.
STG_E_INVALIDPARAMETER
A parameter is invalid.
Remarks
StgOpenPropStg opens the requested property set and supplies the system-provided
, standalone implementation of the IPropertyStorage interface. The requested pro
perty set is contained in the storage or stream object specified by pUnk. The va
lue of the grfFlags parameter indicates whether pUnk specifies a storage or stre
am object. For example, if PROPSETFLAG_NONSIMPLE is set, then pUnk can be querie
d for an IStorage interface on a storage object.
In either case, this function calls pUnk->AddRef for the storage or stream objec
t containing the property set. It is the responsibility of the caller to release
the object when it is no longer needed.
This function is similar to the IPropertySetStorage::Open method. However, StgOp
enPropStg adds the pUnk and grfFlags parameters, including the PROPSETFLAG_UNBUF
FERED value for the grfFlags parameter. Use this function instead of the Open me
thod if you have an IStorage interface that does not support the IPropertySetSto
rage interface, or if you want to use the PROPSETFLAG_UNBUFFERED value. See the
PROPSETFLAG enumeration for more information on using PROPSETFLAG_UNBUFFERED.
The grfFlags parameter is a combination of values taken from the enumeration PRO
PSETFLAG. The new enumeration value PROPSETFLAG_UNBUFFERED is supported. See the
PROPSETFLAG enumeration for more information on the use of this value.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertySetStorage, IPropertySetStorage-Standalone Implementation, IPropertySto
rage, IPropertyStorage-Standalone Implementation, PROPSETFLAG, StgCreatePropStg,
StgCreatePropSetStg

14.7.16 StgOpenStorage
Opens an existing root storage object in the file system. You can use this funct
ion to open compound files, but you cannot use it to open directories, files, or
summary catalogs. Nested storage objects can only be opened using their parent'
s IStorage::OpenStorage method.
WINOLEAPI StgOpenStorage(
const WCHAR * pwcsName, //Points to the path of the file containing stor
age object
IStorage * pstgPriority, //Points to a previous opening of a root storage
object
DWORD grfMode, //Specifies the access mode for the object
SNB snbExclude, //Points to an SNB structure specifying elements to be e
xcluded
DWORD reserved, //Reserved; must be zero
IStorage ** ppstgOpen //Address of output variable that receives
// IStorage interface pointer
);
Parameters
pwcsName
[in] Points to the path of the file containing the storage object to open. This
parameter is ignored if the pStgPriority parameter is not NULL.
pstgPriority
[in] Most often NULL. If not NULL, this parameter is used instead of the pwcsNam
e parameter to specify the pointer to the IStorage interface on the storage obje
ct to open. It points to a previous opening of a root storage object, most often
one that was opened in priority mode.
After the StgOpenStorage function returns, the storage object specified in the p
StgPriority parameter on function entry is invalid, and can no longer be used. I
nstead, use the one specified in the ppStgOpen parameter instead.
grfMode
[in] Specifies the access mode to use to open the storage object.
snbExclude
[in] If not NULL, this parameter points to a block of elements in the storage th
at are to be excluded as the storage object is opened. The exclusion occurs inde
pendent of whether a snapshot copy happens on the open. May be NULL.
reserved
[in] Indicates reserved for future use; must be zero.
ppstgOpen
[out] Address of IStorage* pointer variable that receives the interface pointer
to the opened storage.
Return Values
S_OK
Indicates the storage object was successfully opened.
STG_E_FILENOTFOUND
Indicates the specified file does not exist.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
Indicates the file exists but is not a storage object.
STG_E_TOOMANYOPENFILES
Indicates the storage object was not opened because there are too many open file
s.
STG_E_INSUFFICIENTMEMORY
Indicates the storage object was not opened due to a lack of memory.
STG_E_INVALIDNAME
Indicates bad name in the pwcsName parameter.
STG_E_INVALIDPOINTER
Indicates bad pointer in one of the parameters: snbExclude, pwcsName, pstgPriori
ty, or ppStgOpen.
STG_E_INVALIDFLAG
Indicates bad flag combination in the grfMode parameter.
STG_E_INVALIDFUNCTION
Indicates STGM_DELETEONRELEASE specified in the grfMode parameter.
STG_E_OLDFORMAT
Indicates the storage object being opened was created by the Beta 1 storage prov
ider. This format is no longer supported.
STG_E_NOTSIMPLEFORMAT
Indicates that the STGM_SIMPLE flag was specified in the grfMode parameter and t
he storage object being opened was not written in simple mode.
STG_E_OLDDLL
Indicates the DLL being used to open this storage object is a version prior to t
he one used to create it.
STG_E_PATHNOTFOUND
Specified path does not exist.
This function can also return any file system errors or Win32 errors wrapped in
an HRESULT.
Remarks
The StgOpenStorage function opens the specified root storage object according to
the access mode in the grfMode parameter, and, if successful, supplies an IStor
age pointer to the opened storage object in the ppstgOpen parameter.
To support the Simple mode for saving a storage object with no substorages, the
StgOpenStorage function accepts the following flag combinations as valid modes i
n the grfMode parameter: STGM_SIMPLE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE and STG
M_SIMPLE|STGM_READ|STGM_SHARE_EXCLUSIVE.
To support the single-writer, multi-reader, direct mode, the following flag comb
inations are valid modes in the grfMode parameter: STGM_READWRITE|STGM_SHARE_DEN
Y_WRITE and STGM_READ|STGM_SHARE_DENY_NONE.
Note
Opening a storage object in read and/or write mode without denying writer permis
sion to others (the grfMode parameter specifies STGM_SHARE_DENY_WRITE) can be a
time-consuming operation since the StgOpenStorage call must make a snapshot of t
he entire storage object.
Applications often try to open storage objects with the following access permiss
ions:
STGM_READ_WRITE | STGM_SHARE_DENY_WRITE
// transacted vs. direct mode omitted for exposition
If the application succeeds, it never needs to do a snapshot copy. If it fails,
the application can revert to using the permissions and make a snapshot copy:
STGM_READ_WRITE
// transacted vs. direct mode omitted for exposition
In this case, the application should prompt the user before doing a time-consumi
ng copy. Alternatively, if the document sharing semantics implied by the access
modes are appropriate, the application could try to open the storage as follows:
STGM_READ | STGM_SHARE_DENY_WRITE
// transacted vs. direct mode omitted for exposition
In this case, if the application succeeds, a snapshot copy will not have been ma
de (because STGM_SHARE_DENY_WRITE was specified, denying others write access).
To reduce the expense of making a snapshot copy, applications can open storage o
bjects in priority mode (grfMode specifies STGM_PRIORITY).
The snbExclude parameter specifies a set of element names in this storage object
that are to be emptied as the storage object is opened: streams are set to a le
ngth of zero; storage objects have all their elements removed. By excluding cert
ain streams, the expense of making a snapshot copy can be significantly reduced.
Almost always, this approach is used after first opening the storage object in
priority mode, then completely reading the now-excluded elements into memory. Th
is earlier priority mode opening of the storage object should be passed through
the pstgPriority parameter to remove the exclusion implied by priority mode. The
calling application is responsible for rewriting the contents of excluded items
before committing. Thus, this technique is most likely only useful to applicati
ons whose documents do not require constant access to their storage objects whil
e they are active.
See Also
IStorage, StgCreateDocfileStgOpenStorageEx

14.7.17 StgOpenStorageOnILockBytes
Opens an existing storage object that does not reside in a disk file, but instea
d has an underlying byte array provided by the caller.
WINOLEAPI StgOpenStorageOnILockBytes(
ILockBytes * plkbyt, //Points to the ILockBytes interface on the unde
rlying byte array
IStorage * pStgPriority, //Points to a previous opening of a root storage
object
DWORD grfMode, //Specifies the access mode for the object
SNB snbExclude, //Points to an SNB structure specifying elements to be e
xcluded
DWORD reserved, //Reserved, must be zero
IStorage ** ppstgOpen //Points to location for returning the storage o
bject
);
Parameters
plkbyt
[in] ILockBytes pointer to the underlying byte array object that contains the st
orage object to be opened.
pStgPriority
[in] Most often NULL. If not NULL, this parameter is used instead of the plkbyt
parameter to specify the storage object to open. In this case, it points to the
IStorage interface on a previously opened root storage object, most often one th
at was opened in priority mode.
After the StgOpenStorageOnILockBytes function returns, the storage object specif
ied in the pStgPriority parameter on function entry is invalid, and can no longe
r be used; use the one specified in the ppStgOpen parameter instead.
grfMode
[in] Specifies the access mode to use to open the storage object.
snbExclude
[in] Can be NULL. If not NULL, this parameter points to a block of elements in t
his storage that are to be excluded as the storage object is opened. This exclus
ion occurs independent of whether a snapshot copy happens on the open. .
reserved
[in] Indicates reserved for future use; must be zero.
ppstgOpen
[out] Points to the location of an IStorage pointer to the opened storage on suc
cessful return.
Return Values
S_OK
The storage object was successfully opened.
STG_E_FILENOTFOUND
The specified byte array does not exist.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
STG_E_FILEALREADYEXISTS
The byte array exists but is not a storage object.
STG_E_TOOMANYOPENFILES
The storage object was not opened because there are too many open files.
STG_E_INSUFFICIENTMEMORY
The storage object was not opened due to a lack of memory.
STG_E_INVALIDNAME
Either pwcsName or snbExclude contains an invalid name.
STG_E_INVALIDPOINTER
Either snbExclude, pwcsName, pstgPriority, or ppStgOpen contains an invalid poin
ter.
STG_E_INVALIDFLAG
The grfMode parameter contains a bad flag combination.
STG_E_INVALIDFUNCTION
The access mode STGM_DELETEONRELEASE was specified in the grfMode parameter.
STG_E_OLDDLL
The DLL being used to open this storage object is a version prior to the one use
d to create it.
STG_E_OLDFORMAT
The storage object being opened was created by the Beta 1 storage provider. This
format is no longer supported.
This function can also return any file system errors, or Win32 errors wrapped in
an HRESULT, or ILockBytes interface error return values.
Remarks
StgOpenStorageOnILockBytes opens the specified root storage object. The storage
object is opened according to the access mode in the grfMode parameter; a pointe
r to the IStorage interface on the opened storage object is supplied through the
ppstgOpen parameter.
The storage object must have been previously created by the StgCreateDocfileOnIL
ockBytes function.
Except for specifying a programmer-provided byte-array object, StgOpenStorageOnI
LockBytes is similar to the StgOpenStorage function. For more information, refer
to StgOpenStorage.
See Also
StgOpenStorage, StgCreateDocfileOnILockBytes

14.7.18 StgSetTimes
Sets the creation, access, and modification times of the indicated file, if supp
orted by the underlying file system.
WINOLEAPI StgSetTimes(
WCHAR const * lpszName, //Points to the name of the file to be changed

FILETIME const * pctime, //Points to the new value for the creation time
FILETIME const * patime, //Points to the new value for the access time
FILETIME const * pmtime //Points to the new value for the modification t
ime
);
Parameters
lpszName
[in] Points to the name of the file to be changed.
pctime
[in] Points to the new value for the creation time.
patime
[in] Points to the new value for the access time.
pmtime
[in] Points to the new value for the modification time.
Return Values
S_OK
Indicates time values successfully set.
STG_E_FILENOTFOUND
Indicates element does not exist.
STG_E_INVALIDNAME
Indicates bad name passed in the lpszName parameter, or a file system error.
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
STG_E_SHAREVIOLATION
Access denied because another caller has the file open and locked.
This function can also return any file system errors or Win32 errors wrapped in
an HRESULT.
Remarks
The StgSetTimes function sets the time values for the specified file. Each of th
e 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 un
derlying file system. This function sets the times that can be set and ignores t
he rest.

14.7.19 WriteClassStg
Stores the specified CLSID in a storage object.
WINOLEAPI WriteClassStg(
IStorage * pStg, //Points to the IStorage interface on the storage object
REFCLSID rclsid //Specifies the CLSID to be stored in the storage object
);
Parameters
pStg
[in] IStorage pointer to the storage object that gets a new CLSID.
rclsid
[in] Points to the CLSID to be stored with the object.
Return Values
S_OK
Indicates the CLSID was successfully written to the file.
STG_E_MEDIUMFULL
Indicates the CLSID could not be written due to lack of memory.
IStorage::SetClass method error return values.
Remarks
The WriteClassStg function writes a CLSID to the specified storage object so it
can be read by the ReadClassStg function. Container applications typically call
this function before calling the IPersistStorage::Save method.
See Also
ReadClassStg

14.7.20 WriteClassStm
Stores the specified CLSID in the stream.
WINOLEAPI WriteClassStm(
IStream * pStm, //Points to the IStream interface on the stream object
REFCLSID rclsid //Specifies the CLSID to be stored in the stream object
);
Parameters
pStm
[in] IStream pointer to the stream into which the CLSID is to be written.
rclsid
[in] Specifies the CLSID to write to the stream.
Return Values
S_OK
Indicates the CLSID was successfully written.
STG_E_MEDIUMFULL
The CLSID could not be written because there is no space left on device.
IStorage::SetClass method error return values.
Remarks
The WriteClassStm function writes a CLSID to the specified stream object so it c
an be read by the ReadClassStm function. Most applications do not call WriteClas
sStm directly. COM calls it before making a call to an object s IPersistStream::Sa
ve method.
See Also
ReadClassStm, WriteClassStg, ReadClassStg

14.7.21 WriteFmtUserTypeStg
Writes a clipboard format and user type to the storage object.
WINOLEAPI WriteFmtUserTypeStg(
IStorage * pStg, //Points to the IStorage interface on the storage object
CLIPFORMAT cf, //Specifies the clipboard format
LPWSTR * lpszUserType //Points to the current user type
);
Parameters
pStg
[in] IStorage pointer to the storage object where the information is to be writt
en.
cf
[in] Specifies the clipboard format that describes the structure of the native a
rea of the storage object. The format tag includes the policy for the names of s
treams and substorages within this storage object and the rules for interpreting
data within those streams.
lpszUserType
[in] Points to the object s current user type. It cannot be NULL.
Return Values
S_OK
Indicates the information was written successfully.
STG_E_MEDIUMFULL
Indicates information could not be written due to lack of space on the storage m
edium.
IStream::Write method error return values.
Remarks
The WriteFmtUserTypeStg function must be called in an object s implementation of t
he IPersistStorage::Save method. It must also be called by document-level object
s that use structured storage for their persistent representation in their save
sequence.
To read the information saved, applications call the ReadFmtUserTypeStg function
.
See Also
IPersistStorage::Save, ReadFmtUserTypeStg
14.7.22 CreateILockBytesOnHGlobal
Creates a byte array object that allows you use global memory as the physical de
vice underneath a compound file implementation. This object supports an COM impl
ementation of the ILockBytes interface.
WINOLEAPI CreateILockBytesOnHGlobal(
HGLOBAL hGlobal, //Memory handle for the byte array object
BOOL fDeleteOnRelease, //Whether to free memory when the object is rele
ased
ILockBytes ** ppLkbyt //Address of output variable that receives
// the ILockBytes interface pointer
);
Parameters
hGlobal
[in] Memory handle allocated by the GlobalAlloc function. The handle must be all
ocated as moveable and nondiscardable. If the handle is to be shared between pro
cesses, it must also be allocated as shared. New handles should be allocated wit
h a size of zero. If hGlobal is NULL, CreateILockBytesOnHGlobal internally alloc
ates a new shared memory block of size zero.
fDeleteOnRelease
[in] IWhether the underlying handle for this byte array object should be automat
ically freed when the object is released.
ppLkbyt
[out] Address of ILockBytes* pointer variable that receives the interface pointe
r to the new byte array object.
Return Values
This function supports the standard return values E_INVALIDARG and
E_OUTOFMEMORY, as well as the following:
S_OK
The byte array object was created successfully.
Remarks
The CreateILockBytesOnHGlobal function creates a byte array object based on glob
al memory. This object supports an COM implementation of the ILockBytes interfac
e, and is intended to be used as the basis for a compound file. You can then use
the supplied ILockBytes pointer in a call to the StgCreateDocfileOnILockBytes f
unction to build a compound file on top of this byte array object. The ILockByte
s instance calls the GlobalReAlloc function to grow the memory block as needed.
The current contents of the memory block are undisturbed by the creation of the
new byte array object. After creating the ILockBytes instance, you can use the S
tgOpenStorageOnILockBytes function to reopen a previously existing storage objec
t already contained in the memory block. You can also call GetHGlobalFromILockBy
tes to get the global memory handle associated with the byte array object create
d by CreateILockBytesOnHGlobal.
Note
If you free the hGlobal memory handle, the byte array object is no longer valid.
You must call the ILockBytes::Release method before freeing the memory handle.
The value of the hGlobal parameter can be changed by a subsequent call to the Gl
obalReAlloc function; thus, you cannot rely on this value after the byte array o
bject is created.
See Also
StgOpenStorageOnILockBytes, GetHGlobalFromILockBytes, ILockBytes

14.7.23 CreateStreamOnHGlobal
Creates a stream object stored in global memory.
WINOLEAPI CreateStreamOnHGlobal(
HGLOBAL hGlobal, //Memory handle for the stream object
BOOL fDeleteOnRelease, //Whether to free memory when the object is rele
ased
LPSTREAM * ppstm //Address of output variable that
// receives the IStream interface pointer
);
Parameters
hGlobal
[in] Memory handle allocated by the GlobalAlloc function. The handle must be all
ocated as moveable and nondiscardable. If the handle is to be shared between pro
cesses, it must also be allocated as shared. New handles should be allocated wit
h a size of zero. If hGlobal is NULL, the CreateStreamOnHGlobal function interna
lly allocates a new shared memory block of size zero.
fDeleteOnRelease
[in] Whether the underlying handle for this stream object should be automaticall
y freed when the stream object is released.
ppstm
[out] Address of IStream* pointer variable that receives the interface pointer t
o the new stream object. Its value cannot be NULL.
Return Values
This function supports the standard return values E_INVALIDARG and E_OUTOFMEMORY
, as well as the following:
S_OK
The stream object was created successfully.
Remarks
The CreateStreamOnHGlobal function creates a stream object in memory that suppor
ts the COM implementation of the IStream interface. The returned stream object s
upports both reading and writing, is not transacted, and does not support lockin
g.
The initial contents of the stream are the current contents of the memory block
provided in the hGlobal parameter. If the hGlobal paramter is NULL, this functio
n internally allocates memory.
The current contents of the memory block are undisturbed by the creation of the
new stream object. Thus, you can use this function to open an existing stream in
memory.
The initial size of the stream is the size of the memory handle returned by the
Win32 GlobalSize function. Because of rounding, this is not necessarily the same
size that was originally allocated for the handle. If the logical size of the s
tream is important, you should follow the call to this function with a call to t
he IStream::SetSize method.
After you have created the stream object with CreateStreamOnHGlobal, you can cal
l GetHGlobalFromStream to get the global memory handle associated with the strea
m object.
See Also
CreateStreamOnHGlobal, GetHGlobalFromStream, IStream::SetSize,
In the Win32 Programmer s Reference: GlobalSize in Win32
14.7.24 GetClassFile
Supplies the CLSID associated with the given filename.
WINOLEAPI GetClassFile(
LPCWSTR szFileName, //Pointer to filename for which you are requesti
ng a CLSID
CLSID * pclsid //Pointer to location for returning the CLSID
);
Parameters
szFileName
[in] Points to the filename for which you are requesting the associated CLSID.
pclsid
[out] Points to the location where the associated CLSID is written on return.
Return Values
S_OK
Indicates the CLSID was successfully supplied.
MK_E_CANTOPENFILE
Indicates unable to open the specified filename.
MK_E_INVALIDEXTENSION
Indicates the specified extension in the registry is invalid.
Note
This function can also return any file system errors.
Remarks
When given a filename, the GetClassFile function finds the CLSID associated with
that file. Examples of its use are in OleCreateFromFile, which is passed a file
name and requires an associated CLSID, and in the COM implementation of IMonike
r::BindToObject, which, when a link to a file-based document is activated, calls
GetClassFile to locate the object application that can open the file.
GetClassFile uses the following strategies to determine an appropriate CLSID:
1. If the file contains a storage object, as determined by a call t
o the StgIsStorageFile function, GetClassFile returns the CLSID that was written
with the IStorage::SetClass method.
2. If the file is not a storage object, the GetClassFile function a
ttempts to match various bits in the file against a pattern in the registry. A p
attern in the registry can contain a series of entries of the form:
regdb key = offset, cb, mask, value
The value of the offset item is an offset from the beginning or end of the file
and the cb item is a length in bytes. These two values represent a particular by
te range in the file. (A negative value for the offset item is interpreted from
the end of the file). The mask value is a bit mask that is used to perform a log
ical AND operation with the byte range specified by offset and cb. The result of
the logical AND operation is compared with the value item. If the mask is omitt
ed, it is assumed to be all ones.
Each pattern in the registry is compared to the file in the order of the pattern
s in the database. The first pattern where each of the value items matches the r
esult of the AND operation determines the CLSID of the file. For example, the pa
ttern contained in the following entries of the registry requires that the first
four bytes be AB CD 12 34 and that the last four bytes be FE FE FE FE:
HKEY_CLASSES_ROOT
FileType
{12345678-0000-0001-C000-000000000095}
0 = 0, 4, FFFFFFFF, ABCD1234
1 = -4, 4, , FEFEFEFE
If a file contains such a pattern, the CLSID {12345678-0000-0001-C000-0000000000
95} will be associated with this file.
3. If the above strategies fail, the GetClassFile function searches
for the File Extension key in the registry that corresponds to the .ext portion
of the filename. If the database entry contains a valid CLSID, this function re
turns that CLSID.
4. If all strategies fail, the function returns MK_E_INVALIDEXTENSI
ON.
See Also
WriteClassStg

14.7.25 GetConvertStg
Returns the current value of the convert bit for the specified storage object.
WINOLEAPI GetConvertStg(
IStorage * pStg //Points to the IStorage interface on the storage object
);
Parameter
pStg
[in] IStorage pointer to the storage object from which the convert bit is to be
retrieved.
Return Values
S_OK
Indicates the convert bit is set to TRUE.
S_FALSE
Indicates the convert bit is cleared (FALSE).
STG_E_ACCESSDENIED
Access denied because the caller has insufficient permission, or another caller
has the file open and locked.
STG_E_LOCKVIOLATION
Access denied because another caller has the file open and locked.
IStorage::OpenStream, IStorage::OpenStorage, and IStream::Read storage and strea
m access errors.ISequentialStream::Read storage and stream access errors.
Remarks
The GetConvertStg function is called by object servers that support the conversi
on of an object from one format to another. The server must be able to read the
storage object using the format of its previous CLSID and write the object using
the format of its new CLSID to support the object s conversion. For example, a sp
readsheet created by one application can be converted to the format used by a di
fferent application.
The convert bit is set by a call to the SetConvertStg function. A container appl
ication can call this function on the request of an end user, or a setup program
can call it when installing a new version of an application. An end user reques
ts converting an object through the Convert To dialog box. When an object is con
verted, the new CLSID is permanently assigned to the object, so the object is su
bsequently associated with the new CLSID.
Then, when the object is activated, its server calls the GetConvertStg function
to retrieve the value of the convert bit from the storage object. If the bit is
set, the object s CLSID has been changed, and the server must read the old format
and write the new format for the storage object.
After retrieving the bit value, the object application should clear the convert
bit by calling the SetConvertStg function with its fConvert parameter set to FAL
SE.
See Also
SetConvertStg

14.7.26 GetHGlobalFromILockBytes
Retrieves a global memory handle to a byte array object created using the Create
ILockBytesOnHGlobal function.
WINOLEAPI GetHGlobalFromILockBytes(
ILockBytes * pLkbyt, //Points to the byte array object
HGLOBAL * phglobal //Points to the current memory handle for the specified
byte array
);
Parameters
pLkbyt
[in] Points to the ILockBytes interface on the byte array object previously crea
ted by a call to the CreateILockBytesOnHGlobal function.
phglobal
[out] Points to the current memory handle used by the specified byte array objec
t.
Return Values
S_OK
Indicates the handle was returned successfully.
E_INVALIDARG
Indicates invalid value specified for the pLkbyt parameter. It can also indicate
that the byte array object passed in is not one created by the CreateILockBytes
OnHGlobal function.
Remarks
After a call to CreateILockBytesOnHGlobal, which creates a byte array object on
global memory, GetHGlobalFromILockBytes retrieves a pointer to the handle of the
global memory underlying the byte array object. The handle this function return
s might be different from the original handle due to intervening calls to the Gl
obalRealloc function.
The contents of the returned memory handle can be written to a clean disk file,
and then opened as a storage object using the StgOpenStorage function.
This function only works within the same process from which the byte array was c
reated.
See Also
StgOpenStorage, CreateILockBytesOnHGlobal

14.7.27 GetHGlobalFromStream
Retrieves the global memory handle to a stream that was created through a call t
o the CreateStreamOnHGlobal function.
WINOLEAPI GetHGlobalFromStream(
IStream * pstm, //Points to the stream object
HGLOBAL * phglobal //Points to the current memory handle for the specified
stream
);
Parameters
pstm
[in] IStream pointer to the stream object previously created by a call to the Cr
eateStreamOnHGlobal function.
phglobal
[out] Points to the current memory handle used by the specified stream object.
Return Values
S_OK
Indicates the handle was successfully returned.
E_INVALIDARG
Indicates invalid value specified for the pstm parameter. It can also indicate t
hat the stream object passed in is not one created by a call to the CreateStream
OnHGlobal function.
Remarks
The handle this function returns may be different from the original handle due t
o intervening GlobalRealloc calls.
This function can be called only from within the same process from which the byt
e array was created.
14.8 Persistent Storage Structure Descriptions
14.8.1 FILETIME
The FILETIME data structure is a 64-bit value representing the number of 100-nan
osecond intervals since January 1, 1601. It is the means by which Win32 determin
es the date and time. FILETIME is used by the CoDosDateTimeToFileTime, CoFileTim
eToDosDateTime, and CoFileTimeNow functions. It is defined as follows:
typedef struct STRUCT tagFILETIME
{
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
Members
dwLowDateTime
The low 32 bits of the Win32 date/time value.
dwHighDateTime
The upper 32 bits of the Win32 date/time value.
Remarks
The FILETIME data structure is used in the time conversion functions between DOS
and Win32.
See Also
CoDosDateTimeToFileTime, CoFileTimeNow, CoFileTimeToDosDateTime
14.8.2 RemSNB
The RemSNB structure is used for marshaling the SNB data type.
Defined in the IStorage interface (storag.idl).
typedef struct tagRemSNB {
unsigned long ulCntStr;
unsigned long ulCntChar;
[size_is(ulCntChar)] wchar_t rgString[];
} RemSNB;
typedef [transmit_as(RemSNB)] wchar_t **SNB;
Members
ulCntStr
Number of strings in the rgString buffer.
ulCntChar
Size in bytes of the rgString buffer.
rgString
Pointer to an array of bytes containing the [REVIEW: NULL separated?] stream name s
trings from the SNB.
See Also
IStorage
14.8.4 STATPROPSETSTG
Contains information about a property set. To get this information, call IProper
tyStorage::Stat, which fills in a buffer containing the information describing t
he current property set. To enumerate the STATPROPSETSTG structures for the prop
erty sets in the current property set storage, call IPropertySetStorage::Enum to
get a pointer to an enumerator. You can then call the enumeration methods of th
e IEnumSTATPROPSETSTG interface on the enumerator. The structure is defined as f
ollows:
typedef struct tagSTATPROPSETSTG {
FMTID fmtid;
CLSID clsid;
DWORD grfFlags;
FILETIME mtime;
FILETIME ctime;
FILETIME atime;
} STATPROPSETSTG
Members
fmtid
Format identifier of the current property set.
clsid
The CLSID associated with this property set.
grfFlags
Flag values of the property set, as specified in IPropertySetStorage::Create.
mtime
Time in Universal Coordinated Time (UTC) that the property set was last modified
.
ctime
Time in UTC at which this property set was created.
atime
Time in UCT at which this property set was last accessed.
See Also
IPropertySetStorage::Create, IEnumSTATPROPSETSTG, IPropertyStorage::Stat, FILETI
ME structure
14.8.5 STATPROPSTG
Each STATPROPSTG structure contains information about a single property in a pro
perty set. This information is the property identifier and type tag, and the opt
ional string name that may be associated with the property.
IPropertyStorage::Enum supplies a pointer to the IEnumSTATPROPSTG interface on a
n enumerator object that can be used to enumerate through the STATPROPSTG struct
ures for the properties in the current property set. STATPROPSTG is defined as f
ollows:
typedef struct tagSTATPROPSTG {
LPWSTR lpwstrName;
PROPID propid;
VARTYPE vt;
} STATPROPSTG
Members
lpwstrName
Wide-character string containing the optional string name that can be associated
with the property. May be NULL. This member must be freed using CoTaskMemFree.
propid
A 32-bit identifier that uniquely identifies the property within the property se
t. All properties within property sets must have unique property identifiers.
vt
Type of the property.
See Also
IPropertyStorage::Enum, IEnumSTATPROPSTG
14.8.6 STATSTG
The STATSTG structure contains statistical information about an open storage, st
ream, or byte array object. This structure is used in the IEnumSTATSTG, ILockByt
es, IStorage, and IStream interfaces.
Defined in the IStream interface (stream.idl).
typedef struct tagSTATSTG
{
LPWSTR pwcsName;
DWORD type;
ULARGE_INTEGER cbSize;
FILETIME mtime;
FILETIME ctime;
FILETIME atime;
DWORD grfMode;
DWORD grfLocksSupported;
CLSID clsid;
DWORD grfStateBits;
DWORD reserved;
} STATSTG;
Members
pwcsName
Points to a NULL-terminated string containing the name. Space for this string is
allocated by the method called and freed by the caller (refer to CoTaskMemFree)
. You can specify not to return this member by specifying the STATFLAG_NONAME va
lue when you call a method that returns a STATSTG structure, except for calls to
IEnumSTATSTG::Next, which provides no way to specify this value.
type
Indicates the type of storage object. This is one of the values from the STGTY e
numeration.
cbSize
Specifies the size in bytes of the stream or byte array.
mtime
Indicates the last modification time for this storage, stream, or byte array.
ctime
Indicates the creation time for this storage, stream, or byte array.
atime
Indicates the last access time for this storage, stream or byte array.
grfMode
Indicates the access mode specified when the object was opened. This member is o
nly valid in calls to Stat methods.
grfLocksSupported
Indicates the types of region locking supported by the stream or byte array. See
the LOCKTYPES enumeration for the values available. This member is not used for
storage objects.
clsid
Indicates the class identifier for the storage object; set to CLSID_NULL for new
storage objects. This member is not used for streams or byte arrays.
grfStateBits
Indicates the current state bits of the storage object, that is, the value most
recently set by the IStorage::SetStateBits method. This member is not valid for
streams or byte arrays.
dwStgFmt
Indicates the format of the storage object. This is one of the values from the S
TGFMT enumeration.
See Also
IStorage::SetElementTimes
14.9 Persistent Storage Enumeration Descriptions
14.9.1 LOCKTYPE
The LOCKTYPE enumeration values indicate the type of locking requested for the s
pecified range of bytes. The values are used in the ILockBytes::LockRegion and I
Stream::LockRegion methods.
Defined in the IStream interface (stream.idl).
typedef enum tagLOCKTYPE
{
LOCK_WRITE = 1,
LOCK_EXCLUSIVE = 2,
LOCK_ONLYONCE = 4
} LOCKTYPE;
Elements
LOCK_WRITE
If this lock is granted, the specified range of bytes can be opened and read any
number of times, but writing to the locked range is prohibited except for the o
wner that was granted this lock.
LOCK_EXCLUSIVE
If this lock is granted, writing to the specified range of bytes is prohibited e
xcept for the owner that was granted this lock.
LOCK_ONLYONCE
If this lock is granted, no other LOCK_ONLYONCE lock can be obtained on the rang
e. Usually this lock type is an alias for some other lock type. Thus, specific i
mplementations can have additional behavior associated with this lock type.
14.9.2 PROPSETFLAG
The PROPSETFLAG enumeration values define characteristics of a property set. The
values are used in the grfFlags parameter of IPropertySetStorage methods, the S
tgCreatePropStg function, and the StgOpenPropStg function.
typedef enum PROPSETFLAG {
PROPSETFLAG_DEFAULT = 0,
PROPSETFLAG_NONSIMPLE = 1,
PROPSETFLAG_ANSI = 2,
PROPSETFLAG_UNBUFFERED = 4
} PROPSETFLAG
Elements
PROPSETFLAG_NONSIMPLE
If specified, storage-valued and stream-valued properties are permitted in the n
ewly created set. Otherwise, they are not permitted. In the compound file and st
andalone implementations, property sets may be transacted only if PROPSETFLAG_NO
NSIMPLE is specified.
PROPSETFLAG_ANSI
If specified, all string values in the property set that are not explicitly Unic
ode (those other than VT_LPWSTR) are stored with the current system ANSI code pa
ge (see the Win32 function GetACP). Use of this value is not recommended, as des
cribed in the following Remarks section.
If this value is absent, string values in the new property set are stored in Uni
code. The degree of control afforded by this value is necessary so clients using
the property-related interfaces can interoperate well with standard property se
ts such as the OLE2 summary information, which may exist in the ANSI code page.
PROPSETFLAG_UNBUFFERED
Used only with the StgCreatePropStg and StgOpenPropStg functions, that is, in th
e standalone implementations of property set interfaces. If specified in these f
unctions, changes to the property set are not buffered. Instead, changes are alw
ays written directly to the property set. A property set is changed by calls to
its IPropertyStorage methods. However, by default, changes are buffered in an in
ternal property set cache and are subsequently written to the property set when
the IPropertyStorage::Commit method is called.
Setting PROPSETFLAG_UNBUFFERED reduces performance since the property set's inte
rnal buffer is automatically flushed after every change to the property set. How
ever, writing changes directly prevents coordination problems. For example, supp
ose the storage object is opened in transacted mode, and the property set is buf
fered. Then, if you call the IStorage::Commit method on the storage object, the
property set changes will not be picked up as part of the transaction, since the
y are in a buffer that has not been flushed yet. You must call IPropertyStorage:
:Commit prior to calling IStorage::Commit to flush the property set buffer befor
e committing changes to the storage. As an alternative to making two calls, you
can set PROPSETFLAG_UNBUFFERED so that changes are always written directly to th
e property set and are never buffered in the property set's internal cache. Then
, the changes will be picked up when the transacted storage is committed.
Remarks
These values can be set and checked using bitwise operations that determine how
property sets are created and opened. Property sets are created using the IPrope
rtySetStorage::Create method or the StgCreatePropStg function. They are opened u
sing the IPropertySetStorage::Open method or the StgOpenPropStg function.
It is recommended that property sets be created as Unicode, by not setting the P
ROPSETFLAG_ANSI flag in the grfFlags parameter. It is also recommended that you
avoid using VT_LPSTR values, and use VT_LPWSTR values instead. When the property
set code page is Unicode, VT_LPSTR string values are converted to Unicode when
stored, and converted back to multibyte string values when retrieved. When the c
ode page of the property set is not Unicode, property names, VT_BSTR strings, an
d non-simple property values are converted to multibyte strings when stored, and
converted back to Unicode when retrieved, all using the current system ANSI cod
e page.
Programming Information

Unicode Yes
Import Library IPROP.DLL
Header File IPROPIDL.H
See Also
IPropertySetStorage::Create, IPropertySetStorage::Open, PropStgNameToFmtId, StgC
reatePropSetStg, StgCreatePropStg, StgOpenPropStg

14.9.3 STATSTATE
The STATSTATE enumeration values indicate state information about the storage ob
ject and are used as a mask. The values are used in the IStorage::SetStateBits m
ethod.
<<NOTE TO REVIEWERS: Where is this defined? What file/interface?>>
typedef enum tagSTATSTATE
{
STATSTATE_DOC = 1,
STATSTATE_CONVERT = 2,
STATSTATE_FILESTGSAME = 4
} STATSTATE;
Elements
<<NOTE TO REVIEWERS: More info is needed on these descriptions and in the SetSta
teBits method which says that applications don t use these values. Do apps use the
m? If so, what are they used for?>>
STATSTATE_DOC
<<NOTE TO REVIEWERS: Does this mean compound file? When do nested objects use th
is value?>>
The storage object is a document file. This bit is set on the root storage objec
t as part of a normal File/Save sequence. With nested storage objects, the appli
cation manages the storage objects and sets or clears this bit as appropriate. I
f the nested object is an embedded object, this bit can be ignored. It is cleare
d in a newly created storage object. However, some applications might use this b
it to enable editing an embedded storage object without first copying the object
to the file system. For example, a mail application might set this bit for atta
chments so the attachments can be edited without copying them first to a file.
STATSTATE_CONVERT
<<NOTE TO REVIEWERS: Why would an app want to know if the object was converted??
>>
A convert operation was done on this storage object while it was in a passive st
ate.
STATSTATE_FILESTGSAME
<<NOTE TO REVIEWERS: Does this mean compound file?>>
The embedded object and document representations for the storage object are the
same. Thus, the storage object can be saved in a document file simply by copying
the storage object bits.
See Also
IStorage::SetStateBits

14.9.4 STGC
The STGC enumeration constants specify the conditions for performing the commit
operation in the IStorage::Commit and IStream::Commit methods.
Defined in the IOLETypes pseudo-interface (oletyp.idl).
typedef enum tagSTGC
{
STGC_DEFAULT = 0,
STGC_OVERWRITE = 1,
STGC_ONLYIFCURRENT = 2,
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4
} STGC;
Elements
STGC_DEFAULT
None of the other values apply. You can specify this condition or some combinati
on of the other three. You would use this value mainly to make your code more re
adable.
STGC_OVERWRITE
The commit operation can overwrite existing data to reduce overall space require
ments. This value is not recommended for typical usage because it is not as robu
st as the default case. In this case, it is possible for the commit to fail afte
r the old data is overwritten but before the new data is completely committed. T
hen, neither the old version nor the new version of the storage object will be i
ntact.
You can use this value in cases where:
· the user has indicated a willingness to risk losing the data
· the low memory save sequence will be used to safely save the storage object to a
smaller file
· a previous commit returned STG_E_MEDIUMFULL but overwriting the existing data wo
uld provide enough space to commit changes to the storage object
Note that the commit operation checks for adequate space before any overwriting
occurs. Thus, even with this value specified, if the commit operation fails due
to space requirements, the old data will remain safe. The case where data loss c
an occur is when the commit operation fails due to some reason other than lack o
f space and the STGC_OVERWRITE value was specified.
STGC_ONLYIFCURRENT
Prevents multiple users of a storage object from overwriting one another s changes
. The commit operation occurs only if there have been no changes to the saved st
orage object since the user most recently opened the storage object. Thus, the s
aved version of the storage object is the same version that the user has been ed
iting. If other users have changed the storage object, the commit operation fail
s and returns the STG_E_NOTCURRENT value. You can override this behavior by call
ing the Commit method again using the STGC_DEFAULT value.
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE
Commits the changes to a write-behind disk cache, but does not save the cache to
the disk. In a write-behind disk cache, the operation that writes to disk actua
lly writes to a disk cache, thus increasing performance. The cache is eventually
written to the disk, but usually not until after the write operation has alread
y returned. The performance increase comes at the expense of an increased risk o
f losing data if a problem occurs before the cache is saved and the data in the
cache is lost.
If you do not specify this value, then committing changes to root-level storage
objects is robust even if a disk cache is used. The two-phase commit process ens
ures that data is stored on the disk and not just to the disk cache.
Remarks
You can specify STGC_DEFAULT or some combination of the other three values. Typi
cally, you would use STGC_ONLYIFCURRENT to protect the storage object in cases w
here more than one user can edit the object simultaneously.
See Also
IPropertyStorage, IStorage, IStream

14.9.5 STGFMT
This enumeration is defined, but is not used in the COM for UNIX implementation.
typedef enum tagSTGFMT
{
STGFMT_DOCUMENT = 0,
STGFMT_DIRECTORY = 1,
STGFMT_CATALOG = 2,
STGFMT_FILE = 3
} STGFMT;
Elements
STGFMT_DOCUMENT
Indicates a document format.
STGFMT_DIRECTORY
Indicates a directory format.
STGFMT_CATALOG
Indicates a catalog format.
STGFMT_FILE
Indicates a file format.

14.9.6 STGM
The STGM enumeration values are used in the storage and stream interfaces to ind
icate the conditions for creating and deleting the object and access modes for t
he object.
The STGM values are used in the IStorage and IStream interfaces, and in the StgC
reateDocfile and StgCreateDocfileOnILockBytes functions to indicate the conditio
ns for creating and deleting the object and access modes for the object.
STGM values are as follows:
STGM_DIRECT 0x00000000L
STGM_TRANSACTED 0x00010000L
STGM_SIMPLE 0x08000000L
STGM_READ 0x00000000L
STGM_WRITE 0x00000001L
STGM_READWRITE 0x00000002L
STGM_SHARE_DENY_NONE 0x00000040L
STGM_SHARE_DENY_READ 0x00000030L
STGM_SHARE_DENY_WRITE 0x00000020L
STGM_SHARE_EXCLUSIVE 0x00000010L
STGM_PRIORITY 0x00040000L
STGM_DELETEONRELEASE 0x04000000L 001000L
STGM_CONVERT 0x00020000L
STGM_FAILIFTHERE 0x00000000L
STGM_NOSCRATCH 0x00100000L
Elements
14.9.6.1.1.1.1 STGM_DIRECT. STGM_TRANSACTED, STGM_SIMPLE group:
STGM_DIRECT
In direct mode, each change to a storage element is written as it occurs. This i
s the default.
STGM_TRANSACTED
In transacted mode, changes are buffered and are written only if an explicit com
mit operation is called. The changes can be ignored by calling the Revert method
in the IStream or IStorage interfaces. The COM compound file implementation doe
s not support transacted streams, which means that streams can be opened only in
direct mode, and you cannot revert changes to them. Transacted storages are, ho
wever, supported.
STGM_SIMPLE
STGM_SIMPLE is a mode that provides a much faster implementation of a compound f
ile in a limited, but frequently used case. It is described in detail in the fol
lowing Remarks section.
14.9.6.1.1.1.2 STGM_READ, STGM_WRITE, STGM_READWRITE group:
STGM_READ
For stream objects, STGM_READ allows you to call the IStream::Read method. For s
torage objects, you can enumerate the storage elements and open them for reading
.
STGM_WRITE
STGM_WRITE lets you save changes to the object.
STGM_READWRITE
STGM_READWRITE is the combination of STGM_READ and STGM_WRITE.
14.9.6.1.1.1.3 STGM_SHARE_* group:
STGM_SHARE_DENY_NONE
Specifies that subsequent openings of the object are not denied read or write ac
cess.
STGM_SHARE_DENY_READ
Prevents others from subsequently opening the object in STGM_READ mode. It is ty
pically used on a root storage object.
STGM_SHARE_DENY_WRITE
Prevents others from subsequently opening the object in STGM_WRITE mode. This va
lue is typically used to prevent unnecessary copies made of an object opened by
multiple users. If this value is not specified, a snapshot is made, independent
of whether there are subsequent opens or not. Thus, you can improve performance
by specifying this value.
STGM_SHARE_EXCLUSIVE
The combination of STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE.
14.9.6.1.1.1.4 STGM_PRIORITY
STGM_PRIORITY
Opens the storage object with exclusive access to the most recently committed ve
rsion. Thus, other users cannot commit changes to the object while you have it o
pen in priority mode. You gain performance benefits for copy operations, but you
prevent others from committing changes. So, you should limit the time you keep
objects open in priority mode. You must specify STGM_DIRECT and STGM_READ with p
riority mode.
14.9.6.1.1.1.5 STGM_DELETEONRELEASE
STGM_DELETEONRELEASE
Indicates that the underlying file is to be automatically destroyed when the roo
t storage object is released. This capability is most useful for creating tempor
ary files.
14.9.6.1.1.1.6 STGM_CREATE, STGM_CONVERT, STGM_FAILIFTHERE Group
STGM_CREATE
Indicates that an existing storage object or stream should be removed before the
new one replaces it. A new object is created when this flag is specified, only
if the existing object has been successfully removed.
This flag is used in three situations:
· when you are trying to create a storage object on disk but a file of that name a
lready exists
· when you are trying to create a stream inside a storage object but a stream with
the specified name already exists
· when you are creating a byte array object but one with the specified name alread
y exists
STGM_CONVERT
Creates the new object while preserving existing data in a stream named CONTENTS
. In the case of a storage object or a byte array, the old data is flattened to
a stream regardless of whether the existing file or byte array currently contain
s a layered storage object.
STGM_FAILIFTHERE
Causes the create operation to fail if an existing object with the specified nam
e exists. In this case, STG_E_FILEALREADYEXISTS is returned. STGM_FAILIFTHERE ap
plies to both storage objects and streams.
STGM_NOSCRATCH
Not used.
Remarks
You can combine these flags but you can only choose one flag from each group of
related flags. Groups are indicated under the headings in the previous section.
The STGM_SIMPLE flag is applicable only when combined with:
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE
Note that direct mode is implied by the absence of STGM_TRANSACTED.
This mode is useful for applications that perform complete save operations. It h
as the following constraints:
1. There is no support for substorages.
2. Access to streams follows a linear pattern. Once a stream is rel
eased, that stream cannot be opened for read/write operations again. The IStorag
e::OpenStream method is not supported in this implementation.
3. The storage and stream objects cannot be marshaled.
4. Each stream is at least 4096 bytes in length. If fewer than 4096
bytes are written into a stream by the time the stream is released, the stream
will be extended to contain 4096 bytes.
5. In this compound file implementation, only a subset of the metho
ds of IStorage and IStream are available.
Specifically, in simple mode, supported IStorage methods are QueryInterface, Add
Ref, Release, CreateStream, Commit, and SetClass. In addition, SetElementTimes i
s supported with a NULL name, allowing applications to set times on a root stora
ge in simple mode.
Supported IStream methods are QueryInterface, AddRef, Release, Write, Seek, SetS
ize, and Read.
All the other methods of IStorage and IStream return STG_E_INVALIDFUNCTION.
See Also
StgCreateDocfile, IStream::Read, IStorage, StgCreateDocfileOnILockBytes, StgOpen
Storage, StgOpenStorageOnILockBytes

14.9.7 STGMOVE
The STGMOVE enumeration values indicate whether a storage element is to be moved
or copied. They are used in the IStorage::MoveElementTo method.
Defined in the IOLETypes pseudo-interface (oletyp.idl).
typedef enum tagSTGMOVE
{
STGMOVE_MOVE = 0,
STGMOVE_COPY = 1
} STGMOVE;
Elements
STGMOVE_MOVE
Indicates the method should move the data from the source to the destination.
STGMOVE_COPY
Indicates the method should copy the data from the source to the destination. A
copy is the same as a move except the source element is not removed after copyin
g the element to the destination. Copying an element on top of itself is undefin
ed.
See Also
IStorage::MoveElementTo

14.9.8 STGTY
The STGTY enumeration values are used in the type member of the STATSTG structur
e to indicate the type of the storage element. A storage element is a storage ob
ject, a stream object, or a byte array object (LOCKBYTES).
Defined in the IStream interface (stream.idl).
typedef enum tagSTGTY
{
STGTY_STORAGE = 1,
STGTY_STREAM = 2,
STGTY_LOCKBYTES = 3,
STGTY_PROPERTY = 4
} STGTY;
Elements
STGTY_STORAGE
Indicates that the storage element is a storage object.
STGTY_STREAM
Indicates that the storage element is a stream object.
STGTY_LOCKBYTES
Indicates that the storage element is a byte array object.
STGTY_PROPERTY
Indicates that the storage element is a property storage object.
See Also
IStream, STATSTG

14.9.9 STREAM_SEEK
The STREAM_SEEK enumeration values specify the origin from which to calculate th
e new seek pointer location. They are used for the dworigin parameter in the ISt
ream::Seek method. The new seek position is calculated using this value and the
dlibMove parameter.
Defined in the IStream interface (stream.idl).
typedef enum tagSTREAM_SEEK
{
STREAM_SEEK_SET = 0,
STREAM_SEEK_CUR = 1,
STREAM_SEEK_END = 2
} STREAM_SEEK;
Elements
STREAM_SEEK_SET
The new seek pointer is an offset relative to the beginning of the stream. In th
is case, the dlibMove parameter is the new seek position relative to the beginni
ng of the stream.
STREAM_SEEK_CUR
The new seek pointer is an offset relative to the current seek pointer location.
In this case, the dlibMove parameter is the signed displacement from the curren
t seek position.
STREAM_SEEK_END
The new seek pointer is an offset relative to the end of the stream. In this cas
e, the dlibMove parameter is the new seek position relative to the end of the st
ream.
See Also
IStream::Seek

15. Persistent Intelligent Names: Monikers


15.1 Overview
A moniker is simply an object that supports the IMoniker interface. IMoniker int
erface includes the IPersistStream interface; thus, monikers can be saved to and
loaded from streams. The persistent form of a moniker contains the class identifier
(CLSID) of its implementation which is used during the loading process, and so ne
w kinds of monikers can be created transparently to clients.
The most basic operation in IMoniker interface is that of binding to the object
to which it points, which is supported by IMoniker::BindToObject. This function ta
kes as a parameter the interface identifier by which the caller wishes to talk t
o the object, runs whatever algorithm is necessary in order to locate the object,
then returns a pointer of that interface type to the caller. Each moniker class c
an store arbitrary data its persistent representation, and can run arbitrary cod
e at binding time.
If there is an identifiable piece of persistent storage in which the object refere
nced by the moniker is stored, then IMoniker::BindToStorage can be used to gain acce
to it. Many objects have such identifiable storage, but some, such as the object
s which are the ranges on a Microsoft Excel spreadsheet do not. (These ranges exi
st only as a part of Excel s data structures; they are in effect a figment of Exce
l s imagination and are only reified on demand for clients.)
In most cases, a particular moniker class is designed to be one step along the p
ath to the information source in question. These pieces can be composed together
to form a moniker which represents the complete path. For example, the moniker s
tored inside a chart that refers to its underlying data in a spreadsheet might b
e a composite moniker formed from three pieces:
Figure 15-1. Moniker in a chart referring to a spreadsheet from which it extract
s data.
This composite is itself a moniker; it just happens to be a moniker which is a s
equenced collection of other monikers. The composition here is generic in that i
t has no knowledge of the pieces involved other than that they are monikers.
Most monikers have a textual representation which is meaningful to the user; thi
s can be retrieved with IMoniker::GetDisplayName. The API function MkParseDisplayN
ame goes the other direction: it can turn a textual display name into the approp
riate moniker, though beware that in general this is operation is as expensive as
actually binding to the object.
Monikers can compare themselves to other monikers using IMoniker::IsEqual. A has
h value useful for storing monikers in lookup tables is available through IMonik
er::Hash. Monikers are not a total order or even a partial order; therefore, mon
ikers cannot be stored in tables that rely on sorting for retrieval; use hashing
instead (it is inappropriate to use the display name of a moniker for sorting,
since the display name may not reflect the totality of internal state of the mon
iker).
The earliest time after which the object to which the moniker points is known no
t to have changed can be obtained with IMoniker::GetTimeOfLastChange. This is not n
ecessarily the time of last change of the object; rather, it is the best cheaply
available approximation thereto.
A moniker can be asked to re-write itself into another equivalent moniker by cal
ling IMoniker::Reduce. This function returns a new moniker that will bind to the sa
me object, but does so in a more efficient way. This capability has several uses
:
· It enables the construction of user-defined macros or aliases as new kinds of mo
niker classes. When reduced, the moniker to which the macro evaluates is returne
d.
· It enables the construction of a kind of moniker which tracks data as it moves a
bout. When reduced, the moniker of the data in its current location is returned.
· On file systems such as Macintosh System 7 which support an ID-based method of a
ccessing files which is independent of file names, a File Moniker could be reduc
ed to a moniker which contains one of these IDs.
Figure 15-2 shows a (somewhat contrived) example of moniker reduction. It illustr
ates the reduction of a moniker which names the net income entry for this year s r
eport in the Projects directory of the current user s home directory.
Figure15-2. Reduction of a moniker showing the objects connected to during reduc
tion.
(Note that the particular classes of monikers used here are for illustrative pur
poses only.) As we can see, many monikers in this example are reduced to somethi
ng completely different, and some bind to something during their reduction, but s
ome do not. For example, to reduce the alias Home , the reduction must access the i
nformation that Home was an alias for \\server\share\fred .
The process of moniker reduction may also be tied to a global table called the R
unning Object Table. The Running Object Table serves as the place where monikers
in the process of binding look to see if they are already running or not.
Pointers to instances of IMoniker interface can be marshaled to other processes,
just as any other interface pointer can. Many monikers are of the nature that th
ey are immutable once created and that they maintain no object state outside the
mselves. Item Monikers are an example of a class of such monikers. These moniker
s, which can be replicated at will, will usually want to support custom marshali
ng (see IMarshal interface) so as to simply serialize themselves and de-serializ
e themselves in the destination context (see IPersistStream regarding serializati
on). This is referred to as marshaling an object by value.
15.2 Moniker Interface Descriptions
15.2.1 IBindCtx
The IBindCtx interface provides access to a bind context, which is an object tha
t stores information about a particular moniker binding operation. You pass a bi
nd context as a parameter when calling many methods of IMoniker and in certain f
unctions related to monikers.
A bind context includes the following information:
· A BIND_OPTS structure containing a set of parameters that do not change during t
he binding operation. When a composite moniker is bound, each component uses the
same bind context, so it acts as a mechanism for passing the same parameters to
each component of a composite moniker.
· A set of pointers to objects that the binding operation has activated. The bind
context holds pointers to these bound objects, keeping them loaded and thus elim
inating redundant activations if the objects are needed again during subsequent
binding operations.
· A pointer to the Running Object Table on the machine of the process that started
the bind operation. Moniker implementations that need to access the Running Obj
ect Table should use the IBindCtx::GetRunningObjectTable method rather than usin
g the GetRunningObjectTable function. This allows future enhancements to the sys
tem s IBindCtx implementation to modify binding behavior.
· A table of interface pointers, each associated with a string key. This capabilit
y enables moniker implementations to store interface pointers under a well-known
string so that they can later be retrieved from the bind context. For example,
COM defines several string keys (e.g., ExceededDeadline , ConnectManually ) that can b
e used to store a pointer to the object that caused an error during a binding op
eration.
15.2.1.1.1 When to Implement
You do not need to implement this interface. The system provides an IBindCtx imp
lementation, accessible though a call to the CreateBindCtx function, that is sui
table for all situations.
15.2.1.1.2 When to Use
Anyone writing a new moniker class by implementing the IMoniker interface must c
all IBindCtx methods in the implementation of several IMoniker methods. Moniker
providers (servers that hand out monikers to identify their objects) may also ne
ed to call IBindCtx methods from their implementations of the IOleItemContainer
or IParseDisplayName interfaces.
Moniker clients (objects that use monikers to acquire interface pointers to othe
r objects) typically don t call many IBindCtx methods. Instead, they simply pass a
bind context as a parameter in a call to an IMoniker method. To acquire an inte
rface pointer and activate the indicated object (called binding to an object), m
oniker clients typically do the following:
1. Call the CreateBindCtx function to create a bind context and get
a pointer to the IBindCtx interface on the bind context object..
2. If desired (although this is rarely necessary), the moniker clie
nt can call IBindCtx::SetBindOptions to specify the bind options.
3. Pass the bind context as a parameter to the desired IMoniker met
hod (usually IMoniker::BindToObject).
4. Call IUnknown::Release on the bind context to release it.
Although applications that act as link containers (container applications that a
llow their documents to contain linked objects) are moniker clients, they rarely
call IMoniker methods directly.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IBindCtx Methods Description
RegisterObjectBound Registers an object with the bind context.
RevokeObjectBound Revokes an object s registration.
ReleaseBoundObjects Releases all registered objects.
SetBindOptions Sets the binding options.
GetBindOptions Retrieves the binding options.
GetRunningObjectTable Retrieves a pointer to the Running Object Table.
RegisterObjectParam Associates an object with a string key.
GetObjectParam Returns the object associated with a given string key.
EnumObjectParam Enumerates all the string keys in the table.
RevokeObjectParam Revokes association between an object and a string key.

See Also
CreateBindCtx, IMoniker, IOleItemContainer, IParseDisplayName

15.2.1.2 IBindCtx::EnumObjectParam
Supplies a pointer to an IEnumString interface on an enumerator that can return
the keys of the bind context s string-keyed table of pointers.
HRESULT EnumObjectParam(
IEnumString **ppenum //Indirect pointer to the enumerator object
);
Parameter
ppenum
[out] Indirect pointer to the IEnumString interface on the enumerator. If an err
or occurs, *ppenum is set to NULL. If *ppenum is non-NULL, the implementation ca
lls IUnknown::AddRef on the parameter; it is the caller s responsibility to call I
Unknown::Release.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
An enumerator was successfully created and the pointer supplied.
15.2.1.2.1 Remarks
This method provides an IEnumString pointer to an enumerator that can return the
keys of the bind context s string-keyed table of pointers. The keys returned are
the ones previously specified in calls to IBindCtx::RegisterObjectParam.
15.2.1.2.2 Notes to Callers
A bind context maintains a table of interface pointers, each associated with a s
tring key. This enables communication between a moniker implementation and the c
aller that initiated the binding operation. One party can store an interface poi
nter under a string known to both parties so that the other party can later retr
ieve it from the bind context.
See Also
IBindCtx::RegisterObjectParam, IEnumString

15.2.1.3 IBindCtx::GetBindOptions
Returns the binding options stored in this bind context.
HRESULT GetBindOptions(
BIND_OPTS *pbindopts //Pointer to a structure
);
Parameter
pbindopts
[in, out] Pointer to an initialized BIND_OPTS structure on entry that receives t
he current binding parameters on return.
Return Values
This method supports the standard return value E_UNEXPECTED, as well as the foll
owing:
S_OK
The stored binding options were successfully returned.
Remarks
A bind context contains a block of parameters, stored in a BIND_OPTS structure,
that are common to most IMoniker operations and that do not change as the operat
ion moves from piece to piece of a composite moniker.
15.2.1.3.1 Notes to Callers
You typically call this method if you are writing your own moniker class (this r
equires that you implement the IMoniker interface). You call this method to retr
ieve the parameters specified by the moniker client.
You must initialize the BIND_OPTS structure that is filled in by this method. Be
fore calling this method, you must initialize the cbStruct field of the structur
e to the size of the BIND_OPTS structure.
See Also
IBindCtx::SetBindOptions

15.2.1.4 IBindCtx::GetObjectParam
Retrieves the pointer associated with the specified key in the bind context s stri
ng-keyed table of pointers.
HRESULT GetObjectParam(
LPOLESTR pszKey, //Pointer to the key to be used
IUnknown **ppunk //Indirect pointer to the object associated with the key
);
Parameters
pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character
) containing the key to search for. Key string comparison is case-sensitive.
ppunk
[out] When successful, indirect pointer to the IUnknown interface on the object
associated with pszKey. In this case, the implementation calls IUnknown::AddRef
on the parameter. It is the caller s responsibility to call IUnknown::Release. If
an error occurs, ppunk is set to NULL.
Return Values
This method supports the standard return value E_FAIL, as well as the following:
S_OK
The pointer associated with the specified key was successfully returned.
Remarks
A bind context maintains a table of interface pointers, each associated with a s
tring key. This enables communication between a moniker implementation and the c
aller that initiated the binding operation. One party can store an interface poi
nter under a string known to both parties so that the other party can later retr
ieve it from the bind context.
The pointer this method retrieves must have previously been inserted into the ta
ble using the IBindCtx::RegisterObjectParam method.
15.2.1.4.1 Notes to Callers
Those writing a new moniker class (through an implementation of IMoniker) and so
me moniker clients (objects using a moniker to bind to an object) can call IBind
Ctx::GetObjectParam.
Objects using monikers to locate other objects can call this method when a bindi
ng operation fails to get specific information about the error that occurred. De
pending on the error, it may be possible to correct the situation and retry the
binding operation. See IBindCtx::RegisterObjectParam for more information.
Moniker implementations can call this method to deal with situations where a cal
ler initates a binding operation and requests specific information. By conventio
n, the implementer should use key names that begin with the string form of the C
LSID of a moniker class (see the StringFromCLSID function).
See Also
IBindCtx::RegisterObjectParam, IBindCtx::EnumObjectParam

15.2.1.5 IBindCtx::GetRunningObjectTable
Provides an interface pointer to the Running Object Table (ROT) for the machine
on which this bind context is running.
HRESULT GetRunningObjectTable(
IRunningObjectTable **pprot //Indirect pointer to the Running Object Table
);
Parameter
pprot
[out] When successful, indirect pointer to the IRunningObjectTable interface on
the Running Object Table. If an error occurs, *pprot is set to NULL. If *pprot i
s non-NULL, the implementation calls IUnknown::AddRef on the parameter; it is th
e caller s responsibility to call IUnknown::Release.
Return Values
This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED,
as well as the following:
S_OK
A pointer to the ROT was returned successfully.
Remarks
The Running Object Table is a globally accessible table on each machine. It keep
s track of all the objects that are currently running on the machine.
15.2.1.5.1 Notes to Callers
Typically, those implementing a new moniker class (through an implementation of
IMoniker interface) call IBindCtx::GetRunningObjectTable. It is useful to call t
his method in an implementation of IMoniker::BindToObject or IMoniker::IsRunning
to check whether a given object is currently running. You can also call this me
thod in the implementation of IMoniker::GetTimeOfLastChange to learn when a runn
ing object was last modified.
Moniker implementations should call this method instead of using the GetRunningO
bjectTable function. This makes it possible for future implementations of IBindC
tx to modify binding behavior.
See Also
IMoniker, IRunningObjectTable

15.2.1.6 IBindCtx::RegisterObjectBound
Calls IUnknown::AddRef on the specified object to ensure that the object remains
active until the bind context is released. The method stores a pointer to the o
bject in the bind context s internal list of pointers.
HRESULT RegisterObjectBound(
IUnknown *punk //Pointer to the object being registered
);
Parameter
punk
[in] Pointer to the IUnknown interface on the object that is being registered as
bound.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The object was successfully registered.
Remarks
15.2.1.6.1 Notes to Callers
Those writing a new moniker class (through an implementation of the IMoniker int
erface), should call this method whenever the implementation activates an object
. This happens most often in the course of binding a moniker, but it can also ha
ppen while retrieving a moniker s display name, parsing a display name into a moni
ker, or retrieving the time that an object was last modified.
IBindCtx::RegisterObjectBound calls IUnknown::AddRef to create an additional ref
erence to the object. You must, however, still release your own copy of the poin
ter. Note that calling this method twice for the same object creates two referen
ces to that object. You can release a reference obtained through a call to this
method by calling IBindCtx::RevokeObjectBound. All references held by the bind c
ontext are released when the bind context itself is released.
Calling IBindCtx::RegisterObjectBound to register an object with a bind context
keeps the object active until the bind context is released. Reusing a bind conte
xt in a subsequent binding operation (either for another piece of the same compo
site moniker, or for a different moniker) can make the subsequent binding operat
ion more efficient because it doesn t have to reload that object. This, however, i
mproves performance only if the subsequent binding operation requires some of th
e same objects as the original one, so you need to balance the possible performa
nce improvement of reusing a bind context against the costs of keeping objects a
ctivated unnecessarily.
IBindCtx does not provide a method to retrieve a pointer to an object registered
using IBindCtx::RegisterObjectBound. Assuming the object has registered itself
with the Running Object Table, moniker implementations can call IRunningObjectTa
ble::GetObject to retrieve a pointer to the object.
See Also
IBindCtx::ReleaseBoundObjects, IBindCtx::RevokeObjectBound, IRunningObjectTable:
:GetObject

15.2.1.7 IBindCtx::RegisterObjectParam
Stores an IUnknown pointer on the specified object under the specified key in th
e bind context s string-keyed table of pointers. The method must call IUnknown::Ad
dRef on the stored pointer.
HRESULT RegisterObjectParam(
LPOLESTR pszKey, //Pointer to the key to be used
IUnknown *punk //Pointer to the object to be associated with the key
);
Parameters
pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character
) containing the key under which the object is being registered. Key string comp
arison is case-sensitive.
punk
[in] Pointer to the IUnknown interface on the object that is to be registered.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The pointer was successfully registered under the specified string.
Remarks
A bind context maintains a table of interface pointers, each associated with a s
tring key. This enables communication between a moniker implementation and the c
aller that initiated the binding operation. One party can store an interface poi
nter under a string known to both parties so that the other party can later retr
ieve it from the bind context.
Binding operations subsequent to the use of this method can use IBindCtx::GetObj
ectParam to retrieve the stored pointer.
15.2.1.7.1 Notes to Callers
IBindCtx::RegisterObjectParam is useful to those implementing a new moniker clas
s (through an implementation of IMoniker) and to moniker clients (those who use
monikers to bind to objects).
In implementing a new moniker class, you call this method when an error occurs d
uring moniker binding to inform the caller of the cause of the error. The key th
at you would obtain with a call to this method would depend on the error conditi
on. The following lists common moniker binding errors, describing for each the k
eys that would be appropriate:
MK_E_EXCEEDEDDEADLINE
If a binding operation exceeds its deadline because a given object is not runnin
g, you should register the object s moniker using the first unused key from the li
st: ExceededDeadline , ExceededDeadline1 , ExceededDeadline2 , etc. If the caller later
inds the moniker in the Running Object Table, the caller can retry the binding o
peration.
MK_E_CONNECTMANUALLY
The ConnectManually key indicates a moniker whose binding requires assistance from
the end user. The caller can retry the binding operation after showing the moni
ker s display name to request that the end user manually connect to the object. Co
mmon reasons for this error are that a password is needed or that a floppy needs
to be mounted.
E_CLASSNOTFOUND
The ClassNotFound key indicates a moniker whose class could not be found (the serv
er for the object identified by this moniker could not be located). If this key
is used for an COM compound-document object, the caller can use IMoniker::BindTo
Storage to bind to the object, and then try to carry out a Treat As... or Conver
t To... operation to associate the object with a different server. If this is su
ccessful, the caller can retry the binding operation.
If you re a moniker client with detailed knowledge of the implementation of the mo
niker you re using, you can also call this method to pass private information to t
hat implementation.
You can define new strings as keys for storing pointers. By convention, you shou
ld use key names that begin with the string form of the CLSID of the moniker cla
ss (see the StringFromCLSID function).
If the pszKey parameter matches the name of an existing key in the bind context s
table, the new object replaces the existing object in the table.
When you register an object using this method, the object is not released until
one of the following occurs:
· It is replaced in the table by another object with the same key.
· It is removed from the table by a call to IBindCtx::RevokeObjectParam.
· The bind context is released. All registered objects are released when the bind
context is released.
See Also
IBindCtx::GetObjectParam, IBindCtx::RevokeObjectParam, IBindCtx::EnumObjectParam

15.2.1.8 IBindCtx::ReleaseBoundObjects
Releases all pointers to all objects that were previously registered by calls to
IBindCtx::RegisterObjectBound.
HRESULT ReleaseBoundObjects(void);
Return Value
S_OK
The objects were released successfully.
Remarks
You rarely call this method directly. The system s IBindCtx implementation calls t
his method when the pointer to the IBindCtx interface on the bind context is rel
eased (the bind context is released). If a bind context is not released, all of
the registered objects remain active.
If the same object has been registered more than once, this method calls the IUn
known::Release method on the object the number of times it was registered.
See Also
IBindCtx::RegisterObjectBound

15.2.1.9 IBindCtx::RevokeObjectBound
Releases the IUnknown pointer to the specified object and removes that pointer f
rom the bind context s internal list of pointers. This undoes a previous call to I
BindCtx::RegisterObjectBound for the same object.
HRESULT RevokeObjectBound(
IUnknown *punk //Pointer to the object whose registration is being revo
ked
);
Parameter
punk
[in] Pointer to the IUnknown interface on the object to be released.
Return Values
S_OK
The object was released successfully.
MK_E_NOTBOUND
Indicates that punk was not previously registered with a call to IBindCtx::Regis
terObjectBound.
Remarks
You rarely call this method. This method is included for completeness.
See Also
IBindCtx::RegisterObjectBound

15.2.1.10 IBindCtx::RevokeObjectParam
Removes the specified key and its associated pointer from the bind context s strin
g-keyed table of objects. The key must have previously been inserted into the ta
ble with a call to IBindCtx::RegisterObjectParam.
HRESULT RevokeObjectParam(
LPOLESTR pszKey //Pointer to the key to be revoked
);
Parameter
pszKey
[in] Pointer to a zero-terminated wide character string (two bytes per character
) containing the key to remove. Key string comparison is case-sensitive.
Return Values
S_OK
The specified key was successfully removed from the table.
S_FALSE
No object has been registered with the specified key.
Remarks
A bind context maintains a table of interface pointers, each associated with a s
tring key. This enables communication between a moniker implementation and the c
aller that initiated the binding operation. One party can store an interface poi
nter under a string known to both parties so that the other party can later retr
ieve it from the bind context.
This method is used to remove an entry from the table. If the specified key is f
ound, the bind context also releases its reference to the object.
See Also
IBindCtx::RegisterObjectParam

15.2.1.11 IBindCtx::SetBindOptions
Specifies new values for the binding parameters stored in the bind context. Subs
equent binding operations can call IBindCtx::GetBindOptions to retrieve the para
meters.
HRESULT SetBindOptions(

BIND_OPTS *pbindopts //Pointer to a structure


);
Parameter
pbindopts
[in] Pointer to a BIND_OPTS2 or a BIND_OPTS structure containing the binding par
ameters.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The parameters were stored successfully.
Remarks
A bind context contains a block of parameters, stored in a BIND_OPTS2 or a BIND_
OPTS structure, that are common to most IMoniker operations. These parameters do
not change as the operation moves from piece to piece of a composite moniker.
15.2.1.11.1.1.1 Notes to Callers
This method can be called by moniker clients (those who use monikers to acquire
interface pointers to objects).
When you first create a bind context using the CreateBindCtx function, the field
s of the BIND_OPTS structure are initialized to the following values:
cbStruct = sizeof(BINDOPTS);
grfFlags = 0;
grfMode = STGM_READWRITE;
dwTickCountDeadline = 0;
You can use the IBindCtx::SetBindOptions method to modify these values before us
ing the bind context, if you want values other than the defaults. See BIND_OPTS
for more information.
SetBindOptions only copies the struct members of BIND_OPTS2, but not the COSERVE
RINFO structure and the pointers it contains. Callers may not free any of these
pointers until the bind context is released.
See Also
Bind_OPTS2, IBindCtx::GetBindOptions
15.2.2 IClassActivator
Specifies a method that retrieves a class object.
15.2.2.1.1 When to Implement
No implementation of a moniker or an object supporting IClassActivator currently
exists within the system, however future versions of the operating system may c
ontain such implementations. Implement the IClassActivator interface if you are
writing a custom moniker type which you want to be able to compose to the left o
f a class moniker or any other moniker that supports binding to IClassActivator.
15.2.2.1.2 When to Use
Use IClassActivator if you write a custom moniker class that should behave simil
arly to class monikers when composed to the right of other monikers. File monike
rs also use this interface.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IClassActivator Method Description
GetClassObject Retrieves a class object.
15.2.2.2 IClassActivator::GetClassObject
Retrieves a class object. Similar to CoGetClassObject.
HRESULT GetClassObject(
REFCLSID *pClassID, //CLSID of class object desired
DWORD dwClsContext, //Values from CLSCTX
LCID locale, //LCID constant
REFIID riid, //IID of requested interface
void ** ppv //Indirect pointer to requested interface
);
Parameter
pClassID
[in] Points to the CLSID that Identifies the class whose class object is to be r
etrieved.
dwClsContext
[in] The context in which the class is expected to run; values are taken from th
e CLSCTX enumeration.
locale
[in] Any LCID constant as defined in WINNLS.H.
riid
[in] IID of the interface on the object to which a pointer is desired.
ppv
[out] On successful return, an indirect pointer to the requested interface.
Return Values
This method supports the standard return value E_FAIL, as well as the following:
S_OK
The CLSID was successfully returned.
Remarks
This method returns the class identifier (CLSID) for an object, used in later op
erations to load object-specific code into the caller s context.
See Also
CoGetClassObject
15.2.3 IEnumMoniker
The IEnumMoniker interface is used to enumerate the components of a moniker or t
o enumerate the monikers in a table of monikers. IEnumMoniker has the same metho
ds as all enumerator interfaces: Next, Skip, Reset, and Clone. For general infor
mation on these methods, refer to IEnumXXXX.
15.2.3.1.1 When to Implement
You need to implement IEnumMoniker if you are writing a new type of moniker and
your monikers have an internal structure that can be enumerated. Your implementa
tion of IMoniker::Enum must return an enumerator that implements IEnumMoniker an
d can enumerate your moniker s components. If your moniker has no structure that c
an be enumerated, your IMoniker::Enum method can simply return a NULL pointer.
15.2.3.1.2 When to Use
Call the methods of the IEnumMoniker interface if you need to enumerate the comp
onents of a composite moniker, or to enumerate the monikers in a table.
COM defines two interfaces that supply an IEnumMoniker interface pointer:
· The IMoniker::Enum method gets a pointer to an IEnumMoniker implementation that
can enumerate forwards or backwards through the components of the moniker. For a
description of how two of the system-supplied types of monikers enumerate their
components, see IMoniker File Moniker Implementation and IMoniker Generic Compo
site Moniker Implementation.
· The IRunningObjectTable::EnumRunning method returns a pointer to an IEnumMoniker
implementation that can enumerate the monikers registered in a Running Object T
able.
The prototypes of the methods are as follows:
HRESULT Next(
ULONG celt,
IMoniker * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumMoniker ** ppenum
);
See Also
IEnumXXXX, IMoniker::Enum, IRunningObjectTable::EnumRunning

15.2.4 IEnumString
IEnumString is defined to enumerate strings. LPWSTR is the type that indicates a
pointer to a zero-terminated string of wide, i.e., Unicode, characters. IEnumSt
ring has the same methods as all enumerator interfaces: Next, Skip, Reset, and C
lone. For general information on these methods, refer to IEnumXXXX.
15.2.4.1.1 When to Implement
It is usually not necessary to implement this interface unless you have use for
a custom string enumerator. A system implementation in the bind context object o
n which is the IBindCtx interface also contains an implementation of IEnumString
. IBindCtx::EnumObjectParam returns a pointer to this IEnumString interface on a
n enumerator that can return the keys of the bind context s string-keyed table of
pointers.
15.2.4.1.2 When to Use
Call the methods of IEnumString to enumerate through a set of strings.
The prototypes of the member functions are as follows:
HRESULT Next(
ULONG celt,
LPOLESTR * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumString ** ppenum
);

15.2.5 IEnumUnknown
This enumerator enumerates objects with the IUnknown interface. It can be used t
o enumerate through the objects in a component containing multiple objects. IEnu
mUnknown has the same methods as all enumerator interfaces: Next, Skip, Reset, a
nd Clone. For general information on these methods, refer to IEnumXXXX.
15.2.5.1.1 When to Implement
You can implement this whenever you want a caller to be able to enumerate the ob
jects contained in another object. You get a pointer to IEnumUnknown through a c
all to IOleContainer::EnumObjects.
15.2.5.1.2 When to Implement
Call the methods of IEnumUnknown to enumerate the objects in a compound document
, when you get a pointer to the interface on the enumerator through a call to IO
leContainer::EnumObjects.
The prototypes of the methods are as follows:
HRESULT Next(
ULONG celt,
IUnknown ** rgelt,
ULONG * pceltFetched
);
HRESULT Skip(

ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumUnknown ** ppenum
);

15.2.6 IMoniker
The IMoniker interface contains methods that allow you to use a moniker object,
which contains information that uniquely identifies a COM object. An object that
has a pointer to the moniker object s IMoniker interface can locate, activate, an
d get access to the identified object without having any other specific informat
ion on where the object is actually located in a distributed system.
Like a path to a file in a file system, a moniker contains information that allo
ws a COM object to be located and activated. Monikers can identify any type of C
OM object, from a document object stored in a file to a selection within an embe
dded object. COM provides a set of moniker classes that allow you to create moni
ker objects identifying the objects most commonly found in the system. For examp
le, there might be an object representing a range of cells in a spreadsheet whic
h is itself embedded in a text document stored in a file. In a distributed syste
m, this object s moniker would identify the location of the object s system, the fil
e s physical location on that system, the storage of the embedded object within th
at file, and, finally, the location of the range of cells within the embedded ob
ject.
A moniker object supports the IMoniker interface, which is derived from the IPer
sistStream interface, and uniquely identifies a single object in the system. Onc
e an object providing a moniker has created the moniker object, this information
cannot be changed within that object. If the moniker provider changes the infor
mation, it can only do so by creating a new moniker object, which would then uni
quely identify the object in question.
Monikers have two important capabilites:
· Monikers can be saved to a persistent storage. When a moniker is loaded back int
o memory, it still identifies the same object.
· Monikers support an operation called binding, which is the process of locating th
object named by the moniker, activating it (loading it into memory) if it is no
t already active, and returning a pointer to a requested interface on that objec
t.
Monikers are used as the basis for linking in COM. A linked object contains a mo
niker that identifies its source. When the user activates the linked object to e
dit it, the moniker is bound; this loads the link source into memory.
15.2.6.1.1 When to Implement
Implement IMoniker only if you are writing a new moniker class. This is necessar
y only if you need to identify objects that cannot be identified using one of th
e COM-supplied moniker classes described below.
The COM-supplied moniker classes are sufficient for most situations. Before cons
idering writing your own moniker class, you should make sure that your requireme
nts cannot be satisified by these classes.
If you decide you need to write your own implementation of IMoniker, you must al
so implement the IROTData interface on your moniker class. This interface allows
your monikers to be registered with the Running Object Table (ROT).
15.2.6.1.2 When to Use
Two kinds of objects call the methods of IMoniker:
· A component that contains one or more objects to be identified with a moniker an
d must provide the moniker to other objects
· A client object that must bind to the object identified by the moniker
The component providing a moniker makes it accessible to other objects. It is im
portant to understand the differences between the various system-supplied monike
r classes to know which are appropriate for a given object. COM also provides fu
nctions for creating monikers using the COM-supplied moniker classes.
· File monikers based on a path in the file system. File monikers can be used to i
dentify objects that are saved as files. The associated creation function is Cre
ateFileMoniker.
· Item monikers based on a string that identifies an object in a container. Item m
onikers can be used to identify objects smaller than a file, such as embedded ob
jects in a compound document and pseudo-objects (like a range of cells in a spre
adsheet). The associated creation function is CreateItemMoniker.
· Generic composite monikers consists of two or more monikers of arbitrary type th
at have been composed together. Generic composite monikers allow monikers of dif
ferent classes to be used in combination. The associated creation function is Cr
eateGenericComposite.
· Anti-monikers the inverse of file, item, or pointer monikers. Anti-monikers are u
sed primarily for constructing relative monikers, which are analogous to relativ
e path (such as ..\backup\report.old ), and which specify a location of an object r
elative to the location of another object). The associated creation function is
CreateAntiMoniker.
· Pointer monikers a non-persistent moniker that wraps an interface pointer to an
object loaded in memory. Whereas most monikers identify objects that can be save
d to persistent storage, pointer monikers identify objects that cannot. The asso
ciated creation function is CreatePointerMoniker.
A moniker provider must also implement other interfaces to allow the monikers it
hands out to be bound. COM objects that commonly provide monikers are link sour
ces. These include server applications that support linking and container applic
ations that support linking to their embedded objects.
Binding to an object means that a client uses a moniker to locate the object, ac
tivate it when necessary, and get a pointer to one of the active object s interfac
es. The client of the moniker does not need to be aware of the class of the moni
ker it must just get a pointer to the correct moniker s IMoniker interface. Monike
rs are used most often in this way by container applications that allow their do
cuments to contain linked objects. However, link containers rarely call IMoniker
methods directly
· Class monikers these represent an object class. Class monikers bind to the class
object of the class for which they are created. The associated creation functio
n is CreateClassComposite.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IPersist Methods Description
GetClassID Returns the object s CLSID.
IPersistStream Methods Description
IsDirty Checks whether object has been modified.
Load Loads the object from a stream.
Save Saves the object to a stream.
GetSizeMax Returns the buffer size needed to save the object.
IMoniker Methods Description
BindToObject Binds to the object named by the moniker.
BindToStorage Binds to the object s storage.
Reduce Reduces the moniker to simplest form.
ComposeWith Composes with another moniker.
Enum Enumerates component monikers.
IsEqual Compares with another moniker.
Hash Returns a hash value.
IsRunning Checks whether object is running.
GetTimeOfLastChange Returns time the object was last changed.
Inverse Returns the inverse of the moniker.
CommonPrefixWith Finds the prefix that the moniker has in common with ano
ther moniker.
RelativePathTo Constructs a relative moniker between the moniker and another.
GetDisplayName Returns the display name.
ParseDisplayName Converts a display name into a moniker.
IsSystemMoniker Checks whether moniker is one of the system-supplied types.

See Also
BindMoniker, CreateBindCtx, CreateGenericComposite, CreateFileMoniker, CreateIte
mMoniker, CreateAntiMoniker, CreatePointerMoniker, IPersistStream, IROTData, IMo
niker AntiMoniker Implementation, IMoniker File Moniker Implementation, IMoniker Item
Moniker Implementation, IMoniker Generic Composite Moniker Implementation, IMonike
r Pointer Moniker Implementation

15.2.6.2 IMoniker::BindToObject
Uses the moniker to bind to the object it identifies. The binding process involv
es finding the object, putting it into the running state if necessary, and suppl
ying the caller with a pointer to a specified interface on the identified object
.
HRESULT BindToObject(
IBindCtx *pbc, //Pointer to bind context object to be used
IMoniker *pmkToLeft, //Pointer to moniker that precedes this one in t
he composite
REFIID riidResult, //IID of interface pointer requested
void **ppvResult //Indirect pointer to the specified interface on the obj
ect
);
Parameters
pbc
[in] Pointer to the IBindCtx interface on the bind context object, which is used
in this binding operation. The bind context caches objects bound during the bin
ding process, contains parameters that apply to all operations using the bind co
ntext, and provides the means by which the moniker implementation should retriev
e information about its environment.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to th
e left of this moniker. This parameter is primarily used by moniker implementers
to enable cooperation between the various components of a composite moniker. Mo
niker clients should pass NULL.
riidResult
[in] IID of the interface the client wishes to use to communicate with the objec
t that the moniker identifies.
ppvResult
[out] When successful, indirect pointer to the interface specified in riidResult
on the object the moniker identifies. In this case, the implementation must cal
l IUnknown::AddRef on this pointer. It is the caller s responsibility to release t
he object with a call to IUnknown::Release. If an error occurs, ppvResult should
return NULL.
Return Values
The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, a
s well as the following:
S_OK
The binding operation was successful.
MK_E_NOOBJECT
The object identified by this moniker, or some object identified by the composit
e moniker of which this moniker is a part, could not be found.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by
the bind context s BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The binding operation requires assistance from the end user. The most common rea
sons for returning this value are that a password is needed or that a floppy nee
ds to be mounted. When this value is returned, retrieve the moniker that caused
the error with a call to IBindCtx::GetObjectParam with the key ConnectManually . Yo
u can then call IMoniker::GetDisplayName to get the display name, display a dial
og box that communicates the desired information, such as instructions to mount
a floppy or a request for a password, and then retry the binding operation.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
An intermediate object was found but it did not support an interface required to
complete the binding operation. For example, an item moniker returns this value
if its container does not support the IOleItemContainer interface.
STG_E_ACCESSDENIED
Unable to access the storage object.
IOleItemContainer::GetObject errors
If the moniker used to bind to an object contains an item moniker, errors associ
ated with this method can be returned.
Remarks
IMoniker::BindToObject implements the primary function of a moniker, which is to
locate the object identified by the moniker and return a pointer to one of its
interfaces.
15.2.6.2.1 Notes to Callers
If you are using a moniker as a persistent connection between two objects, you a
ctivate the connection by calling IMoniker::BindToObject.
You typically call IMoniker::BindToObject during the following process:
1. Create a bind context object with a call to the CreateBindCtx fu
nction.
2. Call IMoniker::BindToObject using the moniker, retrieving a poin
ter to a desired interface on the identified object.
3. Release the bind context.
4. Through the acquired interface pointer, perform the desired oper
ations on the object.
5. When finished with the object, release the object s interface poin
ter.
The following code fragment illustrates these steps:
// pMnk is an IMoniker * that points to a previously acquired moniker
// ICellRange is a custom interface designed for an object that is a
// range of spreadsheet cells
ICellRange *pCellRange;
IBindCtx *pbc;
CreateBindCtx( 0, &pbc );
pMnk->BindToObject( pbc, NULL, IID_ICellRange, &pCellRange );
pbc->Release();
// pCellRange now points to the object; safe to use pCellRange
pCellRange->Release();
You can also use the BindMoniker function when you only intend one binding opera
tion and don t need to retain the bind context object. This helper function encaps
ulates the creation of the bind context, calling IMoniker::BindToObject, and rel
easing the bind context.
COM containers that support links to objects use monikers to locate and get acce
ss to the linked object, but typically do not call IMoniker::BindToObject direct
ly
15.2.6.2.2 Notes to Implementers
What your implementation does depends on whether you expect your moniker to have
a prefix, that is, whether you expect the pmkToLeft parameter to be NULL or not
. For example, an item moniker, which identifies an object within a container, e
xpects that pmkToLeft identifies the container. An item moniker consequently use
s pmkToLeft to request services from that container. If you expect your moniker
to have a prefix, you should use the pmkToLeft parameter (for instance, calling
IMoniker::BindToObject on it) to request services from the object it identifies.
If you expect your moniker to have no prefix, your IMoniker::BindToObject implem
entation should first check the Running Object Table (ROT) to see if the object
is already running. To acquire a pointer to the ROT, your implementation should
call IBindCtx::GetRunningObjectTable on the pbc parameter. You can then call the
IRunningObjectTable::GetObject method to see if the current moniker has been re
gistered in the ROT. If so, you can immediately call IUnknown::QueryInterface to
get a pointer to the interface requested by the caller.
When your IMoniker::BindToObject implementation binds to some object, it should
use the pbc parameter to call IBindCtx::RegisterObjectBound to store a reference
to the bound object in the bind context. This ensures that the bound object rem
ains running until the bind context is released, which can avoid the expense of
having a subsequent binding operation load it again later.
If the bind context s BIND_OPTS structure specifies the BINDFLAGS_JUSTTESTEXISTENC
E flag, your implementation has the option of returning NULL in ppvResult (altho
ugh you can also ignore the flag and perform the complete binding operation).
See Also
BindMoniker, IMoniker::BindToStorage

15.2.6.3 IMoniker::BindToStorage
Retrieves an interface pointer to the storage that contains the object identifie
d by the moniker. Unlike the IMoniker::BindToObject method, this method does not
activate the object identified by the moniker.
HRESULT BindToStorage(
IBindCtx *pbc, //Pointer to bind context to be used
IMoniker *pmkToLeft, //Pointer to moniker to the left of this one in
the composite
REFIID riid, //Reference to the identifier of the storage interface r
equested
void **ppvObj //Indirect pointer to interface on storage object contai
ning the identified object
);
Parameters
pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used dur
ing this binding operation. The bind context caches objects bound during the bin
ding process, contains parameters that apply to all operations using the bind co
ntext, and provides the means by which the moniker implementation should retriev
e information about its environment. For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to th
e left of this moniker. This parameter is primarily used by moniker implementers
to enable cooperation between the various components of a composite moniker. Mo
niker clients should pass NULL.
riid
[in] Reference to the identifier of the storage interface requested, whose point
er will be returned in ppvObj. Storage interfaces commonly requested include ISt
orage, IStream, and ILockBytes.
ppvObj
[out] Pointer to the interface identified by riid on the storage of the object i
dentified by the moniker. If ppvObj is non-NULL, the implementation must call IU
nknown::AddRef on the parameter; it is the caller s responsibility to call IUnknow
n::Release. If an error occurs, ppvObj is set to NULL.
Return Values
The method supports the standard return value E_OUTOFMEMORY, as well as the foll
owing:
S_OK
The binding operation was successful.
MK_E_NOSTORAGE
The object identified by this moniker does not have its own storage.
MK_E_EXCEEDEDDEADLINE
The operation could not be completed within the time limit specified by the bind
context s BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The operation was unable to connect to the storage, possibly because a network d
evice could not be connected to. For more information, see IMoniker::BindToObjec
t.
MK_E_INTERMEDIATEINTERFACENOTSUPPORTED
An intermediate object was found but it did not support an interface required fo
r an operation. For example, an item moniker returns this value if its container
does not support the IOleItemContainer interface.
STG_E_ACCESSDENIED
Unable to access the storage object.
IOleItemContainer::GetObject errors
Binding to a moniker containing an item moniker can return any of the errors ass
ociated with this function.
Remarks
There is an important difference between the IMoniker::BindToObject and IMoniker
::BindToStorage methods. If, for example, you have a moniker that identifies a s
preadsheet object, calling IMoniker::BindToObject provides access to the spreads
heet object itself, while calling IMoniker::BindToStorage provides access to the
storage object in which the spreadsheet resides.
15.2.6.3.1 Notes to Callers
Although none of the COM moniker classes call this method in their binding opera
tions, it might be appropriate to call it in the implementation of a new moniker
class. You could call this method in an implementation of IMoniker::BindToObjec
t that requires information from the object identified by the pmkToLeft paramete
r and can get it from the persistent storage of the object without activation. F
or example, if your monikers are used to identify objects that can be activated
without activating their containers, you may find this method useful.
A client that can read the storage of the object its moniker identifies could al
so call this method.
15.2.6.3.2 Notes to Implementers
Your implementation should locate the persistent storage for the object identifi
ed by the current moniker and return the desired interface pointer. Some types o
f monikers represent pseudo-objects, which are objects that do not have their ow
n persistent storage. Such objects comprise some portion of the internal state o
f its container; as, for example, a range of cells in a spreadsheet. If your mon
iker class identifies this type of object, your implementation of IMoniker::Bind
ToStorage should return the error MK_E_NOSTORAGE.
If the bind context s BIND_OPTS structure specifies the BINDFLAGS_JUSTTESTEXISTENC
E flag, your implementation has the option of returning NULL in ppvObj (although
it can also ignore the flag and perform the complete binding operation).
See Also
IMoniker::BindToObject

15.2.6.4 IMoniker::CommonPrefixWith
Creates a new moniker based on the common prefix that this moniker (the one comp
rising the data of this moniker object) shares with another moniker.
HRESULT CommonPrefixWith(
IMoniker *pmkOther, //Pointer to moniker to be used for comparison
IMoniker **ppmkPrefix //Indirect pointer to the prefix
);
Parameters
pmkOther
[in] Pointer to the IMoniker interface on another moniker to be compared with th
is one to determine whether there is a common prefix.
ppmkPrefix
[out] When successful, points to the IMoniker pointer to the moniker that is the
common prefix of this moniker and pmkOther. In this case, the implementation mu
st call IUnknown::AddRef on the parameter; it is the caller s responsibility to ca
ll IUnknown::Release. If an error occurs or if there is no common prefix, the im
plementation should set ppmkPrefix to NULL.
Return Values
The method supports the standard return value E_OUTOFMEMORY, as well as the foll
owing:
S_OK
A common prefix exists that is neither this moniker nor pmkOther.
MK_S_NOPREFIX
No common prefix exists.
MK_S_HIM
The entire pmkOther moniker is a prefix of this moniker.
MK_S_US
The two monikers are identical.
MK_S_ME
This moniker is a prefix of the pmkOther moniker.
MK_E_NOTBINDABLE
This method was called on a relative moniker. It is not meaningful to take the c
ommon prefix on a relative moniker.
Remarks
IMoniker::CommonPrefixWith creates a new moniker that consists of the common pre
fixes of the moniker on this moniker object and another moniker. If, for example
, one moniker represents the path c:\projects\secret\art\pict1.bmp and another mon
iker represents the path c:\projects\secret\docs\chap1.txt, the common prefix of t
hese two monikers would be a moniker representing the path c:\projects\secret.
15.2.6.4.1 Notes to Callers
The IMoniker::CommonPrefixWith method is primarily called in the implementation
of the IMoniker::RelativePathTo method. Clients using a moniker to locate an obj
ect rarely need to call this method.
Call this method only if pmkOther and this moniker are both absolute monikers. A
n absolute moniker is either a file moniker or a generic composite whose leftmos
t component is a file moniker that represents an absolute path. Do not call this
method on relative monikers, because it would not produce meaningful results.
15.2.6.4.2 Notes to Implementers
Your implementation should first determine whether pmkOther is a moniker of a cl
ass that you recognize and for which you can provide special handling (for examp
le, if it is of the same class as this moniker). If so, your implementation shou
ld determine the common prefix of the two monikers. Otherwise, it should pass bo
th monikers in a call to the MonikerCommonPrefixWith function, which correctly h
andles the generic case.
See Also
IMoniker::RelativePathTo, MonikerCommonPrefixWith

15.2.6.5 IMoniker::ComposeWith
Combines the current moniker with another moniker, creating a new composite moni
ker.
HRESULT ComposeWith(
IMoniker *pmkRight, //Pointer to moniker to be composed onto this one
BOOL fOnlyIfNotGeneric, //Indicates if generic composition permissible
IMoniker **ppmkComposite //Indirect pointer to the composite
);
Parameters
pmkRight
[in] Pointer to the IMoniker interface on the moniker to compose onto the end of
this moniker.
fOnlyIfNotGeneric
[in] If TRUE, the caller requires a non-generic composition, so the operation sh
ould proceed only if pmkRight is a moniker class that this moniker can compose w
ith in some way other than forming a generic composite. If FALSE, the method can
create a generic composite if necessary.
ppmkComposite
[out] When the call is successful, indirect pointer to the location of the resul
ting composite moniker pointer. In this case, the implementation must call IUnkn
own::AddRef on the parameter; it is the caller s responsibility to call IUnknown::
Release. If an error occurs or if the monikers compose to nothing (e.g., composi
ng an anti-moniker with an item moniker or a file moniker), ppmkComposite should
be set to NULL.
Return Values
The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, a
s well as the following:
S_OK
The monikers were successfully combined.
MK_E_NEEDGENERIC
Indicates that fOnlyIfNotGeneric was TRUE, but the monikers could not be compose
d together without creating a generic composite moniker.
Remarks
Joining two monikers together is called composition. Sometimes two monikers of t
he same class can be combined in what is called non-generic composition. For exa
mple, a file moniker representing an incomplete path and another file moniker re
presenting a relative path can be combined to form a single file moniker represe
nting the complete path. Non-generic composition for a given moniker class can b
e handled only in the implementation of IMoniker::ComposeWith for that moniker c
lass.
Combining two monikers of any class is called generic composition, which can be
accomplished through a call to the CreateGenericComposite function.
Composition of monikers is an associative operation. That is, if A, B, and C are
monikers, then, where Comp() represents the composition operation:
Comp( Comp( A, B ), C )
is always equal to
Comp( A, Comp( B, C ) )
15.2.6.5.1 Notes to Callers
To combine two monikers, you should call IMoniker::ComposeWith rather than calli
ng the CreateGenericComposite function to give the first moniker a chance to per
form a non-generic composition.
An object that provides item monkers to identify its objects would call IMoniker
::ComposeWith to provide a moniker that completely identifies the location of th
e object. This would apply, for example, to a server that supports linking to po
rtions of a document, or a container that supports linking to embedded objects w
ithin its documents. In such a situation, you would do the following:
1. Create an item moniker identifying an object.
2. Get a moniker that identifies the object s container.
3. Call IMoniker::ComposeWith on the moniker identifying the contai
ner, passing the item moniker as the pmkRight parameter.
Most callers of IMoniker::ComposeWith should set the fOnlyIfNotGeneric parameter
to FALSE.
15.2.6.5.2 Notes to Implementers
You can use either non-generic or generic composition to compose the current mon
iker with the moniker that pmkRight points to. If the class of the moniker indic
ated by pmkRight is the same as that of the current moniker, it is possible to u
se the contents of pmkRight to perform a more intelligent non-generic compositio
n.
In writing a new moniker class, you must decide if there are any kinds of monike
rs, whether of your own class or another class, to which you want to give specia
l treatment. If so, implement IMoniker::ComposeWith to check whether pmkRight is
a moniker of the type that should have this treatment. To do this, you can call
the moniker s GetClassID method (derived from the IPersist Interface), or, if you
have defined a moniker object that supports a custom interface, you can call IU
nknown::QueryInterface on the moniker for that interface. An example of special
treatment would be the non-generic composition of an absolute file moniker with
a relative file moniker. The most common case of a special moniker is the invers
e for your moniker class (whatever you return from your implementation of IMonik
er::Inverse).
If pmkRight completely negates the receiver so the resulting composite is empty,
you should pass back NULL in ppmkComposite and return the status code S_OK.
If the pmkRight parameter is not of a class to which you give special treatment,
examine fOnlyIfNotGeneric to determine what to do next. If fOnlyIfNotGeneric is
TRUE, pass back NULL through ppmkComposite and return the status code MK_E_NEED
GENERIC. If fOnlyIfNotGeneric is FALSE, call the CreateGenericComposite function
to perform the composition generically.
See Also
CreateGenericComposite, IMoniker::Inverse

15.2.6.6 IMoniker::Enum
Supplies a pointer to an enumerator that can enumerate the components of a compo
site moniker.
HRESULT Enum(
BOOL fForward, //Specifies direction of enumeration
IEnumMoniker **ppenumMoniker //Indirect pointer to the IEnumMoniker p
ointer
);
Parameters
fForward
[in] If TRUE, enumerates the monikers from left to right. If FALSE, enumerates f
rom right to left.
ppenumMoniker
[out] When successful, indirect pointer to an IEnumMoniker enumerator on this mo
niker. In this case, the implementation must call IUnknown::AddRef on the parame
ter. It is the caller s responsibility to call IUnknown::Release. If an error occu
rs or if the moniker has no enumerable components, the implementation sets ppenu
mMoniker to NULL.
Return Values
The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, a
s well as the following:
S_OK
Indicates success. This value is returned even if the moniker does not provide a
n enumerator (if ppenumMoniker equals NULL).
Remarks
IMoniker::Enum must supply an IEnumMoniker pointer to an enumerator that can enu
merate the components of a moniker. For example, the implementation of the IMoni
ker::Enum method for a generic composite moniker creates an enumerator that can
determine the individual monikers that make up the composite, while the IMoniker
::Enum method for a file moniker creates an enumerator that returns monikers rep
resenting each of the components in the path.
15.2.6.6.1 Notes to Callers
Call this method to examine the components that make up a composite moniker.
15.2.6.6.2 Notes to Implementers
If the new moniker class has no discernible internal structure, your implementat
ion of this method can simply return S_OK and set ppenumMoniker to NULL.
See Also
IEnumXXXX

15.2.6.7 IMoniker::GetDisplayName
Gets the display name , which is a user-readable representation of this moniker.
HRESULT GetDisplayName(
IBindCtx *pbc, //Pointer to bind context to be used
IMoniker *pmkToLeft, //Pointer to moniker to the left in the composit
e
LPOLESTR *ppszDisplayName //Indirect pointer to the display name
);
Parameters
pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this op
eration. The bind context caches objects bound during the binding process, conta
ins parameters that apply to all operations using the bind context, and provides
the means by which the moniker implementation should retrieve information about
its environment. For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to th
e left of this moniker. This parameter is primarily used by moniker implementers
to enable cooperation between the various components of a composite moniker. Mo
niker clients should pass NULL.
ppszDisplayName
[out] When successful, indirect pointer to a zero-terminated wide character stri
ng (two bytes per character) containing the display name of this moniker. The im
plementation must use IMalloc::Alloc to allocate the string returned in ppszDisp
layName, and the caller is responsible for calling IMalloc::Free to free it. Bot
h the caller and and the one called use the COM task allocator returned by CoGet
Malloc. If an error occurs, ppszDisplayName should be set to NULL.
Return Values
The method supports the standard return value E_OUTOFMEMORY
, as well as the following:
S_OK
The display name was successfully supplied.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by
the bind context s BIND_OPTS structure.
E_NOTIMPL
There is no display name.
Remarks
IMoniker::GetDisplayName provides a string that is a displayable representation
of the moniker. A display name is not a complete representation of a moniker s int
ernal state; it is simply a form that can be read by users. As a result, it is p
ossible (though rare) for two different monikers to have the same display name.
While there is no guarantee that the display name of a moniker can be parsed bac
k into that moniker when calling the MkParseDisplayName function with it, failur
e to do so is rare.
As examples, the file moniker implementation of this method supplies the path th
e moniker represents, and an item moniker s display name is the string identifying
the item that is contained in the moniker.
15.2.6.7.1 Notes to Callers
It is possible that retrieving a moniker s display name may be an expensive operat
ion. For efficiency, you may want to cache the results of the first successful c
all to IMoniker::GetDisplayName, rather than making repeated calls.
15.2.6.7.2 Notes to Implementers
If you are writing a moniker class in which the display name does not change, si
mply cache the display name and supply the cached name when requested. If the di
splay name can change over time, getting the current display name might mean tha
t the moniker has to access the object s storage or bind to the object, either of
which can be expensive operations. If this is the case, your implementation of I
Moniker::GetDisplayName should return MK_E_EXCEEDEDDEADLINE if the name cannot b
e retrieved by the time specified in the bind context s BIND_OPTS structure.
A moniker that is intended to be part of a generic composite moniker should incl
ude any preceding delimiter (such as \ ) as part of its display name. For example,
the display name returned by an item moniker includes the delimiter specified wh
en it was created with the CreateItemMoniker function. The display name for a fi
le moniker does not include a delimiter because file monikers are always expecte
d to be the leftmost component of a composite.
See Also
IMoniker::ParseDisplayName, MkParseDisplayName

15.2.6.8 IMoniker::GetTimeOfLastChange
Provides a number representing the time the object identified by this moniker wa
s last changed. To be precise, the time returned is the earliest time COM can id
entify after which no change has occurred, so this time may be later than the ti
me of the last change to the object.
HRESULT GetTimeOfLastChange(
IBindCtx *pbc, //Bind context to be used
IMoniker *pmkToLeft, //Moniker to the left in the composite
FILETIME *pFileTime //Receives the time of last change
);
Parameters
pbc
[in] Pointer to the bind context to be used in this binding operation. The bind
context caches objects bound during the binding process, contains parameters tha
t apply to all operations using the bind context, and provides the means by whic
h the moniker implementation should retrieve information about its environment.
For more information, see IBindCtx.
pmkToLeft
[in] If the moniker is part of a composite moniker, pointer to the moniker to th
e left of this moniker. This parameter is primarily used by moniker Implementers
to enable cooperation between the various components of a composite moniker. Mo
niker clients should pass NULL.
pFileTime
[out] Pointer to the FILETIME structure receiving the time of last change. A val
ue of {0xFFFFFFFF,0x7FFFFFFF} indicates an error (for example, exceeded time lim
it, information not available).
Return Values
The method supports the standard return value E_UNEXPECTED, as well as the follo
wing:
S_OK
The method successfully returned a time.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by
the bind context s BIND_OPTS structure.
MK_E_CONNECTMANUALLY
The operation was unable to connect to the storage for this object, possibly bec
ause a network device could not be connected to. For more information, see IMoni
ker::BindToObject.
MK_E_UNAVAILABLE
The time of the change is unavailable, and will not be available no matter what
deadline is used.
Remarks
15.2.6.8.1 Notes to Callers
If you re caching information returned by the object identified by the moniker, yo
u may want to ensure that your information is up-to-date. To do so, you would ca
ll IMoniker::GetTimeOfLastChange and compare the time returned with the time you
last retrieved information from the object.
For the monikers stored within linked objects, IMoniker::GetTimeOfLastChange is
primarily called by the default handler s implementation of IOleObject::IsUpToDate
. Container applications call IOleObject::IsUpToDate to determine if a linked ob
ject (or an embedded object containing linked objects) is up-to-date without act
ually binding to the object. This enables an application to determine quickly wh
ich linked objects require updating when the end user opens a document. The appl
ication can then bind only those linked objects that need updating (after prompt
ing the end user to determine whether they should be updated), instead of bindin
g every linked object in the document.
15.2.6.8.2 Notes to Implementers
It is important to perform this operation quickly because, for linked objects, t
his method is called when a user first opens a compound document. Consequently,
your IMoniker::GetTimeOfLastChange implementation should not bind to any objects
. In addition, your implementation should check the deadline parameter in the bi
nd context and return MK_E_EXCEEDEDDEADLINE if the operation cannot be completed
by the specified time.
There are a number of strategies you can use in your implementations:
· For many types of monikers, the pmkToLeft parameter identifies the container of
the object identified by this moniker. If this is true of your moniker class, yo
u can simply call IMoniker::GetTimeOfLastChange on the pmkToLeft parameter, sinc
e an object cannot have changed at a date later than its container.
· You can get a pointer to the Running Object Table (ROT) by calling IBindCtx::Get
RunningObjectTable on the pbc parameter, and then calling IRunningObjectTable::G
etTimeOfLastChange, since the ROT generally records the time of last change.
· You can get the storage associated with this moniker (or the pmkToLeft moniker)
and return the storage s last modification time with a call to IStorage::Stat.
See Also
IBindCtx::GetRunningObjectTable, IRunningObjectTable::GetTimeOfLastChange

15.2.6.9 IMoniker::Hash
Calculates a 32-bit integer using the internal state of the moniker.
HRESULT Hash(
DWORD *pdwHash //Pointer to hash value
);
Parameter
pdwHash
[out] Pointer to the hash value.
Return Value
S_OK
Successfully received a 32-bit integer hash value.
Remarks
15.2.6.9.1 Notes to Callers
You can use the value returned by this method to maintain a hash table of monike
rs. The hash value determines a hash bucket in the table. To search such a table
for a specified moniker, calculate its hash value and then compare it to the mo
nikers in that hash bucket using IMoniker::IsEqual.
15.2.6.9.2 Notes to Implementers
The hash value must be constant for the lifetime of the moniker. Two monikers th
at compare as equal using IMoniker::IsEqual must hash to the same value.
Marshaling and then unmarshaling a moniker should have no effect on its hash val
ue. Consequently, your implementation of IMoniker::Hash should rely only on the
internal state of the moniker, not on its memory address.
See Also
IMoniker::IsEqual

15.2.6.10 IMoniker::Inverse
Provides a moniker that, when composed to the right of this moniker or one of si
milar structure, will destroy it (the moniker will compose to nothing).
HRESULT Inverse(
IMoniker **ppmk //Indirect pointer to the inverse of the moniker
);
Parameter
ppmk
[out] When successful, indirect pointer to the IMoniker interface on a moniker t
hat is the inverse of this moniker. In this case, the implementation must call I
Unknown::AddRef on the parameter. It is the caller s responsibility to call IUnkno
wn::Release. If an error occurs, the implementation should set ppmk to NULL.
Return Values
The method supports the standard return value E_OUTOFMEMORY, as well as the foll
owing:
S_OK
The inverse moniker has been returned successfully.
MK_E_NOINVERSE
The moniker class does not have an inverse.
Remarks
The inverse of a moniker is analogous to the .. directory in MS-DOS file systems;
the .. directory acts as the inverse to any other directory name, because appendin
g .. to a directory name results in an empty path. In the same way, the inverse of
a moniker typically is also the inverse of all monikers in the same class. Howe
ver, it is not necessarily the inverse of a moniker of a different class.
The inverse of a composite moniker is a composite consisting of the inverses of
the components of the original moniker, arranged in reverse order. For example,
if the inverse of A is Inv( A ) and the composite of A, B, and C is Comp( A, B,
C ), then
Inv( Comp( A, B, C ) )
is equal to
Comp( Inv( C ), Inv( B ), Inv( A ) ).
Not all monikers have inverses. Most monikers that are themselves inverses, such
as anti-monikers, do not have inverses. Monikers that have no inverse cannot ha
ve relative monikers formed from inside the objects they identify to other objec
ts outside.
15.2.6.10.1 Notes to Callers
An object that is using a moniker to locate another object usually does not know
the class of the moniker it is using. To get the inverse of a moniker, you shou
ld always call IMoniker::Inverse rather than the CreateAntiMoniker function, bec
ause you cannot be certain that the moniker you re using considers an anti-moniker
to be its inverse.
The IMoniker::Inverse method is also called by the implementation of the IMonike
r::RelativePathTo method, to assist in constructing a relative moniker.
15.2.6.10.2 Notes to Implementers
If your monikers have no internal structure, you can call the CreateAntiMoniker
function in to get an anti-moniker in your implementation of IMoniker::Inverse.
In your implementation of IMoniker::ComposeWith, you need to check for the inver
se you supply in the implementation of IMoniker::Inverse.
See Also
CreateAntiMoniker, IMoniker::ComposeWith, IMoniker::RelativePathTo

15.2.6.11 IMoniker::IsEqual
Compares this moniker with a specified moniker and indicates whether they are id
entical.
HRESULT IsEqual(
IMoniker *pmkOtherMoniker //Pointer to moniker to be used for comparison
);
Parameter
pmkOtherMoniker
[in] Pointer to the IMoniker interface on the moniker to be used for comparison
with this one (the one from which this method is called).
Return Values
S_OK
The two monikers are identical.
S_FALSE
The two monikers are not identical.
Remarks
Previous implementations of the Running Object Table (ROT) called this method. T
he current implementation of the ROT uses the IROTData interface instead.
15.2.6.11.1 Notes to Callers
Call this method to determine if two monikers are identical or not. Note that th
e reduced form of a moniker is considered different from the unreduced form. You
should call the IMoniker::Reduce method before calling IMoniker::IsEqual, becau
se a reduced moniker is in its most specific form. IMoniker::IsEqual may return
S_FALSE on two monikers before they are reduced, and S_OK after they are reduced
.
15.2.6.11.2 Notes to Implementers
Your implementation should not reduce the current moniker before performing the
comparison. It is the caller s responsibility to call IMoniker::Reduce in order to
compare reduced monikers.
Note that two monikers that compare as equal must hash to the same value using I
Moniker::Hash.
See Also
IMoniker::Reduce, IMoniker::Hash, IROTData

15.2.6.12 IMoniker::IsRunning
Determines whether the object identified by this moniker is currently loaded and
running.
HRESULT IsRunning(
IBindCtx *pbc, //Pointer to bind context to be used
IMoniker *pmkToLeft, //Pointer to moniker to the left in the composit
e
IMoniker *pmkNewlyRunning //Pointer to moniker of a newly running object
);
Parameters
pbc
[in] Pointer to theIBindCtx interface on the bind context to be used in this bin
ding operation. The bind context caches objects bound during the binding process
, contains parameters that apply to all operations using the bind context, and p
rovides the means by which the moniker implementation should retrieve informatio
n about its environment. For more information, see IBindCtx.
pmkToLeft
[in] Pointer to theIMoniker interface on the moniker to the left of this moniker
if this moniker is part of a composite. This parameter is primarily used by mon
iker Implementers to enable cooperation between the various components of a comp
osite moniker; moniker clients can usually pass NULL.
pmkNewlyRunning
[in] Pointer to theIMoniker interface on the moniker most recently added to the
Running Object Table (ROT). This can be NULL. If non-NULL, the implementation ca
n return the results of calling IMoniker::IsEqual on the pmkNewlyRunning paramet
er, passing the current moniker. This parameter is intended to enable IMoniker::
IsRunning implementations that are more efficient than just searching the ROT, b
ut the implementation can choose to ignore pmkNewlyRunning without causing any h
arm.
Return Values
The method supports the standard return value E_UNEXPECTED, as well as the follo
wing:
S_OK
The moniker is running.
S_FALSE
The moniker is not running.
Remarks
15.2.6.12.1 Notes to Callers
If speed is important when you re requesting services from the object identified b
y the moniker, you may want those services only if the object is already running
(because loading an object into the running state may be time-consuming). In su
ch a situation, you d call IMoniker::IsRunning to determine if the object is runni
ng.
For the monikers stored within linked objects, IMoniker::IsRunning is primarily
called by the default handler s implementation of IOleLink::BindIfRunning.
15.2.6.12.2 Notes to Implementers
To get a pointer to the Running Object Table (ROT), your implementation should c
all IBindCtx::GetRunningObjectTable on the pbc parameter. Your implementation ca
n then call IRunningObjectTable::IsRunning to determine whether the object ident
ified by the moniker is running. Note that the object identified by the moniker
must have registered itself with the ROT when it first began running.
See Also
IBindCtx::GetRunningObjectTable, IRunningObjectTable::IsRunning

15.2.6.13 IMoniker::IsSystemMoniker
Indicates whether this moniker is of one of the system-supplied moniker classes.
HRESULT IsSystemMoniker(
DWORD *pdwMksys //Pointer to value from MKSYS enumeration
);
Parameter
pdwMksys
[out] Pointer to an integer that is one of the values from the MKSYS enumeration
, and refers to one of the COM moniker classes. This parameter cannot be NULL.
Return Values
S_OK
The moniker is a system moniker.
S_FALSE
The moniker is not a system moniker.
Remarks
15.2.6.13.1 Notes to Callers
New values of the MKSYS enumeration may be defined in the future; therefore you
should explicitly test for each value you are interested in.
15.2.6.13.2 Notes to Implementers
Your implementation of this method must return MKSYS_NONE. You cannot use this f
unction to identify your own monikers (for example, in your implementation of IM
oniker::ComposeWith). Instead, you should use your moniker s implementation of IPe
rsist::GetClassID or use IUnknown::QueryInterface to test for your own private i
nterface.
See Also
IPersist::GetClassID, MKSYS

15.2.6.14 IMoniker::ParseDisplayName
Reads as many characters of the specified display name as it understands and bui
lds a moniker corresponding to the portion read; this procedure is known as parsi
ng the display name.
HRESULT ParseDisplayName(
IBindCtx *pbc, //Pointer to bind context to be used
IMoniker *pmkToLeft, //Pointer to moniker to the left in the composit
e
LPOLESTR pszDisplayName, //Pointer to display name
ULONG *pchEaten, //Pointer to number of characters consumed
IMoniker **ppmkOut //Indirect pointer to moniker built from display name
);
Parameters
pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this bi
nding operation. The bind context caches objects bound during the binding proces
s, contains parameters that apply to all operations using the bind context, and
provides the means by which the moniker implementation should retrieve informati
on about its environment. For more information, see IBindCtx.
pmkToLeft
[in] Pointer to the IMoniker interface on the moniker that has been built out of
the display name up to this point.
pszDisplayName
[in] Pointer to a zero-terminated string containing the remaining display name t
o be parsed. For Win32 applications, the LPOLESTR type indicates a wide characte
r string (two bytes per character); otherwise, the string has one byte per chara
cter.
pchEaten
[out] Pointer to the number of characters in pszDisplayName that were consumed i
n this step.
ppmkOut
[out] When successful, indirect pointer to the IMoniker interface on the moniker
that was built from pszDisplayName. In this case, the implementation must call
IUnknown::AddRef on the parameter; it is the caller s responsibility to call IUnkn
own::Release. If an error occurs, the implementation sets ppmkOut to NULL.
Return Values
The method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED, a
s well as the following:
S_OK
The parsing operation was completed successfully.
MK_E_SYNTAX
An error in the syntax of the input components (pmkToLeft, this moniker, and psz
DisplayName). For example, a file moniker returns this error if pmkToLeft is non
-NULL, and an item moniker returns it if pmkToLeft is NULL.
IMoniker::BindToObject errors
Parsing display names may cause binding. Thus, any error associated with this fu
nction may be returned.
Remarks
15.2.6.14.1 Notes to Callers
Moniker clients do not typically call IMoniker::ParseDisplayName directly. Inste
ad, they call the MkParseDisplayName function when they want to convert a displa
y name into a moniker (for example, in implementing the Links dialog box for a c
ontainer application, or for implementing a macro language that supports referen
ces to objects outside the document). That function first parses the initial por
tion of the display name itself.
[Need to find out how]
It then calls IMoniker::ParseDisplayName on the moniker it has just created, pas
sing the remainder of the display name and getting a new moniker in return; this
step is repeated until the entire display name has been parsed.
15.2.6.14.2 Notes to Implementers
Your implementation may be able to perform this parsing by itself if your monike
r class is designed to designate only certain kinds of objects. Otherwise, you m
ust get an IParseDisplayName interface pointer for the object identified by the
moniker-so-far (i.e., the composition of pmkToLeft and this moniker) and then re
turn the results of calling IParseDisplayName::ParseDisplayName.
There are different strategies for getting an IParseDisplayName pointer:
· You can try to get the object s CLSID (by calling IPersist::GetClassID on the obje
ct), and then call the CoGetClassObject function, requesting the IParseDisplayNa
me interface on the class factory associated with that CLSID.
· You can try to bind to the object itself to get an IParseDisplayName pointer.
· You can try binding to the object identified by pmkToLeft to get an IOleItemCont
ainer pointer, and then call IOleItemContainer::GetObject to get an IParseDispla
yName pointer for the item.
Any objects that are bound should be registered with the bind context (see IBind
Ctx::RegisterObjectBound) to ensure that they remain running for the duration of
the parsing operation.
See Also
IParseDisplayName, MkParseDisplayName

15.2.6.15 IMoniker::Reduce
Returns a reduced moniker; that is, another moniker that refers to the same obje
ct as this moniker but can be bound with equal or greater efficiency.
HRESULT Reduce(
IBindCtx *pbc, //Pointer to bind context to be used
DWORD dwReduceHowFar, //How much reduction should be done
IMoniker **ppmkToLeft, //Indirect pointer to moniker to the left in the
composite
IMoniker **ppmkReduced //Indirect pointer to the reduced moniker
);
Parameters
pbc to the IBindCtx interface on the bind context to be used in this binding ope
ration. The bind context caches objects bound during the binding process, contai
ns parameters that apply to all operations using the bind context, and provides
the means by which the moniker implementation should retrieve information about
its environment. For more information, see IBindCtx.
dwReduceHowFar
[in] DWORD that specifies how far this moniker should be reduced. This parameter
must be one of the values from the MKRREDUCE enumeration.
ppmkToLeft
[in, out] On entry, indirect pointer to the moniker to the left of this moniker,
if this moniker is part of a composite. This parameter is primarily used by mon
iker Implementers to enable cooperation between the various components of a comp
osite moniker; moniker clients can usually pass NULL.
On return, ppmkToLeft is usually set to NULL, indicating no change in the origin
al moniker to the left. In rare situations ppmkToLeft indicates a moniker, indic
ating that the previous moniker to the left should be disregarded and the monike
r returned through ppmkToLeft is the replacement. In such a situation, the imple
mentation must call IUnknown::Release on the passed-in pointer and call IUnknown
::AddRef on the returned moniker; the caller must release it later. If an error
occurs, the implementation can either leave the parameter unchanged or set it to
NULL.
ppmkReduced
[out] Indirect pointer to the IMoniker interface on the reduced form of this mon
iker, which can be NULL if an error occurs or if this moniker is reduced to noth
ing. If this moniker cannot be reduced, ppmkReduced is simply set to this monike
r and the return value is MK_S_REDUCED_TO_SELF. If ppmkReduced is non-NULL, the
implementation must call IUnknown::AddRef on the parameter; it is the caller s res
ponsibility to call IUnknown::Release. (This is true even if ppmkReduced is set
to this moniker.)
Return Values
The method supports the standard return values E_UNEXPECTED and E_OUTOFMEMORY, a
s well as the following:
S_OK
This moniker was reduced.
MK_S_REDUCED_TO_SELF
This moniker could not be reduced any further, so ppmkReduced indicates this mon
iker.
MK_E_EXCEEDEDDEADLINE
The operation could not be completed within the time limit specified by the bind
context s BIND_OPTS structure.
Remarks
IMoniker::Reduce is intended for the following uses:
· It enables the construction of user-defined macros or aliases as new kinds of mo
niker classes. When reduced, the moniker to which the macro evaluates is returne
d.
· It enables the construction of a kind of moniker that tracks data as it moves ab
out. When reduced, the moniker of the data in its current location is returned.
· On file systems that support an identifier-based method of accessing files which
is independent of file names; a file moniker could be reduced to a moniker whic
h contains one of these identifiers.
The intent of the MKRREDUCE flags passed in the dwReduceHowFar parameter is to p
rovide the ability to programmatically reduce a moniker to a form whose display
name is recognizable to the user. For example, paths in the file system, bookmar
ks in word-processing documents, and range names in spreadsheets are all recogni
zable to users. In contrast, a macro or an alias encapsulated in a moniker are n
ot recognizable to users.
15.2.6.15.1 Notes to Callers
The scenarios described above are not currently implemented by the system-suppli
ed moniker classes.
You should call IMoniker::Reduce before comparing two monikers using the IMonike
r::IsEqual method, because a reduced moniker is in its most specific form. IMoni
ker::IsEqual may return S_FALSE on two monikers before they are reduced and retu
rn S_OK after they are reduced.
15.2.6.15.2 Notes to Implementers
If the current moniker can be reduced, your implementation must not reduce the m
oniker in-place. Instead, it must return a new moniker that represents the reduc
ed state of the current one. This way, the caller still has the option of using
the non-reduced moniker (for example, enumerating its components). Your implemen
tation should reduce the moniker at least as far as is requested.
See Also
IMoniker::IsEqual, MKRREDUCE

15.2.6.16 IMoniker::RelativePathTo
Supplies a moniker that, when composed onto the end of this moniker (or one with
a similar structure), yields the specified moniker.
HRESULT RelativePathTo(
IMoniker *pmkOther, //Pointer to moniker to which a relative path should be
taken
IMoniker **ppmkRelPath //Indirect pointer to the relative moniker
);
Parameters
pmkOther
[in] Pointer to the IMoniker interface on the moniker to which a relative path s
hould be taken.
ppmkRelPath
[out] Iindirect pointer to the IMoniker interface on the relative moniker. When
successful, the implementation must call IUnknown::AddRef on the parameter; it i
s the caller s responsibility to call IUnknown::Release. If an error occurs, the i
mplementation sets ppmkRelPath to NULL.
Return Values
The method supports the standard return values E_OUTOFMEMORY and
E_UNEXPECTED, as well as the following:
S_OK
A meaningful relative path has been returned.
MK_S_HIM
No common prefix is shared by the two monikers and the moniker returned in ppmkR
elPath is pmkOther.
MK_E_NOTBINDABLE
This moniker is a relative moniker, such as an item moniker. This moniker must b
e composed with the moniker of its container before a relative path can be deter
mined.
Remarks
A relative moniker is analogous to a relative path (such as ..\backup ). For exampl
e, suppose you have one moniker that represents the path c:\projects\secret\art\p
ict1.bmp and another moniker that represents the path c:\projects\secret\docs\chap
1.txt. Calling IMoniker::RelativePathTo on the first moniker, passing the second
one as the pmkOther parameter, would create a relative moniker representing the
path ..\docs\chap1.txt.
15.2.6.16.1 Notes to Callers
Moniker clients typically do not need to call IMoniker::RelativePathTo. This met
hod is primarily called by the default handler for linked objects. Linked object
s contain both an absolute and a relative moniker to identify the link source (t
his enables link tracking if the user moves a directory tree containing both the
container and source files). The default handler calls this method to create a
relative moniker from the container document to the link source (that is, it cal
ls IMoniker::RelativePathTo on the moniker identifying the container document, p
assing the moniker identifying the link source as the pmkOther parameter).
If you do call IMoniker::RelativePathTo, call it only on absolute monikers; for
example, a file moniker or a composite moniker whose leftmost component is a fil
e moniker, where the file moniker represents an absolute path. Do not call this
method on relative monikers.
15.2.6.16.2 Notes to Implementers
Your implementation of IMoniker::RelativePathTo should first determine whether p
mkOther is a moniker of a class that you recognize and for which you can provide
special handling (for example, if it is of the same class as this moniker). If
so, your implementation should determine the relative path. Otherwise, it should
pass both monikers in a call to the MonikerRelativePathTo function, which corre
ctly handles the generic case.
The first step in determining a relative path is determining the common prefix o
f this moniker and pmkOther. The next step is to break this moniker and pmkOther
into two parts each, say (P, myTail) and (P, otherTail) respectively, where P i
s the common prefix. The correct relative path is then the inverse of myTail com
posed with otherTail:
Comp( Inv( myTail ), otherTail )
Where Comp() represents the composition operation and Inv() represents the inver
se operation.
Note that for certain types of monikers, you cannot use your IMoniker::Inverse m
ethod to construct the inverse of myTail. For example, a file moniker returns an
anti-moniker as an inverse, while its IMoniker::RelativePathTo method must use
one or more file monikers that each represent the path .. to construct the inverse
of myTail.
See Also
IMoniker::Inverse, IMoniker::CommonPrefixWith, MonikerRelativePathTo

15.2.7 IMoniker - Anti-Moniker Implementation


Anti-monikers are the inverse of the COM implementations of file, item, and poin
ter monikers. That is, an anti-moniker composed to the right of a file moniker,
item moniker, or pointer moniker composes to nothing.
15.2.7.1.1 When To Use
If you re a moniker client, you typically do not need to use anti-monikers. When y
ou need the inverse of a moniker, you should call IMoniker::Inverse. For example
, if you need an inverse to remove the last piece of a composite moniker, use IM
oniker::Enum to enumerate the pieces of the moniker and call IMoniker::Inverse o
n the rightmost piece. You shouldn t use an anti-moniker for this purpose because
you can t be sure that the rightmost piece of a composite considers an anti-monike
r to be its inverse.
The only situation in which you should explicitly use an anti-moniker is if you
are writing a new moniker class and if you have no special requirements for cons
tructing inverses to your monikers. In that situation, you can return anti-monik
ers from your implementation of IMoniker::Inverse. In your implementation of IMo
niker::ComposeWith, you should then annihilate one of your monikers for every an
ti-moniker you encounter.
Remarks
IMoniker::BindToObject
This method is not implemented. It returns E_NOTIMPL.
IMoniker::BindToStorage
This method is not implemented. It returns E_NOTIMPL.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If fOnlyIfNotGeneric is TRUE, this method sets ppmkComposite to NULL moniker and
returns MK_E_NEEDGENERIC; otherwise, the method returns the result of combining
the two monikers into a generic composite. Note that composing a file, item, or
pointer moniker to the right of an anti-moniker produces a generic composite ra
ther than composing to nothing, as would be the case if the order of composition
were reversed.
IMoniker::Enum
This method returns S_OK and sets *ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if both are anti-monikers; otherwise, it returns S_FALS
E.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
This method checks the ROT to see if the object is running.
IMoniker::GetTimeOfLastChange
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::Inverse
This method returns MK_E_NOINVERSE and sets *ppmk to NULL.
IMoniker::CommonPrefixWith
If the other moniker is also an anti-moniker, the method returns MK_S_US and set
s ppmkPrefix to this moniker. Otherwise, the method calls the MonikerCommonPrefi
xWith function. This function correctly handles the case where the other moniker
is a generic composite.
IMoniker::RelativePathTo
This method returns MK_S_HIM and sets *ppmkRelPath to the other moniker.
IMoniker::GetDisplayName
For each anti-moniker contained in this moniker, this method return one instance
of \..
IMoniker::ParseDisplayName
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_ANTIMONIKER.
See Also
CreateAntiMoniker, IMoniker

15.2.8 IMoniker - Class Moniker Implementation


Class monikers are monikers that represent an object class. Class monikers bind
to the class object of the class for which they are created.
Class monikers are most useful in composition with other types of monikers, such
as file monikers or item monikers. Class monikers may also be composed to the r
ight of monikers supporting binding to the IClassActivator interface. This allow
s IClassActivator to provide access to the class object and instances of the cla
ss.
15.2.8.1.1 When to Use
To use class monikers, you must use the CreateClassMoniker function to create th
e monikers.
Remarks
IMoniker::BindToObject
If pmkLeft is NULL, calls CoGetClassObject using the CLSID the class moniker was
initialized with (in CreateClassMoniker or through MkParseDisplayName) and the
CLSCTX of the current pbc (IBindContext).
If pmkLeft is non-NULL, calls pmkLeft->BindToObject for IClassActivator and call
s IClassActivator::GetClassObject with the CLSID it was initialized with and the
CLSCTX and LOCALE parameters from of the current pbc (IBindContext).
This process is very roughly sketched out in the following code:
BIND_OPTS2 bindOpts;
IClassActivator *pActivate;
bindOpts.cbStruct = sizeof(bindOpts);
pbc->GetBindOptions(&bindOpts);
if (NULL == pmkToLeft)
return CoGetClassObject(<clsid>, bindOpts.dwClassContext, NULL, riid, pp
vResult);
pmkToLeft->BindToObject(pbc, NULL, IID_IClassActivator, (void **) &pActivate
);
hr = pActivate->GetClassObject(<clsid>, bindOpts.dwClassContext, bindOpts.lo
cale, iid, ppvResult);
pActivate->Release();
return hr;
IMoniker::BindToStorage
This method forwards to the class moniker s BindToObject.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
Follows the contract, and behaves like an Item Moniker in that it can return E_I
NVALIDARG and MK_E_NEEDGENERIC, etc.
IMoniker::Enum
This method returns S_OK and sets ppenumMoniker to NULL. May return E_INVALIDARG
if ppenumMoniker is an invalid pointer.
IMoniker::IsEqual
This method returns S_OK if pmkOther is a class moniker constructed with the sam
e CLSID information as itself. Otherwise, the method returns S_FALSE. May return
E_INVALIDARG if pmkOther is an invalid pointer.
IMoniker::Hash
This method calculates a hash value for the moniker and returns S_OK. may return
E_INVALIDARG if pdwHash is an invalid pointer.
IMoniker::IsRunning
Returns E_NOTIMPL.
IMoniker::GetTimeOfLastChange
Returns MK_E_UNAVAILABLE.
IMoniker::Inverse
This method returns an anti-moniker (i.e., the results of calling CreateAntiMoni
ker).
IMoniker::CommonPrefixWith
If pmkOther IsEqual to this moniker, retrives a pointer to this moniker and retu
rns MK_S_US. If pmkOther is a class moniker but is not equal to this moniker, re
turns MK_E_NOPREFIX. Otherwise returns the result of calling MonikerCommonPrefix
With with itself as pmkThis, pmkOther and ppmkPrefix, which handles the case whr
e pmkOther is a generic composite moniker.
IMoniker::RelativePathTo
This method returns the result of calling This method returns the result of call
ing MonikerRelativePathTo with pmkSrc equal to this moniker, pmkOther, ppmkRelPa
th, and TRUE as dwReserved.
IMoniker::GetDisplayName
The display name for class monikers is of the form:
display-name = CLSID: string-clsid-no-curly-braces *[ ; clsid-options] :
clsid-options = clsid-param = value
clsid-param = none currently defined
Example:
clsid:a7b90590-36fd-11cf-857d-00aa006d2ea4:
IMoniker::ParseDisplayName
This method parses the display name by binding to itself for IParseDisplayName a
nd asking the bound object to parse the display name into a moniker, as follows:
hr = BindToObject(pbc, pmkToLeft, IID_IParseDisplayName, (void**)&ppdn);
if (SUCCEEDED(hr)) {
hr = ppdn->ParseDisplayName(pbc, lpszDisplayName, pchEaten, ppmkOut);
ppdn->Release();
}
return hr;
This method tries to acquire an IParseDisplayName pointer, first by binding to t
he class factory for the object identified by the moniker, and then by binding t
o the object itself. If either of these binding operations is successful, the fi
le moniker passes the unparsed portion of the display name to the IParseDisplayN
ame::ParseDisplayName method.
This method returns MK_E_SYNTAX if pmkToLeft is non-NULL.
IMoniker::IsSystemMoniker
This method returns S_OK, and passes back MKSYS_CLASSMONIKER.
See Also
CreateClassMoniker, IMoniker

15.2.9 IMoniker - File Moniker Implementation


File monikers are monikers that represent a path in the file system; a file moni
ker can identify any object that is saved in its own file. To identify objects c
ontained within a file, you can compose monikers of other classes (for example,
item monikers) to the right of a file moniker. However, the moniker to the left
of a file moniker within a composite must be another file moniker, an anti-monik
er, or a class moniker. It is illegal, for example, for an item moniker to appea
r to the left of a file moniker in a composite.
Note that an anti-moniker is the inverse of an entire file moniker, not the inve
rse of a component of the path that the moniker represents; that is, when you co
mpose an anti-moniker to the right of a file moniker, the entire file moniker is
removed. If you want to remove just the rightmost component of the path represe
nted by a file moniker, you must create a separate file moniker based on the .. pa
th and then compose that to the end of the file moniker.
15.2.9.1.1 When to Use
If you re a moniker client (that is, you re using a moniker to get an interface poin
ter to an object), you typically don t need to know the class of the moniker you re
using; you simply call methods using an IMoniker interface pointer.
If you re a moniker provider (that is, you re handing out monikers that identify you
r objects to make them accessible to moniker clients), you must use file moniker
s if the objects you re identifying are stored in files. If each object resides in
its own file, file monikers are the only type you need. If the objects you re ide
ntifying are smaller than a file, you need to use another type of moniker (for e
xample, item monikers) in addition to file monikers.
To use file monikers, you must use the CreateFileMoniker function to create the
monikers. In order to allow your objects to be loaded when a file moniker is bou
nd, your objects must implement the IPersistFile interface.
The most common example of moniker providers are COM server applications that su
pport linking. If your COM server application supports linking only to file-base
d documents in their entirety, file monikers are the only type of moniker you ne
ed. If your COM server application supports linking to objects smaller than a do
cument (such as sections of a document or embedded objects), you must use item m
onikers as well as file monikers.
Remarks
IMoniker::BindToObject
When pmkToLeft is NULL, the method looks for the moniker in the ROT, and if foun
d, queries the retrieved object for the requested interface pointer. If the moni
ker is not found in the ROT, the method loads the object from the file system an
d retrieves the requested interface pointer.
If pmkLeft is not NULL, then instead of determining the class to instantiate and
initialize with the contents of the file referred to by the file moniker using
GetClassFile (or other means), call pmkLeft->BindToObject for IClassFactory and
IClassActivator, retrieve this pointer in pcf.
If this fails with E_NOINTERFACE, return MK_E_INTERMEDIATEINTERFACENOTSUPPORTED.
If the IClassFactory pointer is successfully retrieved, call pcf->CreateInstance
(IID_IPersistFile, (void**)&ppf) to get a fresh instance of the class to be init
ialized and initialize it using IPersistFile or other appropriate means per the
existing initialization paths of File moniker.
IMoniker::BindToStorage
This method opens the file specified by the path represented by the moniker and
returns an IStorage pointer to that file. The method supports binding to IStorag
e interface only; if IStream or ILockBytes is requested in riid, the method retu
rns E_UNSPEC, and if other interfaces are requested, this method returns E_NOINT
ERFACE. IStream and ILockBytes will be supported in future releases. Unless pmkT
oLeft is a class moniker, pmkToLeft should be NULL, as in the implementation of
IMoniker::BindToObject. For situations where pmkToLeft is non-NULL, see the abov
e description.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL. If pmkRight is a c
omposite whose leftmost component is an anti-moniker, the returned moniker is th
e composite with the leftmost anti-moniker removed. If pmkRight is a file monike
r, this method collapses the two monikers into a single file moniker, if possibl
e. If not possible (e.g., if both file monikers represent absolute paths, as in
d:\work and e:\reports), then the returned moniker is NULL and the return value
is MK_E_SYNTAX. If pmkRight is neither an anti-moniker nor a file moniker, then
the method checks the fOnlyIfNotGeneric parameter; if it is FALSE, the method co
mbines the two monikers into a generic composite; if it is TRUE, the method sets
*ppmkComposite to NULL and returns MK_E_NEEDGENERIC.
IMoniker::Enum
This method returns S_OK and sets ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if *pmkOther is a file moniker and the paths for both m
onikers are identical (using a case-insensitive comparison). Otherwise, the meth
od returns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkNewlyRunning is non-NULL, this method returns TRUE if that moniker is equa
l to this moniker. Otherwise, the method asks the ROT whether this moniker is ru
nning. The method ignores pmkToLeft.
IMoniker::GetTimeOfLastChange
If this moniker is in the ROT, this method returns the last change time register
ed there; otherwise, it returns the last write time for the file. If the file ca
nnot be found, this method returns MK_E_NOOBJECT.
IMoniker::Inverse
This method returns an anti-moniker (i.e., the results of calling CreateAntiMoni
ker).
IMoniker::CommonPrefixWith
If both monikers are file monikers, this method returns a file moniker that is b
ased on the common components at the beginning of two file monikers. Components
of a file moniker can be:
· A machine name of the form \\server\share. A machine name is treated as a single
component, so two monikers representing the paths \\myserver\public\work and \\mys
erver\private\games do not have \\myserver as a common prefix.
· A drive designation (for example, C: ).
· A directory or file name.
If the other moniker is not a file moniker, this method passes both monikers in
a call to the MonikerCommonPrefixWith function. This function correctly handles
the case where the other moniker is a generic composite.
This method returns MK_E_NOPREFIX if there is no common prefix.
IMoniker::RelativePathTo
This method computes a moniker which when composed to the right of this moniker
yields the other moniker. For example, if the path of this moniker is C:\work\doc
s\report.doc and if the other moniker is C:\work\art\picture.bmp, then the path of
the computed moniker would be ..\..\art\picture.bmp.
IMoniker::GetDisplayName
This method returns the path that the moniker represents. If this method is call
ed by a 16-bit application, the method checks to see whether the specified file
exists and, if so, returns a short name for that file because 16-bit application
s are not equipped to handle long file names.
IMoniker::ParseDisplayName
This method tries to acquire an IParseDisplayName pointer, first by binding to t
he class factory for the object identified by the moniker, and then by binding t
o the object itself. If either of these binding operations is successful, the fi
le moniker passes the unparsed portion of the display name to the IParseDisplayN
ame::ParseDisplayName method.
This method returns MK_E_SYNTAX if pmkToLeft is non-NULL.
IMoniker::IsSystemMoniker
This method returns S_OK, and passes back MKSYS_FILEMONIKER.
See Also
CreateFileMoniker, IMoniker, IPersistFile

15.2.10 IMoniker - Generic Composite Moniker Implementation


A generic composite moniker is a composite moniker whose components have no spec
ial knowledge of each other.
Composition is the process of joining two monikers together. Sometimes two monik
ers of specific classes can be combined in a special manner; for example, a file
moniker representing an incomplete path and another file moniker representing a
relative path can be combined to form a single file moniker representing the co
mplete path. This is an example of non-generic composition. Generic composition, on
the other hand, can connect any two monikers, no matter what their classes. Beca
use a non-generic composition depends on the class of the monikers involved, it
can be performed only by a particular class s implementation of the IMoniker::Comp
oseWith method. You can define new types of non-generic compositions if you writ
e a new moniker class. By contrast, generic compositions are performed by the Cr
eateGenericComposite function.
15.2.10.1.1 When to Use
If you re a moniker client (that is, you re using a moniker to get an interface poin
ter to an object), you typically don t need to know the class of the moniker you re
using, or whether it is a generic composite or a non-generic composite; you simp
ly call methods using an IMoniker interface pointer.
If you re a moniker provider (that is, you re handing out monikers that identify you
r objects to make them accessible to moniker clients), you may have to compose t
wo monikers together. (For example, if you are using an item moniker to identify
an object, you must compose it with the moniker identifying the object s containe
r before you hand it out.) You use the IMoniker::ComposeWith method to do this,
calling the method on the first moniker and passing the second moniker as a para
meter; this method may produce either a generic or a non-generic composite.
The only time you should explicitly create a generic composite moniker is if you
are writing your own moniker class. In your implementation of IMoniker::Compose
With, you should attempt to perform a non-generic composition whenever possible;
if you cannot perform a non-generic composition and generic composition is acce
ptable, you can call the CreateGenericComposite function to create a generic com
posite moniker.
Remarks
IMoniker::BindToObject
If pmkToLeft is NULL, this method looks for the moniker in the ROT, and if found
, queries the retrieved object for the requested interface pointer. If pmkToLeft
is not NULL, the method recursively calls IMoniker::BindToObject on the rightmo
st component of the composite, passing the rest of the composite as the pmkToLef
t parameter for that call.
IMoniker::BindToStorage
This method recursively calls BindToStorage on the rightmost component of the co
mposite, passing the rest of the composite as the pmkToLeft parameter for that c
all.
IMoniker::Reduce
This method recursively calls Reduce for each of its component monikers. If any
of the components reduces itself, the method returns S_OK and passes back a comp
osite of the reduced components. If no reduction occurred, the method passes bac
k the same moniker and returns MK_S_REDUCED_TO_SELF.
IMoniker::ComposeWith
If fOnlyIfNotGeneric is TRUE, this method sets *pmkComposite to NULL and returns
MK_E_NEEDGENERIC; otherwise, the method returns the result of combining the two
monikers by calling the CreateGenericComposite function.
IMoniker::Enum
If successful, this method returns S_OK and passes back an enumerator that enume
rates the component monikers that make up the composite; otherwise, the method r
eturns E_OUTOFMEMORY.
IMoniker::IsEqual
This method returns S_OK if the components of both monikers are equal when compa
red in the left-to-right order.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkToLeft is non-NULL, this method composes pmkToLeft with this moniker and c
alls IsRunning on the result.
If pmkToLeft is NULL, this method returns TRUE if pmkNewlyRunning is non-NULL an
d is equal to this moniker.
If pmkToLeft and pmkNewlyRunning are both NULL, this method checks the ROT to se
e whether the moniker is running. If so, the method returns S_OK; otherwise, it
recursively calls IMoniker::IsRunning on the rightmost component of the composit
e, passing the remainder of the composite as the pmkToLeft parameter for that ca
ll. This handles the case where the moniker identifies a pseudo-object that is n
ot registered as running; see the Item Moniker implementation of IMoniker::IsRun
ning.
IMoniker::GetTimeOfLastChange
This method creates a composite of pmkToLeft (if non-NULL) and this moniker and
uses the ROT to retrieve the time of last change. If the object is not in the RO
T, the method recursively calls IMoniker::GetTimeOfLastChange on the rightmost c
omponent of the composite, passing the remainder of the composite as the pmkToLe
ft parameter for that call.
IMoniker::Inverse
This method returns a composite moniker that consists of the inverses of each of
the components of the original composite, stored in reverse order. For example,
if the inverse of A is A -1 , then the inverse of the composite A ° B ° C is C -1 ° B€-
1 ° A -1.
IMoniker::CommonPrefixWith
If the other moniker is a composite, this method compares the components of each
composite from left to right. The returned common prefix moniker might also be
a composite moniker, depending on how many of the leftmost components were commo
n to both monikers. If the other moniker is not a composite, the method simply c
ompares it to the leftmost component of this moniker.
If the monikers are equal, the method returns MK_S_US and sets ppmkPrefix to thi
s moniker. If the other moniker is a prefix of this moniker, the method returns
MK_S_HIM and sets ppmkPrefix to the other moniker. If this moniker is a prefix o
f the other, this method returns MK_S_ME and sets ppmkPrefix to this moniker.
If there is no common prefix, this method returns MK_E_NOPREFIX and sets ppmkPre
fix to NULL.
IMoniker::RelativePathTo
This method finds the common prefix of the two monikers and creates two monikers
that consist of the remainder when the common prefix is removed. Then it create
s the inverse for the remainder of this moniker and composes the remainder of th
e other moniker on the right of it.
IMoniker::GetDisplayName
This method returns the concatenation of the display names returned by each comp
onent moniker of the composite.
IMoniker::ParseDisplayName
This method recursively calls IMoniker::ParseDisplayName on the rightmost compon
ent of the composite, passing everything else as the pmkToLeft parameter for tha
t call.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_GENERICCOMPOSITE.
See Also
CreateGenericComposite, IMoniker

15.2.11 IMoniker - Item Moniker Implementation


Item monikers are used to identify objects within containers, such as a portion
of a document, an embedded object within a compound document, or a range of cell
s within a spreadsheet. Item monikers are often used in combination with file mo
nikers; a file moniker is used to identify the container while an item moniker i
s used to identify the item within the container.
An item moniker contains a text string; this string is used by the container obj
ect to distinguish the contained item from the others. The container object must
implement the IOleItemContainer interface; this interface enables the item moni
ker code to acquire a pointer to an object, given only the string that identifie
s the object.
15.2.11.1.1 When to Use
If you re a moniker client (that is, you re using a moniker to get an interface poin
ter to an object), you typically don t need to know the class of the moniker you re
using; you simply call methods using an IMoniker interface pointer.
If you re a moniker provider (that is, you re handing out monikers that identify you
r objects to make them accessible to moniker clients), you must use item moniker
s if the objects you re identifying are contained within another object and can be
individually identified using a string. You ll also need to use another type of m
oniker (for example, file monikers) in order to identify the container object.
To use item monikers, you must use the CreateItemMoniker function to create the
monikers. In order to allow your objects to be loaded when an item moniker is bo
und, the container of your objects must implement the IOleItemContainer interfac
e.
The most common example of moniker providers are COM applications that support l
inking. If your COM application supports linking to objects smaller than a file-
based document, you need to use item monikers. For a server application that all
ows linking to a selection within a document, you use the item monikers to ident
ify those objects. For a container application that allows linking to embedded o
bjects, you use the item monikers to identify the embedded objects.
Remarks
IMoniker::BindToObject
If pmkToLeft is NULL, this method returns E_INVALIDARG. Otherwise, the method ca
lls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemCon
tainer interface pointer. The method then calls IOleItemContainer::GetObject, pa
ssing the string contained within the moniker, and returns the requested interfa
ce pointer.
IMoniker::BindToStorage
If pmkToLeft is NULL, this method returns E_INVALIDARG. Otherwise, the method ca
lls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemCon
tainer interface pointer. The method then calls IOleItemContainer::GetObjectStor
age for the requested interface.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL; if pmkRight is a c
omposite whose leftmost component is an anti-moniker, the returned moniker is th
e composite after the leftmost anti-moniker is removed. If pmkRight is not an an
ti-moniker, the method combines the two monikers into a generic composite if fOn
lyIfNotGeneric is FALSE; if fOnlyIfNotGeneric is TRUE, the method returns a NULL
moniker and a return value of MK_E_NEEDGENERIC.
IMoniker::Enum
This method returns S_OK and sets *ppenumMoniker to NULL.
IMoniker::IsEqual
This method returns S_OK if both monikers are item monikers and their display na
mes are identical (using a case-insensitive comparison); otherwise, the method r
eturns S_FALSE.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
If pmkToLeft is NULL, this method returns TRUE if pmkNewlyRunning is non-NULL an
d is equal to this moniker. Otherwise, the method checks the ROT to see whether
this moniker is running.
If pmkToLeft is non-NULL, the method calls IMoniker::BindToObject on the pmkToLe
ft parameter, requesting an IOleItemContainer interface pointer. The method then
calls IOleItemContainer::IsRunning, passing the string contained within this mo
niker.
IMoniker::GetTimeOfLastChange
If pmkToLeft is NULL, this method returns MK_E_NOTBINDABLE. Otherwise, the metho
d creates a composite of pmkToLeft and this moniker and uses the ROT to access t
he time of last change. If the object is not in the ROT, the method calls IMonik
er::GetTimeOfLastChange on the pmkToLeft parameter.
IMoniker::Inverse
This method returns an anti-moniker (i.e., the results of calling CreateAntiMoni
ker).
IMoniker::CommonPrefixWith
If the other moniker is an item moniker that is equal to this moniker, this meth
od sets *ppmkPrefix to this moniker and returns MK_S_US; otherwise, the method c
alls the MonikerCommonPrefixWith function. This function correctly handles the c
ase where the other moniker is a generic composite.
IMoniker::RelativePathTo
This method returns MK_E_NOTBINDABLE and sets *ppmkRelPath to NULL.
IMoniker::GetDisplayName
This method returns the concatenation of the delimiter and the item name that we
re specified when the item moniker was created.
IMoniker::ParseDisplayName
If pmkToLeft is NULL, this method returns MK_E_SYNTAX. Otherwise, the method cal
ls IMoniker::BindToObject on the pmkToLeft parameter, requesting an IOleItemCont
ainer interface pointer. The method then calls IOleItemContainer::GetObject, req
uesting an IParseDisplayName interface pointer to the object identified by the m
oniker, and passes the display name to IParseDisplayName::ParseDisplayName.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_ITEMMONIKER.
See Also
CreateItemMoniker, IMoniker, IOleItemContainer

15.2.12 IMoniker - Pointer Moniker Implementation


A pointer moniker essentially wraps an interface pointer so that it looks like a
moniker and can be passed to those interfaces that require monikers. Binding a
pointer moniker is done by calling the pointer s QueryInterface method.
Instances of pointer monikers refuse to be serialized, that is, IPersistStream::
Save will return an error. These monikers can, however, be marshaled to a differ
ent process in an RPC call; internally, the system marshals and unmarshals the p
ointer using the standard paradigm for marshaling interface pointers.
15.2.12.1.1 When to Use
Pointer monikers are rarely needed. Use pointer monikers only if you need monike
rs to identify objects that have no persistent representation. Pointer monikers
allow such objects to participate in a moniker-binding operation.
Remarks
IMoniker::BindToObject
This method queries the wrapped pointer for the requested interface.
IMoniker::BindToStorage
This method queries the wrapped pointer for the requested interface.
IMoniker::Reduce
This method returns MK_S_REDUCED_TO_SELF and passes back the same moniker.
IMoniker::ComposeWith
If pmkRight is an anti-moniker, the returned moniker is NULL; if pmkRight is a c
omposite whose leftmost component is an anti-moniker, the returned moniker is th
e composite after the leftmost anti-moniker is removed. If fOnlyIfNotGeneric is
FALSE, the returned moniker is a generic composite of the two monikers; otherwis
e, the method sets *ppmkComposite to NULL and returns MK_E_NEEDGENERIC.
IMoniker::Enum
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::IsEqual
This method returns S_OK only if both are pointer monikers and the interface poi
nters that they wrap are identical.
IMoniker::Hash
This method calculates a hash value for the moniker.
IMoniker::IsRunning
This method always returns S_OK, because the object identified by a pointer moni
ker must always be running.
IMoniker::GetTimeOfLastChange
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::Inverse
This method returns an anti-moniker (i.e., the results of calling CreateAntiMoni
ker).
IMoniker::CommonPrefixWith
If the two monikers are equal, this method returns MK_S_US and sets *ppmkPrefix
to this moniker. Otherwise, the method returns MK_E_NOPREFIX and sets *ppmkPrefi
x to NULL.
IMoniker::RelativePathTo
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::GetDisplayName
This method is not implemented (that is, it returns E_NOTIMPL).
IMoniker::ParseDisplayName
This method queries the wrapped pointer for the IParseDisplayName interface and
passes the display name to IParseDisplayName::ParseDisplayName.
IMoniker::IsSystemMoniker
This method returns S_OK and indicates MKSYS_POINTERMONIKER.
See Also
IMoniker, CreatePointerMoniker
15.2.13 IOleItemContainer
The IOleItemContainer interface is used by item monikers when they are bound to
the objects they identify.
When any container of objects uses item monikers to identify its objects, it mus
t define a naming scheme for those objects. The container s IOleItemContainer impl
ementation uses knowledge of that naming scheme to retrieve an object given a pa
rticular name. Item monikers use the container s IOleItemContainer implementation
during binding.
15.2.13.1.1 When to Implement
You must implement IOleItemContainer if you re a moniker provider handing out item
monikers. Being a moniker provider means handing out monikers that identify you
r objects to make them accessible to moniker clients. You must use item monikers
if the objects you re identifying are contained within another object and can be
individually identified using a string.
The most common example of moniker providers are COM applications that support l
inking. If your COM application supports linking to objects smaller than a file-
based document, you need to use item monikers. For a server application that all
ows linking to a portion of a document (such as selections within a document), y
ou use the item monikers to identify those objects. For a container application
that allows linking to embedded objects, you use the item monikers to identify t
he embedded objects.
You must define a naming scheme for identifying the objects within the container
; for example, embedded objects in a document could be identified with names of
the form embedobj1, embedobj2, and so forth, while ranges of cells in a spreadsheet
could be identified with names of the form A1:E7, G5:M9, and so forth. (Ranges of ce
lls in a spreadsheet are examples of pseudo-objects because they do not have their
own persistent storage, but simply represent a portion of the container s interna
l state.) You create an item moniker that represents an object s name using the Cr
eateItemMoniker function and hand it out to a moniker client. When an item monik
er is bound, your implementation of IOleItemContainer must be able to take a nam
e and retrieve the corresponding object.
15.2.13.1.2 When to Use
Applications typically do not call IOleItemContainer methods directly. The item
moniker implementation of IMoniker is the primary caller of IOleItemContainer me
thods.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments the reference count.
Release Decrements the reference count.
IParseDisplayName Method Description
ParseDisplayName Parses object s display name to form moniker.
IOleContainer Methods Description
EnumObjects Enumerates objects in a container.
LockContainer Keeps container running until explicitly released.
IOleItemContainer Methods Description
GetObject Returns a pointer to a specified object.
GetObjectStorage Returns a pointer to an object s storage.
IsRunning Checks whether an object is running.
See Also
CreateItemMoniker, IMoniker Item Moniker Implementation

15.2.13.2 IOleItemContainer::GetObject
Returns a pointer to the object identified by the specified name.
HRESULT GetObject(
LPOLESTR pszItem, //Pointer to name of the object requested
DWORD dwSpeedNeeded, //Speed requirements on binding
IBindCtx *pbc, //Pointer to bind context object to be used
REFIID riid, //Reference to the identifier of the interface pointer d
esired
void **ppvObject //Indirect pointer to interface
);
Parameters
pszItem
[in] Pointer to a zero-terminated string containing the container s name for the r
equested object. For Win32 applications, the LPOLESTR type indicates a wide char
acter string (two bytes per character); otherwise, the string has one byte per c
haracter.
dwSpeedNeeded
[in] Indicates approximately how long the caller will wait to get the object. Th
e legal values for dwSpeedNeeded are taken from the enumeration BINDSPEED. For i
nformation on the BINDSPEED enumeration, see the Data Structures section.
pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used in
this binding operation. The bind context caches objects bound during the binding
process, contains parameters that apply to all operations using the bind contex
t, and provides the means by which the binding implementation should retrieve in
formation about its environment. For more information, see IBindCtx.
riid
[in] Reference to the identifier of the interface pointer requested.
ppvObject
[out] When successful, indirect pointer to the location of the interface specifi
ed in riid on the object named by pszItem. In this case, the implementation must
call IUnknown::AddRef on the parameter; it is the caller s responsibility to call
IUnknown::Release. If an error occurs, the implementation sets ppvObject to NUL
L.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The specified object was successfully returned.
MK_E_EXCEEDEDDEADLINE
The binding operation could not be completed within the time limit specified by
the bind context s BIND_OPTS structure, or with the speed indicated by the dwSpeed
Needed parameter.
MK_E_NOOBJECT
The parameter pszItem does not identify an object in this container.
E_NOINTERFACE
The requested interface was not available.
Remarks
The item moniker implementation of IMoniker::BindToObject calls this method, pas
sing the name stored within the item moniker as the pszItem parameter.
15.2.13.2.1.1.1 Notes to Implementers
Your implementation of IOleItemContainer::GetObject should first determine wheth
er pszItem is a valid name for one of the container s objects. If not, you should
return MK_E_NOOBJECT.
If pszItem names an embedded or linked object, your implementation must check th
e value of the dwSpeedNeeded parameter. If the value is BINDSPEED_IMMEDIATE and
the object is not yet loaded, you should return MK_E_EXCEEDEDDEADLINE. If the ob
ject is loaded, your implementation should determine whether the object is runni
ng (for example, by calling the OleIsRunning function). If it is not running and
the dwSpeedNeeded value is BINDSPEED_MODERATE, your implementation should retur
n MK_E_EXCEEDEDDEADLINE. If the object is not running and dwSpeedNeeded is BINDS
PEED_INDEFINITE, your implementation should call the OleRun function to put the
object in the running state. Then it can query the object for the requested inte
rface. Note that it is important the object be running before you query for the
interface.
If pszItem names a pseudo-object, your implementation can ignore the dwSpeedNeed
ed parameter because a pseudo-object is running whenever its container is runnin
g. In this case, your implementation can simply query for the requested interfac
e.
If you want more specific information about the time limit than is given by dwSp
eedNeeded, you can call IBindCtx::GetBindOptions on the pbc parameter to get the
actual deadline parameter.
See Also
IMoniker::BindToObject, IBindCtx::GetBindOptions

15.2.13.3 IOleItemContainer::GetObjectStorage
Returns a pointer to the storage for the object identified by the specified name
.
HRESULT GetObjectStorage(
LPOLESTR pszItem, //Name of the string containing the name of object whose
storage is requested
IBindCtx *pbc, //Pointer to bind context to be used
REFIID riid, //Reference to the identifier of the interface pointer d
esired
void **ppvStorage //Indirect pointer to object s storage
);
Parameters
pszItem
[in] Pointer to a zero-terminated string containing the compound document s name f
or the object whose storage is requested. For Win32 applications, the LPOLESTR t
ype indicates a wide character string (two bytes per character); otherwise, the
string has one byte per character.
pbc
[in] Pointer to the IBindCtx interface on the bind context to be used in this bi
nding operation. The bind context caches objects bound during the binding proces
s, contains parameters that apply to all operations using the bind context, and
provides the means by which the binding implementation should retrieve informati
on about its environment. For more information, see IBindCtx.
riid
[in] Reference to the identifier of the interface to be used to communicate with
the object, usually IStorage.
ppvStorage
[out] When successful, indirect pointer to the location of the interface specifi
ed in riid, on the storage for the object named by pszItem. In this case, the im
plementation must call IUnknown::AddRef on the parameter; it is the caller s respo
nsibility to call IUnknown::Release. If an error occurs, ppvStorage is set to NU
LL.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The storage of the specified object was successfully returned.
MK_E_NOOBJECT
The parameter pszItem does not identify a object in this container.
MK_E_NOSTORAGE
The object does not have its own independent storage.
E_NOINTERFACE
The requested interface is not available.
Remarks
The item moniker implementation of IMoniker::BindToStorage calls this method.
15.2.13.3.1.1.1 Notes to Implementers
If pszItem designates a pseudo-object, your implementation should return MK_E_NO
STORAGE, because pseudo-objects do not have their own independent storage. If ps
zItem designates an embedded object, or a portion of the document that has its o
wn storage, your implementation should return the specified interface pointer on
the appropriate storage object.
See Also
IMoniker - Item Moniker Implementation

15.2.13.4 IOleItemContainer::IsRunning
Indicates whether the object identified by the specified name is running.
HRESULT IsRunning(
LPOLESTR pszItem //Pointer to string containing name of object
);
Parameter
pszItem
[in] Pointer to a zero-terminated wide character string (two bytes per character
) containing the container s name for the object.
Return Values
S_OK
The specified object is running.
S_FALSE
The object is not running.
MK_E_NOOBJECT
The parameter pszItem does not identify an object in this container.
Remarks
The item moniker implementation of IMoniker::IsRunning calls this method.
15.2.13.4.1.1.1 Notes to Implementers
Your implementation of IOleItemContainer::IsRunning should first determine wheth
er pszItem identifies one of the container s objects. If it does not, your impleme
ntation should return MK_E_NOOBJECT. If the object is not loaded, your implement
ation should return S_FALSE. If it is loaded, your implementation can call the O
leIsRunning function to determine whether it is running.
If pszItem names a pseudo-object, your implementation can simply return S_OK bec
ause a pseudo-object is running whenever its container is running.
See Also
IMoniker::IsRunning
15.2.14 IParseDisplayName
The IParseDisplayName interface parses a displayable name string to convert it i
nto a moniker for custom moniker implementations. Display name parsing is necess
ary when the end user inputs a string to identify a component, as in the followi
ng situations:
· A compound document application that supports linked components typically suppor
ts the Edit:Links... dialog box. Through this dialog box, the end user can enter
a display name to specify a new link source for a specified linked component. T
he compound document needs to have this input string converted into a moniker.
· A script language such as the macro language of a spreadsheet can allow textual
references to a component. The language s interpreter needs to have such a referen
ce converted into a moniker in order to execute the macro.
15.2.14.1.1 When to Implement
Compound document applications that support links to embedded components or to p
seudo-objects within their documents must provide an implementation of the IOleI
temContainer interface, which is derived indirectly from IParseDisplayName. In e
ffect, such a compound document is providing a namespace for identifying its int
ernal components; and its IOleItemContainer implementation (which includes the I
ParseDisplayName implementation) is the interface through which another applicat
ion can access this namespace. Alternatively, the compound document application
can implement IParseDisplayName as part of its class object, which is accessible
through the CoGetClassObject function.
Monikers that support their own namespace with custom requirements for parsing n
ames also implement this interface.
15.2.14.1.2 When to Use
If you are implementing your own moniker class, you might need to use this inter
face from your implementation of IMoniker::ParseDisplayName. If you call the MkP
arseDisplayName or the MkParseDisplayNameEx functions, you are indirectly using
IParseDisplayName. These two functions call IParseDisplayName to parse display n
ames for objects that provide custom moniker implementations.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IParseDisplayName Method Description
ParseDisplayName Parses the display name returning a moniker correspondin
g to it.
See Also
IMoniker::ParseDisplayName, IOleItemContainer, MkParseDisplayName, MkParseDispla
yNameEx

15.2.14.2 IParseDisplayName::ParseDisplayName
Parses the display name to extract a component of the string that it can convert
into a moniker, using the maximum number of characters from the left side of th
e string.
HRESULT ParseDisplayName(
IBindCtx *pbc, //Pointer to bind context
LPOLESTR pszDisplayName, //Pointer to string containing display name
ULONG *pchEaten, //Pointer to length, in characters, of display name
IMoniker **ppmkOut //Indirect pointer to moniker that results
);
Parameters
pbc
[in] Pointer to the bind context to be used in this binding operation.
pszDisplayName
[in] Pointer to a zero-terminated string containing the display name to be parse
d. For Win32 applications, the LPOLESTR type indicates a wide character string (
two bytes per character); otherwise, the string has one byte per character.
pchEaten
[out Pointer to the number of characters in the display name that correspond to
the ppmkOut moniker.
ppmkOut
[out] Indirect pointer to the resulting moniker. If an error occurs, the impleme
ntation sets *ppmkOut to NULL. If *ppmkOut is non-NULL, the implementation must
call (*ppmkOut)->IUnknown::AddRef; so it is the caller s responsibility to call (*
ppmkOut)->IUnknown::Release.
Return Values
This method supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED,
as well as the following:
S_OK
The parse operation was successful.
MK_E_SYNTAX
Syntax error in the display name. Parsing failed because szDisplayName could onl
y be partially resolved into a moniker. In this case, *pchEaten has the number o
f characters that were successfully parsed into a moniker prefix. The parameter
ppmkOut should be NULL.
MK_E_NOOBJECT
The display name does not identify a component in this namespace.
E_INVALIDARG
One or more parameters are invalid.
Remarks
In general, the maximum prefix of szDisplayName that is syntactically valid and
that represents an object should be consumed by this method and converted to a m
oniker.
Typically, this method is called by MkParseDisplayName[Ex]. In the initial step
of the parsing operation, the MkParseDisplayName[Ex] function can retrieve the I
ParseDisplayName interface directly from an instance of a class identified with
either the @ProgID or ProgID notation. Subsequent parsing steps can query for the in
terface on an intermediate object.
The main loop of MkParseDisplayName[Ex] finds the next moniker piece by calling
the equivalent method in the IMoniker interface, that is, IMoniker::ParseDisplay
Name, on the moniker that it currently holds. In this call to IMoniker::ParseDis
playName, the MkParseDisplayName[Ex] function passes NULL in the pmkToLeft param
eter. If the moniker currently held by MkParseDisplayName[Ex] is a generic compo
site, the call to IMoniker::ParseDisplayName is forwarded by that composite onto
its last piece, passing the prefix of the composite to the left of the piece in
pmkToLeft.
Some moniker classes will be able to handle this parsing internally to themselve
s since they are designed to designate only certain kinds of objects. Others wil
l need to bind to the object that they designate to accomplish the parsing proce
ss. As is usual, these objects should not be released by IMoniker::ParseDisplayN
ame but instead should be transferred to the bind context via IBindCtx::Register
ObjectBound or IBindCtx::GetRunningObjectTable followed by IRunningObjectTable::
Register for release at a later time.
See Also
MkParseDisplayName, MkParseDisplayNameEx IMoniker::ParseDisplayName

15.2.15 IROTData
The IROTData interface is implemented by monikers to enable the Running Object T
able (ROT) to compare monikers against each other.
The ROT uses the IROTData interface to test whether two monikers are equal. The
ROT must do this when, for example, it checks whether a specified moniker is reg
istered as running.
15.2.15.1.1 When to Implement
You must implement IROTData if you are writing your own moniker class (that is,
writing your own implementation of the IMoniker interface), and if your monikers
are meant to be registered in the ROT.
15.2.15.1.2 When to Use
You typically do not need to use this interface. This interface is used by the s
ystem s implementation of the ROT.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IROTData Method Description
GetComparisonData Retrieve data to allow moniker to be compared with anoth
er.
See Also
IMoniker, IRunningObjectTable

15.2.15.2 IROTData::GetComparisonData
Retrieves data from a moniker that can be used to test the moniker for equality
against another moniker.
HRESULT GetComparisonData(
PVOID *ppvData, //Indirect pointer to a buffer that receives the compari
son data
ULONG cbMax, //Length of buffer
PULONG pcbData //Pointer to the length of the comparison data
);
Parameters
ppvData
[out] Indirect pointer to a buffer that receives the comparison data.
cbMax
[in] Length of the buffer specified in ppvData.
pcbData
[out] Pointer to the length of the comparison data.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The comparison data was successfully returned.
Remarks
The IROTData::GetComparisonData method is primarily called by the Running Object
Table (ROT). The comparison data returned by the method is tested for binary eq
uality against the comparison data returned by another moniker. The pcbData para
meter enables the ROT to locate the end of the data returned.
15.2.15.2.1.1.1 Notes to Implementers
The comparison data that you return must uniquely identify the moniker, while st
ill being as short as possible. The comparison data should include information a
bout the internal state of the moniker, as well as the moniker s CLSID. For exampl
e, the comparison data for a file moniker would include the path name stored wit
hin the moniker, as well as the CLSID of the file moniker implementation. This m
akes it possible to distinguish two monikers that happen to store similar state
information but are instances of different moniker classes.
The comparison data for a moniker cannot exceed 2048 bytes in length. For compos
ite monikers, the total length of the comparison data for all of its components
cannot exceed 2048 bytes; consequently, if your moniker can be a component withi
n a composite moniker, the comparison data you return must be significantly less
than 2048 bytes.
If your comparison data is longer than the value specified by the cbMax paramete
r, you must return an error. Note that when IROTData::GetComparisionData is call
ed on the components of a composite moniker, the value of cbMax becomes smaller
for each moniker in sequence.
See Also
IMoniker, IRunningObjectTable

15.2.16 IRunningObjectTable
The IRunningObjectTable interface manages access to the Running Object Table (RO
T), a globally accessible look-up table on each workstation. A workstation s ROT k
eeps track of those objects that can be identified by a moniker and that are cur
rently running on the workstation. When a client tries to bind a moniker to an o
bject, the moniker checks the ROT to see if the object is already running; this
allows the moniker to bind to the current instance instead of loading a new one.
The ROT contains entries of the form:
(pmkObjectName, pUnkObject)
The pmkObjectName element is a pointer to the moniker that identifies the runnin
g object. The pUnkObject element is a pointer to the running object itself. Duri
ng the binding process, monikers consult the pmkObjectName entries in the Runnin
g Object Table to see if an object is already running.
Objects that can be named by monikers must be registered with the ROT when they
are loaded and their registration must be revoked when they are no longer runnin
g.
15.2.16.1.1 When to Implement
You do not need to implement this interface. The system provides an implementati
on of the Running Object Table that is suitable for all situations.
15.2.16.1.2 When to Use
You typically use the ROT if you re a moniker provider (that is, you hand out moni
kers identifying your objects to make them accessible to others) or if you re writ
ing your own moniker class (that is, implementing the IMoniker interface).
If you are a moniker provider, you register your objects with the ROT when they
begin running and revoke their registrations when they are no longer running. Th
is enables the monikers that you hand out to be bound to running objects. You sh
ould also use the ROT to record the object s last modification time. You can get a
n IRunningObjectTable interface pointer to the local ROT by calling the GetRunni
ngObjectTable function.
The most common type of moniker provider is a compound-document link source. Thi
s includes server applications that support linking to their documents (or porti
ons of a document) and container applications that support linking to embeddings
within their documents. Server applications that do not support linking can als
o use the ROT to cooperate with container applications that support linking to e
mbeddings.
If you are writing your own moniker class, you use the ROT to determine whether
a object is running and to retrieve the object s last modification time. You can g
et an IRunningObjectTable interface pointer to the local ROT by calling the IBin
dCtx::GetRunningObjectTable method on the bind context for the current binding o
peration. Moniker implementations should always use the bind context to acquire
a pointer to the ROT; this allows future implementations of IBindCtx to modify b
inding behavior. Note that you must also implement the IROTData interface on you
r moniker class in order to allow your monikers to be registered with the ROT.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IRunningObjectTable Methods Description
Register Registers an object with the ROT.
Revoke Revokes an object s registration with the ROT.
IsRunning Checks whether an object is running.
GetObject Returns a pointer to an object given its moniker.
NoteChangeTime Notifies the ROT that an object has changed.
GetTimeOfLastChange Returns the time an object was last changed.
EnumRunning Returns an enumerator for the ROT.
See Also
IBindCtx::GetRunningObjectTable, IROTData, GetRunningObjectTable

15.2.16.2 IRunningObjectTable::EnumRunning
Creates and returns a pointer to an enumerator that can list the monikers of all
the objects currently registered in the Running Object Table (ROT).
HRESULT EnumRunning(
IEnumMoniker **ppenumMoniker //Indirect pointer to the enumerator for
ROT
);
Parameter
ppenumMoniker
[out] When successful, indirect pointer to the IEnumMoniker interface on the new
enumerator. In this case, the implementation calls IUnknown::AddRef on the para
meter; it is the caller s responsibility to call IUnknown::Release. If an error oc
curs; the implementation sets ppenumMoniker to NULL.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
An enumerator was successfully returned.
Remarks
IRunningObjectTable::EnumRunning must create and return a pointer to an IEnumMon
iker interface on an enumerator object. The standard enumerator methods can then
be called to enumerate the monikers currently registered in the registry. The e
numerator cannot be used to enumerate monikers that are registered in the ROT af
ter the enumerator has been created.
The EnumRunning method is intended primarily for the use by the system in implem
enting the Alert Object Table. Note that COM 2 does not include an implementatio
n of the Alert Object Table.
See Also
IEnumXXXX, IEnumMoniker

15.2.16.3 IRunningObjectTable::GetObject
Determines whether the object identified by the specified moniker is running, an
d if it is, retrieves a pointer to that object. This method looks for the monike
r in the Running Object Table (ROT), and retrieves the pointer registered there.
HRESULT GetObject(
IMoniker *pmkObjectName, //Pointer to the moniker on the object
IUnknown **ppunkObject //Indirect pointer to the object
);
Parameters
pmkObjectName
[in] Pointer to the moniker to search for in the Running Object Table.
ppunkObject
[out] When successful. indirect pointer to the IUnknown interface on the running
object. In this case, the implementation calls IUnknown::AddRef on the paramete
r; it is the caller s responsibility to call IUnknown::Release. If the object is n
ot running or if an error occurs, the implementation sets ppunkObject to NULL.
Return Values
S_OK
Indicates that pmkObjectName was found in the ROT and a pointer was returned.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies
is no longer running (in which case, the entry is revoked).
Remarks
This method checks the ROT for the moniker specified by pmkObjectName. If that m
oniker had previously been registered with a call to IRunningObjectTable::Regist
er, this method returns the pointer that was registered at that time.
15.2.16.3.1.1.1 Notes to Callers
Generally, you call the IRunningObjectTable::GetObject method only if you are wr
iting your own moniker class (that is, implementing the IMoniker interface). You
typically call this method from your implementation of IMoniker::BindToObject.
However, note that not all implementations of IMoniker::BindToObject need to cal
l this method. If you expect your moniker to have a prefix (indicated by a non-N
ULL pmkToLeft parameter to IMoniker::BindToObject), you should not check the ROT
. The reason for this is that only complete monikers are registered with the ROT
, and if your moniker has a prefix, your moniker is part of a composite and thus
not complete. Instead, your moniker should request services from the object ide
ntified by the prefix (for example, the container of the object identified by yo
ur moniker).
See Also
IMoniker::BindToObject

15.2.16.4 IRunningObjectTable::GetTimeOfLastChange
Returns the time that an object was last modified. The object must have previous
ly been registered with the Running Object Table (ROT). This method looks for th
e last change time recorded in the ROT.
HRESULT GetTimeOfLastChange(
IMoniker *pmkObjectName, //Pointer to moniker on the object whose status
is desired
FILETIME *pfiletime //Pointer to structure that receives object s last change
time
);
Parameters
pmkObjectName
[in] Pointer to the IMoniker interface on the moniker to search for in the ROT.
pfiletime
[out] Pointer to a FILETIME structure that receives the object s last change time.
Return Values
S_OK
The last change time was successfully retrieved.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies
is no longer running (in which case, the entry is revoked).
Remarks
This method returns the change time that was last reported for this object by a
call to IRunningObjectTable::NoteChangeTime. If IRunningObjectTable::NoteChangeT
ime has not been called previously, the method returns the time that was recorde
d when the object was registered.
This method is provided to enable checking whether a connection between two obje
cts (represented by one object holding a moniker that identifies the other) is u
p-to-date. For example, if one object is holding cached information about the ot
her object, this method can be used to check whether the object has been modifie
d since the cache was last updated. See IMoniker::GetTimeOfLastChange.
15.2.16.4.1.1.1 Notes to Callers
Generally, you call IRunningObjectTable::GetTimeOfLastChange only if you are wri
ting your own moniker class (that is, implementing the IMoniker interface). You
typically call this method from your implementation of IMoniker::GetTimeOfLastCh
ange. However, you should do so only if the pmkToLeft parameter of IMoniker::Get
TimeOfLastChange is NULL. Otherwise, you should call IMoniker::GetTimeOfLastChan
ge on your pmkToLeft parameter instead.
See Also
IMoniker::GetTimeOfLastChange, IRunningObjectTable::NoteChangeTime

15.2.16.5 IRunningObjectTable::IsRunning
Determines whether the object identified by the specified moniker is currently r
unning. This method looks for the moniker in the Running Object Table (ROT).
HRESULT IsRunning(
IMoniker *pmkObjectName //Pointer to the moniker of the object whose sta
tus is desired
);
Parameter
pmkObjectName
[in] Pointer to the IMoniker interface on the moniker to search for in the Runni
ng Object Table.
Return Values
S_OK
The object identified by pmkObjectName is running.
S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object it identifies
is no longer running (in which case, the entry is revoked).
Remarks
This method simply indicates whether a object is running. To retrieve a pointer
to a running object, use the IRunningObjectTable::GetObject method.
15.2.16.5.1.1.1 Notes to Callers
Generally, you call the IRunningObjectTable::IsRunning method only if you are wr
iting your own moniker class (that is, implementing the IMoniker interface). You
typically call this method from your implementation of IMoniker::IsRunning. How
ever, you should do so only if the pmkToLeft parameter of IMoniker::IsRunning is
NULL. Otherwise, you should call IMoniker::IsRunning on your pmkToLeft paramete
r instead.
See Also
IMoniker::IsRunning

15.2.16.6 IRunningObjectTable::NoteChangeTime
Records the time that a running object was last modified. The object must have p
reviously been registered with the Running Object Table (ROT). This method store
s the time of last change in the ROT.
HRESULT NoteChangeTime(
DWORD dwRegister, //Value identifying registration being updated
FILETIME *pfiletime //Pointer to structure containing object s last change tim
e
);
Parameters
dwRegister
[in] Value identifying the ROT entry of the changed object. This value was previ
ously returned by IRunningObjectTable::Register.
pfiletime
[in] Pointer to a FILETIME structure containing the object s last change time.
Return Values
This method supports the standard return value E_INVALIDARG, as well as the foll
owing:
S_OK
The change time was recorded successfully.
Remarks
The time recorded by this method can be retrieved by calling IRunningObjectTable
::GetTimeOfLastChange.
This method is provided to enable a program to check whether a connection betwee
n two objects (represented by one object holding a moniker that identifies the o
ther) is up-to-date. For example, if one object is holding cached information ab
out the other object, this method can be used to check whether the object has be
en modified since the cache was last updated. See IMoniker::GetTimeOfLastChange.
15.2.16.6.1.1.1 Notes to Callers
If you re a moniker provider (that is, you hand out monikers identifying your obje
cts to make them accessible to others), you must call the IRunningObjectTable::N
oteChangeTime method whenever your objects are modified. You must have previousl
y called IRunningObjectTable::Register and stored the identifier returned by tha
t method; you use that identifier when calling IRunningObjectTable::NoteChangeTi
me.
The most common type of moniker provider is a compound-document link source. Thi
s includes server applications that support linking to their documents (or porti
ons of a document) and container applications that support linking to embeddings
within their documents. Server applications that do not support linking can als
o use the ROT to cooperate with container applications that support linking to e
mbeddings.
When an object is first registered in the ROT, the ROT records its last change t
ime as the value returned by calling IMoniker::GetTimeOfLastChange on the monike
r being registered.
See Also
IRunningObjectTable::GetTimeOfLastChange, IMoniker::GetTimeOfLastChange

15.2.16.7 IRunningObjectTable::Register
Registers an object and its identifying moniker in the Running Object Table (ROT
).
HRESULT Register(
DWORD grfFlags, //Specifies a weak or a strong reference
IUnknown *punkObject, //Pointer to the object being registered
IMoniker *pmkObjectName, //Pointer to the moniker of the object being reg
istered
DWORD *pdwRegister //Pointer to the value identifying the registration
);
Parameters
grfFlags
[in] Specifies whether the ROT s reference to punkObject is weak or strong. This v
alue must be either zero, indicating a weak reference that does not call IUnknow
n::AddRef; or ROTFLAGS_REGISTRATIONKEEPSALIVE, indicating a strong reference tha
t calls IUnknown::AddRef and can keep the object running. If a strong reference
is registered, a strong reference is released when the object s registration is re
voked. Most callers specify zero, indicating a weak reference.
punkObject
[in] Pointer to the object that is being registered as running.
pmkObjectName
[in] Pointer to the moniker that identifies punkObject.
pdwRegister
[out] Pointer to a 32-bit value that can be used to identify this ROT entry in s
ubsequent calls to IRunningObjectTable::Revoke or IRunningObjectTable::NoteChang
eTime. The caller cannot specify NULL for this parameter. If an error occurs, *p
dwRegister is set to zero.
Return Values
This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY,
as well as the following:
S_OK
The object was successfully registered.
MK_S_MONIKERALREADYREGISTERED
The moniker/object pair was successfully registered, but that another object (po
ssibly the same object) has already been registered with the same moniker.
Remarks
This method registers a pointer to an object under a moniker that identifies the
object. The moniker is used as the key when the table is searched with IRunning
ObjectTable::GetObject.
Registering a second object with the same moniker, or re-registering the same ob
ject with the same moniker, creates a second entry in the ROT. In this case, IRu
nningObjectTable::Register returns MK_S_MONIKERALREADYREGISTERED. Each call to I
RunningObjectTable::Register must be matched by a call to IRunningObjectTable::R
evoke because even duplicate entries have different pdwRegister identifiers. A p
roblem with duplicate registrations is that there is no way to determine which o
bject will be returned if the moniker is specified in a subsequent call to IRunn
ingObjectTable::IsRunning.
15.2.16.7.1.1.1 Notes to Callers
If you re a moniker provider (that is, you hand out monikers identifying your obje
cts to make them accessible to others), you must call the IRunningObjectTable::R
egister method to register your objects when they begin running. You must also c
all this method if you rename your objects while they are loaded.
The most common type of moniker provider is a compound-document link source. Thi
s includes server applications that support linking to their documents (or porti
ons of a document) and container applications that support linking to embeddings
within their documents. Server applications that do not support linking can als
o use the ROT to cooperate with container applications that support linking to e
mbeddings.
If you re writing a container application that supports linking to embeddings, you
should register your document with the ROT when it is loaded.
You must cache the identifier returned in pdwRegister, and use it in a call to I
RunningObjectTable::Revoke to revoke the registration when the object is no long
er running or when its moniker changes. This revocation is important because the
re is no way for the system to automatically remove entries from the ROT.
The system s implementation of IRunningObjectTable::Register calls IMoniker::Reduc
e on the pmkObjectName parameter to ensure that the moniker is fully reduced bef
ore registration. If a object is known by more than one fully reduced moniker, t
hen it should be registered under all such monikers.
See Also
IMoniker::Reduce, IRunningObjectTable::IsRunning, IRunningObjectTable::Revoke

15.2.16.8 IRunningObjectTable::Revoke
Removes from the Running Object Table (ROT) an entry that was previously registe
red by a call to IRunningObjectTable::Register.
HRESULT Revoke(
DWORD dwRegister //Value identifying registration to be revoked
);
Parameter
dwRegister
[in] Value identifying the ROT entry to revoke. This value was previously return
ed by IRunningObjectTable::Register.
Return Values
This method supports the standard return value E_INVALIDARG, as well as the foll
owing:
S_OK
The object s registration was successfully revoked.
Remarks
This method undoes the effect of a call to IRunningObjectTable::Register, removi
ng both the moniker and the pointer to the object identified by that moniker.
15.2.16.8.1.1.1 Notes to Callers
If you re a moniker provider (that is, you hand out monikers identifying your obje
cts to make them accessible to others), you must call the IRunningObjectTable::R
evoke method to revoke the registration of your objects when they stop running.
You must have previously called IRunningObjectTable::Register and stored the ide
ntifier returned by that method; you use that identifier when calling IRunningOb
jectTable::Revoke.
The most common type of moniker provider is a compound-document link source. Thi
s includes server applications that support linking to their documents (or porti
ons of a document) and container applications that support linking to embeddings
within their documents. Server applications that do not support linking can als
o use the ROT to cooperate with container applications that support linking to e
mbeddings.
If you re writing a container application, you must revoke a document s registration
when the document is closed. You must also revoke a document s registration befor
e re-registering it when it is renamed.
If you re writing a server application, you must revoke an object s registration whe
n the object is closed. You must also revoke an object s registration before re-re
gistering it when its container document is renamed.
See Also
IRunningObjectTable::Register

15.3 Moniker API Descriptions


15.3.1 BindMoniker
Locates an object by means of its moniker, activates the object if it is inactiv
e, and retrieves a pointer to the specified interface on that object.
HRESULT BindMoniker(
LPMONIKER pmk, //Pointer to the object s moniker
DWORD grfOpt, //Reserved
REFIID iidResult, //Interface identifier
LPVOID FAR *ppvResult //Indirect pointer to requested interface
);
Parameters
pmk
[in] Pointer to the object s moniker.
grfOpt
[in] Reserved for future use; must be zero.
iidResult
[in] Interface identifier to be used to communicate with the object.
ppvResult
[out] Address of pointer variable that receives the interface pointer requested
in iidResult. Upon successful return, *ppvResult contains the requested interfac
e pointer. If an error occurs, *ppvResult is NULL. If the call is successful, th
e caller is responsible for releasing the pointer with a call to the object's IU
nknown::Release.
Return Values
S_OK
The object was located and activated, if necessary, and that a pointer to the re
quested interface was returned.
MK_E_NOOBJECT
The object that the moniker object identified could not be found.
This function can also return any of the error values returned by the IMoniker::
BindToObject method.
Remarks
BindMoniker is a helper function supplied as a convenient way for a client that
has the moniker of an object to obtain a pointer to one of its interfaces. The B
indMoniker function packages the following calls:
CreateBindCtx(0, &pbc);
pmk->BindToObject(pbc, NULL, riid, ppvObj);
CreateBindCtx creates a bind context object that supports the system implementat
ion of IBindContext. The pmk parameter is actually a pointer to the IMoniker imp
lementation on a moniker object. This implementation s BindToObject method supplie
s the pointer to the requested interface pointer.
If you have several monikers to bind in quick succession, and if you know that t
hose monikers will activate the same object, it may be more efficient to call th
e IMoniker::BindToObject method directly, which allows you to use the same bind
context object for all the monikers. See the IBindCtx interface for more informa
tion.
Container applications that allow their documents to contain linked objects are
a special client that generally does not make direct calls to IMoniker methods
See Also
CreateBindCtx, IMoniker::BindToObject
15.3.2 MkParseDisplayName
Converts a string into a moniker that identifies the object named by the string.
This is the inverse of the IMoniker::GetDisplayName operation, which retrieves
the display name associated with a moniker.
WINOLEAPI MkParseDisplayName(
LPBC pbc, //Pointer to the bind context object
LPCOLESTR szUserName, //Pointer to display name
ULONG FAR *pchEaten, //Pointer to the number of characters consumed
LPMONIKER FAR *ppmk //Address of output variable that receives
// the IMoniker interface pointer
);
Parameters
pbc
[in] Pointer to the IBindCtx interface on the bind context object to be used in
this binding operation.
szUserName
[in] Pointer to a zero-terminated wide character string (two bytes per character
) containing the display name to be parsed.
pchEaten
[out] Pointer to the number of characters of szUserName that were consumed. If t
he function is successful, *pchEaten is the length of szUserName; otherwise, it
is the number of characters successfully parsed.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the moniker that was built from szUserName. When successful, the function has
called IUnknown::AddRef on the moniker and the caller is responsible for callin
g IUnknown::Release. If an error occurs, the supplied interface pointer value is
NULL.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The parse operation was successful and the moniker was created.
MK_E_SYNTAX
Error in the syntax of a file name or an error in the syntax of the resulting co
mposite moniker.
This function can also return any of the error values returned by IMoniker::Bind
ToObject, IOleItemContainer::GetObject, or IParseDisplayName::ParseDisplayName.
Remarks
The MkParseDisplayName function parses a human-readable name into a moniker that
can be used to identify a link source. The resulting moniker can be a simple mo
niker (such as a file moniker), or it can be a generic composite made up of the
component moniker pieces. For example, the following display name:
"c:\mydir\somefile!item 1"
could be parsed into the following generic composite moniker:
(FileMoniker based on "c:\mydir\somefile") + (ItemMoniker based on "item 1")
The most common use of MkParseDisplayName is in the implementation of the standa
rd Links dialog box, which allows an end user to specify the source of a linked
object by typing in a string. You may also need to call MkParseDisplayName if yo
ur application supports a macro language that permits remote references (referen
ce to elements outside of the document).
Parsing a display name often requires activating the same objects that would be
activated during a binding operation, so it can be just as expensive (in terms o
f performance) as binding. Objects that are bound during the parsing operation a
re cached in the bind context passed to the function. If you plan to bind the mo
niker returned by MkParseDisplayName, it is best to do so immediately after the
function returns, using the same bind context, which removes the need to activat
e objects a second time.
MkParseDisplayName parses as much of the display name as it understands into a m
oniker. The function then calls IMoniker::ParseDisplayName on the newly created
moniker, passing the remainder of the display name. The moniker returned by IMon
iker::ParseDisplayName is composed onto the end of the existing moniker and, if
any of the display name remains unparsed, IMoniker::ParseDisplayName is called o
n the result of the composition. This process is repeated until the entire displ
ay name has been parsed.
The MkParseDisplayName function attempts the following strategies to parse the b
eginning of the display name, using the first one that succeeds:
1. The function looks in the Running Object Table for file monikers
corresponding to all prefixes of szDisplayName that consist solely of valid fil
e name characters. This strategy can identify documents that are as yet unsaved.
2. The function checks the maximal prefix of szDisplayName, which c
onsists solely of valid file name characters, to see if an OLE 1 document is reg
istered by that name (this may require some DDE broadcasts). In this case, the r
eturned moniker is an internal moniker provided by the OLE 1 compatibility layer
of COM.
3. The function consults the file system to check whether a prefix
of szDisplayName matches an existing file. The file name can be drive-absolute,
drive-relative, working-directory relative, or begin with an explicit network sh
are name. This is the common case.
4. If the initial character of szDisplayName is an @ , the function fi
nds the longest string immediately following it that conforms to the legal ProgI
D syntax. The function converts this string to a CLSID using the CLSIDFromProgID
function. If the CLSID represents a COM class, the function loads the correspon
ding class object and asks for an IParseDisplayName interface pointer. The resul
ting IParseDisplayName interface is then given the whole string to parse, starti
ng with the @ . If the CLSID represents an OLE 1 class, then the function treats th
e string following the ProgID as an OLE1/DDE link designator having <filename>!<
item> syntax.
See Also
IMoniker::ParseDisplayName, IMoniker::GetDisplayName, IParseDisplayName

15.3.3 MkParseDisplayNameEx
Given a string, this function returns a moniker of the object that the string de
notes.
HRESULT MkParseDisplayNameEx(
IBindCtx*pbc, //Pointer to the bind context
LPWSTR szDisplayName, //Display name to be parsed
ULONG *pcchEaten, //Pointer to the number of characters of the display nam
e successfully parsed
IMoniker ** ppmk //Address of output variable that receives
// IMoniker interface pointer
);
Parameters
pbc
[in] Pointer to the bind context in which to accumulate bound objects.
szDisplayName
[in] Display name to be parsed.
pcchEaten
[out] Pointer to the number of characters of the display name that were successf
ully parsed. Most useful on syntax error, when a non-zero value is often returne
d and therefore a subsequent call to MkParseDisplayNameEx with the same pbc and
a shortened szDisplayName should return a valid moniker.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the resulting moniker.
Return Values
S_OK
The operation was successful.
MK_E_SYNTAX
Parsing failed because szDisplayName could only be partially resolved into a mon
iker. In this case, *pcchEaten has the number of characters that were successful
ly parsed into a moniker prefix.
E_OUTOFMEMORY
The operation ran out of memory.
Remarks
Given a string, this function returns a moniker for the object that the string d
enotes. This operation is known as parsing. A display name is parsed into a moni
ker; it is resolved into its component moniker parts.
If a syntax error occurs, then an indication of how much of the string was succe
ssfully parsed is returned in *pcchEaten and NULL is returned through **ppmk. Ot
herwise, the value returned through *pcchEaten indicates the entire size of the
display name.
This function differs from the original MkParseDisplayName function in that it s
upports Universal Resource Indicator (URI) syntax. See the IETC RFC1630 specific
ation for more information on URIs.
Parsing a display name may in some cases be as expensive as binding to the objec
t that it denotes, since, along the way, the parsing mechanism must connect to v
arious non-trivial name space managers (such as a spreadsheet application that c
an parse into ranges in its sheets). As might be expected, objects are not relea
sed by the parsing operation itself, but are instead handed over to the bind con
text that was passed in through IBindCtx::RegisterObjectBound. Thus, if the moni
ker resulting from the parse is immediately bound using this same bind context,
redundant loading of objects is maximally avoided.
In many other cases, however, parsing a display name may be quite inexpensive si
nce a single name-space manager may quickly return a moniker that will perform f
urther expensive analysis on any acceptable name during IMoniker::BindToObject o
r other methods. An example of such an inexpensive parser is the Win32 implement
ation of a File Moniker. A theoretical example would be a naive URL moniker whic
h parsed from any valid URL strings (i.e., http: , file: ) and only during binding took
time to resolve the string against the Internet, a potentially expensive operati
on.
The parsing process is an inductive one, in that there is an initial step that g
ets the process going, followed by the repeated application of an inductive step
. At any point after the beginning of the parse, a certain prefix of szDisplayNa
me has been parsed into a moniker, and a suffix of the display name remains unre
solved.
The inductive step asks the moniker-so-far using IMoniker::ParseDisplayName to c
onsume as much as it would like of the remaining suffix and return the correspon
ding moniker and the new suffix. The moniker is composed onto the end of the exi
sting moniker, and the process repeats.
Implementations of IMoniker::ParseDisplayName vary in exactly where the knowledg
e of how to carry out the parsing is kept. Some monikers by their nature are onl
y used in particular kinds of containers. It is likely that these monikers thems
elves have the knowledge of the legal display name syntax within the objects tha
t they themselves denote, so they can carry out the processes completely within
IMoniker::ParseDisplayName. The common case, however, is that the moniker is gen
eric in the sense that is not specific to one kind of container, and thus cannot
know the legal syntax for elements within the container. File monikers are an e
xample of these, as are Item Monikers. These monikers in general employ the foll
owing strategy to carry out parsing. First, the moniker connects to the class of
object that it currently denotes, asking for IParseDisplayName interface. If th
at succeeds, then it 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 interface. If this fai
ls, then the parse is aborted.
The effect is that ultimately an object always gets to be in control of the synt
ax of elements contained inside of itself. It s just that objects of a certain nat
ure can carry out parsing more efficiently by having a moniker or their class do
the parsing on their behalf.
Notice that since MkParseDisplayNameEx knows nothing of the legal syntax of disp
lay names (with the exception of the initial parsing step; see below). It is of
course beneficial to the user that display names in different contexts not have
gratuitously different syntax. While there some rare situations which call for s
pecial purpose syntax, it is recommended that, unless there are compelling reaso
ns to do otherwise, the syntax for display names should be the same as or simila
r to the native file system syntax. The aim is to build on user familiarity. Mos
t important about this are the characters allowed for the delimiters used to sep
arate the display name of one of the component monikers from the next. Unless th
rough some special circumstances they have very good reason not to, all moniker
implementations should use inter-moniker delimiters from the following character
set:
\ / : ! [
Standardization in delimiters promotes usability. But more importantly, notice t
hat the parsing algorithm has the characteristic that a given container consumes
as much as it can of the string being parsed before passing the remainder on to
the designated object inside themselves. If the delimiter expected of the next-
to-be-generated moniker in fact forms (part of) a valid display name in the cont
ainer, then the container s parse will consume it!
Monikers and objects which 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 s
hould also show delimiters appropriate to the platform on which they are current
ly running, even if they were originally created on a different platform. In tot
al, users will always deal with delimiters appropriate for the host platform.
The initial step of the parsing process is a bit tricky, in that it needs to som
ehow determine the initial moniker. MkParseDisplayNameEx is omniscient with resp
ect to the syntax with which the display name of a moniker may legally begin, an
d it uses this omniscience to choose the initial moniker.
The initial moniker is determined by trying the following strategies in order, u
sing the first to succeed.
1. ProgID: Case: If a prefix of szDisplayName conforms to the legal P
rogID syntax, is more than 1 character long, and is followed by a colon ( : ), the P
rogID is converted to a CLSID with CLSIDFromProgID, An instance of this class is
asked for the IParseDisplayName interface, and IParseDisplayName:ParseDisplayNa
me is called with the entire szDisplayName. This case distinguishes MkParseDispl
ayNameEx from MkParseDisplayName.
2. ROT Case: All prefixes of szDisplayName that consist solely of v
alid file name characters are consulted as file monikers in the Running Object T
able.
3. File-System Case: The file system is consulted to check if a pre
fix of szDisplayName matches an existing file. Said file name may be drive absol
ute, drive relative, working-directory relative, or begin with an explicit netwo
rk share name. This is a common case.
4. @ProgID Case: If the initial character of szDisplayName is @ , then t
he maximal string immediately following the @ which conforms to the legal ProgID s
yntax is determined. This is converted to a CLSID with CLSIDFromProgID. An insta
nce of this class is asked in turn for IParseDisplayName interface; the IParseDi
splayName interface so found is then given the whole string (starting with the @ )
to continue parsing.
See Also
IParseDisplayName

15.3.4 MonikerCommonPrefixWith
Creates a new moniker based on the common prefix that this moniker (the one comp
rising the data of this moniker object) shares with another moniker. This functi
on is intended to be called only in implementations of IMoniker::CommonPrefixWit
h.
WINOLEAPI MonikerCommonPrefixWith(
LPMONIKER pmkThis, //Pointer to the first moniker being compared
LPMONIKER pmkOther, //Pointer to the second moniker being compared
LPMONIKER FAR *ppmkCommon //Address of output variable that receives the
// IMoniker interface pointer
);
Parameters
pmkThis
[in] Pointer to the IMoniker interface on one of the monikers for which a common
prefix is sought; usually the moniker in which this call is used to implement I
Moniker::CommonPrefixWith.
pmkOther
[in] Pointer to the IMoniker interface on the other moniker to compare with the
first moniker.
ppmkCommon
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the moniker based on the common prefix of pmkThis and pmkOther. When successf
ul, the function has called IUnknown::AddRef on the moniker and the caller is re
sponsible for calling IUnknown::Release. If an error occurs, the supplied interf
ace pointer value is NULL.
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
A common prefix exists that is neither pmkThis nor pmkOther.
MK_S_HIM
The entire pmkOther moniker is a prefix of the pmkThis moniker.
MK_S_ME
The entire pmkThis moniker is a prefix of the pmkOther moniker.
MK_S_US
The pmkThis and pmkOther monikers are equal.
MK_E_NOPREFIX
The monikers have no common prefix.
MK_E_NOTBINDABLE
This function was called on a relative moniker. It is not meaningful to take the
common prefix of relative monikers.
Remarks
Call MonikerCommonPrefixWith only in the implementation of IMoniker::CommonPrefi
xWith for a new moniker class.
Your implementation of IMoniker::CommonPrefixWith should first check whether the
other moniker is of a type that you recognize and handle in a special way. If n
ot, you should call MonikerCommonPrefixWith, passing itself as pmkThis and the o
ther moniker as pmkOther. MonikerCommonPrefixWith correctly handles the cases wh
ere either moniker is a generic composite.
You should call this function only if pmkThis and pmkOther are both absolute mon
ikers (where an absolute moniker is either a file moniker or a generic composite
whose leftmost component is a file moniker, and where the file moniker represen
ts an absolute path). Do not call this function on relative monikers.
See Also
IMoniker::CommonPrefixWith

15.3.5 MonikerRelativePathTo
Provides a moniker that, when composed onto the end of the first specified monik
er (or one with a similar structure), yields the second specified moniker. This
function is intended for use only by IMoniker::RelativePathTo implementations.
WINOLEAPI MonikerRelativePathTo(
LPMONIKER pmkSrc, //Pointer to the source identified by the moniker

LPMONIKER pmkDest, //Pointer to the destination identified by the moniker


LPMONIKER FAR * ppmkRelPath, //Address of output variable that receiv
es the
//IMoniker interface pointer
BOOL dwReserved //Reserved; must be non-zero
);
Parameters
pmkSrc
[in] Pointer to the IMoniker interface on the moniker that, when composed with t
he relative moniker to be created, produces pmkDest. This moniker identifies the
source of the relative moniker to be created.
pmkDest
[in] Pointer to the IMoniker interface on the moniker to be expressed relative t
o pmkSrc. This moniker identifies the destination of the relative moniker to be
created.
ppmkRelPath
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the new relative moniker. When successful, the function has called IUnknown::
AddRef on the moniker and the caller is responsible for calling IUnknown::Releas
e. If an error occurs, the interfacepointer value is NULL.
dwReserved
[in] Reserved; must be non-zero.
Return Values
This function supports the standard return value E_INVALIDARG, E_OUTOFMEMORY, an
d E_UNEXPECTED, as well as the following:
S_OK
A meaningful relative path has been returned.
MK_S_HIM
The only form of the relative path is the other moniker.
MK_E_NOTBINDABLE
Indicates that pmkSrc is a relative moniker, such as an item moniker, and must b
e composed with the moniker of its container before a relative path can be deter
mined.
Remarks
Call MonikerRelativePathTo only in the implementation of IMoniker::RelativePathT
o if you are implementing a new moniker class.
Your implementation of IMoniker::RelativePathTo should first check whether the o
ther moniker is of a type you recognize and handle in a special way. If not, you
should call MonikerRelativePathTo, passing itself as pmkThis and the other moni
ker as pmkOther. MonikerRelativePathTo correctly handles the cases where either
moniker is a generic composite.
You should call this function only if pmkSrc and pmkDest are both absolute monik
ers, where an absolute moniker is either a file moniker or a generic composite w
hose leftmost component is a file moniker, and where the file moniker represents
an absolute path. Do not call this function on relative monikers.
See Also
IMoniker::RelativePathTo
15.3.6 CreateAntiMoniker
Creates and supplies a new anti-moniker.
WINOLEAPI CreateAntiMoniker(
LPMONIKER FAR *ppmk //Address of output variable that receives
// the IMoniker interface pointer
);
Parameter
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the new anti-moniker. When successful, the function has called IUnknown::AddR
ef on the anti-moniker and the caller is responsible for calling IUnknown::Relea
se. When an error occurs, the pointer is NULL.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The anti-moniker has been created successfully.
Remarks
You would call this function only if you are writing your own moniker class (imp
lementing the IMoniker interface). If you are writing a new moniker class that h
as no internal structure, you can use CreateAntiMoniker in your implementation o
f the IMoniker::Inverse method, and then check for an anti-moniker in your imple
mentation of IMoniker::ComposeWith.
Like the .. directory in MS-DOS file systems, which acts as the inverse to any dir
ectory name just preceding it in a path, an anti-moniker acts as the inverse of
a simple moniker that precedes it in a composite moniker. An anti-moniker is use
d as the inverse of simple monikers with no internal structure. For example, the
system-provided implementations of file monikers, item monikers, and pointer mo
nikers all use anti-monikers as their inverse; consequently, an anti-moniker com
posed to the right of one of these monikers composes to nothing.
A moniker client (an object that is using a moniker to bind to another object) t
ypically does not know the class of a given moniker, so the client cannot be sur
e that an anti-moniker is the inverse. Therefore, to get the inverse of a monike
r, you would call IMoniker::Inverse rather than CreateAntiMoniker.
To remove the last piece of a composite moniker, you would do the following:
1. Call IMoniker::Enum on the composite, specifying FALSE as the fi
rst parameter. This creates an enumerator that returns the component monikers in
reverse order.
2. Use the enumerator to retrieve the last piece of the composite.
3. Call IMoniker::Inverse on that moniker. The moniker returned by
IMoniker::Inverse will remove the last piece of the composite.
See Also
IMoniker::Inverse, IMoniker::ComposeWith, IMoniker - Anti-Moniker Implementation
ISequentialStream::Read storage and stream access errors.

15.3.7 CreateBindCtx
Supplies a pointer to an implementation of IBindCtx (a bind context object). Thi
s object stores information about a particular moniker-binding operation. The po
inter this function supplies is required as a parameter in many methods of the I
Moniker interface and in certain functions related to monikers.
WINOLEAPI CreateBindCtx(
DWORD reserved, //Reserved for future use
LPBC FAR* ppbc //Address of output variable that receives the
// IBindCtx interface pointer
);
Parameters
reserved
[in] Reserved for future use; must be zero.
ppbc
[out] onAddress of IBindCtx* pointer variable that receives the interface pointe
r to the new bind context object. When the function is successful, the caller is
responsible for calling IUnknown::Release on the bind context . A NULL value fo
r the bind context indicates that an error occurred.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The bind context was allocated and initialized successfully.
Remarks
CreateBindCtx is most commonly used in the process of binding a moniker (locatin
g and getting a pointer to an interface by identifying it through a moniker), as
in the following steps:
1. Get a pointer to a bind context by calling the CreateBindCtx fun
ction.
2. Call the IMoniker::BindToObject method on the moniker, retrievin
g an interface pointer to the object to which the moniker refers.
3. Release the bind context.
4. Use the interface pointer.
5. Release the interface pointer.
The following code fragment illustrates these steps:
// pMnk is an IMoniker * that points to a previously acquired moniker
IFoo *pFoo;
IBindCtx *pbc;
CreateBindCtx( 0, &pbc );
pMnk->BindToObject( pbc, NULL, IID_IFoo, &pFoo );
pbc->Release();
// pFoo now points to the object; safe to use pFoo
pFoo->Release();
Bind contexts are also used in other methods of the IMoniker interface besides I
Moniker::BindToObject and in the MkParseDisplayName function.
A bind context retains references to the objects that are bound during the bindi
ng operation, causing the bound objects to remain active (keeping the object s ser
ver running) until the bind context is released. Reusing a bind context when sub
sequent operations bind to the same object can improve performance. You should,
however, release the bind context as soon as possible, because you could be keep
ing the objects activated unnecessarily.
A bind context contains a BIND_OPTS structure, which contains parameters that ap
ply to all steps in a binding operation. When you create a bind context using Cr
eateBindCtx, the fields of the BIND_OPTS structure are initialized to the follow
ing values:
cbStruct = sizeof(BIND_OPTS)
grfFlags = 0
grfMode = STGM_READWRITE
dwTickCountDeadline = 0.
You can call the IBindCtx::SetBindOptions method to modify these default values.
See Also
BIND_OPTS, IBindCtx, IMoniker, MkParseDisplayName

15.3.8 CreateClassMoniker
Creates a file moniker based on the specified path.
WINOLEAPI CreateClassMoniker(

REFCLSID rclsid, //Class this moniker binds to


IMoniker **ppmk //Address of output variable that receives the
// IMoniker interface pointer
);
Parameters
rclsid
[in] Reference to the CLSID of the object type to which this moniker binds.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the new class moniker. On successful return, the function has called IUnknown
::AddRef on the moniker and the caller is responsible for calling IUnknown::Rele
ase. When an error occurs, the value of the moniker pointer is NULL.
Return Values
S_OK
The moniker has been created successfully.
E_INVALIDARG
One or more arguments are invalid.
Remarks
CreateClassMoniker creates a class moniker that refers to the given class. The c
lass moniker will supports binding to a fresh instance of the class identified b
y the CLSID in rclsid..
See Also
IMoniker - Class Moniker Implementation
15.3.9 CreateFileMoniker
Creates a file moniker based on the specified path.
WINOLEAPI CreateFileMoniker(
LPCOLESTR lpszPathName, //Pointer to path to be used
LPMONIKER FAR *ppmk //Address of output variable that receives
// the IMoniker interface pointer
);
Parametersointer to a zero-terminated wide character string (two bytes per chara
cter) containing the path on which this moniker is based.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the new file moniker. When successful, the function has called IUnknown::AddR
ef on the file moniker and the caller is responsible for calling IUnknown::Relea
se. When an error occurs, the value of the interface pointer is NULL.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The moniker has been created successfully.
MK_E_SYNTAX
Error in the syntax of a path was encountered while creating a moniker.
Remarks
CreateFileMoniker creates a moniker for an object that is stored in a file. A mo
niker provider (an object that provides monikers to other objects) can call this
function to create a moniker to identify a file-based object that it controls,
and can then make the pointer to this moniker available to other objects. An obj
ect identified by a file moniker must also implement the IPersistFile interface
so it can be loaded when a file moniker is bound.
When each object resides in its own file, as in an COM server application that s
upports linking only to file-based documents in their entirety, file monikers ar
e the only type of moniker necessary. To identify objects smaller than a file, t
he moniker provider must use another type of moniker (such as an item moniker) i
n addition to file monikers, creating a composite moniker. Composite monikers wo
uld be needed in an COM server application that supports linking to objects smal
ler than a document (such as sections of a document or embedded objects).
The lpszPathName can be a relative path, a UNC path (e.g., \\server\share\path),
or a drive-letter-based path (e.g., c:\). If based on a relative path, the resu
lting moniker must be composed onto another file moniker before it can be bound.
A file moniker can be composed to the right only of another file moniker when th
e first moniker is based on an absolute path and the other is a relative path, r
esulting in a single file moniker based on the combination of the two paths. A m
oniker composed to the right of another moniker must be a refinement of that mon
iker, and the file moniker represents the largest unit of storage. To identify o
bjects stored within a file, you would compose other types of monikers (usually
item monikers) to the right of a file moniker.
See Also
IMoniker - File Moniker Implementation

15.3.10 CreateGenericComposite
Performs a generic composition of two monikers and supplies a pointer to the res
ulting composite moniker.
WINOLEAPI CreateGenericComposite(
LPMONIKER pmkFirst, //Pointer to the first moniker
LPMONIKER pmkRest, //Pointer to the second moniker
LPMONIKER FAR *ppmkComposite //Address of output variable that receiv
es the
// IMoniker interface pointer
);
Parameters
pmkFirst
[in] Pointer to the moniker to be composed to the left of the moniker that pmkRe
st points to. Can point to any kind of moniker, including a generic composite.
pmkRest
[in] Pointer to the moniker to be composed to the right of the moniker that pmkF
irst points to. Can point to any kind of moniker compatible with the type of the
pmkRest moniker, including a generic composite.
ppmkComposite
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the composite moniker object that is the result of composing pmkFirst and pmk
Rest. This object supports the COM composite moniker implementation of IMoniker.
When successful, the function has called IUnknown::AddRef on the moniker and t
he caller is responsible for calling IUnknown::Release. If either pmkFirst or pm
kRest are NULL, the supplied pointer is the one that is non-NULL. If both pmkFir
st and pmkRest are NULL, or if an error occurs, the returned pointer is NULL.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The two input monikers were successfully composed.
MK_E_SYNTAX
The two monikers could not be composed due to an error in the syntax of a path (
for example, if both pmkFirst and pmkRest are file monikers based on absolute pa
ths).
Remarks
CreateGenericComposite joins two monikers into one. The moniker classes being jo
ined can be different, subject only to the rules of composition. Call this funct
ion only if you are writing a new moniker class by implementing the IMoniker int
erface, within an implementation of IMoniker::ComposeWith that includes generic
composition capability.
Moniker providers should call IMoniker::ComposeWith to compose two monikers toge
ther. Implementations of ComposeWith should (as do COM implementations) attempt,
when reasonable for the class, to perform non-generic compositions first, in wh
ich two monikers of the same class are combined. If this is not possible, the im
plementation can call CreateGenericComposite to do a generic composition, which
combines two monikers of different classes, within the rules of composition. You
can define new types of non-generic compositions if you write a new moniker cla
ss.
During the process of composing the two monikers, CreateGenericComposite makes a
ll possible simplifications. Consider the example where pmkFirst is the generic
composite moniker, A + B + C, and pmkRest is the generic composite moniker, C -1
+ B -1 + Z (where C -1 is the inverse of C). The function first composes C to C
-1, which composes to nothing. Then it composes B and B -1 to nothing. Finally,
it composes A to Z, and supplies a pointer to the generic composite moniker, A
+ Z.
See Also
IMoniker::ComposeWith, IMoniker - Generic Composite Moniker Implementation

15.3.11 CreateItemMoniker
Creates an item moniker that identifies an object within a containing object (ty
pically a compound document).
WINOLEAPI CreateItemMoniker(
LPCOLESTR lpszDelim, //Pointer to delimiter string
LPCOLESTR lpszItem, //Pointer to item name
LPMONIKER FAR *ppmk //Address of output variable that receives
// the IMoniker interface pointer
);
Parameters
lpszDelim
[in] Pointer to a wide character string (two bytes per character) zero-terminate
d string containing the delimiter (typically ! ) used to separate this item s display
name from the display name of its containing object.
lpszItem
[in] Pointer to a zero-terminated string indicating the containing object s name f
or the object being identified. This name can later be used to retrieve a pointe
r to the object in a call to IOleItemContainer::GetObject.
ppmk
[outAddress of IMoniker* pointer variable that receives the interface pointer to
the the new item moniker. When successful, the function has called IUnknown::Ad
dRef on the item moniker and the caller is responsible for calling IUnknown::Rel
ease. If an error occurs, the supplied interface pointer has a NULL value.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The moniker was created successfully.
Remarks
A moniker provider, which hands out monikers to identify its objects so they are
accessible to other parties, would call CreateItemMoniker to identify its objec
ts with item monikers. Item monikers are based on a string, and identify objects
that are contained within another object and can be individually identified usi
ng a string. The containing object must also implement the IOleContainer interfa
ce.
Most moniker providers are COM applications that support linking. Applications t
hat support linking to objects smaller than file-based documents, such as a serv
er application that allows linking to a selection within a document, should use
item monikers to identify the objects. Container applications that allow linking
to embedded objects use item monikers to identify the embedded objects.
The lpszItem parameter is the name used by the document to uniquely identify the
object. For example, if the object being identified is a cell range in a spread
sheet, an appropriate name might be something like A1:E7. An appropriate name when
the object being identified is an embedded object might be something like embedo
bj1. The containing object must provide an implementation of the IOleItemContaine
r interface that can interpret this name and locate the corresponding object. Th
is allows the item moniker to be bound to the object it identifies.
Item monikers are not used in isolation. They must be composed with a moniker th
at identifies the containing object as well. For example, if the object being id
entified is a cell range contained in a file-based document, the item moniker id
entifying that object must be composed with the file moniker identifying that do
cument, resulting in a composite moniker that is the equivalent of C:\work\sales.
xls!A1:E7.
Nested containers are allowed also, as in the case where an object is contained
within an embedded object inside another document. The complete moniker of such
an object would be the equivalent of C:\work\report.doc!embedobj1!A1:E7. In this c
ase, each containing object must call CreateItemMoniker and provide its own impl
ementation of the IOleItemContainer interface.
See Also
IMoniker::ComposeWith, IOleItemContainer, IMoniker - Item Moniker Implementation

15.3.12 CreatePointerMoniker
Creates a pointer moniker based on a pointer to an object.
WINOLEAPI CreatePointerMoniker(
LPUNKNOWN punk, //Pointer to the interface to be used
LPMONIKER FAR *ppmk //Address of output variable that receives
// the IMoniker interface pointer
);
Parameters
punk
[in] Pointer to an IUnknown interface on the object to be identified by the resu
lting moniker.
ppmk
[out] Address of IMoniker* pointer variable that receives the interface pointer
to the new pointer moniker. When successful, the function has called IUnknown::A
ddRef on the moniker and the caller is responsible for calling IUnknown::Release
. When an error occurs, the returned interface pointer has a NULL value.
Return Values
This function supports the standard return values E_OUTOFMEMORY and E_UNEXPECTED
, as well as the following:
S_OK
The pointer moniker was created successfully.
Remarks
A pointer moniker wraps an existing interface pointer in a moniker that can be p
assed to those interfaces that require monikers. Pointer monikers allow an objec
t that has no persistent representation to participate in a moniker-binding oper
ation.
Pointer monikers are not commonly used, so this function is not often called.
See Also
IMoniker - Pointer Moniker Implementation
15.4 Moniker Structure Definitions
15.4.1 BIND_OPTS
Contains parameters used during a moniker-binding operation. The BIND_OPTS2 stru
cture may be used in its place. A BIND_OPTS structure is stored in a bind contex
t; the same bind context is used by each component of a composite moniker during
binding, allowing the same parameters to be passed to all components of a compo
site moniker. See IBindCtx for more information about bind contexts.
If you re a moniker client (that is, you use a moniker to acquire an interface poi
nter to an object), you typically do not need to specify values for the fields o
f this structure. The CreateBindCtx function creates a bind context with the bin
d options set to default values that are suitable for most situations; the BindM
oniker function does the same thing when creating a bind context for use in bind
ing a moniker. If you want to modify the values of these bind options, you can d
o so by passing a BIND_OPTS structure to the IBindCtx::SetBindOptions method. Mo
niker implementers can pass a BIND_OPTS structure to the IBindCtx::GetBindOption
s method to retrieve the values of these bind options.
The BIND_OPTS structure is defined in OBJIDL.IDL
typedef struct tagBIND_OPTS
{
DWORD cbStruct;
DWORD grfFlags;
DWORD grfMode;
DWORD dwTickCountDeadline;
} BIND_OPTS, *LPBIND_OPTS;
Members
cbStruct
Size of this structure in bytes (that is, the size of the BIND_OPTS structure).
grfFlags
Flags that control aspects of moniker binding operations. This value is any comb
ination of the bit flags in the BINDFLAGS enumeration. New values may be defined
in the future, so moniker implementations should ignore any bits in this field
that they do not understand. The CreateBindCtx function initializes this field t
o zero.
grfMode
Flags that should be used when opening the file that contains the object identif
ied by the moniker. The values are taken from the STGM enumeration. The binding
operation uses these flags in the call to IPersistFile::Load when loading the fi
le. If the object is already running, these flags are ignored by the binding ope
ration. The CreateBindCtx function initializes this field to STGM_READWRITE.
dwTickCountDeadline
Clock time (in milliseconds, as returned by the GetTickCount function) by which
the caller would like the binding operation to be completed. This member lets th
e caller limit the execution time of an operation when speed is of primary impor
tance. A value of zero indicates that there is no deadline. Callers most often u
se this capability when calling the IMoniker::GetTimeOfLastChange method, though
it can be usefully applied to other operations as well. The CreateBindCtx funct
ion initializes this field to zero.
Typical deadlines allow for a few hundred milliseconds of execution. This deadli
ne is a recommendation, not a requirement; however, operations that exceed their
deadline by a large amount may cause delays for the end user. Each moniker impl
ementation should try to complete its operation by the deadline, or fail with th
e error MK_E_EXCEEDEDDEADLINE.
If a binding operation exceeds its deadline because one or more objects that it
needs are not running, the moniker implementation should register the objects re
sponsible in the bind context using the IBindCtx::RegisterObjectParam. The objec
ts should be registered under the parameter names ExceededDeadline , ExceededDeadlin
e1 , ExceededDeadline2 , and so on. If the caller later finds the object in the Runni
ng Object Table, the caller can retry the binding operation.
The GetTickCount function indicates the number of milliseconds since system star
tup, and wraps back to zero after 2^31 milliseconds. Consequently, callers shoul
d be careful not to inadvertently pass a zero value (which indicates no deadline
), and moniker implementations should be aware of clock wrapping problems (see t
he GetTickCount function for more information).
See Also
BIND_OPTS2, BIND_FLAGS, CreateBindCtx, IBindCtx::SetBindOptions
15.4.2 BIND_OPTS2
Contains parameters used during a moniker-binding operation. A BIND_OPTS2 struct
ure is stored in a bind context; the same bind context is used by each component
of a composite moniker during binding, allowing the same parameters to be passe
d to all components of a composite moniker. See IBindCtx for more information ab
out bind contexts. BIND_OPTS2 replaces the previously defined BIND_OPTS structur
e, including the previously defined members, and adding four new members.
Moniker clients (those using a moniker to acquire an interface pointer to an obj
ect) typically do not need to specify values for the fields of this structure. T
he CreateBindCtx function creates a bind context with the bind options set to de
fault values that are suitable for most situations. The BindMoniker function doe
s the same thing when creating a bind context for use in binding a moniker. If y
ou want to modify the values of these bind options, you can do so by passing a B
IND_OPTS2 structure to the IBindCtx::SetBindOptions method. Moniker implementers
can pass a BIND_OPTS2 structure to the IBindCtx::GetBindOptions method to retri
eve the values of these bind options.
The BIND_OPTS2 structure is defined in OBJIDL.IDL
typedef struct tagBIND_OPTS2 {
DWORD cbStruct; // sizeof(BIND_OPTS2)
DWORD grfFlags;
DWORD grfMode;
DWORD dwTickCountDeadline;
DWORD dwTrackFlags;
DWORD dwClassContext;
LCID locale;
COSERVERINFO * pServerInfo;
} BIND_OPTS2, * LPBIND_OPTS2;
Members
cbStruct
Size of this structure in bytes (that is, the size of the BIND_OPTS2 structure).
grfFlags
Flags that control aspects of moniker binding operations. This value is any comb
ination of the bit flags in the BINDFLAGS enumeration. New values may be defined
in the future, so moniker implementations should ignore any bits in this field
that they do not understand. The CreateBindCtx function initializes this field t
o zero.
grfMode
Flags that should be used when opening the file that contains the object identif
ied by the moniker. The values are taken from the STGM enumeration. The binding
operation uses these flags in the call to IPersistFile::Load when loading the fi
le. If the object is already running, these flags are ignored by the binding ope
ration. The CreateBindCtx function initializes this field to STGM_READWRITE.
dwTickCountDeadline
Clock time (in milliseconds, as returned by the GetTickCount function) by which
the caller would like the binding operation to be completed. This member lets th
e caller limit the execution time of an operation when speed is of primary impor
tance. A value of zero indicates that there is no deadline. Callers most often u
se this capability when calling the IMoniker::GetTimeOfLastChange method, though
it can be usefully applied to other operations as well. The CreateBindCtx funct
ion initializes this field to zero.
Typical deadlines allow for a few hundred milliseconds of execution. This deadli
ne is a recommendation, not a requirement; however, operations that exceed their
deadline by a large amount may cause delays for the end user. Each moniker impl
ementation should try to complete its operation by the deadline, or fail with th
e error MK_E_EXCEEDEDDEADLINE.
If a binding operation exceeds its deadline because one or more objects that it
needs are not running, the moniker implementation should register the objects re
sponsible in the bind context using the IBindCtx::RegisterObjectParam. The objec
ts should be registered under the parameter names ExceededDeadline , ExceededDeadlin
e1 , ExceededDeadline2 , and so on. If the caller later finds the object in the Runni
ng Object Table, the caller can retry the binding operation.
The GetTickCount function indicates the number of milliseconds since system star
tup, and wraps back to zero after 2^31 milliseconds. Consequently, callers shoul
d be careful not to inadvertently pass a zero value (which indicates no deadline
), and moniker implementations should be aware of clock wrapping problems (see t
he GetTickCount function for more information).
dwTrackFlags
A moniker can use this value during link tracking. If the original persisted dat
a that the moniker is referencing has been moved, the moniker can attempt to ree
stablish the link by searching for the original data though some adequate mechan
ism. dwTrackFlags provides additional information on how the link should be reso
lved. See the documentation of the fFlags parameter in IShellLink::Resolve in th
e Win32 SDK for more details.
COM's file moniker implementation uses the shell link mechanism to reestablish l
inks and passes these flags to IShellLink::Resolve.
dwClassContext
The class context, taken from the CLSCTX enumeration, that is to be used for ins
tantiating the object. Monikers typically pass this value to the dwClsContext pa
rameter of CoCreateInstance.
locale
The LCID value indicating the client s preference for the locale to be used by the
object to which they are binding. A moniker passes this value to IClassActivato
r::GetClassObject.
pServerInfo
Points to a COSERVERINFO structure. This member allows clients calling IMoniker:
:BindToObject to specify server information. Clients may pass a BIND_OPTS2 struc
ture to the IBindCtx::SetBindOptions method. If a server name is specified in th
e COSERVERINFO struct, the moniker bind will be forwarded to the specified machi
ne. SetBindOptions only copies the struct members of BIND_OPTS2, not the COSERVE
RINFO structure and the pointers it contains. Callers may not free any of these
pointers until the bind context is released. COM's new class moniker does not cu
rrently honor the pServerInfo flag.
See Also
BIND_OPTS, BIND_FLAGS, CreateBindCtx, IBindCtx::SetBindOptions
15.5 Moniker Enumeration Definitions
15.5.1 BIND_FLAGS
The BIND_FLAGS enumeration values are used to control aspects of moniker binding
operations. The values are used in the BIND_OPTS structure. Callers of IMoniker
methods can specify values from this enumeration, and implementers of IMoniker
methods can use these values in determining what they should do.
The BIND_FLAGS enumeration is defined in OBJIDL.IDL and in OBJIDL.H]
typedef enum tagBIND_FLAGS
{
BIND_MAYBOTHERUSER = 1,
BIND_JUSTTESTEXISTENCE = 2,
} BIND_FLAGS;
Elements
BIND_MAYBOTHERUSER
If this flag is specified, the moniker implementation can interact with the end
user. If not present, the moniker implementation should not interact with the us
er in any way, such as by asking for a password for a network volume that needs
mounting. If prohibited from interacting with the user when it otherwise would,
a moniker implementation can use a different algorithm that does not require use
r interaction, or it can fail with the error MK_MUSTBOTHERUSER.
BIND_JUSTTESTEXISTENCE
If this flag is specified, the caller is not interested in having the operation
carried out, but only in learning whether the operation could have been carried
out had this flag not been specified. For example, this flag lets the caller ind
icate only an interest in finding out whether an object actually exists by using
this flag in a IMoniker::BindToObject call. Moniker implementations can, howeve
r, ignore this possible optimization and carry out the operation in full. Caller
s must be able to deal with both cases.
See Also
BIND_OPTS, IBindCtx

15.5.2 MKRREDUCE
The MKRREDUCE enumeration constants are used to specify how far the moniker shou
ld be reduced. They are used in the IMoniker::Reduce method. MKRREDUCE is defined
in OBJIDL.IDL and in OBJIDL.H].
typedef enum tagMKRREDUCE
{
MKRREDUCE_ONE = 3<<16,
MKRREDUCE_TOUSER = 2<<16,
MKRREDUCE_THROUGHUSER = 1<<16,
MKRREDUCE_ALL = 0
} MKRREDUCE;
Elements
MKRREDUCE_ONE
Performs only one step of reducing the moniker. In general, the caller must have
specific knowledge about the particular kind of moniker to take advantage of th
is option.
MKRREDUCE_TOUSER
Reduces the moniker to a form that the user identifies as a persistent object. I
f no such point exists, then this option should be treated as MKRREDUCE_ALL.
MKRREDUCE_THROUGHUSER
Reduces the moniker to where any further reduction would reduce it to a form tha
t the user does not identify as a persistent object. Often, this is the same sta
ge as MKRREDUCE_TOUSER.
MKRREDUCE_ALL
Reduces the moniker until it is in its simplest form, that is, reduce it to itse
lf.
See Also
IMoniker::Reduce

15.5.3 MKSYS
The MKSYS enumeration constants indicate the moniker s class. They are returned fr
om the IMoniker::IsSystemMoniker method. MKSYS is defined in Objidl.h.
typedef enum tagMKSYS
{
MKSYS_NONE = 0,
MKSYS_GENERICCOMPOSITE = 1,
MKSYS_FILEMONIKER = 2,
MKSYS_ANTIMONIKER = 3,
MKSYS_ITEMMONIKER = 4,
MKSYS_POINTERMONIKER = 5,
MKSYS_CLASSMONIKER = 7
} MKSYS;
Elements
MKSYS_NONE
Indicates a custom moniker implementation.
MKSYS_GENERICCOMPOSITE
Indicates the system s generic composite moniker class.
MKSYS_FILEMONIKER
Indicates the system s file moniker class.
MKSYS_ANTIMONIKER
Indicates the system s anti-moniker class.
MKSYS_ITEMMONIKER
Indicates the system s item moniker class.
MKSYS_POINTERMONIKER
Indicates the system s pointer moniker class.
MKSYS_CLASSMONIKER
Indicates the system s class moniker class.
See Also
IMoniker::IsSystemMoniker
16. Uniform Data Transfer
COM provides a standard mechanism for transferring data between applications. Th
is mechanism is the data object, which is simply any COM object that implements
the IDataObject interface. Some data objects, such as a piece of text copied to
the clipboard, have IDataObject as their sole interface. Others, such as compoun
d document objects, expose several interfaces, of which IDataObject is simply on
e. Data objects are fundamental to the working of compound documents, although t
hey also have widespread application outside that COM technology.
By exchanging pointers to a data object, providers and consumers of data can man
age data transfers in a uniform manner, regardless of the format of the data, th
e type of medium used to transfer the data, or the target device on which it is
to be rendered. You can include support in your application for basic clipboard
transfers, drag and drop transfers, and COM compound document transfers with a s
ingle implementation of IDataObject. Having done that, the amount of code requir
ed to accommodate the special semantics of each protocol is minimal.
16.1 Data Transfer Interfaces
The IDataObject interface provides consumers of data with methods for getting an
d setting an object s data, determining which formats the object supports, and reg
istering for and receiving notifications when data in the object changes. When o
btaining data, a caller can specify the format in which it wants to render the d
ata. The source of the data, however, determines the storage medium, which it re
turns in an out parameter provided by the caller.
By itself, IDataObject supplies all the tools you need to implement Microsoft® Win
dows® clipboard transfers or compound document transfers in your applications. If
you also want to support drag and drop transfers, you need to implement the IDro
pSource and IDropTarget interfacesalong with IDataObject.
The IDataObject interface combined with COM clipboard APIs provide all the capab
ilities of the Microsoft® Win32® clipboard APIs. Using both the platform s clipboard A
PIs and COM s is usually redundant and unnecessary. Suppliers of data that support
either drag and drop transfers or COM compound documents must implement the IDa
taObject interface. If your application supports only clipboard transfers now, b
ut you intend to add drag and drop or compound documents in later releases, you
may want to implement IDataObject and the COM clipboard APIs now in order to min
imize the amount of time spent recoding and debugging later. You may also want t
o implement IDataObject in order to utilize transfer media other than global mem
ory.
16.2 Data Formats and Transfer Media
Most platforms, including Windows, define a standard protocol for transferring d
ata between applications, based on a set of functions called the clipboard. Appl
ications using these functions can share data even if their native data formats
are wildly different. Generally, these clipboards have two significant shortcomi
ngs that COM has overcome.
First, data descriptions use only a format identifier, such as the single 16-bit
clipboard format identifier on Windows, which means the clipboard can only desc
ribe the structure of its data, that is, the ordering of the bits. It can report
, I have a bitmap or I have some text, but it cannot specify the target devices for
which the data is composed, which views or aspects of itself the data can provid
e, or which storage media are best suited for its transfer. For example, it cann
ot report, I have a string of text that is stored in global memory and formatted
for presentation either on screen or on a printer or I have a thumbnail sketch bit
map rendered for a 100 dpi dot-matrix printer and stored as a disk file.
Second, all data transfers using the clipboard generally occur through global me
mory. Using global memory is reasonably efficient for small amounts of data but
horribly inefficient for large amounts, such as a 20 MB multimedia object. Globa
l memory is slow for a large data object, whose size requires considerable swapp
ing to virtual memory on disk. In cases where the data being exchanged is going
to reside mostly on disk anyway, forcing it through this virtual-memory bottlene
ck is highly inefficient. A better way would skip global memory entirely and sim
ply transfer the data directly to disk.
To alleviate these problems, COM introduces two new data structures: FORMATETC a
nd STGMEDIUM.
16.2.1 The FORMATETC Structure
The FORMATETC structure is a generalized clipboard format, enhanced to encompass
a target device, an aspect or view of the data, and a storage medium. A data co
nsumer, such as an COM container application, passes the FORMATETC structure as
an argument in calls to IDataObject to indicate the type of data it wants from a
data source, such as a compound document object. The source uses the FORMATETC
structure to describe what formats it can provide. FORMATETC can describe virtua
lly any data, including other objects such as monikers. A container can ask one
of its embedded objects to list its data formats by calling IDataObject::EnumFor
matetc, which returns an enumerator object that implements the IEnumFormatEtc in
terface. Instead of replying merely that it has text and a bitmap, the object can
provide a detailed description of the data, including the device (normally scree
n or printer) for which it is rendered, the aspect to be presented to the user (
full contents, thumbnail, icon, or formatted for printing), and the storage medi
um containing the data (global memory, disk file, storage object, or stream). Th
is ability to tightly describe data will, in time, result in higher quality prin
ter and screen output as well as more efficiency in data browsing, where a thumb
nail sketch is much faster to retrieve and display than a fully detailed renderi
ng.
The following table lists fields of the FORMATETC data structure and the informa
tion they specify:
Field Specifies
cfFormat The format in which the data is to be rendered, which can be a s
tandard clipboard format, a proprietary format, or an COM format.
ptd A DVTARGETDEVICE structure, which contains enough information about a Wi
ndows target device, such as a screen or printer, so that a handle to its device
context (hDC) can be created using the Windows CreateDC function.
dwAspect The aspect or view of the data to be rendered; can be the full c
ontents, a thumbnail sketch, an icon, or formatted for printing.
lindex The part of the aspect that is of interest; for the present, the value m
ust be -1, indicating that the entire view is of interest.
tymed The data s storage medium, which can be global memory, disk file, or an in
stance of one of COM s structured-storage interfaces.
16.2.2 The STGMEDIUM Structure
Just as the FORMATETC structure is an enhancement of the Windows clipboard forma
t identifier, so the STGMEDIUM structure is an improvement of the global memory
handle used to transfer the data. The STGMEDIUM structure includes a flag, tymed
, which indicates the medium to be used, and a union comprising pointers and a h
andle for getting whichever medium is specified in tymed.
The STGMEDIUM structure enables both data sources and consumers to choose the mo
st efficient exchange medium on a per-rendering basis. If the data is so big tha
t it should be kept on disk, the data source can indicate a disk-based medium in
its preferred format, only using global memory as a backup if that s the only med
ium the consumer understands. Being able to use the best medium for exchanges as
the default improves overall performance of data exchange between applications.
For example, if some of the data to be transferred is already on disk, the sour
ce application can move or copy it to a new destination, either in the same appl
ication or in some other, without having first to load the data into global memo
ry. At the receiving end, the consumer of the data does not have to write it bac
k to disk.
16.3 Data Notification
Objects that consume data from an external source sometimes need to be informed
when data in that source changes. For example, a stock ticker tape viewer that r
elies on data in some spreadsheet needs to be notified when that data changes so
it can update its display. Similarly, a compound document needs information abo
ut data changes in its embedded objects so that it can update its data caches. I
n cases such as this, where dynamic updating of data is desirable, sources of da
ta require some mechanism of notifying data consumers of changes as they occur w
ithout obligating the consumers to drop everything in order to update their data
. Ideally, having been notified that a change has occurred in the data source, a
consuming object can ask for an updated copy at its leisure.
COM s mechanism for handling asynchronous notifications of this type is an object
called an advise sink, which is simply any COM object that implements an interfa
ce called IAdviseSink. Consumers of data implement the IAdviseSink. They registe
r to receive notifications by handing a pointer to the data object of interest.
The IAdviseSink interfaces exposes the following methods for receiving asynchron
ous notifications:
Method Notifies the Advise Sink that
OnDataChange Calling object s data has changed.
OnViewChange Instructions for drawing the calling object have changed.
OnRename Calling object s moniker has changed.
OnSave Calling object has been saved to persistent storage.
OnClose Calling object has been closed.
As the table indicates, the IAdviseSink interface exposes methods for notifying
the advise sink of events other than changes in the calling object s data. The cal
ling object can also notify the sink when the way in which it draws itself chang
es, or it is renamed, saved, or closed. These other notifications are used mainl
y or entirely in the context of compound documents, although the notification me
chanism is identical. In order to take advantage of the advise sink, a data sour
ce must implement IDataObject::DAdvise, IDataObject::DUnadvise, and IDataObject:
:EnumDAdvise. A data consumer calls the DAdvise mothod to notify a data object t
hat it wishes to be notified when the object s data changes. The consuming object
calls the DUnadvise method to tear down this connection. Any interested party ca
n call the EnumAdvise method to learn the number of objects having an advisory c
onnection with a data object.
When data changes at the source, the data object calls IAdviseSink::OnDataChange
on all data consumers that have registered to receive notifications. To keep tr
ack of advisory connections and manage the dispatch of notifications, data sourc
es rely on an object called a data advise holder. You can create your own data a
dvise holder by implementing the IDataAdviseHolder interface. Or, you can let CO
M do it for you by calling the helper function CreateDataAdviseHolder.
16.4 Uniform Data Transfer Interface Descriptions
IAdviseSink
The IAdviseSink interface enables containers and other objects to receive notifi
cations of data changes, view changes, and compound-document changes occurring i
n objects of interest. Container applications, for example, require such notific
ations to keep cached presentations of their linked and embedded objects up-to-d
ate. Calls to IAdviseSink methods are asynchronous, so the call is sent and then
the next instruction is executed without waiting for the call's return.
For an advisory connection to exist, the object that is to receive notifications
must implement IAdviseSink, and the objects in which it is interested must use
IDataObject::DAdvise.
As shown in the following table, an object that has implemented an advise sink r
egisters its interest in receiving certain types of notifications by calling the
appropriate method:
Call This Method To Register for These Notifications
IDataObject::DAdvise When a document's data changes.
When an event occurs that applies to a registered notification type, the object
application calls the appropriate IAdviseSink method. For example, when an embed
ded object closes, it calls the IAdviseSink::OnClose method to notify its contai
ner. These notifications are asynchronous, occurring after the events that trigg
er them.
When to Implement
Objects, such as container applications and compound documents, implement IAdvis
eSink to receive notification of changes in data, presentation, name, or state o
f their linked and embedded objects. Implementers register for one or more types
of notification, depending on their needs.
Notifications of changes to an embedded object originate in the server and flow
to the container by way of the object handler. If the object is a linked object,
the OLE link object intercepts the notifications from the object handler and no
tifies the container directly. All containers, the object handler, and the OLE l
ink object register for OLE notifications. The typical container also registers
for view notifications. Data notifications are usually sent to the OLE link obje
ct and object handler.
When to Use
Servers call the methods of IAdviseSink to notify objects with which they have a
n advisory connection of changes in an object's data, view, name, or state.
Note
OLE does not permit synchronous calls in the implementation of asynchronous meth
ods, so you cannot make synchronous calls within any of the the IAdviseSink inte
rface's methods. For example, an implementation of IAdviseSink::OnDataChange can
not contain a call to IDataObject::GetData.
Methods in Vtable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IAdviseSink Methods Description
OnDataChange Advises that data has changed.
OnViewChange Advises that view of object has changed.
OnRename Advises that name of object has changed.
OnSave Advises that object has been saved to disk.
OnClose Advises that object has been closed.
See Also
IAdviseSink2, IDataAdviseHolder
IAdviseSink::OnClose
Called by the server to notify all registered advisory sinks that the object has
changed from the running to the loaded state.
Void OnClose();
Remarks
OnClose notification indicates that an object is making the transition from the
running to the loaded state, so its container can take appropriate measures to e
nsure orderly shutdown. For example, an object handler must release its pointer
to the object.
If the object that is closing is the last open object supported by its OLE serve
r application, the application can also shut down.
In the case of a link object, the notification that the object is closing should
always be interpreted to mean that the connection to the link source has broken
.
IAdviseSink::OnDataChange
Called by the server to notify a data object's currently registered advise sinks
that data in the object has changed.
void OnDataChange(
FORMATETC * pFormatetc, //Pointer to format information
STGMEDIUM * pStgmed //Pointer to storage medium
);
Parameters
pFormatetc
[in] Pointer to the FORMATETC structure, which describes the format, target devi
ce, rendering, and storage information of the calling data object.
pStgmed
[in] Pointer to the STGMEDIUM structure, which defines the storage medium (globa
l memory, disk file, storage object, stream object, GDI object, or undefined) an
d ownership of that medium for the calling data object.
Remarks
Object handlers and containers of link objects implement IAdviseSink::OnDataChan
ge to take appropriate steps when notified that data in the object has changed.
They also must call IDataObject::DAdvise to set up advisory connections with the
objects in whose data they are interested. (See IDataObject::DAdvise for more i
nformation on how to specify an advisory connection for data objects.)
Containers that take advantage of OLE's caching support do not need to register
for data-change notifications, because the information necessary to update the c
ontainer's presentation of the object, including any changes in its data, are ma
intained in the object's cache.
Notes to Implementers
If you implement IAdviseSink::OnDataChange for a container, remember that this m
ethod is asynchronous and that making synchronous calls within asynchronous meth
ods is not valid. Therefore, you cannot call IDataObject::GetData to obtain the
data you need to update your object. Instead, you either post an internal messag
e, or invalidate the rectangle for the changed data by calling InvalidateRect an
d waiting for a WM_PAINT message, at which point you are free to get the data an
d update the object.
The data itself, which is valid only for the duration of the call, is passed usi
ng the storage medium pointed to by pStgmed. Since the caller owns the medium, t
he advise sink should not free it. Also, if pStgmed points to an IStorage or ISt
ream interface, the sink must not increment the reference count.
See Also
IDataObject::DAdvise
IAdviseSink::OnRename
Called by the server to notify all registered advisory sinks that the object has
been renamed.
Void OnRename(
IMoniker * pmk //Pointer to the new moniker of the object
);
Parameter
pmk
[in] Pointer to the IMoniker interface on the new full moniker of the object.
Remarks
OLE link objects normally implement IAdviseSink::OnRename to receive notificatio
n of a change in the name of a link source or its container. The object serving
as the link source calls OnRename and passes its new full moniker to the object
handler, which forwards the notification to the link object. In response, the li
nk object must update its moniker. The link object, in turn, forwards the notifi
cation to its own container.
IAdviseSink::OnSave
Called by the server to notify all registered advisory sinks that the object has
been saved.
Void OnSave();
Remarks
Object handlers and link objects normally implement IAdviseSink::OnSave to recei
ve notifications of when an object is saved to disk, either to its original stor
age (through a Save operation) or to new storage (through a Save As operation).
Object Handlers and link objects register to be notified when an object is saved
for the purpose of updating their caches, but then only if the advise flag pass
ed during registration specifies ADVFCACHE_ONSAVE. Object handlers and link obje
cts forward these notifications to their containers.

IAdviseSink::OnViewChange
Notifies an object's registered advise sinks that its view has changed.
Void OnViewChange(
DWORD dwAspect, //Value specifying aspect of object
LONG lindex //Currently must be -1
);
Parameters
dwAspect
[in] The aspect, or view, of the object. Contains a value taken from the enumera
tion, DVASPECT.
lindex
[in] The portion of the view that has changed. Currently only -1 is valid.
Remarks
Even though DVASPECT values are individual flag bits, dwAspect may represent onl
y one value. That is, dwAspect cannot contain the result of an OR operation comb
ining two or more DVASPECT values.
The lindex member represents the part of the aspect that is of interest. The val
ue of lindex depends on the value of dwAspect. If dwAspect is either DVASPECT_TH
UMBNAIL or DVASPECT_ICON, lindex is ignored. If dwAspect is DVASPECT_CONTENT, li
ndex must be -1, which indicates that the entire view is of interest and is the
only value that is currently valid.
16.4.1 IDataAdviseHolder
The IDataAdviseHolder interface contains methods that create and manage advisory
connections between a data object and one or more advise sinks. Its methods are
intended to be used to implement the advisory methods of IDataObject. IDataAdvi
seHolder is implemented on an advise holder object. Its methods establish and de
lete data advisory connections and send notification of change in data from a da
ta object to an object that requires this notification, such as an COM container
, which must contain an advise sink.
Advise sinks are objects that require notification of change in the data the obj
ect contains and implement the IAdviseSink interface. Advise sinks are also usua
lly associated with OLE compound document containers.
16.4.1.1.1.1.1 When to implement
Typically, you use the COM-provided implementation of IDataAdviseHolder to simpl
ify your implementation of the DAdvise, DUnadvise, and EnumDAdvise methods in th
e IDataObject interface, and to send notification of data change as appropriate.
It would be necessary to implement IDataAdviseHolder only in the case where the
re may be a need for a custom data advise holder object, whose methods are to be
used to implement the IDataObject methods in a set of servers.
16.4.1.1.1.1.2 When to use
Your implementation of the advisory methods of IDataObject can call the methods
in IDataAdviseHolder. The first time you receive a call to IDataObject::DAdvise,
call the function CreateDataAdviseHolder to create an instance of the COM-provi
ded advise holder and get a pointer to its IDataAdviseHolder interface. Then, in
implementing the IDataObject interface on the data object, you delegate impleme
ntations of the DAdvise, DUnadvise, and EnumDAdvise methods to the corresponding
methods in IDataAdviseHolder.
When the data of interest to an advise sink actually changes, you call IDataAdvi
seHolder::SendOnDataChange from the data object to carry out the necessary notif
ications.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IDataAdviseHolder Methods Description
Advise Creates a connection between an advise sink and a data object so the adv
ise sink can receive notification of change in the data object.
Unadvise Destroys a notification connection previously set up with the Ad
vise method.
EnumAdvise Returns an object that can be used to enumerate the current advi
sory connections.
SendOnDataChange Sends a change notification back to each advise sink tha
t is currently being managed.
See Also
IDataObject, IAdviseSink

16.4.1.2 IDataAdviseHolder::Advise
Creates a connection between an advise sink and a data object for receiving noti
fications.
HRESULT Advise(
IDataObject * pDataObject, //Pointer to the data object for which notificat
ions are requested
FORMATETC * pFormatetc, //Pointer to the description of data to the advi
se sink
DWORD advf, //Flags that specify how the notification takes place
IAdviseSink * pAdvSink, //Pointer to the advise sink requesting notifica
tion
DWORD * pdwConnection //Pointer to the connection token
);
Parameters
pDataObject
[in] Pointer to the IDataObject interface on the data object for which notificat
ions are requested. If data in this object changes, a notification is sent to th
e advise sinks that have requested notification.
pFormatetc
[in] Pointer to the specified format, medium, and target device that is of inter
est to the advise sink requesting notification. For example, one sink may want t
o know only when the bitmap representation of the data in the data object change
s. Another sink may be interested in only the metafile format of the same object
. Each advise sink is notified when the data of interest changes. This data is p
assed back to the advise sink when notification occurs.
advf
[in] Contains a group of flags for controlling the advisory connection. Valid va
lues are from the enumeration ADVF. However, only some of the possible ADVF valu
es are relevant for this method. The following table briefly describes the relev
ant values; a more detailed description can be found in the description of the A
DVF enumeration.
ADVF Value Description
ADVF_NODATA Asks that no data be sent along with the notification.
ADVF_ONLYONCE Causes the advisory connection to be destroyed after the first n
otification is sent. An implicit call to IDataAdviseHolder::Unadvise is made on
behalf of the caller to remove the connection.
ADVF_PRIMEFIRST Causes an initial notification to be sent regardless of whether
or not data has changed from its current state.
ADVF_DATAONSTOP When specified with ADVF_NODATA, this flag causes a last notific
ation with the data included to be sent before the data object is destroyed. Whe
n ADVF_NODATA is not specified, this flag has no effect.
pAdvSink
[in] Pointer to the IAdviseSink interface on the advisory sink that receives the
change notification.
pdwConnection
[out] Pointer to a DWORD token that identifies this connection. The calling obje
ct can later delete the advisory connection by passing this token to IDataAdvise
Holder::Unadvise. If this value is zero, the connection was not established.
Return Values
This method supports the standard return value E_INVALIDARG, as well as the foll
owing:
S_OK
The advisory connection was created.
Remarks
Through the connection established through this method, the advisory sink can re
ceive future notifications in a call to IAdviseSink::OnDataChange.
An object issues a call to IDataObject::DAdvise to request notification on chang
es to the format, medium, or target device of interest. This data is specified i
n the pFormatetc parameter. The DAdvise method is usually implemented to call ID
ataAdviseHolder::Advise to delegate the task of setting up and tracking a connec
tion to the advise holder. When the format, medium, or target device in question
changes, the data object calls IDataAdviseHolder::SendOnDataChange to send the
necessary notifications.
The established connection can be deleted by passing the value in pdwConnection
in a call to IDataAdviseHolder::Unadvise.
See also
ADVF, CreateDataAdviseHolder, FORMATETC, IDataAdviseHolder::Unadvise, IDataObjec
t::DAdvise

16.4.1.3 IDataAdviseHolder::EnumAdvise
Returns a pointer to an IEnumStatdata interface on an enumeration object that ca
n be used to enumerate the current advisory connections.
HRESULT EnumAdvise(
IEnumSTATDATA ** ppenumAdvise //Indirect pointer on the new enumerator
object
);
Parameter
ppenumAdvise
[out] Indirect pointer to the IEnumStatdata interface on the new enumerator obje
ct. If this value is NULL, there are no connections to advise sinks at this time
.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The enumerator object is successfully instantiated or there are no connections.
Remarks
This method must supply a pointer to an implementation of the IEnumSTATDATA inte
rface, one of the standard enumerator interfaces that contain the Next, Reset, C
lone, and Skip methods, on an enumerator object. Its methods allow you to enumer
ate the data stored in an array of STATDATA structures. You get a pointer to the
COM implementation of IDataAdviseHolder through a call to CreateDataAdviseHolde
r, and then call IDataAdviseHolder::EnumAdvise to implement IDataObject::EnumDAd
vise.
Adding more advisory connections while the enumerator object is active has an un
defined effect on the enumeration that results from this method.
See Also
IEnumXXXX, IEnumSTATDATA, IDataObject::EnumDAdvise

16.4.1.4 IDataAdviseHolder::SendOnDataChange
Sends notifications to each advise sink for which there is a connection establis
hed by calling the IAdviseSink::OnDataChange method for each advise sink current
ly being handled by this instance of the advise holder object.
HRESULT SendOnDataChange(
IDataObject * pDataObject, //Pointer to the data object that has changed
DWORD dwReserved, //Reserved
DWORD advf //Advise flags
);
Parameters
pDataObject
[in] Pointer to the IDataObject interface on the data object in which the data h
as just changed. This pointer is used in subsequent calls to IAdviseSink::OnData
Change.
dwReserved
[in] Reserved for future use; must be zero.
advf
[in] Container for advise flags that specify how the call to IAdviseSink::OnData
Change is made. These flag values are from the enumeration ADVF. Typically, the
value for advf is NULL. The only exception occurs when the data object is shutti
ng down and must send a final notification that includes the actual data to sink
s that have specified ADVF_DATAONSTOP and ADVF_NODATA in their call to IDataObje
ct::DAdvise. In this case, advf contains ADVF_DATAONSTOP.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The call to IAdviseSink::OnDataChange was made.
Remarks
The data object must call this method when it detects a change that would be of
interest to an advise sink that has previously requested notification.
Most notifications include the actual data with them. The only exception is if t
he ADVF_NODATA flag was previously specified when the connection was initially s
et up in the IDataAdviseHolder::Advise method.
Before calling the IAdviseSink::OnDataChange method for each advise sink, this m
ethod obtains the actual data by calling the IDataObject::GetData method through
the pointer specified in the pDataObject parameter.
See Also
ADVF, IAdviseSink::OnDataChange

16.4.1.5 IDataAdviseHolder::Unadvise
Removes a connection between a data object and an advisory sink that was set up
through a previous call to IDataAdviseHolder::Advise. IDataAdviseHolder::Unadvis
e is typically called in the implementation of IDataObject::DUnadvise.
HRESULT Unadvise(
DWORD dwConnection //Connection to remove
);
Parameter
dwConnection
[in] DWORD token that specifies the connection to remove. This value was returne
d by IDataAdviseHolder::Advise when the connection was originally established.
Return Values
S_OK
The specified connection was successfully deleted.
OLE_E_NOCONNECTION
The specified dwConnection is not a valid connection.
See Also
IDataAdviseHolder::Advise, IDataObject::DUnadvise

16.4.2 IDataObject
The IDataObject interface specifies methods that enable data transfer and notifi
cation of changes in data. Data transfer methods specify the format of the trans
ferred data along with the medium through which the data is to be transferred. O
ptionally, the data can be rendered for a specific target device. In addition to
methods for retrieving and storing data, the IDataObject interface specifies me
thods for enumerating available formats and managing connections to advisory sin
ks for handling change notifications.
The term data object is used to mean any object that supports an implementation of
the IDataObject interface. Implementations vary, depending on what the data obj
ect is required to do; in some data objects, the implementation of certain metho
ds not supported by the object could simply be the return of E_NOTIMPL. For exam
ple, some data objects do not allow callers to send them data. Other data object
s do not support advisory connections and change notifications. However, for tho
se data objects that do support change notifications, COM provides an object cal
led a data advise holder. An interface pointer to this holder is available throu
gh a call to the helper function CreateDataAdviseHolder. A data object can have
multiple connections, each with its own set of attributes. The COM data advise h
older simplifies the task of managing these connections and sending the appropri
ate notifications.
16.4.2.1.1.1.1 When to Implement
Implement the IDataObject interface if you are developing a container or server
application that is capable of transferring data. For example, if your applicati
on allows its data to be pasted or dropped into another application, you must im
plement the IDataObject interface. OLE compound document object servers that sup
port objects that can be embedded or linked must implement IDataObject.
COM provides implementations in its default object handler and its cache.
16.4.2.1.1.1.2 When to Use
Any object that can receive data calls the methods in the IDataObject interface.
When you call the data transfer methods in the IDataObject interface, you specif
y a format, a medium, and, optionally, a target device for which the data should
be rendered. Objects, such as containers, that want to be notified through thei
r advise sinks when the data in the data object changes call the IDataObject adv
isory methods to set up an advisory connection through which notifications can b
e sent.
Methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.
IDataObject Methods Description
GetData Renders the data described in a FORMATETC structure and transfers it thr
ough the STGMEDIUM structure.
GetDataHere Renders the data described in a FORMATETC structure and transfer
s it through the STGMEDIUM structure allocated by the caller.
QueryGetData Determines whether the data object is capable of rendering the d
ata described in the FORMATETC structure.
GetCanonicalFormatEtc Provides a potentially different but logically equivalen
t FORMATETC structure.
SetData Provides the source data object with data described by a FORMATETC struc
ture and an STGMEDIUM structure.
EnumFormatEtc Creates and returns a pointer to an object to enumerate the FORM
ATETC supported by the data object.
DAdvise Creates a connection between a data object and an advise sink so the adv
ise sink can receive notifications of changes in the data object.
DUnadvise Destroys a notification previously set up with the DAdvise metho
d.
EnumDAdvise Creates and returns a pointer to an object to enumerate the curr
ent advisory connections.

16.4.2.2 IDataObject::DAdvise
Called by an object supporting an advise sink to create a connection between a d
ata object and the advise sink. This enables the advise sink to be notified of c
hanges in the data of the object.
HRESULT DAdvise(
FORMATETC * pFormatetc, //Pointer to data of interest to the advise sink
DWORD advf, //Flags that specify how the notification takes place
IAdviseSink * pAdvSink, //Pointer to the advise sink
DWORD * pdwConnection //Pointer to a token that identifies this connec
tion
);
Parameters
pFormatetc
[in] Pointer to a FORMATETC structure that defines the format, target device, as
pect, and medium that will be used for future notifications. For example, one si
nk may want to know only when the bitmap representation of the data in the the d
ata object changes. Another sink may be interested in only the metafile format o
f the same object. Each advise sink is notified when the data of interest change
s. This data is passed back to the advise sink when notification occurs.
advf
[in] DWORD that specifies a group of flags for controlling the advisory connecti
on. Valid values are from the enumeration ADVF. However, only some of the possib
le ADVF values are relevant for this method. The following table briefly describ
es the relevant values. Refer to ADVF for a more detailed description.
ADVF Value Description
ADVF_NODATA Asks the data object to avoid sending data with the notification
s. Typically data is sent. This flag is a way to override the default behavior.
When ADVF_NODATA is used, the TYMED member of the STGMEDIUM structure that is pa
ssed to OnDataChange will usually contain TYMED_NULL. The caller can then retrie
ve the data with a subsequent IDataObject::GetData call.
ADVF_ONLYONCE Causes the advisory connection to be destroyed after the first c
hange notification is sent. An implicit call to IDataObject::DUnadvise is made o
n behalf of the caller to remove the connection.
ADVF_PRIMEFIRST Asks for an additional initial notification. The combination of
ADVF_ONLYONCE and ADVF_PRIMEFIRST provides, in effect, an asynchronous IDataObje
ct::GetData call.
ADVF_DATAONSTOP When specified with ADVF_NODATA, this flag causes a last notific
ation with the data included to to be sent before the data object is destroyed.
If used without ADVF_NODATA, IDataObject::DAdvise can be implemented in one of t
he following ways:
· the ADVF_DATAONSTOP can be ignored.
· the object can behave as if ADVF_NODATA was specified.
· a change notification is sent only in the shutdown case. Data changes
prior to shutdown do not cause a notification to be sent.
pAdvSink
[in] Pointer to the IAdviseSink interface on the advisory sink that will receive
the change notification.
pdwConnection
[out] Pointer to a DWORD token that identifies this connection. You can use this
token later to delete the advisory connection (by passing it to IDataObject::DU
nadvise). If this value is zero, the connection was not established.
Return Values
This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and
E_OUTOFMEMORY, as well as the following:
S_OK
The advisory connection was created.
E_NOTIMPL
This method is not implemented on the data object.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
OLE_E_ADVISENOTSUPPORTED
The data object does not support change notification.
Remarks
IDataObject::DAdvise creates a change notification connection between a data obj
ect and the caller. The caller provides an advisory sink to which the notificati
ons can be sent when the object s data changes.
Objects used simply for data transfer typically do not support advisory notifica
tions and return OLE_E_ADVISENOTSUPPORTED from IDataObject::DAdvise.
16.4.2.2.1.1.1 Notes to Callers
The object supporting the advise sink calls IDataObject::DAdvise to set up the c
onnection, specifying the format, aspect, medium, and/or target device of intere
st in the FORMATETC structure passed in. If the data object does not support one
or more of the requested attributes or the sending of notifications at all, it
can refuse the connection by returning OLE_E_ADVISENOTSUPPORTED.
Containers of linked objects can set up advisory connections directly with the b
ound link source or indirectly through the standard COM link object that manages
the connection. Connections set up with the bound link source are not automatic
ally deleted. The container must explicitly call IDataObject::DUnAdvise on the b
ound link source to delete an advisory connection. Connections set up through th
e COM link object are destroyed when the link object is deleted.
The COM default link object creates a wildcard advise with the link source so COM
can maintain the time of last change. This advise is specifically used to note t
he time that anything changed. COM ignores all data formats that may have change
d, noting only the time of last change. To allow wildcard advises, set the FORMA
TETC 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 COM shou
ld always be accepted by applications.
16.4.2.2.1.1.2 Notes to Implementers
To simplify the implementation of DAdvise and the other notification methods in
IDataObject (DUnadvise and EnumAdvise) that supports notification, COM provides
an advise holder object that manages the registration and sending of notificatio
ns. To get a pointer to this object, call the helper function CreateDataAdviseHo
lder on the first invocation of DAdvise. This supplies a pointer to the object s I
DataAdviseHolder interface. Then, delegate the call to the IDataAdviseHolder::Ad
vise method in the data advise holder, which creates, and subsequently manages,
the requested connection.
See Also
ADVF, FORMATETC, CreateDataAdviseHolder , IDataAdviseHolder - OLE implementation , I
AdviseSink::OnDataChange, IDataObject::DUnAdvise

16.4.2.3 IDataObject::DUnadvise
Destroys a notification connection that had been previously set up.
HRESULT DUnadvise(
DWORD dwConnection //Connection to remove
);
Parameter
dwConnection
[in] DWORD token that specifies the connection to remove. Use the value returned
by IDataObject::DAdvise when the connection was originally established.
Return Values
S_OK
The specified connection was successfully deleted.
OLE_E_NOCONNECTION
The specified dwConnection is not a valid connection.
OLE_E_ADVISENOTSUPPORTED
This IDataObject implementation does not support notification.
Remarks
This methods destroys a notification created with a call to the IDataObject::DAd
vise method.
If the advisory connection being deleted was initially set up by delegating the
IDataObject::DAdvise call to IDataAdviseHolder::Advise, you must delegate this c
all to IDataAdviseHolder::Unadvise to delete it.
See Also
IDataObject::DAdvise, IDataAdviseHolder::Unadvise

16.4.2.4 IDataObject::EnumDAdvise
Creates an object that can be used to enumerate the current advisory connections
.
HRESULT EnumDAdvise(
IEnumSTATDATA ** ppenumAdvise //Indirect pointer
);
Parameter
ppenumAdvise
[out] Indirect pointer to the IEnumSTATDATA interface on the new enumerator obje
ct. If the supplied value is NULL, there are no connections to advise sinks at t
his time.
Return Values
This method supports the standard return value E_OUTOFMEMORY, as well as the fol
lowing:
S_OK
The enumerator object is successfully instantiated or there are no connections.
OLE_E_ADVISENOTSUPPORTED
Advisory notifications are not supported by this object.
Remarks
The enumerator object created by this method implements the IEnumSTATDATA interf
ace, one of the standard enumerator interfaces that contain the Next, Reset, Clo
ne, and Skip methods. IEnumSTATDATA permits the enumeration of the data stored i
n an array of STATDATA structures. Each of these structures provides information
on a single advisory connection, and includes FORMATETC and ADVF information, a
s well as the pointer to the advise sink and the token representing the connecti
on.
16.4.2.4.1.1.1 Notes to Callers
After getting a pointer through this method, the data object can call the approp
riate enumeration methods. While the enumeration is in progress, the effect of a
dding more advisory connections on the subsequent enumeration is undefined.
16.4.2.4.1.1.2 Notes to Implementers
It is recommended that you use the COM data advise holder object to handle advis
ory connections. With the pointer obtained through a call to CreateDataAdviseHol
der, implementing IDataObject::EnumDAdvise becomes a simple matter of delegating
the call to IDataAdviseHolder::EnumAdvise. This creates the enumerator and supp
lies the pointer to the COM implementation of IEnumSTATDATA. At that point, you
can call its methods to enumerate the current advisory connections.
See Also
IEnumSTATDATA, IDataAdviseHolder::EnumAdvise

16.4.2.5 IDataObject::EnumFormatEtc
Creates an object for enumerating the FORMATETC structures for a data object. Th
ese structures are used in calls to IDataObject::GetData or IDataObject::SetData
.
HRESULT EnumFormatEtc(
DWORD dwDirection, //Specifies a value from the enumeration DATADIR
IEnumFORMATETC ** ppenumFormatetc //Indirect pointer to the new enumerator
object
);
Parameters
dwDirection
[in] Direction of the data through a value from the enumeration DATADIR.
typedef enum tagDATADIR
{
DATADIR_GET = 1,
DATADIR_SET = 2,
} DATADIR;
The value DATADIR_GET enumerates the formats that can be passed in to a call to
IDataObject::GetData. The value DATADIR_SET enumerates those formats that can be
passed in to a call to IDataObject::SetData.
ppenumFormatetc
[out] Indirect pointer to the IEnumFORMATETC interface on the new enumerator obj
ect.
Return Values
This method supports the standard return values E_INVALIDARG and E_OUTOFMEMORY,
as well as the following:
S_OK
Enumerator object was successfully created.
E_NOTIMPL
The direction specified by dwDirection is not supported.
OLE_S_USEREG
Requests that COM enumerate the formats from the registry.
Remarks
IDataObject::EnumFormatEtc creates an enumerator object that can be used to dete
rmine all of the ways the data object can describe data in a FORMATETC structure
, and supplies a pointer to its IEnumFORMATETC interface. This is one of the sta
ndard enumerator interfaces.
16.4.2.5.1.1.1 Notes to Callers
Having obtained the pointer, the caller can enumerate the FORMATETC structures b
y calling the enumeration methods of IEnumFORMATETC. Because the formats can cha
nge over time, there is no guarantee that an enumerated format is currently supp
orted because the formats can change over time. Accordingly, applications should
treat the enumeration as a hint of the format types that can be passed. The cal
ler is responsible for calling IEnumFormatEtc::Release when it is finished with
the enumeration.
IDataObject::EnumFormatEtc is called when one of the following actions occurs:
· An application calls OleSetClipboard. COM must determine what data to place on t
he Clipboard and whether it is necessary to put COM 1 compatibility formats on t
he Clipboard.
· Data is being pasted from the Clipboard or dropped. An application uses the firs
t acceptable format.
· The Paste Special dialog box is displayed. The target application builds the lis
t of formats from the FORMATETC entries.
16.4.2.5.1.1.2 Notes to Implementers
Formats can be registered statically in the registry or dynamically during objec
t initialization. If an object has an unchanging list of formats and these forma
ts are registered in the registry, COM provides an implementation of a FORMATETC
enumeration object that can enumerate formats registered under a specific CLSID
in the registry. A pointer to its IEnumFORMATETC interface is available through
a call to the helper function OleRegEnumFormatEtc. In this situation, therefore
, you can implement the EnumFormatEtc method simply with a call to this function
.
EXE applications can effectively do the same thing by implementing the method to
return the value OLE_S_USEREG. This return value instructs the default object h
andler to call OleRegEnumFormatEtc. Object applications that are implemented as
DLL object applications cannot return OLE_S_USEREG, so must call OleRegEnumForma
tEtc 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 fo
rmats can be enumerated for COM objects (all versions after OLE 1), if they are
registered with the GetDataFormats or SetDataFormats keys.
For OLE 1 objects whose servers do not have RequestDataFormats or SetDataFormats
information registered in the registry, a call to IDataObject::EnumFormatEtc pa
ssing DATADIR_GET only enumerates the Native and Metafile formats, regardless of
whether they support these formats or others. Calling EnumFormatEtc passing DAT
ADIR_SET on such objects only enumerates Native, regardless of whether the objec
t supports being set with other formats.
The FORMATETC structure returned by the enumeration usually indicates a NULL tar
get device (ptd). This is appropriate because, unlike the other members of FORMA
TETC, the target device does not participate in the object s decision as to whethe
r it can accept or provide the data in either a SetData or GetData call.
The TYMED member of FORMATETC often indicates that more than one kind of storage
medium is acceptable. You should always mask and test for this by using a Boole
an OR operator.
See Also
FORMATETC, IEnumFormatEtc, IDataObject::SetData, IDataObject::GetData

16.4.2.6 IDataObject::GetCanonicalFormatEtc
Provides a standard FORMATETC structure that is logically equivalent to one that
is more complex. You use this method to determine whether two different FORMATE
TC structures would return the same data, removing the need for duplicate render
ing.
HRESULT GetCanonicalFormatEtc(
FORMATETC * pFormatetcIn, //Pointer to the FORMATETC structure
FORMATETC * pFormatetcOut //Pointer to the canonical equivalent FORMATETC
structure
);
Parameters
pFormatetcIn
[in] Pointer to the FORMATETC structure that defines the format, medium, and tar
get device that the caller would like to use to retrieve data in a subsequent ca
ll such as IDataObject::GetData. The TYMED member is not significant in this cas
e and should be ignored.
pFormatetcOut
[out] Pointer to a FORMATETC structure that contains the most general informatio
n possible for a specific rendering, making it canonically equivalent to pFormat
etcIn. The caller must allocate this structure and the GetCanonicalFormatEtc met
hod must fill in the data. To retrieve data in a subsequent call like IDataObjec
t::GetData, the caller uses the supplied value of pFormatetcOut, unless the valu
e supplied is NULL. This value is NULL if the method returns DATA_S_SAMEFORMATET
C. The TYMED member is not significant in this case and should be ignored.
Return Values
This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and
E_OUTOFMEMORY, as well as the following:
S_OK
The returned FORMATETC structure is different from the one that was passed.
DATA_S_SAMEFORMATETC
The FORMATETC structures are the same and NULL is returned in pFormatetcOut.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
OLE_E_NOTRUNNING
Object application is not running.
Remarks
If a data object can supply exactly the same data for more than one requested FO
RMATETC structure, IDataObject::GetCanonicalFormatEtc can supply a canonical , or s
tandard FORMATETC that gives the same rendering as a set of more complicated FOR
MATETC structures. For example, it is common for the data returned to be insensi
tive to the target device specified in any one of a set of otherwise similar FOR
MATETC structures.
16.4.2.6.1.1.1 Notes to Callers
A call to this method can determine whether two calls to IDataObject::GetData on
a data object, specifying two different FORMATETC structures, would actually pr
oduce the same renderings, thus eliminating the need for the second call and imp
roving performance. If the call to GetCanonicalFormatEtc results in a canonical
format being written to the pFormatetcOut parameter, the caller then uses that s
tructure in a subsequent call to IDataObject::GetData.
16.4.2.6.1.1.2 Notes to Implementers
Conceptually, it is possible to think of FORMATETC structures in groups defined
by a canonical FORMATETC that provides the same results as each of the group mem
bers. In constructing the canonical FORMATETC, you should make sure it contains
the most general information possible that still produces a specific rendering.
For data objects that never provide device-specific renderings, the simplest imp
lementation of this method is to copy the input FORMATETC to the output FORMATET
C, store a NULL in the ptd field of the output FORMATETC, and return DATA_S_SAME
FORMATETC.
See Also
IDataObject::GetData, FORMATETC

16.4.2.7 IDataObject::GetData
Called by a data consumer to obtain data from a source data object. The GetData
method renders the data described in the specified FORMATETC structure and trans
fers it through the specified STGMEDIUM structure. The caller then assumes respo
nsibility for releasing the STGMEDIUM structure.
HRESULT GetData(
FORMATETC * pFormatetc, //Pointer to the FORMATETC structure
STGMEDIUM * pmedium //Pointer to the STGMEDIUM structure
);
Parameters
pFormatetc
[in] Pointer to the FORMATETC structure that defines the format, medium, and tar
get device to use when passing the data. It is possible to specify more than one
medium by using the Boolean OR operator, allowing the method to choose the best
medium among those specified.
pmedium
[out] Pointer to the STGMEDIUMstructure that indicates the storage medium contai
ning the returned data through its tymed member, and the responsibility for rele
asing the medium through the value of its punkOuter member. If punkForRelease is
NULL, the receiver of the medium is responsible for releasing it; otherwise, pu
nkForRelease points to the IUnknown on the appropriate object so its Release met
hod can be called. The medium must be allocated and filled in by IDataObject::Ge
tData.
Return Values
This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and
E_OUTOFMEMORY, as well as the following:
S_OK
Data was successfully retrieved and placed in the storage medium provided.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
STG_E_MEDIUMFULL
An error occurred when allocating the medium.
Remarks
A data consumer calls IDataObject::GetData to retrieve data from a data object,
conveyed through a storage medium (defined through the STGMEDIUM structure).
16.4.2.7.1.1.1 Notes to Callers
You can specify more than one acceptable TYMED medium with the Boolean OR operat
or. IDataObject::GetData must choose from the OR d values the medium that best rep
resents the data, do the allocation, and indicate responsibility for releasing t
he medium.
Data transferred across a stream extends from position zero of the stream pointe
r through to the position immediately before the current stream pointer (that is
, the stream pointer position upon exit).
16.4.2.7.1.1.2 Notes to Implementers
IDataObject::GetData must check all fields in the FORMATETC structure. It is imp
ortant that IDataObject::GetData render the requested aspect and, if possible, u
se the requested medium. If the data object cannot comply with the information s
pecified in the FORMATETC, the method should return DV_E_FORMATETC. If an attemp
t to allocate the medium fails, the method should return STG_E_MEDIUMFULL. It is
important to fill in all of the fields in the STGMEDIUM structure.
Although the caller can specify more than one medium for returning the data, IDa
taObject::GetData can supply only one medium. If the initial transfer fails with
the selected medium, this method can be implemented to try one of the other med
ia specified before returning an error.
See Also
IDataObject::GetDataHere, IDataObject::SetData, FORMATETC, STGMEDIUM

16.4.2.8 IDataObject::GetDataHere
Called by a data consumer to obtain data from a source data object. This method
differs from the GetData method in that the caller must allocate and free the sp
ecified storage medium.
HRESULT GetDataHere(
FORMATETC * pFormatetc, //Pointer to the FORMATETC structure
STGMEDIUM * pmedium //Pointer to the STGMEDIUM structure
);
Parameters
pFormatetc
[in] Pointer to the FORMATETC structure that defines the format, medium, and tar
get device to use when passing the data. Only one medium can be specified in TYM
ED, and only the following TYMED values are valid: TYMED_STORAGE, TYMED_STREAM,
TYMED_HGLOBAL, or TYMED_FILE.
pmedium
[out] Pointer to the STGMEDIUM structure that defines the storage medium contain
ing the data being transferred. The medium must be allocated by the caller and f
illed in by IDataObject::GetDataHere. The caller must also free the medium. The
implementation of this method must always supply a value of NULL for the punkFor
Release member of the STGMEDIUM structure to which this parameter points.
Return Values
This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and
E_OUTOFMEMORY, as well as the following:
S_OK
Data was successfully retrieved and placed in the storage medium provided.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
STG_E_MEDIUMFULL
The medium provided by the caller is not large enough to contain the data.
Remarks
The IDataObject::GetDataHere method is similar to IDataObject::GetData, except t
hat the caller must both allocate and free the medium specified in pmedium. GetD
ataHere renders the data described in a FORMATETC structure and copies the data
into that caller-provided STGMEDIUM structure. For example, if the medium is TYM
ED_HGLOBAL, this method cannot resize the medium or allocate a new hGlobal.
Some media are not appropriate in a call to GetDataHere, including GDI types suc
h as metafiles. The GetDataHere method cannot put data into a caller-provided me
tafile. In general, the only storage media it is necessary to support in this me
thod are TYMED_ISTORAGE, TYMED_ISTREAM, and TYMED_FILE.
When the transfer medium is a stream, COM makes assumptions about where the data
is being returned and the position of the stream s seek pointer. In a GetData cal
l, the data returned is from stream position zero through just before the curren
t seek pointer of the stream (that is, the position on exit). For GetDataHere, t
he data returned is from the stream position on entry through just before the po
sition on exit.
See Also
IDataObject::GetData, FORMATETC, STGMEDIUM

16.4.2.9 IDataObject::QueryGetData
Determines whether the data object is capable of rendering the data described in
the FORMATETC structure. Objects attempting a paste or drop operation can call
this method before calling IDataObject::GetData to get an indication of whether
the operation may be successful.
HRESULT QueryGetData(
FORMATETC * pFormatetc //Pointer to the FORMATETC structure
);
Parameter
pFormatetc
[in] Pointer to the FORMATETC structure defining the format, medium, and target
device to use for the query.
Return Values
This method supports the standard return values E_INVALIDARG, E_UNEXPECTED, and
E_OUTOFMEMORY, as well as the following:
S_OK
Subsequent call to IDataObject::GetData would probably be successful.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
Remarks
The client of a data object calls IDataObject::QueryGetData to determine whether
passing the specified FORMATETC structure to a subsequent call to IDataObject::
GetData is likely to be successful. A successful return from this method does no
t necessarily ensure the success of the subsequent paste or drop operation.
See Also
IDataObject::GetData, FORMATETC

16.4.2.10 IDataObject::SetData
Called by an object containing a data source to transfer data to the object that
implements this method.
HRESULT SetData(
FORMATETC * pFormatetc, //Pointer to the FORMATETC structure
STGMEDIUM * pmedium, //Pointer to STGMEDIUM structure
BOOL fRelease //Indicates which object owns the storage medium after t
he call is completed
);
Parameters
pFormatetc
[in] Pointer to the FORMATETC structure defining the format used by the data obj
ect when interpreting the data contained in the storage medium.
pmedium
[in] Pointer to the STGMEDIUM structure defining the storage medium in which the
data is being passed.
fRelease
[in] If TRUE, the data object called, which implements IDataObject::SetData, own
s the storage medium after the call returns. This means it must free the medium
after it has been used by calling the ReleaseStgMedium function. If FALSE, the c
aller retains ownership of the storage medium and the data object called uses th
e storage medium for the duration of the call only.
Return Values
This method supports the standard return values E_FAIL, E_INVALIDARG, E_UNEXPECT
ED, and E_OUTOFMEMORY, as well as the following:
S_OK
Data was successfully transferred.
E_NOTIMPL
This method is not implemented for the data object.
DV_E_LINDEX
Invalid value for lindex; currently, only -1 is supported.
DV_E_FORMATETC
Invalid value for pFormatetc.
DV_E_TYMED
Invalid tymed value.
DV_E_DVASPECT
Invalid dwAspect value.
OLE_E_NOTRUNNING
Object application is not running.
Remarks
IDataObject::SetData allows another object to attempt to send data to the implem
enting data object. A data object implements this method if it supports receivin
g data from another object. If it does not support this, it should be implemente
d to return E_NOTIMPL.
The caller allocates the storage medium indicated by the pmedium, in which the d
ata is passed. The data object called does not take ownership of the data until
it has successfully received it and no error code is returned. The value of the
fRelease parameter indicates the ownership of the medium after the call returns.
FALSE indicates the caller still owns the medium, and the data object only has
the use of it during the call; TRUE indicates that the data object now owns it a
nd must release it when it is no longer needed.
The type of medium (TYMED) specified in the pformatetc and pmedium parameters mu
st be the same. For example, one cannot be an hGlobal (global handle) and the ot
her a stream.
See Also
IDataObject::GetData, FORMATETC, STGMEDIUM
16.4.3 IEnumFORMATETC
The IEnumFORMATETC interface is used to enumerate an array of FORMATETC structur
es. IEnumFORMATETC has the same methods as all enumerator interfaces: Next, Skip
, Reset, and Clone. For general information on these methods, refer to IEnumXXXX
.
16.4.3.1.1.1.1 When to Implement
IEnumFORMATETC must be implemented by all data objects to support calls to IData
Object::EnumFormatEtc, which supplies a pointer to the enumerator s IEnumFORMATETC
interface. If the data object supports a different set of FORMATETC information
depending on the direction of the data (whether a call is intended for the SetD
ata or GetData method of IDataObject), the implementation of IEnumFORMATETC must
be able to operate on both.
The order of formats enumerated through the IEnumFORMATETC object should be the
same as the order that the formats would be in when placed on the clipboard. Typ
ically, this order starts with private data formats and ends with presentation f
ormats such as CF_METAFILEPICT.
16.4.3.1.1.1.2 When to Use
Call the methods of IEnumFORMATETC when you need to enumerate the FORMATETC stru
ctures defining the formats and media supported by a given data object. This is
necessary in most data transfer operations, such as clipboard and drag-and-drop,
so the object on the other end of the data transfer can determine whether the a
ppropriate format and media for the data is supported.
The prototypes of the methods are as follows:
HRESULT Next(
ULONG celt,
FORMATETC * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumFORMATETC ** ppenum
);
See Also
FORMATETC, IEnumXXXX

16.4.4 IEnumSTATDATA
The IEnumSTATDATA interface is used to enumerate through an array of STATDATA st
ructures, which contain advisory connection information for a data object. IEnum
STATDATA has the same methods as all enumerator interfaces: Next, Skip, Reset, a
nd Clone. For general information on these methods, refer to IEnumXXXX.
16.4.4.1.1.1.1 When to Implement
IEnumSTATDATA is implemented to enumerate advisory connections. Most application
s will not implement this directly, but will use the COM-provided implementation
. Pointers to this implementation are available in two ways:
· In a data object, call CreateDataAdviseHolder to get a pointer to the COM data a
dvise holder object, and then, to implement IDataObject::EnumDAdvise, call IData
AdviseHolder::EnumAdvise, which creates the enumeration object and supplies a po
inter to the implementation of IEnumSTATDATA.
· In a compound document object, call CreateOLEAdviseHolder to get a pointer to th
e COM advise holder object.
16.4.4.1.1.1.2 When to Use
Containers usually call methods that return a pointer to IEnumSTATDATA so the co
ntainer can use its methods to enumerate the existing advisory connections, and
use this information to instruct an object to release each of its advisory conne
ctions prior to closing down. IDataObject::EnumDAdvise, IDataAdviseHolder::EnumA
dvise, and methods all supply a pointer to IEnumSTATDATA.
The prototypes of the methods are as follows:
HRESULT Next(

ULONG celt,
STATDATA * rgelt,
ULONG * pceltFetched
);
HRESULT Skip(
ULONG celt
);
HRESULT Reset(void)
HRESULT Clone(
IEnumSTATDATA ** ppenum
);
See Also
STATDATA, IEnumXXXX, IDataObject::EnumDAdvise, IDataAdviseHolder::EnumAdvise

16.5 Uniform Data Transfer API Descriptions


16.5.1 CreateDataAdviseHolder
Supplies a pointer to the COM implementation of IDataAdviseHolder on the data ad
vise holder object.
WINOLEAPI CreateDataAdviseHolder(
IDataAdviseHolder **ppDAHolder //Address of output variable that
// receives the IDataAdviseHolder
// interface pointer
);
Parameter
ppDAHolder
[out] Address of IDataAdviseHolder* pointer variable that receives the interface
pointer to the new advise holder object.
Return Values
This function supports the standard return value E_OUTOFMEMORY, as well as the f
ollowing:
S_OK
The advise holder object has been instantiated and the pointer supplied.
Remarks
Call CreateDataAdviseHolder in your implementation of IDataObject::DAdvise to ge
t a pointer to the COM implementation of IDataAdviseHolder interface. With this
pointer, you can then complete the implementation of IDataObject::DAdvise by cal
ling the IDataAdviseHolder::Advise method, which creates an advisory connection
between the calling object and the data object.
See Also
IDataAdviseHolder

16.5.2 CreateFormatEnumerator
Creates an object that implements IEnumFORMATETC over a static array of FORMATET
C structures.
HRESULT CreateFormatEnumerator(
UINT cfmtetc, //Number of FORMATETC structures in rgfmtetc
FORMATETC *rgfmtetc, //Static array of formats.
IenumFORMATETC **ppenumfmtetc //Address of output variable that receiv
es the
// IEnumFORMATETC interface pointer
);
Parameters
cfmtetc
[in] Number of FORMATETC structures in the static array specified by the rgfmtet
c parameter. The cfmtetc parameter cannot be zero.
rgfmtetc
[in] Pointer to a static array of FORMATETC structures.
ppenumfmtetc
[out] Address of IEnumFORMATETC* pointer variable that receives the interface po
inter to the enumerator object.
Return Values
S_OK
The operation was successful.
E_INVALIDARG
One or more parameters are invalid.
Remarks
The CreateFormatEnumerator function creates an enumerator object that implements
IEnumFORMATETC over a static array of FORMATETC structures. The cfmtetc paramet
er specifies the number of these structures. With the pointer, you can call the
standard enumeration methods to enumerate the structures, as described in the IE
numXXX reference.
16.5.3 ReleaseStgMedium
Frees the specified storage medium.
void ReleaseStgMedium(
STGMEDIUM * pmedium //Pointer to storage medium to be freed
);
Parameter
pmedium
[in] Pointer to the storage medium that is to be freed.
Return Value
None.
Remarks
The ReleaseStgMedium function calls the appropriate method or function to releas
e the specified storage medium. Use this function during data transfer operation
s where storage medium structures are parameters, such as IDataObject::GetData o
r IDataObject::SetData. In addition to identifying the type of the storage mediu
m, this structure specifies the appropriate IUnknown::Release method for releasi
ng the storage medium when it is no longer needed.
It is common to pass a STGMEDIUM from one body of code to another, such as in ID
ataObject::GetData, in which the one called can allocate a medium and return it
to the caller. ReleaseStgMedium permits flexibility in whether the receiving bod
y of code owns the medium, or whether the original provider of the medium still
owns it, in which case the receiving code needs to inform the provider that it c
an free the medium.
When the original provider of the medium is responsible for freeing the medium,
the provider calls ReleaseStgMedium, specifying the medium and the appropriate I
Unknown pointer as the punkForRelease structure member. Depending on the type of
storage medium being freed, one of the following actions is taken, followed by
a call to the Release method on the specified IUnknown pointer:
Medium ReleaseStgMedium Action
TYMED_HGLOBAL None.
TYMED_GDI None.
TYMED_ENHMF None.
TYMED_MFPICT None.
TYMED_FILE Frees the file name string using standard memory management mech
anisms.
TYMED_ISTREAM Calls IStream::Release.
TYMED_ISTORAGE Calls IStorage::Release.
The provider indicates that the receiver of the medium is responsible for freein
g the medium by specifying NULL for the punkForRelease structure member. Then th
e receiver calls ReleaseStgMedium, which makes a call as described in the follow
ing table depending on the type of storage medium being freed:
Medium ReleaseStgMedium Action
TYMED_HGLOBAL Calls the Win32 GlobalFree function on the handle.
TYMED_GDI Calls the Win32 DeleteObject function on the handle.
TYMED_ENHMF Deletes the enhanced metafile.
TYMED_MFPICT The hMF that it contains is deleted with the Win32 DeleteMetaFil
e function; then the handle itself is passed to GlobalFree.
TYMED_FILE Frees the disk file by deleting it. Frees the file name string b
y using the standard memory management paradigm.
TYMED_ISTREAM Calls IStream::Release.
TYMED_ISTORAGE Calls IStorage::Release.
In either case, after the call to ReleaseStgMedium, the specified storage medium
is invalid and can no longer be used.
See Also
STGMEDIUM structure
16.6 Uniform Data Transfer Structure Descriptions
16.6.1 DVASPECTINFO
The DVASPECTINFO structure is used in the IViewObject::Draw method to optimize r
endering of an inactive object by making more efficient use of the GDI. The pvAs
pect parameter in IViewObject::Draw points to this structure. It is defined as f
ollows:
typedef struct STRUCT tagDVASPECTINFO
{
UNIT cb;
DWORD dwFlags;
} DVASPECTINFO;
Members
cb
Size of the structure in bytes. The size includes this member as well as the dwF
lags member.
dwFlags
A value taken from the DVASPECTINFOFLAG enumeration.
See Also
DVASPECTINFOFLAG
16.6.2 DVEXTENTINFO
The DVEXTENTINFO structure is used in IViewObjectEx::GetNaturalExtent.
typedef struct tagDVEXTENTINFO
{
ULONG cb;
DWORD dwExtentMode;
SIZEL sizelProposed;
}DVEXTENTINFO;
Members
cb
Size of the structure in bytes. The size includes this member as well as the dwE
xtentMode and sizelProposed members.
dwExtentMode
Indicates whether the sizing mode is content or integral sizing. See the DVEXTEN
TMODE enumeration for these values.
sizelProposed
Specifies the proposed size in content sizing or the preferred size in integral
sizing.
See Also
DVEXTENTMODE
16.6.3 DVTARGETDEVICE
Use the DVTARGETDEVICE structure to specify information about the target device
for which data is being composed. DVTARGETDEVICE contains enough information abo
ut a Windows target device so a handle to a device context (hDC) can be created
using the Windows CreateDC function.
typedef struct tagDVTARGETDEVICE
{
DWORD tdSize;
WORD tdDriverNameOffset;
WORD tdDeviceNameOffset;
WORD tdPortNameOffset;
WORD tdExtDevmodeOffset;
BYTE tdData[1];
}DVTARGETDEVICE;
Members
tdSize
Size, in bytes, of the DVTARGETDEVICE structure. The initial size is included so
the structure can be copied more easily.
tdDriverNameOffset
Offset, in bytes, from the beginning of the structure to the device driver name,
which is stored as a NULL-terminated string in the tdData buffer.
tdDeviceNameOffset
Offset, in bytes, from the beginning of the structure to the device name, which
is stored as a NULL-terminated string in the tdData buffer. This value can be ze
ro to indicate no device name.
tdPortNameOffset
Offset, in bytes, from the beginning of the structure to the port name, which is
stored as a NULL-terminated string in the tdData buffer. This value can be zero
to indicate no port name.
tdExtDevmodeOffset
Offset, in bytes, from the beginning of the structure to the DEVMODE structure r
etrieved by calling ExtDeviceMode.
tdData
Aray of bytes containing data for the target device. It is not necessary to incl
ude empty strings in tdData (for names where the offset value is zero).
Remarks
Some OLE 1 client applications incorrectly construct target devices by allocatin
g too few bytes in the DEVMODE structure for the OLETARGETDEVICE. They typically
only supply the number of bytes in the DEVMODE.dmSize member. The number of byt
es to be allocated should be the sum of DEVMODE.dmSize + DEVMODE.dmDriverExtra.
When a call is made to the CreateDC function with an incorrect target device, th
e printer driver tries to access the additional bytes and unpredictable results
can occur. To protect against a crash and make the additional bytes available, C
OM pads the size of COM target devices created from OLE 1 target devices.
See Also
FORMATETC, IEnumFORMATETC,
16.6.4 FORMATETC
The FORMATETC structure is a generalized Clipboard format. It is enhanced to enc
ompass a target device, the aspect or view of the data, and a storage medium ind
icator. Where one might expect to find a Clipboard format, COM uses a FORMATETC
data structure instead. This structure is used as a parameter in COM functions a
nd methods that require data format information.
Defined in the IEnumFORMATETC interface.
typedef struct tagFORMATETC
{
CLIPFORMAT cfFormat;
DVTARGETDEVICE *ptd;
DWORD dwAspect;
LONG lindex;
DWORD tymed;
}FORMATETC, *LPFORMATETC;
Members
cfFormat
Particular clipboard format of interest. There are three types of formats recogn
ized by COM:
· Standard interchange formats, such as CF_TEXT.
· Private application formats understood only by the application offering the form
at, or by other applications offering similar features.
· COM formats, which are used to create linked or embedded objects.
ptd
Pointer to a DVTARGETDEVICE structure containing information about the target de
vice for which the data is being composed. A NULL value is used whenever the spe
cified data format is independent of the target device or when the caller doesn t
care what device is used. In the latter case, if the data requires a target devi
ce, the object should pick an appropriate default device (often the display for
visual components). Data obtained from an object with a NULL target device, such
as most metafiles, is independent of the target device. The resulting data is u
sually the same as it would be if the user chose the Save As command from the Fi
le menu and selected an interchange format.
dwAspect
One of the DVASPECT enumeration constants that indicate how much detail should b
e contained in the rendering. A single clipboard format can support multiple asp
ects or views of the object. Most data and presentation transfer and caching met
hods pass aspect information. For example, a caller might request an object s icon
ic picture, using the metafile clipboard format to retrieve it. Note that only o
ne DVASPECT value can be used in dwAspect. That is, dwAspect cannot be the resul
t of a BOOLEAN OR operation on several DVASPECT values.
lindex
Part of the aspect when the data must be split across page boundaries. The most
common value is -1, which identifies all of the data. For the aspects DVASPECT_T
HUMBNAIL and DVASPECT_ICON, lindex is ignored.
tymed
One of the TYMED enumeration constants which indicate the type of storage medium
used to transfer the object s data. Data can be transferred using whatever medium
makes sense for the object. For example, data can be passed using global memory
, a disk file, or structured storage objects. For more information, see the TYME
D enumeration.
Remarks
The FORMATETC structure is used by methods in the data transfer and presentation
interfaces as a parameter specifying the data being transferred. For example, t
he IDataObject::GetData method uses the FORMATETC structure to indicate exactly
what kind of data the caller is requesting.
See Also
DVASPECT, IDataAdviseHolder, IDataObject, IEnumFORMATETC, STATDATA, STGMEDIUM, T
YMED
16.6.5 STATDATA
The STATDATA structure is the data structure used to specify each advisory conne
ction. It is used for enumerating current advisory connections. It holds data re
turned by the IEnumSTATDATA enumerator. This enumerator interface is returned by
IDataObject:DAdvise. Each advisory connection is specified by a unique STATDATA
structure.
Defined in com.h.
typedef struct tagSTATDATA
{
FORMATETC formatetc;
DWORD grfAdvf;
IAdviseSink* pAdvSink;
DWORD dwConnection;
} STATDATA;
Members
formatetc
The FORMATETC structure for the data of interest to the advise sink. The advise
sink receives notification of changes to the data specified by this FORMATETC st
ructure.
grfAdvf
The ADVF enumeration value that determines when the advisory sink is notified of
changes in the data.
pAdvSink
The pointer for the IAdviseSink interface that will receive change notifications
.
dwConnection
The token that uniquely identifies the advisory connection. This token is return
ed by the method that sets up the advisory connection.
See Also
IEnumSTATDATA
16.6.6 STGMEDIUM
The STGMEDIUM structure is a generalized global memory handle used for data tran
sfer operations by the IAdviseSink, and IDataObject.
Defined in the IAdviseSink interface (advsnk.idl).
typedef struct tagSTGMEDIUM
{
DWORD tymed;
[switch_type(DWORD), switch_is((DWORD) tymed)]
union {
[case(TYMED_GDI)] HBITMAP hBitmap;
[case(TYMED_MFPICT)] HMETAFILEPICT hMetafilePict;
[case(TYMED_ENHMF)] HENHMETAFILE hEnhMetaFile;
[case(TYMED_HGLOBAL)] HGLOBAL hGlobal;
[case(TYMED_FILE)] LPWSTR lpszFileName;
[case(TYMED_ISTREAM)] IStream *pstm;
[case(TYMED_ISTORAGE)] IStorage *pstg;
[default] ;
};
[unique] IUnknown *pUnkForRelease;
}STGMEDIUM;
typedef STGMEDIUM *LPSTGMEDIUM;
Members
tymed
Type of storage medium. The marshaling and unmarshaling routines use this value
to determine which union member was used. This value must be one of the elements
of the TYMED enumeration.
union member
Handle, string, or interface pointer that the receiving process can use to acces
s the data being transferred. If tymed is TYMED_NULL, the union member is undefi
ned; otherwise, it is one of the following:
hBitmap
Bitmap handle. The tymed member is TYMED_GDI.
hMetafilePict
Metafile handle. The tymed member is TYMED_MFPICT.
hEnhMetaFile
Enhanced metafile handle. The tymed member is TYMED_ENHMF.
hGlobal
Global memory handle. The tymed member is TYMED_HGLOBAL.
lpszFileName
Pointer to the path of a disk file that contains the data. The tymed member is T
YMED_FILE.
pstm
Pointer to an IStream interface. The tymed member is TYMED_ISTREAM.
pstg
Pointer to an IStorage interface. The tymed member is TYMED_ISTORAGE.
pUnkForRelease
Pointer to an interface instance that allows the sending process to control the
way the storage is released when the receiving process calls the ReleaseStgMediu
m function. If pUnkForRelease is NULL, ReleaseStgMedium uses default procedures
to release the storage; otherwise, ReleaseStgMedium uses the specified IUnknown
interface.
See Also
FORMATETC, IAdviseSink, IDataObject, ReleaseStgMedium
16.7 Uniform Data Transfer Enumeration Descriptions
16.7.1 ADVF
The ADVF enumeration values are flags used by a container object to specify the
requested behavior when setting up an advise sink or a caching connection with a
n object. These values have different meanings, depending on the type of connect
ion in which they are used, and each interface uses its own subset of the flags.
typedef enum tagADVF
{
ADVF_NODATA = 1,
ADVF_ONLYONCE = 2,
ADVF_PRIMEFIRST = 4,
ADVFCACHE_NOHANDLER = 8,
ADVFCACHE_FORCEBUILTIN = 16,
ADVFCACHE_ONSAVE = 32,
ADVF_DATAONSTOP = 64
} ADVF;
Elements
ADVF_NODATA
For data advisory connections (IDataObject::DAdvise or IDataAdviseHolder::Advise
), this flag requests the data object not to send data when it calls IAdviseSink
::OnDataChange. The recipient of the change notification can later request the d
ata by calling IDataObject::GetData. The data object can honor the request by pa
ssing TYMED_NULL in the STGMEDIUM parameter, or it can provide the data anyway.
For example, the data object might have multiple advisory connections, not all o
f which specified ADVF_NODATA, in which case the object might send the same noti
fication to all connections. Regardless of the container s request, its IAdviseSin
k implementation must check the STGMEDIUM parameter because it is responsible fo
r releasing the medium if it is not TYMED_NULL.
ADVF_NODATA is not a valid flag for view advisory connections (IViewObject::SetA
dvise) and it returns E_INVALIDARG.
ADVF_PRIMEFIRST
Requests that the object not wait for the data or view to change before making a
n initial call to IAdviseSink::OnDataChange (for data or view advisory connectio
ns) or updating the cache (for cache connections). Used with ADVF_ONLYONCE, this
parameter provides an asynchronous GetData call.
ADVF_ONLYONCE
Requests that the object make only one change notification or cache update befor
e deleting the connection.
ADVF_ONLYONCE automatically deletes the advisory connection after sending one da
ta or view notification. The advisory sink receives only one IAdviseSink call. A
nonzero connection identifier is returned if the connection is established, so
the caller can use it to delete the connection prior to the first change notific
ation.
For data change notifications, the combination of ADVF_ONLYONCE and ADVF_PRIMEFI
RST provides, in effect, an asynchronous IDataObject::GetData call.
When used with caching, ADVF_ONLYONCE updates the cache one time only, on receip
t of the first OnDataChange notification. After the update is complete, the advi
sory connection between the object and the cache is disconnected. The source obj
ect for the advisory connection calls the IAdviseSink::Release method.
ADVF_DATAONSTOP
For data advisory connections, assures accessibility to data. This flag indicate
s that when the data object is closing, it should call IAdviseSink::OnDataChange
, providing data with the call. Typically, this value is used in combination wit
h ADVF_NODATA. Without this value, by the time an OnDataChange call without data
reaches the sink, the source might have completed its shutdown and the data mig
ht not be accessible. Sinks that specify this value should accept data provided
in OnDataChange if it is being passed, because they may not get another chance t
o retrieve it.
For cache connections, this flag indicates that the object should update the cac
he as part of object closure.
ADVF_DATAONSTOP is not a valid flag for view advisory connections.
ADVFCACHE_NOHANDLER
Synonym for ADVFCACHE_FORCEBUILTIN, which is used more often.
ADVFCACHE_FORCEBUILTIN
This value is used by DLL object applications and object handlers that perform t
he drawing of their objects. ADVFCACHE_FORCEBUILTIN instructs COM to cache prese
ntation data to ensure that there is a presentation in the cache. This value is
not a valid flag for data or view advisory connections. For cache connections, t
his flag caches data that requires only code shipped with COM (or the underlying
operating system) to be present in order to produce it with IDataObject::GetDat
a or IViewObject::Draw. By specifying this value, the container can ensure that
the data can be retrieved even when the object or handler code is not available.
ADVFCACHE_ONSAVE
For cache connections, this flag updates the cached representation only when the
object containing the cache is saved. The cache is also updated when the COM ob
ject transitions from the running state back to the loaded state (because a subs
equent save operation would require rerunning the object). This value is not a v
alid flag for data or view advisory connections.
Remarks
For a data or view advisory connection, the container uses the ADVF constants wh
en setting up a connection between an IAdviseSink instance and and either an IDa
taObject or IViewObject instance. These connections are set up using the IDataOb
ject::DAdvise, IDataAdviseHolder::Advise, or IViewObject::SetAdvisemethods.
These constants are also used in the advf member of the STATDATA structure. This
structure is used by IEnumSTATDATA to describe the enumerated connections, and
the advf member indicates the flags that were specified when the advisory or cac
he connection was established. When STATDATA is used for an IOleObject::EnumAdvi
se enumerator, the advf member is indeterminate.
See Also
IDataAdviseHolder, IDataObject, IEnumSTATDATA,
16.7.2 DATADIR
The DATADIR enumeration values specify the direction of the data flow in the dwD
irection parameter of the IDataObject::EnumFormatEtc method. This determines the
formats that the resulting enumerator can enumerate.
typedef enum tagDATADIR
{
DATADIR_GET = 1,
DATADIR_SET = 2
} DATADIR;
Elements
DATADIR_GET
Requests that IDataObject::EnumFormatEtc supply an enumerator for the formats th
at can be specified in IDataObject::GetData.
DATADIR_SET
Requests that IDataObject::EnumFormatEtc supply an enumerator for the formats th
at can be specified in IDataObject::SetData.
See Also
IDataObject
16.7.3 DVASPECT
The DVASPECT enumeration values specify the desired data or view aspect of the o
bject when drawing or getting data.
typedef enum tagDVASPECT
{
DVASPECT_CONTENT = 1,
DVASPECT_THUMBNAIL = 2,
DVASPECT_ICON = 4,
DVASPECT_DOCPRINT = 8
} DVASPECT;
Elements
DVASPECT_CONTENT
Provides a representation of an object so it can be displayed as an embedded obj
ect inside of a container. This value is typically specified for compound docume
nt objects. The presentation can be provided for the screen or printer.
DVASPECT_THUMBNAIL
Provides a thumbnail representation of an object so it can be displayed in a bro
wsing tool. The thumbnail is approximately a 120 by 120 pixel, 16-color (recomme
nded) device-independent bitmap potentially wrapped in a metafile.
DVASPECT_ICON
Provides an iconic representation of an object.
DVASPECT_DOCPRINT
Provides a representation of the object on the screen as though it were printed
to a printer using the Print command from the File menu. The described data may
represent a sequence of pages.
Remarks
Values of this enumeration are used to define the dwAspect field of the FORMATET
C structure. Only one DVASPECT value can be used to specify a single presentatio
n aspect in a FORMATETC structure. The FORMATETC structure is used in many COM f
unctions and interface methods that require information on data presentation.
See Also
IAdviseSink, IDataObject, FORMATETC
16.7.4 DVASPECT2
The DVASPECT2 enumeration value is used in IViewObject::Draw to specify new draw
ing aspects used to optimize the drawing process.
typedef enum tagDVASPECT2
{
DVASPECT_OPAQUE = 16,
DVASPECT_TRANSPARENT = 32
} DVASPECT2;
Elements
DVASPECT_OPAQUE
Represents the opaque, easy to clip parts of an object. Objects may or may not s
upport this aspect.
DVASPECT_TRANSPARENT
Represents the transparent or irregular parts of on object, typically parts that
are expensive or impossible to clip out. Objects may or may not support this as
pect.
Remarks
To support drawing optimizations to reduce flicker, an object needs to be able t
o draw and return information about three separate aspects of itself:
DVASPECT_CONTENT
Same as before. Specifies the entire content of an object. All objects should su
pport this aspect.
DVASPECT_OPAQUE
Represents the opaque, easy to clip parts of an object. Objects may or may not s
upport this aspect.
DVASPECT_TRANSPARENT
Represents the transparent or irregular parts of on object, typically parts that
are expensive or impossible to clip out. Objects may or may not support this as
pect.
The container can determine which of these drawing aspects an object supports by
calling the new method IViewObjectEx::GetViewStatus. Individual bits return inf
ormation about which aspects are supported. If an object does not support the IV
iewObjectEx interface, it is assumed to support only DVASPECT_CONTENT.
Depending on which aspects are supported, the container can ask the object to dr
aw itself during the front to back pass only, the back to front pass only, or bo
th. The various possible cases are:
· Objects supporting only DVASPECT_CONTENT should be drawn during the back to fron
t pass, with all opaque parts of any overlapping object clipped out. Since all o
bjects should support this aspect, a container not concerned about flickering -
maybe because it is drawing in an offscreen bitmap - can opt to draw all objects
that way and skip the front to back pass.
· Objects supporting DVASPECT_OPAQUE may be asked to draw this aspect during the f
ront to back pass. The container is responsible for clipping out the object s opaq
ue regions (returned by IViewObjectEx::GetRegion) before painting any further o
bject behind it.
· Objects supporting DVASPECT_TRANSPARENT may be asked to draw this aspect during
the back to front pass. The container is responsible for clipping out opaque par
ts of overlapping objects before letting an object draw this aspect.
Even when DVASPECT_OPAQUE and DVASPECT_TRANSPARENT are supported, the container
is free to use these aspects or not. In particular, if it is painting in an offs
creen bitmap and consequently is unconcerned about flicker, the container may us
e DVASPECT_CONTENT and a one-pass drawing only. However, in a two-pass drawing,
if the container uses DVASPECT_OPAQUE during the front to back pass, then it mus
t use DVASPECT_TRANSPARENT during the back to front pass to complete the renderi
ng of the object.
16.7.5 DVASPECTINFOFLAG
The DVASPECTINFOFLAG enumeration value is used in the DVASPECTINFO structure to
indicate whether an object can support optimized drawing of itself.
typedef enum tagDVASPECTINFOFLAG
{
DVASPECTINFOFLAG_CANOPTIMIZE = 1
} DVASPECTINFOFLAG;
Elements
DVASPECTINFOFLAG_CANOPTIMIZE
If TRUE, indicates that the object can support optimized rendering of itself. Si
nce most objects on a form share the same font, background color, and border typ
es, leaving these values in the device context allows the next object to use the
m without having to re-select them. Specifically, the object can leave the font,
brush, and pen selected on return from the IViewObject::Draw method instead of
deselecting these from the device context. The container then must deselect thes
e values at the end of the overall drawing process. The object can also leave ot
her drawing state changes in the device context, such as the background color, t
he text color, raster operation code, the current point, the line drawing, and t
he poly fill mode. The object cannot change state values unless other objects ar
e capable of restoring them. For example, the object cannot leave a changed mode
, transformation value, selected bitmap, clip region, or metafile.
See Also
DVASPECTINFO
16.7.6 STATFLAG
The STATFLAG enumeration values indicate whether the method should try to return
a name in the pwcsName member of the STATSTG structure. The values are used in
the ILockBytes::Stat, IStorage::Stat, and IStream::Stat methods to save memory w
hen the pwcsName member is not needed.
Defined in the IOLETypes pseudo-interface (oletyp.idl).
typedef enum tagSTATFLAG
{
STATFLAG_DEFAULT = 0,
STATFLAG_NONAME = 1
} STATFLAG;
Elements
STATFLAG_DEFAULT
Requests that the statistics include the pwcsName member of the STATSTG structur
e.
STATFLAG_NONAME
Requests that the statistics not include the pwcsName member of the STATSTG stru
cture. If the name is omitted, there is no need for the Stat methods to allocate
and free memory for the string value for the name and the method can save an Al
loc and Free operation.
See Also
ILockBytes::Stat, IStorage::Stat, IStream::Stat
16.7.7 TYMED
The TYMED enumeration values indicate the type of storage medium being used in a
data transfer. They are used in the STGMEDIUM or FORMATETC structures.
typedef [transmit_as(long)] enum tagTYMED
{
TYMED_HGLOBAL = 1,
TYMED_FILE = 2,
TYMED_ISTREAM = 4,
TYMED_ISTORAGE = 8,
TYMED_GDI = 16,
TYMED_MFPICT = 32,
TYMED_ENHMF = 64,
TYMED_NULL = 0
} TYMED;
Elements
TYMED_HGLOBAL
The storage medium is a global memory handle (HGLOBAL). Allocate the global hand
le with the GMEM_SHARE flag. If the STGMEDIUM punkForRelease member is NULL, the
destination process should use GlobalFree to release the memory.
TYMED_FILE
The storage medium is a disk file identified by a path. If the STGMEDIUM punkFor
Release member is NULL, the destination process should use OpenFile to delete th
e file.
TYMED_ISTREAM
The storage medium is a stream object identified by an IStream pointer. Use IStr
eam::Read to read the data. If the STGMEDIUM punkForRelease member is NULL, the
destination process should use IStream::Release to release the stream component.
TYMED_ISTORAGE
The storage medium is a storage component identified by an IStorage pointer. The
data is in the streams and storages contained by this IStorage instance. If the
STGMEDIUM punkForRelease member is NULL, the destination process should use ISt
orage::Release to release the storage component.
TYMED_GDI
The storage medium is a GDI component (HBITMAP). If the STGMEDIUM punkForRelease
member is NULL, the destination process should use DeleteObject to delete the b
itmap.
TYMED_MFPICT
The storage medium is a metafile (HMETAFILEIf the STGMEDIUM punkForRelease membe
r is NULL, the destination process should use DeleteMetaFile to delete the bitma
p.
TYMED_ENHMF
The storage medium is an enhanced metafile. If the STGMEDIUM punkForRelease memb
er is NULL, the destination process should use DeleteEnhMetaFile to delete the b
itmap.
TYMED_NULL
No data is being passed.
Remarks
During data transfer operations, a storage medium is specified. This medium must
be released after the data transfer operation. The provider of the medium indic
ates its choice of ownership scenarios in the value it provides in the STGMEDIUM
structure. A NULL value for the IUNKNOWN field indicates that the receiving bod
y of code owns and can free the medium. A non-NULL pointer specifies that Releas
eStgMedium can always be called to free the medium.
See Also
FORMATETC, IAdviseSink, IDataObject, ReleaseStgMedium, STGMEDIUM
17. Type Libraries
Type libraries and the type description interfaces provide a way to read and bin
d to the descriptions of objects in a type library. These descriptions are used
by COM clients when they browse, create, and manipulate COM objects.
The type description interfaces described in this chapter include:
· ITypeLib Retrieves information about a type library.
· ITypeLib2 Allows ITypeLib to cast to an ITypeLib2 in performance-sensitive cases
.
· ITypeInfo Reads the type information within the type library.
· ITypeInfo2 Allows ITypeInfo to cast to an ITypeInfo2 in performance-sensitive cas
es.
· ITypeComp Creates compilers that use type information.
This chapter also describes functions for loading, registering, and querying typ
e libraries.
17.1 Overview of Type Description Interfaces
A type library is a container for type descriptions of one or more objects, and
is accessed through the ITypeLib interface. The ITypeLib interface provides acce
ss to information about the type description in a type library. The descriptions
of individual objects are accessed through the ITypeInfo interface.
In addition, there are two new interfaces for Automation:
ITypeInfo2::ITypeInfo
ITypeLib2::ITypeLib
Because they inherit from ITypeInfo and ITypeLib, an ITypeInfo can be cast to an
ITypeInfo2 instead of using the calls QueryInterface() and Release().
By adding the new methods described in the following section, QueryInterface can
be called to ITypeInfo2 and ITypeLib2 in the same way as ITypeInfo and ITypeLib
.
The ITypeInfo interface is typically used for reading information about objects.
For example, an object browser tool can use ITypeInfo to extract information ab
out the characteristics and capabilities of objects from type libraries.
Implemented by Used by Header file name
Oleaut32.dll (32-bit systems)
Typelib.dll (16-bit systems) Tools that need to access the descriptions of ob
jects contained in type libraries. Oleauto.h
Dispatch.h
Type information interfaces are intended to describe the parts of the applicatio
n that can be called by outside clients, rather than those that might be used in
ternally 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, th
is 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 on.
The type description of an IDispatch interface can be used to implement the inte
rface. For more information, see the description of CreateStdDispatch in Chapter
18.
An instance of ITypeInfo provides various information about the type of an objec
t, and is used in different ways. A compiler can use an ITypeInfo to compile ref
erences to members of the type. A type interface browser can use it to find info
rmation about each member of the type. An IDispatch implementor can use it to pr
ovide automatic delegation of IDispatch calls to an interface.
17.1.1 Type Descriptions
The information associated with an object described by ITypeInfo can include a s
et of functions, a set of data members, and various type attributes. It is essen
tially the same as the information described by a C++ class declaration, which c
an be used to define both interfaces and structures, as well as any combination
of functions and data members. In addition to interfaces and structure definitio
ns, the ITypeInfo interface is used to describe other types, including enumerati
ons and aliases. Because the interface to a C file or library is simply a set of
functions and variable declarations, ITypeInfo can also be used to describe the
m.
Type information comprises individual type descriptions. Each type description m
ust have one of the following forms:
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 typ
es.
module module Data and functions not accessed through virtual function table (
VTBL) entries.
IDispatch interface dispinterface IDispatch properties and methods accesse
d through IDispatch::Invoke.
COM interface interface COM member functions accessed through VTBL entri
es.
dual interface dual Supports either VTBL or IDispatch.
component object class coclass A component object class. Specifies an implement
ation of one or more COM interfaces and one or more IDispatch interfaces.
Note
All bit flags that are not used specifically should be set to zero for future co
mpatibility.
17.1.1.1 Alias
An alias has TypeKind = TKIND_ALIAS. An alias is an empty set of functions, an e
mpty set of data members, and a type description (located in the TYPEATTR), whic
h gives the actual type definition (typedef) of the alias.
17.1.1.2 Enumeration (Statement)
An enumeration (enum) has TypeKind = TKIND_ENUM. An enumeration is an empty set
of functions and a set of constant data members.
17.1.1.3 Structure (Statement)
A structure (struct) description has TypeKind = TKIND_RECORD. A structure is an
empty set of functions and a set of per-instance data members.
17.1.1.4 Union (Statement)
A union description has TypeKind = TKIND_UNION. A union is an empty set of funct
ions and a set of per-instance data members, each of which has an instance offse
t of zero.
17.1.1.5 Module (Statement)
A module has TypeKind = TKIND_MODULE. A module is a set of static functions and
a set of static data members.
17.1.1.6 V-table Interface
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 VTBL is the first 4 byte
s of the instance.
The type information fully describes the member functions in the VTBL, including
parameter names and types and function return types. It may inherit from no mor
e than one other interface.
With interfaces and dispinterfaces, all members should have different names, exc
ept 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 t
he functions (because they define the same property conceptually).
17.1.1.7 IDispatch Interface
These include objects (TypeKind = TKIND_DISPATCH) that support the IDispatch int
erface with a specification of the dispatch data members (such as properties) an
d methods supported through the object ' s Invoke implementation. All members of the di
spinterface should have different IDs, except for the accessor functions of prop
erties.
17.1.1.8 Dual Interface
Dual interfaces (dual) have two different type descriptions for the same interfa
ce. The TKIND_INTERFACE type description describes the interface as a standard
Component Object Model (COM) interface. The TKIND_DISPATCH type description desc
ribes the interface as a standard dispatch interface. The lcid and retval parame
ters, and the HRESULT return types are removed, and the return type of the membe
r is specified to be the same type as the retval parameter.
By default, the TYPEKIND enumeration for a dual interface is TKIND_DISPATCH. Too
ls that bind to interfaces should check the type flags for TYPEFLAG_FDUAL. If th
is flag is set, the TKIND_INTERFACE type description is available through a call
to ITypeInfo::GetRefTypeOfImplType with an index of 1, followed by a call to ITy
peInfo::GetRefTypeInfo.
17.1.1.9 Component Object Classes
These coclass objects (TypeKind = TKIND_COCLASS) support a set of implemented in
terfaces, which can be of either TKIND_INTERFACE or TKIND_DISPATCH.
17.2 Type Library Interface Descriptions
17.2.1 IProvideClassInfo
See IProvideClassInfo information in section 4.7.2.
17.2.2 IProvideClassInfo2
See IProvideClassInfo2 information in section 4.7.3.
17.2.3 ITypeLib
The data that describes a set of objects is stored in a type library. A type lib
rary can be a stand-alone binary file (.tlb), a resource in a dynamic link libra
ry or executable file (.dll or .exe), or part of a compound document file.
Implemented by Used by Header file name
Oleaut32.dll (32-bit systems)
Typelib.dll (16-bit systems) Tools that need to access the descriptions of ob
jects contained in type libraries. Oleauto.h
Dispatch.h
The system registry contains a list of all the installed type libraries.
The ITypeLib interface provides methods for accessing a library of type descript
ions. This interface supports the following:
· Generalized containment for type information. ITypeLib allows iteration over the
type descriptions contained in the library.
· Global functions and data. A type library can contain descriptions of a set of m
odules, each of which is the equivalent of a C or C++ source file that exports d
ata and functions. The type library supports compiling references to the exporte
d data and functions.
· General information, including a user-readable name for the library and help for
the library as a whole.
17.2.3.1 ITypeLib::FindName
HRESULT FindName(
OLECHAR FAR* szNameBuf,
unsigned long lHashVal,
ITypeInfo FAR* FAR* ppTInfo,
MEMBERID FAR* rgMemId,
unsigned int FAR* pcFound
);
Finds occurrences of a type description in a type library. This may be used to q
uickly verify that a name exists in a type library.
Parameters
szNameBuf
The name to search for.
lHashVal
A hash value to speed up the search, computed by the LHashValOfNameSys function.
If lHashVal = 0, a value is computed.
ppTInfo
On return, an array of pointers to the type descriptions that contain the name s
pecified in szNameBuf. Cannot be Null.
rgMemId
An array of the MEMBERIDs of the found items; rgMemId[i] is the MEMBERID that in
dexes into the type description specified by ppTInfo[i]. Cannot be Null.
pcFound
On entry, indicates how many instances to look for. For example, *pcFound = 1 ca
n be called to find the first occurrence. The search stops when one is found.
On exit, indicates the number of instances that were found. If the in and out va
lues of *pcFound are identical, there may be more type descriptions that contain
the name.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 write to the file.
TYPE_E_INVDATAREAD The function could not read from the file.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_CANTLOADLIBRARY The library or .dll file could not be loaded.
TYPE_E_ELEMENTNOTFOUND The element was not found.
Comments
Passing *pcFound = n indicates that there is enough room in the ppTInfo and rgMe
mId arrays for n (ptinfo, memid) pairs. The function returns MEMBERID_NIL in rgM
emId[i], if the name in szNameBuf is the name of the type information in ppTInfo
[i].
17.2.3.2 ITypeLib::GetDocumentation
HRESULT GetDocumentation(
int index,
BSTR FAR* pBstrName,
BSTR FAR* pBstrDocString,
unsigned long FAR* pdwHelpContext,
BSTR FAR* pBstrHelpFile
);
Retrieves the library ' s documentation string, the complete Help file name and path, a
nd the context identifier for the library Help topic in the Help file.
Parameters
index
Index of the type description whose documentation is to be returned. I If index
is-1, then the documentation for the library itself is returned.
pBstrName
Returns a BSTR that contains the name of the specified item. If the caller does
not need the item name, then pBstrName can be Null.
pBstrDocString
Returns a BSTR that contains the documentation string for the specified item. If
the caller does not need the documentation string, then pBstrDocString can be N
ull.
pdwHelpContext
Returns the Help context identifier (ID) associated with the specified item. If
the caller does not need the Help context ID, then pdwHelpContext can be Null.
pBstrHelpFile
Returns a BSTR that contains the fully qualified name of the Help file. If the c
aller does not need the Help file name, then pBstrHelpFile can be Null.
Return Value
The return value obtained from the returned HRESULT is one of the following:

Return value 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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND The element was not found.
Comments
The caller should free the BSTR parameters pBstrName, pBstrDocString, and pBstrH
elpFile.
Example
for (i = 0; i < utypeinfoCount; i++)
{
CHECKRESULT(ptlib->GetDocumentation(i, &bstrName, NULL, NULL, NULL));
.
.
.
SysFreeString(bstrName);
}
17.2.3.3 ITypeLib::GetLibAttr
HRESULT GetLibAttr(
TLIBATTR FAR* FAR* ppTLibAttrr
);
Retrieves the structure that contains the library ' s attributes.
Parameter
ppTLibAttrr
Pointer to a structure that contains the library ' s attributes.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 write to the file.
TYPE_E_INVDATAREAD The function could not read from the file.
TYPE_E_UNSUPFORMAT The type library has an unsupported format.
TYPE_E_INVALIDSTATE The type library could not be opened.
Comments
Use ITypeLib::ReleaseTLibAttr to free the memory occupied by the TLIBATTR struct
ure.
17.2.3.4 ITypeLib::GetTypeComp
HRESULT GetTypeComp(
ITypeComp FAR* FAR* ppTComp
);
Enables a client compiler to bind to a library ' s types, variables, constants, and glo
bal functions.
Parameter
ppTComp
Points to a pointer to the ITypeComp instance for this ITypeLib. A client compil
er uses the methods in the ITypeComp interface to bind to types in ITypeLib, as
well as to the global functions, variables, and constants defined in ITypeLib.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
The Bind 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 TYPEKIND enumerations of TKIND_MODULE, TKIND_ENUM, and TKIND_C
OCLASS. These names shadow any global names defined within the type information.
The members of TKIND_ENUM, TKIND_MODULE, and TKIND_COCLASS types marked as Appl
ication objects can be directly bound to from ITypeComp without specifying the n
ame of the module.
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, TK
IND_MODULE, and TKIND_COCLASS) share the name space with variables, functions, c
onstants, and enumerators.
If a member requires qualification to differentiate it from other items in the n
ame space, GetTypeComp can be called successively for each qualifier in order to
bind to the desired member. This allows programming language compilers to acces
s members of modules, enumerations, and coclasses, even though the member can ' t be bo
und to with a qualified name.
17.2.3.5 ITypeLib::GetTypeInfo
HRESULT GetTypeInfo(
unsigned int index,
ITypeInfo FAR* FAR* ppTInfo
);
Retrieves the specified type description in the library.
Parameters
index
Index of the ITypeInfo interface to be returned.
ppTInfo
If successful, returns a pointer to the pointer to the ITypeInfo interface.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
TYPE_E_ELEMENTNOTFOUND The index parameter is outside the range of 0 to GetType
InfoCount() 1.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
TYPE_E_IOERROR The function could not read from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_REGISTRYACCESS There was an error accessing the system registration dat
abase.
TYPE_E_INVALIDSTATE The type library could not be opened.
Comments
For dual interfaces, ITypeLib::GetTypeInfo returns only the TKIND_DISPATCH type
information. To get the TKIND_INTERFACE type information, ITypeInfo::GetRefTypeO
fImplType can be called on the TKIND_DISPATCH type information, passing an index
of 1. Then, the returned type information handle can be passed to ITypeInfo::Get
RefTypeInfo.
Example
The following example gets the TKIND_INTERFACE type information for a dual inter
face.
ptlib->GetTypeInfo((unsigned int) dwIndex, &ptypeinfoDisp);
ptypeinfoDisp->GetRefTypeOfImplType(-1, &phreftype);
ptypeinfoDisp->GetRefTypeInfo(phreftype, &ptypeinfoInt);
17.2.3.6 ITypeLib::GetTypeInfoCount
HRESULT GetTypeInfoCount()
Parameter
none
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_NOTIMPL Failure.
Comments
Returns the number of type descriptions in the type library.
17.2.3.7 ITypeLib::GetTypeInfoOfGuid
HRESULT GetTypeInfoOfGuid(
REFGUID guid,
ITypeInfo FAR* FAR* ppTinfo
);
Retrieves the type description that corresponds to the specified GUID.
Parameters
guid
Pointer to the GUID of the type description.
ppTinfo
Pointer to a pointer to the ITypeInfo interface.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning

S_OK Success.
TYPE_E_ELEMENTNOTFOUND No type description was found in the library with the sp
ecified GUID.
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 older format.
TYPE_E_REGISTRYACCESS There was an error accessing the system registration dat
abase.
TYPE_E_INVALIDSTATE The type library could not be opened.
17.2.3.8 ITypeLib::GetTypeInfoType
HRESULT GetTypeInfoType(
unsigned int index,
TYPEKIND FAR* pTKind
);
Retrieves the type of a type description.
Parameters
index
The index of the type description within the type library.
pTKind
A pointer to the TYPEKIND enumeration for the type description.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
TYPE_E_ELEMENTNOTFOUND Index is outside the range of 0 to GetTypeInfoCount() 1.
17.2.3.9 ITypeLib::IsName
HRESULT IsName(
OLECHAR FAR* szNameBuf,
unsigned long lHashVal,
BOOL pfName
);
Indicates whether a passed-in string contains the name of a type or member descr
ibed in the library.
Parameter
szNameBuf
The string to test. If IsName() is successful, szNameBuf is modified to match th
e case (capitalization) found in the type library.
lHashVal
The hash value of szNameBuf.
pfName
On return, set to True if szNameBuf was found in the type library; otherwise Fal
se.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
17.2.3.10 ITypeLib::ReleaseTLibAttr
HRESULT ReleaseTLibAttr(
TLIBATTR FAR* pTLibAttr
);
Releases the TLIBATTR originally obtained from ITypeLib::GetLibAttr.
Parameter
pTLibAttr
Pointer to the TLIBATTR to be freed.
Comments
Releases the specified TLIBATTR. This TLIBATTR was previously obtained with a ca
ll to GetTypeLib::GetLibAttr.
17.2.4 ITypeLib2
The ITypeLib2 interface inherits from the ITypeLib interface. This allows ITypeL
ib to cast to an ITypeLib2 in performance-sensitive cases, rather than perform e
xtra QueryInterface() and Release() calls.
Example
DECLARE_INTERFACE_(ITypeLib2, ITypeLib)
{
17.2.4.1 ITypeLib2::GetCustData
HRESULT GetCustData(
REFGUID guid,
VARIANT *pVarVal
);
Gets the custom data.
Parameter
guid
GUID used to identify the data.
pVarVal
Where to put the retrieved data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.4.2 ITypeLib2::GetDocumentation2
HRESULT GetDocumentation2(
[in] int index,
[in] LCID lcid,
[out] BSTR FAR* pbstrHelpString,
[out] unsigned long FAR* pdwHelpStringContext,
BSTR FAR* pbstrHelpStringDll
);
Retrieves the library ' s documentation string, the complete Help file name and path, t
he localization context to use, and the context ID for the library Help topic in
the Help file.
Parameters
index
Index of the type description whose documentation is to be returned; if index is
-1, then the documentation for the library is returned.
lcid
Locale identifier.
pbstrHelpString
Returns a BSTR that contains the name of the specified item. If the caller does
not need the item name, then pbstrHelpString can be Null.
pdwHelpStringContext
Returns the Help localization context. If the caller does not need the Help cont
ext, then it can be Null.
pbstrHelpStringDll
Returns a BSTR that contains the fully qualified name of the file containing the
DLL used for Help file. If the caller does not need the file name, then it can
be Null.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND The element was not found.
Comments
Gets information at the type library level. The caller should free the BSTR para
meters.
This function will call _DLLGetDocumentation in the specified DLL to retrieve th
e desired Help string, if there is a Help string context for this item. If no He
lp string context exists or an error occurs, then it will defer to the GetDocume
ntation method and return the associated documentation string.
17.2.4.3 ITypeLib2::GetLibStatistics
HRESULT GetLibStatistics(
unsigned long* pcUniqueNames,
unsigned long* pcchUniqueNames
);
Returns statistics about a type library that are required for efficient sizing o
f hash tables.
Parameter
pcUniqueNames
Returns a pointer to a count of unique names. If the caller does not need this i
nformation, set to NULL.
pcchUniqueNames
Returns a pointer to a change in the count of unique names.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning

S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Example
ITypeLib2::GetLibStatistics(DWORD *pcUniqueNames, DWORD * pcchUniqueNames)
17.2.4.4 ITypeLib2::GetAllCustData
HRESULT GetAllCustData(
CUSTDATA *pCustData
);
Gets all custom data items for the library.
Parameter
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:

Return value Meaning


S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Comments
After the call, the caller needs to release memory used to hold the custom data
item by calling ClearCustData().
17.2.5 ITypeInfo
17.2.5.1 ITypeInfo::AddressOfMember
HRESULT AddressOfMember(
MEMBERID memid,
INVOKEKIND invKind,
VOID FAR* FAR* ppv
);
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 i
s defined by the DISPID.
invKind
Specifies whether the member is a property, and if so, what kind.
ppv
On return, points to a pointer to the static member.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from 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 older 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.
Comments
The addresses are valid until the caller releases its reference to the type desc
ription. The invKind parameter can be ignored unless the address of a property f
unction is being requested.
If the type description inherits from another type description, this function is
recursive to the base type description, if necessary, to find the item with the
requested member ID.
17.2.5.2 ITypeInfo::CreateInstance
HRESULT CreateInstance(
IUnknown FAR* pUnkOuter,
REFIID riid,
VOID FAR* FAR* ppvObj
);
Creates a new instance of a type that describes a component object class (coclas
s).
Parameters
pUnkOuter
A pointer to the controlling IUnknown. If Null, then a stand-alone instance is c
reated. If valid, then an aggregate object is created.
riid
An ID for the interface that the caller will use to communicate with the resulti
ng object.
ppvObj
On return, points to a pointer to an instance of the created object.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
TYPE_E_WRONGTYPEKIND Type mismatch.
E_INVALIDARG One or more of the arguments is invalid.
E_NOINTERFACE COM could not find an implementation of one or more required int
erfaces.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
Other return codes Additional errors may be returned from GetActiveObject o
r CoCreateInstance.
Comments
For types that describe a component object class (coclass), CreateInstance creat
es a new instance of the class. Normally, CreateInstance calls CoCreateInstance
with the type description ' s GUID. For an Application object, it first calls GetActive
Object. If the application is active, GetActiveObject returns the active object;
otherwise, if GetActiveObject fails, CreateInstance calls CoCreateInstance.
17.2.5.3 ITypeInfo::GetContainingTypeLib
HRESULT GetContainingTypeLib(
ITypeLib FAR* FAR* ppTLib,
unsigned int FAR* pIndex
);
Retrieves the containing type library and the index of the type description with
in that type library.
Parameters
ppTLib
On return, points to the containing type library.
pIndex
On return, points to the index of the type description within the containing typ
e library.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
E_NOINTERFACE COM could not find an implementation of one or more required int
erfaces.
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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
17.2.5.4 ITypeInfo::GetDllEntry
HRESULT GetDllEntry(
MEMBERID memid,
INVOKEKIND invKind,
BSTR FAR* pBstrDllName,
BSTR FAR* pBstrName,
unsigned short FAR* pwOrdinal
);
Retrieves a description or specification of an entry point for a function in a D
LL.
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 properti
es, because one memid can identify up to three separate functions.
pBstrDllName
If not Null, the function sets pBstrDllName to a BSTR that contains the name of
the DLL.
pBstrName
If not Null, the function sets lpbstrName to a BSTR that contains the name of th
e entry point. If the entry point is specified by an ordinal, *lpbstrName is set
to Null.
pwOrdinal
If not Null, and if the function is defined by an ordinal, then lpwOrdinal is se
t to point to the ordinal.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
E_NOINTERFACE COM could not find an implementation of one or more required int
erfaces.
TYPE_E_ELEMENTNOTFOUND The element was not found.
TYPE_E_IOERROR The function could not read from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
The caller passes in a member ID, which represents the member function whose ent
ry description is desired. If the function has a DLL entry point, the name of th
e DLL that contains the function, as well as its name or ordinal identifier, are
placed in the passed-in pointers allocated by the caller. If there is no DLL en
try point for the function, an error is returned.
If the type description inherits from another type description, this function is
recursive to the base type description, if necessary, to find the item with the
requested member ID.
The caller should use SysFreeString() to free the BSTRs referenced by pBstrName
and pBstrDllName.
17.2.5.5 ITypeInfo::GetDocumentation
HRESULT GetDocumentation(
MEMBERID memid,
BSTR FAR* pBstrName,
BSTR FAR* pBstrDocString,
unsigned long FAR* pdwHelpContext,
BSTR FAR* pBstrHelpFile
);
Retrieves the documentation string, the complete Help file name and path, and th
e context ID for the Help topic for a specified type description.
Parameters
memid
ID of the member whose documentation is to be returned.
pBstrName
Pointer to a BSTR allocated by the callee into which the name of the specified i
tem is placed. If the caller does not need the item name, pBstrName can be Null.
pBstrDocString
Pointer to a BSTR into which the documentation string for the specified item is
placed. If the caller does not need the documentation string, pBstrDocString can
be Null.
pdwHelpContext
Pointer to the Help context associated with the specified item. If the caller do
es not need the Help context, the pdwHelpContext can be Null.
pBstrHelpFile
Pointer to a BSTR into which the fully qualified name of the Help file is placed
. If the caller does not need the Help file name, pBstrHelpFile can be Null.
Return Value
The return value obtained from the returned HRESULT is one of the following:

Return value 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 from the file.
TYPE_E_ELEMENTNOTFOUND The element was not found.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND The element was not found.
Comments
The function GetDocumentation provides access to the documentation for the membe
r specified by the memid parameter. If the passed-in memid is MEMBERID_NIL, then
the documentation for the type description is returned.
If the type description inherits from another type description, this function is
recursive to the base type description, if necessary, to find the item with the
requested member ID.
The caller should use SysFreeString() to free the BSTRs referenced by pBstrName,
pBstrDocString, and pBstrHelpFile.
Example
CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL,
NULL));
.
.
.
SysFreeString (bstrName);
17.2.5.6 ITypeInfo::GetFuncDesc
HRESULT GetFuncDesc(
unsigned int index,
FUNCDESC FAR* FAR* ppFuncDesc
);
Retrieves the FUNCDESC structure that contains information about a specified fun
ction.
Parameters
index
Index of the function whose description is to be returned. The index should be i
n the range of 0 to 1 less than the number of functions in this type.
ppFuncDesc
On return, points to a pointer to a FUNCDESC that describes the specified functi
on.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
Comments
The function GetFuncDesc provides access to a FUNCDESC structure that describes
the function with the specified index. The FUNCDESC should be freed with ITypeIn
fo::ReleaseFuncDesc(). The number of functions in the type is one of the attribu
tes 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);
17.2.5.7 ITypeInfo::GetIDsOfNames
HRESULT GetIDsOfNames(
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
MEMBERID FAR* pMemId
);
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.
pMemId
Caller-allocated array in which name mappings are placed.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 locale identifier (LCID) could not be found in the C
OM 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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_WRONGTYPEKIND Type mismatch.

Comments
The function GetIDsOfNames maps the name of a member (rgszNames[0]) and its para
meters (rgszNames[1] ...rgszNames[cNames - 1]) to the ID of the member (rgid[0])
, and to the IDs of the specified parameters (rgid[1] ... rgid[cNames - 1]). The
IDs of parameters are 0 for the first parameter in the member function ' s argument li
st, 1 for the second, and so on.
If the type description inherits from another type description, this function is
recursive to the base type description, if necessary, to find the item with the
requested member ID.
17.2.5.8 ITypeInfo::GetImplTypeFlags
HRESULT GetImplTypeFlags(
unsigned int index,
int* pImplTypeFlags
);
Retrieves the IMPLTYPEFLAGS enumeration for one implemented interface or base in
terface in a type description.
Parameters
index
Index of the implemented interface or base interface for which to get the flags.
pImplTypeFlags
On return, pointer to the IMPLTYPEFLAGS enumeration.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
The flags are associated with the act of inheritance, and not with the inherited
interface.
17.2.5.9 ITypeInfo::GetMops
HRESULT GetMops(
MEMBERID memid,
BSTR FAR* pBstrMops
);
Retrieves marshaling information.
Parameters
memid
The member ID that indicates which marshaling information is needed.
pBstrMops
On return, contains a pointer to the opcode string used in marshaling the fields
of the structure described by the referenced type description, or returns Null
if there is no information to return.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_UNSUPFORMAT The type library has an older 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 stri
ng for marshaling the fields of the structure described by the type description.
Otherwise, it returns the opcode string for marshaling the function specified b
y the index.
If the type description inherits from another type description, this function re
curses on the base type description, if necessary, to find the item with the req
uested member ID.
17.2.5.10 ITypeInfo::GetNames
HRESULT GetNames(
MEMBERID memid,
BSTR FAR* rgBstrNames,
unsigned int cMaxNames,
unsigned int FAR* pcNames
);
Retrieves the variable with the specified member ID (or the name of the property
or method and its parameters) that correspond to the specified function ID.
Parameters
memid
The ID of the member whose name (or names) is to be returned.
rgBstrNames
Pointer to the caller-allocated array. On return, each of these lpcName elements
is filled in to point to a BSTR that contains the name (or names) associated wi
th the member.
cMaxNames
Length of the passed-in rgBstrNames array.
pcNames
On return, points to the number that represents the number of names in rgBstrNam
es array.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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 (Basic string) array.
If the member ID identifies a property that is implemented with property functio
ns, the property name is returned.
For property get functions, the names of the function and its parameters are alw
ays returned.
For property put and put reference functions, the right side of the assignment i
s unnamed. If cMaxNamesis less than is required to return all of the names of th
e parameters of a function, then only the names of the first cMaxNames- 1 parame
ters are returned. The names of the parameters are returned in the array in the
same order that they appear elsewhere in the interface (for example, the same or
der in the parameter array associated with the FUNCDESC enumeration).
If the type description inherits from another type description, this function is
recursive to the base type description, if necessary, to find the item with the
requested member ID.
17.2.5.11 ITypeInfo::GetRefTypeInfo
HRESULT GetRefTypeInfo(
HREFTYPE hRefType,
ITypeInfo FAR* FAR* ppTInfo
);
If a type description references other type descriptions, it retrieves the refer
enced type descriptions.
Parameters
hRefType
Handle to the referenced type description to be returned.
ppTInfo
Points a pointer to a pointer to the referenced type description.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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 dat
abase.
TYPE_E_LIBNOTREGISTERED The type library was not found in the system registratio
n database.
Comments
On return, the second parameter contains a pointer to a pointer to a type descri
ption 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 varia
bles, function parameters, or function return types. For example, if the type of
a data member is a record type, the type description for that data member conta
ins the hRefTypeof a referenced type description. To get a pointer to the type d
escription, the reference is passed to GetRefTypeInfo.
17.2.5.12 ITypeInfo::GetRefTypeOfImplType
HRESULT GetRefTypeOfImplType(
unsigned int index,
HREFTYPE FAR* pRefType
);
If a type description describes a COM class, it retrieves the type description o
f the implemented interface types. For an interface, GetRefTypeOfImplType return
s 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 TYPEATTR structure.
pRefType
On return, points to a handle for the implemented interface (if any). This handl
e can be passed to ITypeInfo::GetRefTypeInfo to get the type description.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
TYPE_E_ELEMENTNOTFOUND Passed index is outside the range 0 to 1 less than the n
umber of function descriptions.
E_INVALIDARG One or more of the arguments is invalid.
TYPE_E_IOERROR The function could not read from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
Comments
If the TKIND_DISPATCH type description is for a dual interface, the TKIND_INTERF
ACE type description can be obtained by calling GetRefTypeOfImplType with an ind
ex of 1, and by passing the returned pRefType handle to GetRefTypeInfo to retriev
e the type information.
17.2.5.13 ITypeInfo::GetTypeAttr
HRESULT GetTypeAttr(
TYPEATTR FAR* FAR* ppTypeAttr
);
Retrieves a TYPEATTR structure that contains the attributes of the type descript
ion.
Parameter
ppTypeAttr
On return, points to a pointer to a structure that contains the attributes of th
is type description.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 write to the file.
TYPE_E_INVDATAREAD The function could not read from the file.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
Comments
To free the TYPEATTR structure, use ITypeInfo::ReleaseTypeAttr.
Example
CHECKRESULT(ptypeinfoCur->GetTypeAttr(&ptypeattrCur));
.
.
.
ptypeinfoCur->ReleaseTypeAttr(ptypeattrCur);
17.2.5.14 ITypeInfo::GetTypeComp
HRESULT GetTypeComp(
ITypeComp FAR* FAR* ppTComp
);
Retrieves the ITypeComp interface for the type description, which enables a clie
nt compiler to bind to the type description ' s members.
Parameter
ppTComp
On return, points to a pointer to the ITypeComp of the containing type library.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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
.
17.2.5.15 ITypeInfo::GetVarDesc
HRESULT GetVarDesc(
unsigned int index,
VARDESC FAR* FAR* ppVarDesc
);
Retrieves a VARDESC structure that describes the specified variable.
Parameters
index
Index of the variable whose description is to be returned. The index should be i
n the range of 0 to 1 less than the number of variables in this type.
ppVarDesc
On return, points to a pointer to a VARDESC that describes the specified variabl
e.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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->memid;
CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL,
NULL));
ptypeinfo->ReleaseVarDesc(pvardesc);
17.2.5.16 ITypeInfo::Invoke
HRESULT Invoke(
VOID FAR* pvInstance,
MEMBERID memid,
unsigned short wFlags,
DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult,
EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr
);
Invokes a method, or accesses a property of an object, that implements the inter
face described by the type description.
Parameters
pvInstance
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 is accessed as a method. If there is ambiguity, both
this and the DISPATCH_PROPERTYGET flag can be set.
DISPATCH_PROPERTYGET The member is retrieved as a property or data member.
DISPATCH_PROPERTYPUT The member is changed as a property or data member.
DISPATCH_PROPERTYPUTREF The member is changed by using a reference assignment, r
ather than a value assignment. This value is only valid when the property accept
s a reference to an object.
pDispParams
Points to a structure that contains an array of arguments, an array of DISPIDs f
or named arguments, and counts of the number of elements in each array.
pVarResult
Should be Null if the caller does not expect any result. Otherwise, it should be
a pointer to the location at which the result is to be stored. If wFlags specif
ies DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF, pVarResultis ignored.
pExcepInfo
Points to an exception information structure, which is filled in only if DISP_E_
EXCEPTION is returned. If pExcepInfois Null on input, only an HRESULT error will
be returned.
puArgErr
If Invoke returns DISP_E_TYPEMISMATCH, puArgErr indicates the index (within rgva
rg) of the argument with incorrect type. If more than one argument returns an er
ror, puArgErr indicates only the first argument with an error. Arguments in pDis
pParams->rgvarg appear in reverse order, so the first argument is the one having
the highest index in the array. Cannot be Null.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning

S_OK Success.
E_INVALIDARG One or more of the arguments is invalid.
DISP_E_EXCEPTION The member being invoked has returned an error HRESULT.
If the member implements IErrorInfo, details are available in the error object.
Otherwise, the pExcepInfo parameter contains details.
TYPE_E_IOERROR The function could not read from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_REGISTRYACCESS There was an error accessing the system registration dat
abase.
TYPE_E_LIBNOTREGISTERED The type library was not found in the system registratio
n 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 return codes Any of the IDispatch::Invoke errors may also be returned
.
Comments
Use the function ITypeInfo::Invoke to access a member of an object or invoke a m
ethod that implements the interface described by this type description. For obje
cts that support the IDispatch interface, you can use Invoke to implement IDispa
tch::Invoke.
ITypeInfo::Invoke takes a pointer to an instance of the class. Otherwise, its pa
rameters are the same as IDispatch::Invoke, except that ITypeInfo::Invoke omits
the refiid and lcid parameters. When called, ITypeInfo::Invoke performs the acti
ons described by the IDispatch::Invoke parameters on the specified instance.
For VTBL interface members, ITypeInfo::Invoke passes the LCID of the type inform
ation into parameters tagged with the lcid attribute, and the returned value int
o the retval attribute.
If the type description inherits from another type description, this function re
curses on the base type description to find the item with the requested member I
D.
17.2.5.17 ITypeInfo::ReleaseFuncDesc
HRESULT ReleaseFuncDesc(
FUNCDESC FAR* pFuncDesc
);
Releases a FUNCDESC previously returned by GetFuncDesc.
Parameter
pFuncDesc
Pointer to the FUNCDESC to be freed.
Comments
The function ReleaseFuncDesc releases a FUNCDESC that was returned through IType
Info::GetFuncDesc.
Example
ptypeinfoCur->ReleaseFuncDesc(pfuncdesc);
17.2.5.18 ITypeInfo::ReleaseTypeAttr
HRESULT ReleaseTypeAttr(
TYPEATTR FAR* pTypeAttr
);
Releases a TYPEATTR previously returned by GetTypeAttr.
Parameter
pTypeAttr
Pointer to the TYPEATTR to be freed.
Comments
The function ReleaseTypeAttr releases a TYPEATTR that was returned through IType
Info::GetTypeAttr.
17.2.5.19 ITypeInfo::ReleaseVarDesc
HRESULT ReleaseVarDesc(
VARDESC FAR* pVarDesc
);
Releases a VARDESC previously returned by GetVarDesc.
Parameter
pVarDesc
Pointer to the VARDESC to be freed.
Comments
ReleaseVarDesc releases a VARDESC that was returned through ITypeInfo::GetVarDes
c.
Example
VARDESC FAR *pVarDesc;
CHECKRESULT(ptypeinfo->GetVarDesc(i, &pvardesc));
idMember = pvardesc->memid;
CHECKRESULT(ptypeinfo->GetDocumentation(idMember, &bstrName, NULL, NULL,
NULL));
ptypeinfo->ReleaseVarDesc(pvardesc);
17.2.6 ITypeInfo2
An ITypeInfo can be cast to an ITypeInfo2 instead of using the calls QueryInterf
ace() and Release().
17.2.6.1 ITypeInfo2::GetTypeKind
HRESULT GetTypeKind(
TYPEKIND *pTypeKind
);
Returns the TYPEKIND enumeration quickly, without doing any allocations.
Parameter
pTypeKind
Reference to a TYPEKIND enumeration.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.

Example
HRESULT ITypeInfo2::GetTypeKind(TYPEKIND * ptypekind)
17.2.6.2 ITypeInfo2::GetTypeFlags
HRESULT GetTypeFlags(
unsigned long *pTypeFlags
);
Returns the type flags without any allocations. This returns a DWORD type flag,
which expands the type flags without growing the TYPEATTR (type attribute).
Parameter
pTypeFlags
The DWORD reference to a TYPEFLAG.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Example
HRESULT ITypeInfo2::GetTypeFlags(DWORD * pTypeFlags)
17.2.6.3 ITypeInfo2::GetFuncIndexOfMemId
HRESULT GetFuncIndexOfMemId(
MEMBERID memid,
INVOKEKIND invKind,
unsigned int *pFuncIndex
);
Binds to a specific member based on a known DISPID, where the member name is not
known (for example, when binding to a default member).
Parameter
memid
Member identifier.
invKind
Invoke kind.
pFuncIndex
Returns an index into the function.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Example
ITypeInfo2::GetFuncIndexOfMemId(
MEMID memid,
INVOKEKIND invKind,
UINT * pfuncIndex)
17.2.6.4 ITypeInfo2::GetVarIndexOfMemId
HRESULT GetVarIndexOfMemId(
MEMBERID memid,
unsigned int *pVarIndex
);
Binds to a specific member based on a known DISPID, where the member name is not
known (for example, when binding to a default member).
Parameters
memid
Member identifier.
pVarIndex
Returns the index.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Example
ITypeInfo2::GetVarIndexOfMemId(MEMID memid, UINT * pvarIndex)
17.2.6.5 ITypeInfo2::GetCustData
HRESULT GetCustData(

REFGUID guid,
VARIANT *pVarVal
);
Gets the custom data.
Parameters
guid
GUID used to identify the data.
pVarVal
Where to put the retrieved data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.6 ITypeInfo2::GetAllCustData
HRESULT GetAllCustData(
CUSTDATA *pCustData
);
Gets all custom data items for the library.
Parameters
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Comments
After the call, the caller needs to release memory used to hold the custom data
item by calling ClearCustData().
17.2.6.7 ITypeInfo2::GetAllFuncCustData
HRESULT GetAllFuncCustData(
unsigned int index
CUSTDATA *pCustData
);
Gets all custom data from the specified function.
Parameters
index
The index of the function for which to get the custom data.
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
Comments
After the call, the caller needs to release memory used to hold the custom data
item by calling ClearCustData().
17.2.6.8 ITypeInfo2::GetAllImplTypeCustData
HRESULT GetAllImplTypeCustData(
unsigned int index,
CUSTDATA *pCustData
);
Gets all custom data for the specified implementation type.
Parameters
index
Index of the implementation type for the custom data.
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.9 ITypeInfo2::GetAllParamCustData
HRESULT GetAllParamCustData(
unsigned int indexFunc,
unsigned int indexParam,
CUSTDATA *pCustData
);
Gets all of the custom data for the specified function parameter.
Parameters
indexFunc
Index of the function for which to get the custom data.
IndexParam
Index of the parameter of this function for which to get the custom data.
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.10 ITypeInfo2::GetAllVarCustData
HRESULT GetAllVarCustData(
unsigned int index,
CUSTDATA *pCustData
);
Gets the variable for the custom data.
Parameters
index
Index of the variable for which to get the custom data.
pCustData
Returns a pointer to CUSTDATA (that holds all custom data items).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.11 ITypeInfo2::GetFuncCustData
HRESULT GetFuncCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal
);
Gets the custom data from the specified function.
Parameters
index
The index of the function for which to get the custom data.
guid
The GUID used to identify the data.
pVarVal
Where to put the data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.12 ITypeInfo2::GetParamCustData
HRESULT GetParamCustData(
unsigned int indexFunc,
unsigned int indexParam,
REFGUID guid,
VARIANT *pVarVal
);
Gets the specified custom data parameter.
Parameters
indexFunc
Index of the function for which to get the custom data.
IndexParam
Index of the parameter of this function for which to get the custom data.
guid
GUID used to identify the data.
pVarVal
Where to put the retrieved data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.13 ITypeInfo2::GetVarCustData
HRESULT GetVarCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal
);
Gets the variable for the custom data.
Parameters
index
Index of the variable for which to get the custom data.
guid
GUID used to identify the data.
PVarVal
Where to put the retrieved data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning

S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.14 ITypeInfo2::GetImplTypeCustData
HRESULT GetImplTypeCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal
);
Gets the implementation type of the custom data.
Parameters
index
Index of the implementation type for the custom data.
guid
GUID used to identify the data.
pVarVal
Where to put the retrieved data.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
17.2.6.15 ITypeInfo2::GetDocumentation2
HRESULT GetDocumentation2(
[in] MEMID memid,
[in] LCID lcid,
[out] BSTR FAR* pbstrHelpString,
[out] unsigned long FAR* pdwHelpStringContext,
BSTR FAR* pbstrHelpStringDll
);
Retrieves the documentation string, the complete Help file name and path, the lo
calization context to use, and the context ID for the library Help topic in the
Help file.
Parameters
memid
Member identifier for the type description.
lcid
Locale identifier (LCID).
pbstrHelpString
Returns a BSTR that contains the name of the specified item. If the caller does
not need the item name, then pbstrHelpString can be Null.
pdwHelpStringContext
Returns the Help localization context. If the caller does not need the Help cont
ext, it can be Null.
pbstrHelpStringDll
Returns a BSTR that contains the fully qualified name of the file containing the
DLL used for Help file. If the caller does not need the file name, it can be Nu
ll.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 older format.
TYPE_E_INVALIDSTATE The type library could not be opened.
TYPE_E_ELEMENTNOTFOUND The element was not found.
Comments
Gets information at the type information level (about the type information and
its members). The caller should free the BSTR parameters.
This function will call _DLLGetDocumentation in the specified DLL to retrieve th
e desired Help string, if there is a Help string context for this item. If no He
lp string context exists or an error occurs, then it will defer to the GetDocume
ntation method and return the associated documentation string.
17.2.7 ITypeComp
The ITypeComp interface provides a fast way to access information that compilers
need when binding to and instantiating structures and interfaces. Binding is th
e process of mapping names to types and type members.
Implemented by Used by Header file name
Oleaut32.dll (32-bit systems)
Typelib.dll (16-bit systems) Tools that need to access the descriptions of ob
jects contained in type libraries. Oleauto.h
Dispatch.h
17.2.7.1 ITypeComp::Bind
HRESULT Bind(
OLECHAR FAR* szName,
unsigned long lHashVal,
unsigned short wFlags,
ITypeInfo FAR*FAR* ppTInfo,
DESCKIND FAR* pDescKind,
BINDPTR FAR* pBindPtr
);
Maps a name to a member of a type, or binds global variables and functions conta
ined 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 a property
. When binding to a variable, specify the flag INVOKE_PROPERTYGET. Specify zero
to bind to any type of member.
ppTInfo
If a FUNCDESC or VARDESC was returned, then ppTInfo points to a pointer to the t
ype description that contains the item to which it is bound.
pDescKind
Pointer to a DESCKIND enumerator that indicates whether the name bound to is a V
ARDESC, FUNCDESC, or TYPECOMP. If there was no match, points to DESCKIND_NONE.
pBindPtr
On return, contains a pointer to the bound-to VARDESC, FUNCDESC, or ITypeComp in
terface.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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 l
ibrary.
Comments
Use Bind for binding to the variables and methods of a type, or for binding to t
he global variables and methods in a type library. The returned DESCKIND pointer
pDescKind indicates whether the name was bound to a VARDESC, a FUNCDESC, or to
an ITypeComp instance. The returned pBindPtr points to the VARDESC, FUNCDESC, or
ITypeComp.
If a data member or method is bound to, then ppTInfo points to the type descript
ion that contains the method or data member.
If Bind binds the name to a nested binding context, it returns a pointer to an I
TypeComp instance in pBindPtr and a Null type description pointer in ppTInfo. Fo
r example, if the name of a type description is passed for a module (TKIND_MODUL
E), enumeration (TKIND_ENUM), or coclass (TKIND_COCLASS), Bind returns the IType
Comp instance of the type description for the module, enumeration, or coclass. T
his feature supports languages such as Visual Basic that allow references to mem
bers of a type description to be qualified by the name of the type description.
For example, a function in a module can be referenced by modulename.functionname
.
The members of TKIND_ENUM, TKIND_MODULE, and TKIND_COCLASS types marked as Appli
cation objects can be bound to directly from ITypeComp, without specifying the n
ame of the module. The ITypeComp of a coclass defers to the ITypeComp of its def
ault interface.
As with other methods of ITypeComp, ITypeInfo, and ITypeLib, the calling code is
responsible for releasing the returned object instances or structures. If a VAR
DESC or FUNCDESC is returned, the caller is responsible for deleting it with the
returned type description and releasing the type description instance itself. O
therwise, if an ITypeComp instance is returned, the caller must release it.
Special rules apply if you call a type library ' s Bind method, passing it the name of
a member of an Application object class (a class that has the TYPEFLAG_FAPPOBJEC
T flag set). In this case, Bind returns DESCKIND_IMPLICITAPPOBJ in pDescKind, a
VARDESC that describes the Application object in pBindPtr, and the ITypeInfo of
the Application object class in ppTInfo. To bind to the object, ITypeInfo::GetTy
peComp must make a call to get the ITypeComp of the Application object class, an
d then reinvoke its Bind method with the name initially passed to the type libra
ry ' s ITypeComp.
The caller should use the returned ITypeInfo pointer (ppTInfo) to get the addres
s of the member.
Note
The wflags parameter is the same as the wflags parameter in IDispatch::Invoke.
17.2.7.2 ITypeComp::BindType
HRESULT BindType(
OLECHAR FAR* szName,
unsigned long lHashVal,
ITypeInfo FAR* FAR* ppTInfo,
ITypeComp FAR* FAR* ppTComp
);
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.
ppTInfo
On return, contains a pointer to a pointer to an ITypeInfo of the type to which
the name was bound.
ppTComp
Passes a valid pointer, such as the address of an ITypeComp* variable.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from the file.
TYPE_E_INVDATAREAD Invalid data.
TYPE_E_UNSUPFORMAT The type library has an older 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 l
ibrary.

Comments
Use the function BindType for binding a type name to the ITypeInfo that describe
s the type. This function is invoked on the ITypeComp that is returned by ITypeL
ib::GetTypeComp to bind to types defined within that library. It can also be use
d in the future for binding to nested types.
Example
TypeComp * ptcomp;
ptemp -> BindType(szName, lhashval, &ptinfo, &ptemp)

17.3 Type Library API Descriptions


17.3.1 CreateTypeLib
HRESULT CreateTypeLib(syskind, szFile, lplpctlib)
SYSKIND syskind
OLECHAR FAR* szFile
ICreateTypeLib FAR* FAR* lplpctlib
Provides access to a new object instance that supports the ICreateTypeLib interf
ace.
Parameters
syskind
The target operating system for which to create a type library.
szFile
The name of the file to create.
lplpctlib
Pointer to an instance supporting the ICreateTypeLib interface.

Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
TYPE_E_IOERROR The function could not create the file.
Other return codes All FACILITY_STORAGE errors.
Comments
CreateTypeLib sets its output parameter (lplpctlib) to point to a newly created
object that supports the ICreateTypeLib interface.
17.3.2 LHashValOfName
HRESULT LHashValOfName(

LCID lcid,
OLECHAR FAR* szName
);
Computes a hash value for a name that can then be passed to ITypeComp::Bind, ITy
peComp::BindType, ITypeLib::FindName, or ITypeLib::IsName.
Parameters
lcid
The LCID for the string.
szName
String whose hash value is to be computed.
Return Value
A 32-bit hash value that represents the passed-in name.
Comments
This function is equivalent to LHashValOfNameSys. The header file Oleauto.h cont
ains macros that define LHashValOfName as LHashValOfNameSys, with the target ope
rating system (syskind) based on the build preprocessor flags.
LHashValOfName computes a 32-bit hash value for a name that can be passed to ITy
peComp::Bind, ITypeComp::BindType, ITypeLib::FindName, or ITypeLib::IsName. The
returned hash value is independent of the case of the characters in szName, as l
ong as the language of the name is one of the languages supported by the COM Nat
ional Language Specification API. Any two strings that match when a case-insensi
tive comparison is done using any language produce the same hash value.
17.3.3 LHashValOfNameSys
HRESULT LHashValOfNameSys(
SYSKIND syskind,
LCID lcid,
OLECHAR FAR* szName
);
Computes a hash value for a name that can then be passed to ITypeComp::Bind, ITy
peComp::BindType, ITypeLib::FindName, or ITypeLib::IsName.
Parameters
syskind
The SYSKIND of the target operating system.
lcid
The LCID for the string.
szName
String whose hash value is to be computed.
Return Value
A 32-bit hash value that represents the passed-in name.
17.3.4 LoadTypeLibEx
HRESULT LoadTypeLibEx(
OLECHAR FAR* szFile,
REGKIND regkind,
ITypeLib FAR* FAR* pptlib
);
Loads and optionally registers a type library.
Parameters
szFile
Contains the name of the file from which LoadTypeLib should attempt to load a ty
pe library.
regkind
Identifies the kind of registration to perform for the type library (REGKIND_DEF
AULT, REGKIND_REGISTER, or REGKIND_NONE).
pptlib
On return, contains a pointer to a pointer to the loaded type library.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 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 older format.
TYPE_E_UNKNOWNLCID The LCID could not be found in the COM-supported DLLs.
TYPE_E_CANTLOADLIBRARY The type library or DLL could not be loaded.
TYPE_E_REGISTRYACCESS The system registration database could not be opened.
Other return codes All FACILITY_STORAGE errors can be returned.
Comments
The function LoadTypeLibEx loads a type library (usually created with MIDL) that
is stored in the specified file. If szFile specifies only a file name without a
ny path, LoadTypeLibEx searches for the file and proceeds as follows:
· If the file is a stand-alone type library (usually with a .TLB extension), the t
ype library is loaded directly.
· If the file is a DLL or an executable file, it is loaded. By default, the type l
ibrary is extracted from the first resource of type ITypeLib. To load a differen
t type of library resource, append an integer index to szFile. For example:
LoadTypeLib( " C:\MONTANA\EXE\MFA.EXE\3 " , pptlib)
This statement loads the type library resource 3 from the file Mfa.exe file.
· If the file is none of the above, the file name is parsed into a moniker (an obj
ect that represents a file-based link source) using MkParseDisplayName, and then
the moniker is bound to IID_ITypeLib. This approach allows LoadTypeLibEx to be
used on foreign type libraries, including in-memory type libraries. Foreign type
libraries cannot reside in a DLL or an executable file.
If the type library is already loaded, LoadTypeLibEx increments the type library ' s re
ference count and returns a pointer to the type library.
The regKind parameter enables programmers to specify whether or not the type lib
rary should be registered in the system registry (for future loading via LoadReg
TypeLib). When REGKIND_NONE is specified, the library is not registered in the s
ystem registry. When REGKIND_DEFAULT is specified, LoadTypeLibEx will register t
he type library if the path is not specified in the szFile parameter, otherwise
LoadTypeLibEx will not register the type library. When REGKIND_REGISTER is speci
fied, LoadTypeLibEx will always register the type library in the system registry
.
It is recommended that RegisterTypeLib be used to register a type library.
17.3.5 LoadRegTypeLib
HRESULT LoadRegTypeLib(
REFGUID rguid,
Unsigned short wVerMajor,
Unsigned short wVerMinor,
LCID lcid,
ITypeLib FAR* FAR* pptlib
);
Uses registry information to load a type library.
Parameters
rguid
The GUID of the library being loaded.
wVerMajor
Major version number of the library being loaded.
wVerMinor
Minor version number of the library being loaded.
lcid
National language code of the library being loaded.
pptlib
On return, points to a pointer to the loaded type library.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 from 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 older format.
TYPE_E_UNKNOWNLCID The passed in LCID could not be found in the COM-support
ed DLLs.
TYPE_E_CANTLOADLIBRARY The type library or DLL could not be loaded.
Other return codes All FACILITY_STORAGE and system registry errors can also
be returned.

Comments
The function LoadRegTypeLib defers to LoadTypeLibEx 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 versi
on number, and has a greater minor version number than that requested, the one w
ith the greatest minor version number is loaded.
· If none of the registered type libraries exactly match the requested major versi
on number (or if none of those that do exactly match the major version number al
so have a minor version number greater than or equal to the requested minor vers
ion number), then LoadRegTypeLib returns an error.
17.3.6 RegisterTypeLib
HRESULT RegisterTypeLib(
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.
Can be Null.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 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
The function RegisterTypeLib can be used during application initialization to re
gister the application ' s type library correctly.
In addition to filling in a complete registry entry under the type library key,
RegisterTypeLib adds entries for each of the dispinterfaces and Automation-compa
tible interfaces, including dual interfaces. This information is required to cre
ate instances of these interfaces. Coclasses are not registered (that is, Regist
erTypeLib does not write any values to the CLSID key of the coclass).
17.3.7 UnRegisterTypeLib
HRESULT UnRegisterTypeLib(
REFGUID libID,
unsigned short wVerMajor,
unsigned short wVerMinor,
LCID lcid,
SYSKIND syskind
);
Removes type library information from the system registry. Use this API to allow
applications to properly uninstall themselves. In-process objects typically cal
l this API from DllUnregisterServer.
Parameters
libID
Globally unique identifier.
wVerMajor
Major version number of the type library being removed.
wVerMinor
Minor version number of the type library being removed.
lcid
Locale identifier.
syskind
The target operating system (SYSKIND).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 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
In-process objects typically call this API from DllUnregisterServer.
17.3.8 QueryPathOfRegTypeLib
HRESULT QueryPathOfRegTypeLib(
REFGUID guid,
unsigned short wVerMajor,
unsigned short wVerMinor,
LCID lcid,
LPBSTR lpbstrPathName
);
Retrieves the path of a registered type library.
Parameters
guid
GUID 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 return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
Comments
Returns the fully qualified file name that is specified for the type library in
the registry. The caller allocates the BSTR that is passed in, and must free it
after use.

18. Automation
Automation provides a way to expose and access objects within an application in
a late bound way. Automation defines the following dispatch interfaces and funct
ions.
IDispatch interface Exposes objects, methods, and properties to Automation progr
amming tools and other applications.
Dispatch API functions Simplifies the implementation of the IDispatch interface.
Use these functions to generate an IDispatch interface automatically.
IEnumVARIANT interface Provides a way for COM clients to iterate over collection
objects. This is a dispatch interface.
18.1 Overview of the IDispatch Interface
The following table describes the member functions of the IDispatch interface.
Interface Member function Purpose
IDispatch GetIDsOfNames Maps a single member name and an optional set of
argument names to a corresponding set of integer dispatch identifiers (DISPIDs)
, which can then be used on subsequent calls to Invoke.
GetTypeInfo Retrieves the type information for an object.
GetTypeInfoCount Retrieves the number of type information interfa
ces that an object provides (either 0 or 1).
Invoke Provides access to properties and methods exposed by an object.

18.1.1 Implementing the IDispatch Interface


IDispatch is located in the Oleauto.h header file on 32-bit systems, and in Disp
atch.h on 16-bit systems.
COM objects can implement the IDispatch interface for access by COM clients, suc
h as Visual Basic. The object ' s properties and methods can be accessed using IDispatc
h::GetIDsOfNames and IDispatch::Invoke.
The following examples show how to access a COM object through the IDispatch int
erface. The code is abbreviated for brevity, 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};
EXCEPINFO excepinfo;
UINT nArgErr;
In the following code, the OleInitialize function loads the COM dynamic-link lib
raries (DLLs), and the CoCreateInstance function initializes the COM object s clas
s factory. For more information on these two functions, see the OLE Programmer's
Reference in the Win32 Software Development Kit (SDK).
// Initialize OLE DLLs.
hresult = OleInitialize(NULL);
// OLE function CoCreateInstance starts application using GUID.
hresult = CoCreateInstance(CLSID_Hello, NULL, CLSCTX_SERVER, IID_IUnknown, (void
FAR* FAR*)&punk);
QueryInterface checks whether the object supports IDispatch. (As with any call t
o QueryInterface, the returned pointer must be released when it is no longer nee
ded.)
// Call QueryInterface to see if object supports IDispatch.
hresult = punk->QueryInterface(IID_IDispatch, &pdisp);
GetIDsOfNames retrieves the DISPID for the indicated method or property, in this
case, szMember.
// Retrieve the dispatch identifier for the SayHello method.
// Use defaults where possible.
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1,
LOCALE_USER_DEFAULT,
&dispid);
In the following call to Invoke, the DISPID indicates the property or method to
invoke. The SayHello method does not take any parameters, so the fifth argument
(&dispparamsNoArgs), contains a Null and 0, as initialized at declaration.
To invoke a property or method that requires parameters, supply the parameters i
n 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);
18.2 Registering the Object
These functions let you identify a running instance of an object.
When an application is started with the /Automation switch on its command-line,
it should initialize its Application object as the active object by calling Regi
sterActiveObject.
Applications can also register other top-level objects as the active object. For
example, an application that exposes a Document object may want to let COM clie
nts retrieve and modify the currently active document.
18.3 National Language Support Functions
The National Language Support (NLS) functions provide support for applications t
hat use multiple locales at one time, especially applications that support Autom
ation. Locale information is passed to allow the application to interpret both t
he member names and the argument data in the proper locale context. The informat
ion in this appendix applies only to 16-bit Windows systems.
Implemented by Used by Header filename Import library name
Ole2nls.dll Applications that support multiple national languages Olenls.h
Ole2nls.lib
For Automation, applications need to get locale information and to compare and t
ransform strings into the proper format for each locale.
A locale is simply user-preference information, represented as a list of values
describing the user ' s language and sublanguage. National language support incorporate
s several disparate definitions of a locale into one coherent model. This model
is designed to be general enough at a low level to support multiple, distinct, h
igh-level functions, such as the ANSI C locale functions.
A code page is the mapping between character glyphs (shapes) and the 1-byte or 2
-byte numeric values that are used to represent them. Microsoft Windows uses one
of several code pages, depending on the installed localized version of Windows.
For example, the Russian version uses code page 1251 (Cyrillic), while the Engl
ish U.S. and Western European versions use code page 1252 (Multilingual). For hi
storical reasons, the Windows code page in effect is referred to as the ANSI cod
e page.
Because only one code page is in effect at a time, it is impossible for a comput
er running English U.S. Windows to display or print data correctly from the Cyri
llic code page. The fonts do not contain the Cyrillic characters. However, it ca
n still manipulate the characters internally, and they will display correctly ag
ain if moved back to a machine running Russian Windows.
All NLS functions use the locale identifier (LCID) to identify which code page a
piece of text is assumed to lie in. For example, when returning locale informat
ion (such as month names) for Russian, the returned string can be meaningfully d
isplayed in the Cyrillic code page only, because other code pages do not contain
the appropriate characters. Similarly, when attempting to change the case of a
string with the Russian locale, the case-mapping rules assume the characters are
in the Cyrillic code page.
These functions can be divided into two categories.
· String transformation NLS functions support uppercasing, lowercasing, generating
sort keys (all locale-dependent), and getting string type information.
· Locale manipulation NLS functions return information about installed locales for
use in string transformations.
18.3.1 Overview of Functions
The following table lists the NLS functions.

Function Purpose
CompareString Compares two strings of the same locale.
LCMapString Transforms the case or sort order of a string.
GetLocaleInfo Retrieves locale information from the user ' s system.
GetStringType Retrieves locale type information about each character in a stri
ng.
GetSystemDefaultLangID Retrieves the default language ID (LANGID) from a user ' s syst
em.
GetSystemDefaultLCID Retrieves the default LCID from a user ' s system.
GetUserDefaultLangID Retrieves the default LANGID from a user ' s system.
GetUserDefaultLCID Retrieves the default LCID from a user ' s system.1

18.3.2 Localized Member Names


An application may expose a set of objects whose members have names that differ
across localized versions of a product. This poses a problem for programming lan
guages that want to access such objects, because it means that late binding is s
ensitive to the locale of the application. The IDispatch and virtual function ta
ble (VTBL) interfaces allow software developers a range of solutions that vary i
n cost of implementation and quality of national language support. All methods o
f the IDispatch interface that are potentially sensitive to language are passed
an LCID.
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 interface will typically be accessed only by advanced users. For example
, the member names for COM interfaces will never be localized.
· Simply return an error (DISP_E_UNKNOWNLCID) if the caller ' s LCID doesn ' t
ed version of the class. This would prevent users from being able to write late-
bound code which runs on machines with different localized implementations of th
e class.
· Recognize the particular version ' s localized names, as well as one language th
ecognized in all versions. For example, a French version might accept French and
English names, where English is the language supported in all versions. This wo
uld constrain users to use English when writing code that runs in all countries,
.
· Accept all LCIDs supported by all versions of the product. This means that the i
mplementation of GetIDsOfNames would need to interpret the passed array of names
based on the given LCID. This is the preferred solution because users would be
able to write code in their national language and run the code on any localized
version of the application.
At the very least, the application must check the LCID before interpreting membe
r names. Also note that the meaning of parameters passed to a member function ma
y depend on the caller ' s national language. For example, a spreadsheet application mi
ght interpret the arguments to a SetFormula method differently, depending on the
LCID.
18.3.3 Locale Identifier (LCID)
The IDispatch interface uses the 32-bit Windows definition of a LCID to identify
locales. An LCID is a DWORD value that contains the LANGID in the lower word an
d a reserved value in the upper word.
This LCID has the components necessary to uniquely identify one of the installed
system-defined locales.
/*
* 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 defaul
t locale, and LOCALE_USER_DEFAULT is the current user ' s locale. However, when queryin
g the NLS APIs for information, it is more efficient to query once for the curre
nt locale with GetSystemDefaultLCID or GetUserDefaultLCID, rather than using the
se constants.
18.3.4 Language Identifier (LANGID)
A LANGID is a 16-bit value that is the combination of a primary and sublanguage
ID. Macros are provided for constructing a LANGID and extracting the fields:
LANGID creation/extraction macros include:
· MAKELANGID - construct LANGID from primary LANGID and sublanguage ID.
· PRIMARYLANGID - extract primary LANGID from a LANGID.
· SUBLANGID - extract sublanguage identifier (ID) from a LANGID.
· LANGIDFROMLCID - get the LANGID from an LCID.
#define MAKELANGID(p, s) ((((USHORT)(s)) << 10) | (USHORT)(p))
#define PRIMARYLANGID(lgid) ((USHORT)(lgid) & 0x3ff)
#define SUBLANGID(lgid) ((USHORT)(lgid) >> 10)
#define LANGIDFROMLCID(lcid) ((WORD)(lcid))
The following three combinations of primary and sublanguage IDs have special mea
nings:
PRIMARYLANGID SUBLANGID Meaning
LANG_NEUTRAL SUBLANG_NEUTRAL Language neutral
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. The following table lists the primary
LANGIDs supported by Automation:
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
Serbo Croatian 0x1a
Slovak 0x1b
Spanish 0x0a
Swedish 0x1d
Turkish 0x1F
For sublanguage IDs, the range 0x20 to 0x3f is user definable. The range 0x00 to
0x1f is reserved for system use. The following table lists the sublanguage IDs
supported by Automation:
Sublanguage SUBLANGID
Neutral 0x00
Default 0x01
System Default 0x02
Chinese (Simplified) 0x02
Chinese (Traditional) 0x01
Dutch 0x01
Dutch (Belgian) 0x02
English (U.S.) 0x01
English (U.K.) 0x02
English (Australian) 0x03
English (Canadian) 0x04
English (Irish) 0x06
English (New Zealand) 0x05
French 0x01
French (Belgian) 0x02
French (Canadian) 0x03
French (Swiss) 0x04
German 0x01
German (Swiss) 0x02
German (Austrian) 0x03
Greek 0x01
Icelandic 0x01
Italian 0x01
Italian (Swiss) 0x02
Japanese 0x01
Korean 0x01
Norwegian (Bokmal) 0x01
Norwegian (Nynorsk) 0x02
Portuguese 0x02
Portuguese (Brazilian) 0x01
Serbo Croatian (Latin) 0x01
Spanish (Castilian)1 0x01
Spanish (Mexican) 0x02
Spanish (Modern)1 0x03
Turkish 0x01
1 The only difference between Spanish (Castilian) and Spanish (Modern) is
the sort ordering. All of the LCType values are the same.
18.3.5 Locale Constants (LCTYPE)
An LCTYPE is a constant that specifies a particular piece of locale information.
For example:
typedef DWORD LCTYPE;
The list of supported LCTYPES follows. All values are null-terminated, variable-
length strings. Numeric values are expressed as strings of decimal digits, unles
s otherwise noted. The values in the brackets indicate the maximum number of cha
racters allowed for the string (including the null termination). If no maximum i
s indicated, the string may be of variable length.
Constant name Description
LOCALE_ILANGUAGE A LANGID represented in hexadecimal digits. See the prev
ious sections. [5]
LOCALE_SLANGUAGE The full localized name of the language.
LOCALE_SENGLANGUAGE The full English U.S. name of the language from the ISO
Standard 639. This will always be restricted to characters that can be mapped in
to the ASCII 127-character subset.
LOCALE_SABBREVLANGNAME The abbreviated name of the language, created by taking
the two-letter language abbreviation, as found in ISO Standard 639, and adding a
third letter as appropriate to indicate the sublanguage.
LOCALE_SNATIVELANGNAME The native name of the language.
LOCALE_ICOUNTRY The country code, based on international phone codes, also refer
red to as IBM country codes. [6]
LOCALE_SCOUNTRY The full localized name of the country.
LOCALE_SENGCOUNTRY The full English U.S. name of the country. This will alw
ays be restricted to characters that can be mapped into the ASCII 127-character
subset.
LOCALE_SABBREVCTRYNAME The abbreviated name of the country as found in ISO Stan
dard 3166.
LOCALE_SNATIVECTRYNAME The native name of the country.
LOCALE_IDEFAULTLANGUAGE LANGID for the principal language spoken in this locale.
This is provided so that partially specified locales can be completed with defa
ult values. [5]
LOCALE_IDEFAULTCOUNTRY Country code for the principal country in this locale. T
his is provided so that partially specified locales can be completed with defaul
t values. [6]
LOCALE_IDEFAULTANSICODEPAGE The ANSI code page associated with this locale.
Format: 4 Unicode decimal digits plus a Unicode null terminator. [10] [6]
LOCALE_IDEFAULTCODEPAGE The OEM code page associated with the country. [6]
LOCALE_SLIST Characters used to separate list items. For example, a comma is
used in many locales.
LOCALE_IMEASURE This value is 0 for the metric system (S.I.) and 1 for the U.S.
system of measurements. [2]
LOCALE_SDECIMAL Characters used for the decimal separator.
LOCALE_STHOUSAND Characters used as the separator between groups of digit
s left of the decimal.
LOCALE_SGROUPING Sizes for each group of digits to the left of the decima
l. An explicit size is required for each group. Sizes are separated by semicolon
s. If the last value is 0, the preceding value is repeated. To group thousands,
specify 3;0.
LOCALE_IDIGITS The number of fractional digits. [3]
LOCALE_ILZERO Whether to use leading zeros in decimal fields. [2] A setting of
0 means use no leading zeros; 1 means use leading zeros.
LOCALE_SNATIVEDIGITS The ten characters that are the native equivalent of the
ASCII 0-9.
LOCALE_INEGNUMBER Negative number mode. [2]
" 0 " (1.1)
" 1 " -1.1
" 2 " -1.1
" 3 " 1.1
" 4 " 1.1
LOCALE_SCURRENCY The string used as the local monetary symbol.
LOCALE_SINTLSYMBOL Three characters of the International monetary symbol sp
ecified in ISO 4217, Codes for the Representation of Currencies and Funds, follo
wed by the character separating this string from the amount.
LOCALE_SMONDECIMALSEP Characters used for the monetary decimal separators.
LOCALE_SMONTHOUSANDSEP Characters used as monetary separator between groups of
digits left of the decimal.
LOCALE_SMONGROUPING Sizes for each group of monetary digits to the left of t
he decimal. An explicit size is needed for each group. Sizes are separated by se
micolons. If the last value is 0, the preceding value is repeated. To group thou
sands, specify 3;0.
LOCALE_ICURRDIGITS Number of fractional digits for the local monetary forma
t. [3]
LOCALE_IINTLCURRDIGITS Number of fractional digits for the international moneta
ry format. [3]
LOCALE_ICURRENCY Positive currency mode. [2]
0 Prefix, no separation.
1 Suffix, no separation.
2 Prefix, 1-character separation.
3 Suffix, 1-character separation.
LOCALE_INEGCURR Negative currency mode. [2]
0 ($1.1)
1 -$1.1
2 $-1.1
3 $1.1-
4 $(1.1$)
5 -1.1$
6 1.1-$
7 1.1$-
8 -1.1 $ (space before $)
9 -$ 1.1 (space after $)
10 1.1 $- (space before $)
LOCALE_ICALENDARTYPE The type of calendar currently in use. [2]
1 Gregorian (as in U.S.)
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 The additional calendar types available for this
LCID. Can be a null-separated list of all valid optional calendars. [2]
0 None available
1 Gregorian (as in U.S.)
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_SDATE Characters used for the date separator.
LOCALE_STIME Characters used for the time separator.
LOCALE_STIMEFORMAT Time-formatting string. [80]
LOCALE_SSHORTDATE Short Date_Time formatting strings for this locale.
LOCALE_SLONGDATE Long Date_Time formatting strings for this locale.
LOCALE_IDATE Short Date format-ordering specifier. [2]
0 Month Day Year
1 Day Month Year
2 Year Month Day
LOCALE_ILDATE Long Date format ordering specifier. [2]
0 Month Day Year
1 Day Month Year
2 Year Month Day
LOCALE_ITIME Time format specifier. [2]
0 AM/PM 12-hour format.
1 24-hour format.
LOCALE_ITIMEMARKPOSN Whether the time marker string (AM|PM) precedes or follo
ws the time string. (The registry value is named ITimePrefix for previous Far Ea
st version compatibility.)
0 Suffix (9:15 AM).
1 Prefix (AM 9:15).
LOCALE_ICENTURY Whether to use full 4-digit century. [2]
0 Two digit.
1 Full century.
LOCALE_ITLZERO Whether to use leading zeros in time fields. [2]
0 No leading zeros.
1 Leading zeros for hours.
LOCALE_IDAYLZERO Whether to use leading zeros in day fields. [2]
0 No leading zeros.
1 Leading zeros.
LOCALE_IMONLZERO Whether to use leading zeros in month fields. [2]
0 No leading zeros.
1 Leading zeros.
LOCALE_S1159 String for the AM designator.
LOCALE_S2359 String for the PM designator.
LOCALE_IFIRSTWEEKOFYEAR Specifies which week of the year is considered first. [2
]
0 Week containing 1/1 is the first week of the year.
1 First full week following 1/1is the first week of the year.
2 First week with at least 4 days is the first week of the year.
LOCALE_IFIRSTDAYOFWEEK Specifies the day considered first in the week. [2]
0 SDAYNAME1
1 SDAYNAME2
2 SDAYNAME3
3 SDAYNAME4
4 SDAYNAME5
5 SDAYNAME6
6 DAYNAME7
LOCALE_SDAYNAME1 Long name for Monday.
LOCALE_SDAYNAME2 Long name for Tuesday.
LOCALE_SDAYNAME2 Long name for Tuesday.
LOCALE_SDAYNAME3 Long name for Wednesday.
LOCALE_SDAYNAME4 Long name for Thursday.
LOCALE_SDAYNAME5 Long name for Friday.
LOCALE_SDAYNAME6 Long name for Saturday.
LOCALE_SDAYNAME7 Long name for Sunday.
LOCALE_SABBREVDAYNAME1 Abbreviated name for Monday.
LOCALE_SABBREVDAYNAME2 Abbreviated name for Tuesday.
LOCALE_SABBREVDAYNAME3 Abbreviated name for Wednesday.
LOCALE_SABBREVDAYNAME4 Abbreviated name for Thursday.
LOCALE_SABBREVDAYNAME5 Abbreviated name for Friday.
LOCALE_SABBREVDAYNAME6 Abbreviated name for Saturday.
LOCALE_SABBREVDAYNAME7 Abbreviated name for Sunday.
LOCALE_SMONTHNAME1 Long name for January.
LOCALE_SMONTHNAME2 Long name for February.
LOCALE_SMONTHNAME3 Long name for March.
LOCALE_SMONTHNAME4 Long name for April.
LOCALE_SMONTHNAME5 Long name for May.
LOCALE_SMONTHNAME6 Long name for June.
LOCALE_SMONTHNAME7 Long name for July.
LOCALE_SMONTHNAME8 Long name for August.
LOCALE_SMONTHNAME9 Long name for September.
LOCALE_SMONTHNAME10 Long name for October.
LOCALE_SMONTHNAME11 Long name for November.
LOCALE_SMONTHNAME12 Long name for December.
LOCALE_SMONTHNAME13 Native name for 13th month, if it exists.
LOCALE_SABBREVMONTHNAME1 Abbreviated name for January.
LOCALE_SABBREVMONTHNAME2 Abbreviated name for February.
LOCALE_SABBREVMONTHNAME3 Abbreviated name for March.
LOCALE_SABBREVMONTHNAME4 Abbreviated name for April.
LOCALE_SABBREVMONTHNAME5 Abbreviated name for May.
LOCALE_SABBREVMONTHNAME6 Abbreviated name for June.
LOCALE_SABBREVMONTHNAME7 Abbreviated name for July.
LOCALE_SABBREVMONTHNAME8 Abbreviated name for August.
LOCALE_SABBREVMONTHNAME9 Abbreviated name for September.
LOCALE_SABBREVMONTHNAME10 Abbreviated name for October.
LOCALE_SABBREVMONTHNAME11 Abbreviated name for November.
LOCALE_SABBREVMONTHNAME12 Abbreviated name for December.
LOCALE_SABBREVMONTHNAME13 Native abbreviated name for 13th month, if it ex
ists.
LOCALE_SPOSITIVESIGN String value for the positive sign.
LOCALE_SNEGATIVESIGN String value for the negative sign.
LOCALE_IPOSSIGNPOSN Formatting index for positive values. [2]
0 Parentheses surround the amount and the monetary symbol.
1 The sign string precedes the amount and the monetary symbol.
2 The sign string precedes the amount and the monetary symbol.
3 The sign string precedes the amount and the monetary symbol.
4 The sign string precedes the amount and the monetary symbol.
LOCALE_INEGSIGNPOSN Formatting index for negative values. [2]
0 Parentheses surround the amount and the monetary symbol.
1 The sign string precedes the amount and the monetary symbol.
2 The sign string precedes the amount and the monetary symbol.
3 The sign string precedes the amount and the monetary symbol.
4 The sign string precedes the amount and the monetary symbol.
LOCALE_IPOSSYMPRECEDES If the monetary symbol precedes, 1. If it succeeds a pos
itive amount, 0. [2]
LOCALE_IPOSSEPBYSPACE If the monetary symbol is separated by a space from a po
sitive amount, 1. Otherwise, 0. [2]
LOCALE_INEGSYMPRECEDES If the monetary symbol precedes, 1. If it succeeds a neg
ative amount, 0. [2]
LOCALE_INEGSEPBYSPACE If the monetary symbol is separated by a space from a ne
gative amount, 1. Otherwise, 0. [2]
The following table shows the equivalence between LCTYPE values and the informat
ion stored in the [intl] section of Win.ini. These values are retrieved from Win
.ini if information for the current system locale is queried. Values for LCTYPEs
that are not in the following table do not depend on information stored in Win.
ini.
Win.ini settings LCTYPE
sLanguage1 LOCALE_SABBREVLANGNAME
ICountry LOCALE_ICOUNTRY
SCountry LOCALE_SCOUNTRY
SList LOCALE_SLIST
IMeasure LOCALE_IMEASURE
SDecimal LOCALE_SDECIMAL
SThousand LOCALE_STHOUSAND
IDigits LOCALE_IDIGITS
ILZero LOCALE_ILZERO
SCurrency LOCALE_SCURRENCY
ICurrDigits LOCALE_ICURRDIGITS
ICurrency LOCALE_ICURRENCY
INegCurr LOCALE_INEGCURR
SDate LOCALE_SDATE
STime LOCALE_STIME
SShortDate LOCALE_SSHORTDATE
SLongDate LOCALE_SLONGDATE
IDate LOCALE_IDATE
ITime LOCALE_ITIME
ITLZero LOCALE_ITLZERO
s1159 LOCALE_S1159
s2359 LOCALE_S2359
1 Unlike in Win.ini, values returned by LOCALE_SABBREVLANGNAME are always
in uppercase.
18.4 Automation Interface Descriptions
18.4.1 IDispatch
18.4.1.1 IDispatch::GetIDsOfNames
HRESULT GetIDsOfNames(
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 se
t of integer DISPIDs, which can be used on subsequent calls to IDispatch::Invoke
. The dispatch function DispGetIDsOfNames provides a standard implementation of
GetIDsOfNames.
Parameters
riid
Reserved for future use. Must be IID_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 identifier (ID) corres
ponding to one of the names passed in the rgszNames array. The first element rep
resents the member name. The subsequent elements represent each of the member ' s param
eters.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
DISP_E_UNKNOWNNAME One or more of the names were not known. The returned ar
ray of DISPIDs contains DISPID_UNKNOWN for each entry that corresponds to an unk
nown name.
DISP_E_UNKNOWNLCID The locale identifier (LCID) was not recognized.

Comments
An IDispatch implementation can associate any positive integer ID value with a g
iven name. Zero is reserved for the default, or Value property; 1 is reserved to
indicate an unknown name; and other negative values are defined for other purpos
es. For example, if GetIDsOfNames is called, and the implementation does not rec
ognize one or more of the names, it returns DISP_E_UNKNOWNNAME, and the rgDispId
array contains DISPID_UNKNOWN for the entries that correspond to the unknown na
mes.
The member and parameter DISPIDs must remain constant for the lifetime of the ob
ject. This allows a client to obtain 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 the names
of the member ' s parameters.
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 inter
face, a different ID as a member of a different interface, and different mapping
for each time it appears as a parameter.
The IDispatch interface binds to names at run time. To bind at compile time inst
ead, an IDispatch client can map names to DISPIDs by using the type information
interfaces described in Chapter 17. This allows a client to bind to members at c
ompile time and avoid calling GetIDsOfNames at run time. For a description of bi
nding at compile time, see Chapter 17.
The implementation of GetIDsOfNames is case insensitive. Users that need case-se
nsitive name mapping should use type information interfaces to map names to DISP
IDs, rather than call GetIDsOfNames.
Examples
The following code from the Lines sample file Lines.cpp implements the GetIDsOfN
ames member function for the CLine class. The COM object uses the standard imple
mentation, DispGetIDsOfNames.
STDMETHODIMP
CLine::GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
UINT cNames,
LCID lcid,
DISPID FAR* rgDispId)
{
return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgDispId);
}
The following code might appear in an COM client that calls GetIDsOfNames to get
the DISPID of the CLine Color property.
HRESULT hresult;
IDispatch FAR* pdisp = (IDispatch FAR*)NULL;
DISPID dispid;
OLECHAR FAR* szMember = "color";
// Code that sets a pointer to the dispatch (pdisp) is omitted.
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1, LOCALE_SYSTEM_DEFAULT,
&dispid);
See Also
CreateStdDispatch, DispGetIDsOfNames, ITypeInfo::GetIDsOfNames
18.4.1.2 IDispatch::GetTypeInfo
HRESULT GetTypeInfo(
unsigned int iTInfo,
LCID lcid,
ITypeInfo FAR* FAR* ppTInfo
);
Retrieves the type information for an object, which can then be used to get the
type information for an interface.
Parameters
iTInfo
The type information to return. Pass 0 to retrieve type information for the IDis
patch implementation.
lcid
The locale identifier for the type information. An object may be able to return
different type information for different languages. This is important for classe
s that support localized member names. For classes that do not support localized
member names, this parameter can be ignored.
ppTInfo
Receives a pointer to the requested type information object.

Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success; the type information element exists.
DISP_E_BADINDEX Failure; iTInfo argument was not 0.
TYPE_E_ELEMENTNOTFOUND Failure; iTInfo argument was not 0.
Example
The following code from the sample file Lines.cpp loads information from the typ
e library and implements the member function GetTypeInfo:
// These lines are from CLines::Create load type information for the
// Lines collection from the type library.
hr = LoadTypeInfo(&pLines->m_ptinfo, IID_ILines);
if (FAILED(hr))
goto error;
// Additional code omitted for brevity.
// This function implements GetTypeInfo for the CLines collection.
STDMETHODIMP
CLines::GetTypeInfo(
UINT iTInfo,
LCID lcid,
ITypeInfo FAR* FAR* ppTInfo)
{
*ppTInfo = NULL;
if(iTInfo != 0)
return ResultFromScode(DISP_E_BADINDEX);
m_ptinfo->AddRef();
*ppTInfo = m_ptinfo;
return NOERROR;
}
See also
CreateStdDispatch, CreateDispTypeInfo.
18.4.1.3 IDispatch::GetTypeInfoCount
HRESULT GetTypeInfoCount(
unsigned int FAR* pctinfo
);
Retrieves the number of type information interfaces that an object provides (eit
her 0 or 1).
Parameter
pctinfo
Points to a location that receives the number of type information interfaces pro
vided by the object. If the object provides type information, this number is 1;
otherwise the number is 0.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_NOTIMPL Failure.
Comments
The function may return zero, which indicates that the object does not provide a
ny type information. In this case, the object may still be programmable through
IDispatch, but does not provide type information for browsers, compilers, or oth
er programming tools that access type information. This can be useful for hiding
an object from browsers or for preventing early binding on an object.
Example
This code from the Lines sample file Lines.cpp implements the GetTypeInfoCount m
ember function for the CLines class (COM object).
STDMETHODIMP
CLines::GetTypeInfoCount(UINT FAR* pctinfo)
{
*pctinfo = 1;
return NOERROR;
}
See Also
CreateStdDispatch
18.4.1.4 IDispatch::Invoke
HRESULT Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcep
Info, puArgErr)
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult,
EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr
);
Provides access to properties and methods exposed by an object. The dispatch fun
ction DispInvoke provides a standard implementation of IDispatch::Invoke.
Parameters
dispIdMember
Identifies the member. Use GetIDsOfNames or the object ' s documentation to obtain the
dispatch identifier.
riid
Reserved for future use. Must be IID_NULL.
lcid
The locale context in which to interpret arguments. The lcid is used by the GetI
DsOfNames function, and is also passed to Invoke to allow the object to interpre
t its arguments specific to a locale.
Applications that do not support multiple national languages can ignore this par
ameter.
wFlags
Flags describing the context of the Invoke call, include:
Value Description
DISPATCH_METHOD The member is invoked as a method. If a property has the same na
me, both this and the DISPATCH_PROPERTYGET flag may be set.
DISPATCH_PROPERTYGET The member is retrieved as a property or data member.
DISPATCH_PROPERTYPUT The member is changed as a property or data member.
DISPATCH_PROPERTYPUTREF The member is changed by a reference assignment, rather
than a value assignment. This flag is valid only when the property accepts a ref
erence to an object.
pDispParams
Pointer to a structure containing an array of arguments, an array of argument DI
SPIDs for named arguments, and counts for the number of elements in the arrays.
See the Comments section that follows for a description of the DISPPARAMS struct
ure.
pVarResult
Pointer to the location 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 that contains exception information. This structure shoul
d be filled in if DISP_E_EXCEPTION is returned. Can be Null.
puArgErr
The index within rgvarg of the first argument that has an error. Arguments are s
tored in pDispParams->rgvarg in reverse order, so the first argument is the one
with the highest index in the array. This parameter is returned only when the re
sulting return value is DISP_E_TYPEMISMATCH or DISP_E_PARAMNOTFOUND. For details
, see " Returning Errors " in the following Comments section.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
DISP_E_BADPARAMCOUNT The number of elements provided to DISPPARAMS is differe
nt from the number of arguments accepted by the method or property.
DISP_E_BADVARTYPE One of the arguments in rgvarg is not a valid variant ty
pe.
DISP_E_EXCEPTION The application needs to raise an exception. In this cas
e, the structure passed in pExcepInfo should be filled in.
DISP_E_MEMBERNOTFOUND The requested member does not exist, or the call to Invo
ke tried to set the value of a read-only property.
DISP_E_NONAMEDARGS This implementation of IDispatch does not support named
arguments.
DISP_E_OVERFLOW One of the arguments in rgvarg could not be coerced to the speci
fied type.
DISP_E_PARAMNOTFOUND One of the parameter DISPIDs does not correspond to a pa
rameter on the method. In this case, puArgErr should be set to the first argumen
t that contains the error.
DISP_E_TYPEMISMATCH One or more of the arguments could not be coerced. The i
ndex within rgvarg of the first parameter with the incorrect type is returned in
the puArgErr parameter.
DISP_E_UNKNOWNINTERFACE The interface identifier passed in riid is not IID_NULL.
DISP_E_UNKNOWNLCID The member being invoked interprets string arguments acc
ording to the 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.
In 16-bit versions, you can define your own errors using the MAKE_SCODE value ma
cro.
Comments
Generally, you should not implement Invoke directly. Instead, use the dispatch i
nterface create functions CreateStdDispatch and DispInvoke.
If some application-specific processing needs to be performed before calling a m
ember, the code should perform the necessary actions, and then call ITypeInfo::I
nvoke to invoke the member. ITypeInfo::Invoke acts exactly like IDispatch::Invok
e. The standard implementations of IDispatch::Invoke created by CreateStdDispatc
h and DispInvoke defer to ITypeInfo::Invoke.
In an COM client, IDispatch::Invoke should be used to get and set the values of
properties, or to call a method of an COM object. The dispIdMember argument iden
tifies the member to invoke. The DISPIDs that identify members are defined by th
e implementor of the object and can be determined by using the object ' s documentation
, the IDispatch::GetIDsOfNames function, or the ITypeInfo interface.
The information that follows addresses developers of COM clients and others who
use code to expose COM objects. It describes the behavior that users of exposed
objects should expect.
18.4.1.4.1 Calling a Method With No Arguments
The simplest use of Invoke is to call a method that does not have any arguments.
You only need to pass the DISPID of the method, a LCID, the DISPATCH_METHOD fla
g, and an empty DISPPARAMS structure. For example:
HRESULT hresult;
IUnknown FAR* punk;
IDispatch FAR* pdisp = (IDispatch FAR*)NULL;
OLECHAR FAR* szMember = "Simple";
DISPID dispid;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
hresult = CoCreateInstance(CLSID_CMyObject, NULL, CLSCTX_SERVER,
IID_Unknown, (void FAR* FAR*)&punk);
hresult = punk->QueryInterface(IID_IDispatch,
(void FAR* FAR*)&pdisp);
hresult = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_USER_DEFAULT, &dispid);
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispparamsNoArgs, NULL, NULL, NULL);
The example invokes a method named Simple on an object of the class CMyObject. F
irst, it calls CoCreateInstance, which instantiates the object and returns a poi
nter to the object ' s IUnknown interface (punk). Next, it calls QueryInterface, receiv
ing a pointer to the object ' s IDispatch interface (pdisp). It then uses pdisp to call
the object ' s GetIDsOfNames function, passing the string Simple in szMember to get th
e DISPID for the Simple method. With the DISPID for Simple in dispid, it calls I
nvoke to invoke the method, specifying DISPATCH_METHOD for the wFlags parameter
and using the system default locale.
To further simplify the code, the example declares a DISPPARAMS structure named
dispparamsNoArgs that is appropriate to an Invoke call with no arguments.
Because the Simple method does not take any arguments and does not return a resu
lt, the puArgErr and pVarResult parameters are Null. In addition, the example pa
sses Null for pExcepInfo, indicating that it is not prepared to handle exception
s and will handle only HRESULT errors.
Most methods, however, take one or more arguments. To invoke these methods, the
DISPPARAMS structure should be filled in, as described in " Passing Parameters " la
is chapter.
Automation defines special DISPIDs for invoking an object ' s Value property (the defau
lt), and the members _NewEnum, and Evaluate.
18.4.1.4.2 Getting and Setting Properties
Properties are accessed in the same way as methods, except you specify DISPATCH_
PROPERTYGET or DISPATCH_PROPERTYPUT instead of DISPATCH_METHOD. Some languages c
an not distinguish between retrieving a property and calling a method. In this c
ase, you should set the flags DISPATCH_PROPERTYGET and DISPATCH_METHOD.
The following example gets the value of a property named On. You can assume that
the object has been created, and that its interfaces have been queried, as in t
he previous example.
VARIANT FAR *pVarResult;
// Code omitted for brevity.
szMember = "On";
hresult = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_USER_DEFAULT, &dispid);
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET,
&dispparamsNoArgs, pVarResult, NULL, NULL);
As in the previous example, the code calls GetIDsOfNames for the DISPID of the O
n property, and then passes the ID to Invoke. Then, Invoke returns the property ' s val
ue in pVarResult. In general, the return value does not set VT_BYREF. However, t
his bit may be set and a pointer returned to the return value, if the lifetime o
f the return value is the same as that of the object.
To change the property ' s value, the call looks like this:
VARIANT FAR *pVarResult;
DISPPARAMS dispparams;
DISPID mydispid = DISP_PROPERTYPUT
// Code omitted for brevity.
szMember = "On";
dispparams.rgvarg[0].vt = VT_BOOL;
dispparams.rgvarg[0].bool = FALSE;
dispparams.rgdispidNamedArgs = &mydispid;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 1;
hresult = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_USER_DEFAULT, &dispid);
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYPUT,
&dispparams, NULL, NULL, NULL);
The new value for the property (the Boolean value False) is passed as an argumen
t when the On property ' s Put function is invoked. The DISPID for the argument is DISP
ID_PROPERTYPUT. This DISPID is defined by Automation to designate the parameter
that contains the new value for a property ' s Put function. The remaining details of t
he DISPPARAMS structure are described in the next section, " Passing Parameters. "
The DISPATCH_PROPERTYPUT flag in the previous example indicates that a property
is being set by value. In Visual Basic, the following statement assigns the Valu
e property (the default) of YourObj to the Prop property:
MyObj.Prop = YourObj
This statement should be flagged as a DISPATCH_PROPERTYPUT. Similarly, statement
s like the following assign the Value property of one object to the Value proper
ty of another object.
Worksheet.Cell(1,1) = Worksheet.Cell(6,6)
MyDoc.Text1 = YourDoc.Text1
These statements result in a PROPERTY_PUT operation on Worksheet.Cell(1,1) and M
yDoc.Text1.
Use the DISPATCH_PROPERTYPUTREF flag to indicate a property or data member that
should be set by reference. For example, the following Visual Basic statement as
signs the pointer YourObj to the property Prop, and should be flagged as DISPATC
H_PROPERTYPUTREF.
Set MyObj.Prop = YourObj
The Set statement causes a reference assignment, rather than a value assignment.
The parameter on the right side is always passed by name, and should not be acce
ssed positionally.
18.4.1.4.3 Passing Parameters
Arguments to the method or property being invoked are passed in the DISPPARAMS s
tructure. This structure consists of a pointer to an array of arguments represen
ted as variants, a pointer to an array of DISPIDs for named arguments, and the n
umber 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 are passed in the array rgvarg[ ], with the number of arguments pa
ssed in cArgs. The arguments in the array should be placed from last to first, s
o rgvarg[0] has the last argument and rgvarg[cArgs -1] has the first argument. T
he method or property may change the values of elements within the array rgvarg,
but only if it has set the VT_BYREF flag. Otherwise, consider the elements as
read-only.
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 not 0, each element of rgdispidNamedArgs[ ] contains the DISP
ID of a named argument, and the value of the argument is in the matching element
of rgvarg[ ]. The DISPIDs of the named arguments are always contiguous in rgdis
pidNamedArgs, and their values are in the first cNamedArgs elements of rgvarg. N
amed arguments cannot be accessed positionally, and positional arguments cannot
be named.
The DISPID of an argument is its zero-based position in the argument list. For e
xample, the following method takes three arguments.
BOOL _export CDECL
CCredit::CheckCredit(BSTR bstrCustomerID, // DISPID = 0.
BSTR bstrLenderID, // DISPI
D = 1.
CURRENCY cLoanAmt) // DISPI
D = 2.
{
// Code omitted.
}
If you include the DISPID with each named argument, you can pass the named argum
ents 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 argum
ent would be in rgvarg[3]. The ordering of named arguments is not important to t
he IDispatch implementation, but these arguments are generally passed in reverse
order. The argument A would be in rgvarg[2], with the DISPID of A in rgdispidNa
medArgs[2]. The argument B would be in rgvarg[1], with the corresponding DISPID
in rgdispidNamedArgs[1]. The argument C would be in rgvarg[0], with the DISPID c
orresponding to C in rgdispidNamedArgs[0]. You can also use Invoke on members wi
th optional arguments, but all optional arguments must be of type VARIANT. As wi
th required arguments, the contents of the argument vector depend on whether the
arguments are positional or named. The invoked member must ensure that the argu
ments are valid. Invoke merely passes the DISPPARAMS structure it receives.
Omitting named arguments is straightforward. You would pass the arguments in rgv
arg and their DISPIDs in rgdispidNamedArgs. To omit the argument named B (in the
preceding example) you would set rgvarg[0] to the value of C, with its DISPID i
n rgdispidNamedArgs[0]; and rgvarg[1] to the value of A, with its DISPID in rgdi
spidNamedArgs[1]. The subsequent positional arguments would occupy elements 2 an
d 3 of the arrays. In this case, cArgs is 4 and cNamedArgs
is 2.
If the arguments are positional (unnamed), you would set cArgs to the total numb
er of possible arguments, cNamedArgs to 0, and pass VT_ERROR as the type of the
omitted arguments, with the status code DISP_E_PARAMNOTFOUND as the value. For e
xample, the following code invokes ShowMe (,1).
VARIANT FAR *pVarResult;
EXCEPINFO FAR *pExcepInfo;
unsigned int FAR *puArgErr;
DISPPARAMS dispparams;
// Code omitted for brevity.
szMember = "ShowMe";
hresult = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_USER_DEFA
ULT, &dispid) ;
dispparams.rgvarg[0].vt = VT_I2;
dispparams.rgvarg[0].ival = 1;
dispparams.rgvarg[1].vt = VT_ERROR;
dispparams.rgvarg[1].scode = DISP_E_PARAMNOTFOUND;
dispparams.cArgs = 2;
dispparams.cNamedArgs = 0;
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispparams, pVarResult, pExcepInfo, puArgErr);
The example takes two positional arguments, but omits the first. Therefore, rgva
rg[0] contains 1, the value of the last argument in the argument list, and rgvar
g[1] contains VT_ERROR and the error return value, indicating the omitted first
argument.
The calling code is responsible for releasing all strings and objects referred t
o by rgvarg[ ] or placed in *pVarResult. As with other parameters that are passe
d by value, if the invoked member must maintain access to a string after returni
ng, you should copy the string. Similarly, if the member needs access to a passe
d-object pointer after returning, it must call the AddRef function on the object
. A common example occurs when an object property is changed to refer to a new o
bject, using the DISPATCH_PROPERTYPUTREF flag.
For those implementing IDispatch::Invoke, Automation provides the DispGetParam f
unction to retrieve parameters from the argument vector and coerce them to the p
roper type. For details, see " DispGetParam " later in this chapter.
18.4.1.4.4 Indexed Properties
When you invoke indexed properties of any dimension, you must pass the indexes a
s additional arguments. To set an indexed property, place the new value in the f
irst element of the rgvarg[ ] vector, and the indexes in the subsequent elements
. To get an indexed property, pass the indexes in the first n elements of rgvarg
, and the number of indexes in cArg. Invoke returns the value of the property in
pVarResult.
Automation stores array data in column-major order, which is the same ordering s
cheme used by Visual Basic and FORTRAN, but different from C, C++, and Pascal. I
f you are programming in C, C++, or Pascal, you must pass the indexes in the rev
erse order. The following example shows how to fill the DISPPARAMS structure in
C++.
dispparams.rgvarg[0].vt = VT_I2;
dispparams.rgvarg[0].iVal = 99;
dispparams.rgvarg[1].vt = VT_I2;
dispparams.rgvarg[1].iVal = 2;
dispparams.rgvarg[2].vt = VT_I2;
dispparams.rgvarg[2].iVal = 1;
dispparams.rgdispidNamedArgs = DISPID_PROPERTYPUT;
dispparams.cArgs = 3;
dispparams.cNamedArgs = 1;
The example changes the value of Prop[1,2] to 99. The new property value is pass
ed in rgvarg[0]. The right-most index is passed in rgvarg[1], and the next index
in rgvarg[2]. The cArgs field specifies the number of elements of rgvarg[ ] tha
t contain data, and cNamedArgs is 1, indicating the new value for the property.
Property collections are an extension of this feature.
18.4.1.4.5 Raising Exceptions During Invoke
When you implement IDispatch::Invoke, errors can be communicated either through
the normal return value or by raising an exception. An exception is a special si
tuation that is normally handled by jumping to the nearest routine enclosing the
exception handler.
To raise an exception, IDispatch::Invoke returns DISP_E_EXCEPTION and fills the
structure passed through pExcepInfo with information about the cause of the exce
ption or error. You can use the information to understand the cause of the excep
tion and proceed as necessary.
The exception information structure includes an error code number that identifie
s the kind of exception (a string that describes the error in a human-readable w
ay). It also includes a Help file and a Help context number that can be passed t
o Windows Help for details about the error. At a minimum, the error code number
must be filled with a valid number.
If you consider IDispatch another way to call C++ methods in an interface, EXCEP
INFO models the raising of an exception or longjmp() call by such a method.
18.4.1.4.6 Returning Errors
Invoke returns DISP_E_MEMBERNOTFOUND if one of the following conditions occurs:
· A member or parameter with the specified DISPID and matching cArgs cannot be fou
nd, and the parameter is not optional.
· The member is a void function, and the caller did not set pVarResult to Null.
· The member is a read-only property, and the caller set wFlags to DISPATCH_PROPER
TYPUT or DISPATCH_PROPERTYPUTREF.
If Invoke finds the member, but uncovers errors in the argument list, it returns
one of several other errors. DISP_E_BAD_PARAMCOUNT means that the DISPPARAMS st
ructure contains an incorrect number of parameters for the property or method. D
ISP_E_NONAMEDARGS means that Invoke received named arguments, but they are not s
upported by the member.
DISP_E_PARAMNOTFOUND means that the correct number of parameters was passed, but
the DISPID for one or more parameters was incorrect. If Invoke cannot convert o
ne of the arguments to the desired type, it returns DISP_E_TYPEMISMATCH. In thes
e two cases, if it can identify which argument is incorrect, Invoke sets *puArgE
rr to the index within rgvarg of the argument with the error. For example, if an
Automation method expects a reference to a double-precision number as an argume
nt, but receives a reference to an integer, the argument is coerced. However, if
the method receives a date, IDispatch::Invoke returns DISP_E_TYPEMISMATCH and s
ets *puArgErr to the index of the integer in the argument array.
Automation provides functions to perform standard conversions of VARIANT, and th
ese functions should be used for consistent operation. DISP_E_TYPEMISMATCH is re
turned only when these functions fail.
Example
This code from the Lines sample file Lines.cpp implements the Invoke member func
tion for the CLines class.
STDMETHODIMP
CLines::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult,
EXCEPINFO FAR* pExcepInfo,
UINT FAR* puArgErr)
{
return DispInvoke(
this, m_ptinfo,
dispidMember, wFlags, pDispParams,
pVarResult, pExcepInfo, puArgErr);
}
The next code example calls the CLines::Invoke member function to get the value
of the Color property:
HRESULT hr;
EXCEPINFO excepinfo;
UINT nArgErr;
VARIANT vRet;
DISPPARAMS FAR* pdisp;
OLECHAR FAR* szMember;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
// Initialization code omitted for brevity.
szMember = "Color";
hr = pdisp->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT,
&dispid);
// Get Color property.
hr = pdisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparams, &vRet, &excepinfo, &nArgErr);
See Also
CreateStdDispatch, DispInvoke, DispGetParam, ITypeInfo::Invoke
ICreateTypeInfo Interface
The ICreateTypeInfo interface provides the tools for creating and administering
the type information defined through the type description
ICreateTypeInfo::AddFuncDesc
HRESULT AddFuncDesc(
unsigned int index,
FUNCDESC FAR* pFuncDesc
);
Adds a function description to the type description.
Parameters
index
Index of the new FUNCDESC in the type information.
pFuncDesc
Pointer to a FUNCDESC structure that describes the function. The bstrIDLInfo fie
ld in the FUNCDESC should be set to Null for future compatibility.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
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 l
ess than the number of functions in the type information, an error is returned.
Calling this function does not pass ownership of the FUNCDESC structure to ICrea
teTypeInfo. Therefore, the caller must still de-allocate the FUNCDESC structure.
The passed-in virtual function table (VTBL) field (oVft) of the FUNCDESC is igno
red. This attribute is set when ICreateTypeInfo::LayOut is called.
The function AddFuncDesc uses the passed-in member identifier (ID) fields within
each FUNCDESC for classes with TYPEKIND = TKIND_DISPATCH or TKIND_INTERFACE. If
the member IDs are set to MEMBERID_NIL, AddFuncDesc assigns member IDs to the f
unctions. Otherwise, the member ID fields within each FUNCDESC are ignored.
Any HREFTYPE fields in the FUNCDESC structure must have been produced by the sam
e instance of ITypeInfo for which AddFuncDesc is called.
The get and put accessor functions for the same property must have the same disp
atch identifier (DISPID).
ICreateTypeInfo::AddImplType
HRESULT AddImplType(
unsigned int index,
HREFTYPE hRefType
);
Specifies an inherited interface, or an interface implemented by a component obj
ect class (coclass).
Parameters
index
Index of the implementation class to be added. Specifies the order of the type r
elative to the other type.
hRefType
Handle to the referenced type description obtained from the AddRefType descripti
on.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
To specify an inherited interface, use index = 0. For a dispinterface with Synta
x 2, call ICreateTypeInfo::AddImplType twice, once with nindex = 0 for the inher
ited IDispatch and once with nindex = 1 for the interface that is being wrapped.
For a dual interface, call ICreateTypeInfo::AddImplType with nindex = -1 for th
e TKIND_INTERFACE type information component of the dual interface.
ICreateTypeInfo::AddRefTypeInfo
HRESULT AddRefTypeInfo(
ITypeInfo FAR* pTInfo,
HREFTYPE FAR* phRefType
);
Adds a type description to those referenced by the type description being create
d.
Parameters
pTInfo
Pointer to the type description to be referenced.
phRefType
On return, pointer to the handle that this type description associates with the
referenced type information.

Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
The second parameter returns a pointer to the handle of the added type informati
on. If AddRefTypeInfo has been called previously for the same type information,
the index that was returned by the previous call is returned in phRefType. If th
e referenced type description is in the type library being created, its type inf
ormation can be obtained by calling IUnknown::QueryInterface(IID_ITypeInfo, ...)
on the ICreateTypeInfo interface of that type description.
ICreateTypeInfo::AddVarDesc
HRESULT AddVarDesc(
unsigned int index,
VARDESC FAR* pVarDesc
);
Adds a variable or data member description to the type description.
Parameters
index
Index of the variable or data member to be added to the type description.
pVarDesc
Pointer to the variable or data member description to be added.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.

Comments
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 ICreateTypeInf
o. The instance field (oInst) of the VARDESC structure is ignored. This attribut
e is set only when ICreateTypeInfo::LayOut is called. Also, the member ID fields
within the VARDESCs are ignored unless the TYPEKIND of the class is TKIND_DISPA
TCH.
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 DefineFuncAsDllEntry(
unsigned int index,
OLECHAR FAR* szDllName,
OLECHAR FAR* szProcName
);
Associates a DLL entry point with the function that has the specified index.
Parameters
index
Index of the function.
szDllName
Name of the DLL that contains the entry point.
szProcName
Name of the entry point or an ordinal (if the high word is zero).
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 cannot be found.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
If the high word of szProcName is zero, then the low word must contain the ordin
al of the entry point; otherwise, szProcName points to the zero-terminated name
of the entry point.
ICreateTypeInfo::LayOut
HRESULT LayOut();
Assigns VTBL offsets for virtual functions and instance offsets for per-instance
data members, and creates the two type descriptions for dual interfaces.
Parameters
None
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_UNDEFINEDTYPE Bound to unrecognized type.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
TYPE_E_WRONGTYPEKIND Type mismatch.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.
TYPE_E_AMBIGUOUSNAME More than one item exists with this name.
TYPE_E_SIZETOOBIG The type information is too long.
TYPE_E_TYPEMISMATCH Type mismatch.
Comments
LayOut also assigns member ID numbers to the functions and variables, unless the
TYPEKIND of the class is TKIND_DISPATCH. Call LayOut after all members of the t
ype information are defined, and before the type library is saved.
Use ICreateTypeLib::SaveAllChanges to save the type information after calling La
yOut. Other members of the ICreateTypeInfo interface should not be called after
calling LayOut.
Note
Different implementations of ICreateTypeInfo or other interfaces that create typ
e information are free to assign any member ID numbers, provided that all member
s (including inherited members), have unique IDs. For examples, see the ICreateT
ypeInfo2 interface later in this chapter..
ICreateTypeInfo::SetAlignment
HRESULT SetAlignment(
unsigned short cbAlignment
);
Specifies the data alignment for an item of TYPEKIND=TKIND_RECORD.
Parameter
cbAlignment
Alignment method for the type. A value of 0 indicates alignment on the 64K bound
ary; 1 indicates no special alignment. For other values, n indicates alignment o
n byte n.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
Comments
The alignment is the minimum of the natural alignment (for example, byte data on
byte boundaries, word data on word boundaries, and so on), and the alignment de
noted by cbAlignment.
ICreateTypeInfo::SetDocString
HRESULT SetDocString(
OLECHAR FAR* pStrDoc
);
Sets the documentation string displayed by type browsers.
Parameter
pStrDoc
Pointer to the documentation string.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
Comments
The documentation string is a brief description of the type description being cr
eated.
ICreateTypeInfo::SetFuncAndParamNames
HRESULT SetFuncAndParamNames(
unsigned int index,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames
);
Sets the name of a function and the names of its parameters to the names in the
array of pointers rgszNames.
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 e
lements are names of parameters.
cNames
Number of elements in the rgszNames array.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.
Comments
The function SetFuncAndParamNames needs to be used once for each property. The l
ast parameter for put and putref accessor functions is unnamed.
ICreateTypeInfo::SetFuncDocString
HRESULT SetFuncDocString(
unsigned int index,
OLECHAR FAR* szDocString
);
Sets the documentation string for the function with the specified index.
Parameters
index
Index of the function.
szDocString
Pointer to the documentation string.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.

Comments
The documentation string is a brief description of the function intended for use
by tools such as type browsers. SetFuncDocString only needs to be used once for
each property, because all property accessor functions are identified by one na
me.
ICreateTypeInfo::SetFuncHelpContext
HRESULT SetFuncHelpContext(
unsigned int index,
unsigned long dwHelpContext
);
Sets the Help context ID for the function with the specified index.
Parameters
index
Index of the function.
dwHelpContext
Help context ID for the Help topic.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.
E_INVALIDARG One or more of the arguments is invalid.
Comments
SetFuncHelpContext only needs to be set once for each property, because all prop
erty accessor functions are identified by one name.
ICreateTypeInfo::SetGuid
HRESULT SetGuid(
REFGUID guid
);
Sets the globally unique identifier (GUID) associated with the type description.
Parameter
guid
Globally unique ID to be associated with the type description.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
Comments
For an interface, this is an interface ID (IID); for a coclass, it is a class ID
(CLSID).
ICreateTypeInfo::SetHelpContext
HRESULT SetHelpContext(
unsigned long dwHelpContext
);
Sets the Help context ID of the type information.
Parameter
dwHelpContext
Handle to the Help context.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
ICreateTypeInfo::SetImplTypeFlags
HRESULT SetImplTypeFlags(
unsigned int index,
int implTypeFlags
);
Sets the attributes for an implemented or inherited interface of a type.
Parameters
index
Index of the interface for which to set type flags.
implTypeFlags
IMPLTYPE flags to be set.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
Comments
SetImplTypeFlags sets the IMPLTYPE flags for the indexed interface. For more inf
ormation, see Chapter 17.
ICreateTypeInfo::SetMops
HRESULT SetMops(
unsigned int index
BSTR bstrMops
);
Sets the marshaling opcode string associated with the type description or the fu
nction.
Parameters
index
Index of the member for which to set the opcode string. If index is 1, sets the o
pcode string for the type description.
bstrMops
The marshaling opcode string.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
ICreateTypeInfo::SetTypeDescAlias
HRESULT SetTypeDescAlias(
TYPEDESC FAR* pTDescAlias
);
Sets the type description for which this type description is an alias, if TYPEKI
ND=TKIND_ALIAS.
Parameter
pTDescAlias
Pointer to a type description that describes the type for which this is an alias
.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
To set the type for an alias, call SetTypeDescAlias for a type description whose
TYPEKIND is TKIND_ALIAS.
ICreateTypeInfo::SetTypeFlags
HRESULT SetTypeFlags(
unsigned int uTypeFlags
);
Sets type flags of the type description being created.
Parameter
uTypeFlags
Settings for the type flags.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 Cannot write to the destination.
TYPE_E_WRONGTYPEKIND Type mismatch.
Comments
Use SetTypeFlags to set the flags for the type description. For details, see Cha
pter 17.
ICreateTypeInfo::SetVarDocString
HRESULT SetVarDocString(
unsigned int index,
OLECHAR FAR* szDocString
);
Sets the documentation string for the variable with the specified index.
Parameters
index
Index of the variable being documented.
szDocString
The documentation string to be set.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element was not found.
ICreateTypeInfo::SetVarHelpContext
HRESULT SetVarHelpContext(
unsigned int index,
unsigned long dwHelpContext
);
Sets the Help context ID for the variable with the specified index.
Parameters
index
Index of the variable described by the type description.
dwHelpContext
Handle to the Help context ID for the Help topic on the variable.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.
ICreateTypeInfo::SetVarName
HRESULT SetVarName(
unsigned int index,
OLECHAR FAR* szName
);
Sets the name of a variable.
Parameters
index
Index of the variable whose name is being set.
szName
Name for the variable.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
STG_E_INSUFFICIENTMEMORY Out of memory.
E_OUTOFMEMORY Out of memory.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_ELEMENTNOTFOUND The element cannot be found.
ICreateTypeInfo::SetVersion
HRESULT SetVersion(
unsigned short wMajorVerNum,
unsigned short wMinorVerNum
);
Sets the major and minor version number of the type information.
Parameters
wMajorVerNum
Major version number for the type.
wMinorVerNum
Minor version number for the type.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_ACCESSDENIED Cannot write to the destination.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
ICreateTypeInfo2 Interface
The ICreateTypeInfo2 interface derives from ICreateTypeInfo, and adds methods fo
r deleting items that have been added through ICreateTypeInfo.
The ICreateTypeInfo::LayOut method provides a way for the creator of the type in
formation to check for any errors. A call to QueryInterface() can be made to the
ICreateTypeInfo instance at any time for its ITypeInfo interface. Calling any o
f the methods in the ITypeInfo interface that require layout information lays ou
t the type information automatically.
Example
interface ICreateTypeInfo2 : ICreateTypeInfo
ICreateTypeInfo2::SetName
HRESULT SetName(
OLECHAR FAR* szName
);
Sets the name of the typeinfo.
Parameter
szName
Name to be assigned to the typeinfo.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type info is not valid for this operati
on.

ICreateTypeInfo2::DeleteFuncDesc
HRESULT DeleteFuncDesc(
unsigned int index
);
Deletes a function description specified by the index number.
Parameter
index
Index of the function whose description is to be deleted. The index should be in
the range of 0 to 1 less than the number of functions in this type.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::DeleteFuncDescByMemId
HRESULT DeleteFuncDescByMemId(
MEMBERID memid,
INVOKEKIND invKind
);
Deletes the function description (FUNCDESC) specified by memid.
Parameters
memid
Member identifier of the FUNCDESC to delete.
invKind
The type of the invocation.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::DeleteVarDesc
HRESULT DeleteVarDesc(
unsigned int index
);
Deletes the specified VARDESC structure.
Parameter
index
Index number of the VARDESC structure.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
TYPE_E_IOERROR The function cannot read from the file.
TYPE_E_INVDATAREAD The function cannot read from the file.
TYPE_E_UNSUPFORMAT The type library has an old format.
TYPE_E_INVALIDSTATE The type library cannot be opened.
Example
ptypeinfo->DeleteVarDesc(index);
ICreateTypeInfo2::DeleteVarDescByMemId
HRESULT DeleteVarDescByMemId(
MEMBERID memid
);
Deletes the specified VARDESC structure.
Parameter
memid
Member identifier of the VARDESC to be deleted.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
TYPE_E_IOERROR The function cannot read from the file.
TYPE_E_INVDATAREAD The function cannot read from the file.
TYPE_E_UNSUPFORMAT The type library has an older format.
TYPE_E_INVALIDSTATE The type library cannot be opened.
ICreateTypeInfo2::DeleteImplType
HRESULT DeleteImplType(
unsigned int index
);
Deletes the IMPLTYPE flags for the indexed interface.
Parameter
index
Index of the interface for which to delete the type flags.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetCustData
HRESULT SetCustData(
REFGUID guid,
VARIANT *pVarVal
);
Sets a value for custom data.
Parameters
guid
Unique identifier that can be used to identify the data.
pVarVal
The data to store (any variant except an object).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetHelpStringContext
HRESULT SetHelpStringContext(
DWORD *dwHelpStringContext
);
Sets the context number for the specified Help string.
Parameter
dwHelpStringContext
Pointer to the Help string context number.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG Argument is invalid.
ICreateTypeInfo2::SetFuncCustData
HRESULT SetFuncCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal
);
Sets a value for a specified custom function.
Parameters
index
The index of the function for which to set the custom data.
guid
Unique identifier used to identify the data.
pVarVal
The data to store (any variant except an object).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetFuncHelpStringContext
HRESULT SetFuncHelpStringContext(
unsigned int index,
DWORD dwHelpStringContext,
);
Sets a Help context value for a specified custom function.
Parameters
index
The index of the function for which to set the custom data.
dwHelpStringContext
Help string context for a localized string
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetVarCustData
HRESULT SetVarCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal
);
Sets a custom data variable.
Parameters
index
Index of the variable for which to set the custom data.
guid
Globally unique ID (GUID) used to identify the data.
pVarVal
Data to store (any legal variant except an object).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetParamCustData
HRESULT SetParamCustData(
unsigned int indexFunc,
unsigned int indexParam,
REFGUID guid,
VARIANT *pVarVal
);
Sets the specified parameter for the custom data.
Parameters
indexFunc
Index of the function for which to set the custom data.
indexParam
Index of the parameter of the function for which to set the custom data.
guid
Globally unique identifier (GUID) used to identify the data.
pvarVal
The data to store (any legal variant except an object).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetImplTypeCustData
HRESULT SetImplTypeCustData(
unsigned int index,
REFGUID guid,
VARIANT *pVarVal,
);
Sets the implementation type for custom data.
Parameters
index
Index of the variable for which to set the custom data.
guid
Unique identifier used to identify the data.
pVarVal
Reference to the value of the variable.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeInfo2::SetVarHelpStringContext
HRESULT SetVarHelpStringContext(
unsigned int index,
DWORD dwHelpStringContext,
);
Sets a Help context value for a specified variable.
Parameters
index
The index of the variable.
dwHelpStringContext
Help string context for a localized string
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeLib Interface
The ICreateTypeLib interface provides the methods for creating and managing the
component or file that contains type information. Type libraries are created fro
m type descriptions using the MkTypLib utility or the MIDL compiler. These type
libraries are accessed through the ITypeLib interface.
ICreateTypeLib::CreateTypeInfo
HRESULT CreateTypeInfo(
OLECHAR FAR* szName,
TYPEKIND tkind,
ICreateTypeInfo FAR* FAR* ppCTInfo
);
Creates a new type description instance within the type library.
Parameters
szName
Name of the new type.
tkind
TYPEKIND of the type description to be created.
ppCTInfo
On return, contains a pointer to the type description.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
TYPE_E_NAMECONFLICT The provided name is not unique.
TYPE_E_WRONGTYPEKIND Type mismatch.

Comments
Use the function CreateTypeInfo to create a new type description instance within
the library. An error is returned if the specified name already appears in the
library. Valid tkind values are described in Chapter 17. To get the type informa
tion of the type description that is being created, call IUnknown::QueryInterfac
e(IID_ITypeInfo, ...) on the returned ICreateTypeInfo. This type information can
be used by other type descriptions that reference it by using ICreateTypeInfo::
AddRefTypeInfo.
ICreateTypeLib::SaveAllChanges
HRESULT SaveAllChanges();
Saves the ICreateTypeLib instance following the layout of type information.
Parameters
None.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 cannot write to the file.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
Other return codes All FACILITY_STORAGE errors.
Comments
You should not call any other ICreateTypeLib methods after calling SaveAllChange
s.
ICreateTypeLib::SetDocString
HRESULT SetDocString(
OLECHAR FAR* szDoc
);
Sets the documentation string associated with the library.
Parameter
szDoc
A documentation string that briefly describes the type library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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
The documentation string is a brief description of the library intended for use
by type information browsing tools.
ICreateTypeLib::SetGuid
HRESULT SetGuid(
REFGUID guid
);
Sets the universal unique identifier (UUID) associated with the type library (Al
so known as the globally unique identifier (GUID)).
Parameter
guid
The globally unique identifier to be assigned to the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
Comments
ICreateTypeLib::SetHelpContext
HRESULT SetHelpContext(
unsigned long dwHelpContext
);
Sets the Help context ID for retrieving general Help information for the type li
brary.
Parameter
dwHelpContext
Help context ID to be assigned to the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
Comments
Calling SetHelpContext with a Help context of zero is equivalent to not calling
it at all, because zero indicates a null Help context.
ICreateTypeLib::SetHelpFileName
HRESULT SetHelpFileName(
OLECHAR FAR* szHelpFileName
);
Sets the name of the Help file.
Parameter
szHelpFileName
The name of the Help file for the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.

Comments
Each type library can reference a single Help file.
The GetDocumentation method of the created ITypeLib returns a fully qualified pa
th for the Help file, which is formed by appending the name passed into szHelpFi
leName 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 SetLibFlags(
unsigned int uLibFlags
);
Sets library flags, such as LIBFLAG_FRESTRICTED.
Parameter
uLibFlags
The flags to set for the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
Comments
ICreateTypeLib::SetLcid
HRESULT SetLcid(
LCID lcid
);
Sets the binary Microsoft national language ID associated with the library.
Parameter
lcid
Represents the locale ID for the type library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
Comments
ICreateTypeLib::SetName
HRESULT SetName(
OLECHAR FAR* szName
);
Sets the name of the type library.
Parameter
szName
Name to be assigned to the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
ICreateTypeLib::SetVersion
HRESULT SetVersion(
unsigned short wMajorVerNum,
unsigned short wMinorVerNum
);
Sets the major and minor version numbers of the type library.
Parameters
wMajorVerNum
Major version number for the library.
wMinorVerNum
Minor version number for the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
TYPE_E_INVALIDSTATE The state of the type library is not valid for this oper
ation.
ICreateTypeLib2 Interface
ICreateTypeLib2 inherits from ICreateTypeLib, and has four member functions. The
ICreateTypeInfo instance returned from ICreateTypeLib can be accessed through a
QueryInterface() call to ICreateTypeInfo2.
Example
interface ICreateTypeLib2 : ICreateTypeLib
ICreateTypeLib2::SetName
HRESULT SetName(
OLECHAR FAR* szName
);
Sets the name of the type library.
Parameter
szName
Name to be assigned to the library.
Return Value
The return value of the returned HRESULT is one of the following:
Return value 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 state of the type library is not valid for this oper
ation.
ICreateTypeLib2::DeleteTypeInfo
HRESULT DeleteTypeInfo(
OLECHAR FAR* szName
);
Deletes a specified type information from the type library.
Parameter
szName
Name of the type information to remove.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeLib2::SetCustData
HRESULT SetCustData(
REFGUID guid,
VARIANT *pVarVal
);
Sets a value to custom data.
Parameters
guid
Unique identifier used to identify the data.
pVarVal
The data to store (any variant except an object).
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeLib2::SetHelpStringContext
HRESULT SetHelpStringContext(
DWORD *dwHelpStringContext
);
Sets the Help string context number.
Parameter
DwHelpStringContext
The Help string context number.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.
ICreateTypeLib2::SetHelpStringDll
HRESULT SetHelpStringDll(
LPOLESTR szFileName
);
Sets the DLL name to be used for Help string lookup (for localization purposes).
Parameter
szFileName
The DLL file name.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG One or more of the arguments is invalid.

18.5 Automation API Descriptions


18.5.1 BstrFromVector
HRESULT BstrFromVector(
SAFEARRAY FAR* psa,
BSTR FAR* pbstr
);
Returns a BSTR, assigning each element of the vector to a character in the BSTR.
Parameters
psa
The vector to be converted to a BSTR.
pbstr
On exit, pbstr points to a BSTR, each character of which is assigned to an eleme
nt from the vector.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG The argument psa is Null.
DISP_E_TYPEMISMATCH The argument psa is not a vector (not an array of bytes)
.
18.5.2 CreateDispTypeInfo
HRESULT CreateDispTypeInfo(
INTERFACEDATA pidata,
LCID lcid,
ITypeInfo FAR* FAR* pptinfo
);
Creates simplified type information for use in an implementation of IDispatch.
Parameters
pidata
The interface description that this type information describes.
lcid
The locale identifier for the names used in the type information.
pptinfo
On return, pointer to a type information implementation for use in DispGetIDsOfN
ames and DispInvoke.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK The interface is supported.
E_INVALIDARG Either the interface description or the LCID is invalid.
E_OUTOFMEMORY Insufficient memory to complete the operation.

Comments
You can construct type information at run time by using CreateDispTypeInfo and a
n INTERFACEDATA structure that describes the object being exposed.
The type information returned by this function is primarily designed to automate
the implementation of IDispatch. CreateDispTypeInfo does not return all of the
type information described in Chapter 17. The argument pidata is not a complete
description of an interface. It does not include Help information, comments, opt
ional parameters, and other type information that is useful in different context
s.
Accordingly, the recommended method for providing type information about an obje
ct is to describe the object using the Object Description Language (ODL), and to
compile the object description into a type library using the Microsoft Interfac
e Definition Language (MIDL) compiler or the MkTypLib utility.
To use type information from a type library, use the LoadTypeLib and GetTypeInfo
OfGuid functions instead of CreateDispTypeInfo. For more information, see Chapte
r 17.
Example
The code that follows creates type information from INTERFACEDATA to expose the
CCalc object.
static METHODDATA NEARDATA rgmdataCCalc[] =
{
PROPERTY(VALUE, IMETH_ACCUM, IDMEMBER_ACCUM, VT_I4)
PROPERTY(ACCUM, IMETH_ACCUM, IDMEMBER_ACCUM, VT_I4)
PROPERTY(OPND, IMETH_OPERAND, IDMEMBER_OPERAND, VT_I4)
PROPERTY(OP, IMETH_OPERATOR, IDMEMBER_OPERATOR, VT_I2)
METHOD0(EVAL, IMETH_EVAL, IDMEMBER_EVAL, VT_BOOL)
METHOD0(CLEAR, IMETH_CLEAR, IDMEMBER_CLEAR, VT_EMPTY)
METHOD0(DISPLAY, IMETH_DISPLAY, IDMEMBER_DISPLAY, VT_EMPTY)
METHOD0(QUIT, IMETH_QUIT, IDMEMBER_QUIT, VT_EMPTY)
METHOD1(BUTTON, IMETH_BUTTON, IDMEMBER_BUTTON, VT_BOOL)
};
INTERFACEDATA NEARDATA g_idataCCalc =
{
rgmdataCCalc, DIM(rgmdataCCalc)
};
// Use Dispatch interface API functions to implement IDispatch.
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 type information 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
// type information.
hresult = CreateStdDispatch(
pcalc, // Controlling unknown.
parith, // Instance to dispatch on.
ptinfo, // Type information describing t
he instance.
&punkStdDisp);
ptinfo->Release();
if(hresult != NOERROR)
goto LError0;
pcalc->m_punkStdDisp = punkStdDisp;
return pcalc;
LError0:;
pcalc->Release();
return NULL;
}
18.5.3 CreateStdDispatch
HRESULT CreateStdDispatch(
IUnknown FAR* punkOuter,
void FAR* pvThis,
ITypeInfo FAR* ptinfo,
IUnknown FAR* FAR* ppunkStdDisp
);
Creates a standard implementation of the IDispatch interface through a single fu
nction call. This simplifies exposing objects through 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
This is the private unknown for the object that implements the IDispatch interfa
ce QueryInterface call. This pointer is Null if the function fails.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 creating an object instead of implementing th
e IDispatch member functions for the object. However, the implementation that Cr
eateStdDispatch creates has these limitations:
· Supports only one national language.
· Supports only dispatch-defined exception codes returned from Invoke.
LoadTypeLib, GetTypeInfoOfGuid, and CreateStdDispatch comprise the minimum set o
f functions that you need to call to expose an object using a type library. For
more information on LoadTypeLib and GetTypeInfoOfGuid, see Chapter 17.
CreateDispTypeInfo and CreateStdDispatch comprise the minimum set of dispatch co
mponents you need to call to expose an object using type information provided by
the INTERFACEDATA structure.
Example
The following code implements the IDispatch interface for the CCalc class using
CreateStdDispatch.
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 type information 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
// type information.
hresult = CreateStdDispatch(
pcalc, // Controlling unknown.
parith, // Instance to dispatch on.
ptinfo, // Type information describing t
he instance.
&punkStdDisp);
ptinfo->Release();
if(hresult != NOERROR)
goto LError0;
pcalc->m_punkStdDisp = punkStdDisp;
return pcalc;
LError0:;
pcalc->Release();
return NULL;
}
18.5.4 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 recommen
ded implementation of IDispatch::GetIDsOfNames.
Parameters
ptinfo
Pointer to the type information for an interface. This type information is speci
fic to one interface and language code, so it is not necessary to pass an interf
ace identifier (IID) or LCID to this function.
rgszNames
An array of name strings that can be the same array passed to DispInvoke in the
DISPPARAMS structure. If cNames is greater than 1, the first name is interpreted
as a method name, and subsequent names are interpreted as parameters to that me
thod.
cNames
The number of elements in rgszNames.
rgdispid
Pointer to an array of DISPIDs to be filled in by this function. The first ID co
rresponds to the method name. Subsequent IDs are interpreted as parameters to th
e method.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value 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 retur
ned array of DISPIDs contains DISPID_UNKNOWN for each entry that corresponds to
an unknown name.
Other return codes Any of the ITypeInfo::Invoke errors can also be returned
.
Example
This code from the Lines sample file Points.cpp implements the member function G
etIDsOfNames for the CPoints class using DispGetIDsOfNames.
STDMETHODIMP
CPoints::GetIDsOfNames(
REFIID riid,
char FAR* FAR* rgszNames,
UINT cNames,
LCID lcid,
DISPID FAR* rgdispid)
{
return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
}
See Also
CreateStdDispatch, IDispatch::GetIDsOfNames
18.5.5 DispGetParam
HRESULT DispGetParam(pdispparams, position, vtTarg, pvarResult, puArgErr)
DISPPARAMS FAR* pdispparams,
unsigned int position,
VARTYPE vtTarg,
VARIANT FAR* pvarResult,
unsigned int FAR* puArgErr
);
Retrieves a parameter from the DISPPARAMS structure, checking both named paramet
ers and positional parameters, and coerces the parameter to the
specified type.
Parameters
pdispparams
Pointer to the parameters passed to IDispatch::Invoke.
position
The position of the parameter in the parameter list. DispGetParam starts at the
end of the array, so if position is 0, the last parameter in the array is return
ed.
vtTarg
The type the argument should be coerced to.
pvarResult
Pointer to the variant to pass the parameter into.
puArgErr
On return, pointer to the index of the argument that caused a DISP_E_TYPEMISMATC
H error. This pointer is returned to Invoke to indicate the position of the argu
ment in DISPPARAMS that caused the error.
Return Value
The return value obtained from the HRESULT is one of the following:
Return value Meaning
S_OK Success.
DISP_E_BADVARTYPE The variant type vtTarg is not supported.
DISP_E_OVERFLOW The retrieved parameter could not be coerced to the specified ty
pe.
DISP_E_PARAMNOTFOUND The parameter indicated by position could not be found.
DISP_E_TYPEMISMATCH The argument could not be coerced to the specified type.
E_INVALIDARG One of the arguments was invalid.
E_OUTOFMEMORY Insufficient memory to complete operation.
Comments
The output parameter pvarResult must be a valid variant. Any existing contents a
re released in the standard way. The contents of the variant are freed with Vari
antFree.
If you have used DispGetParam to get the right side of a property put operation,
the second parameter should be DISPID_PROPERTYPUT. For example:
DispGetParam(&dispparams, DISPID_PROPERTYPUT, VT_BOOL, &varResult)
Named parameters cannot be accessed positionally, and vice versa.
Example
The following example uses 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 valid.
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 otherwise is found.
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
18.5.6 DispInvoke
HRESULT DispInvoke(_this, ptinfo, dispidMember, wFlags, pparams, pvarResult, pex
cepinfo, 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 the type information
for the interface. You can describe an interface with type information and impl
ement IDispatch::Invoke for the interface using this single call.
Parameters
_this
Pointer to an implementation of the IDispatch interface described by ptinfo.
ptinfo
Pointer to the type information that describes the interface.
dispidMember
Identifies the member. Use GetIDsOfNames or the object ' s documentation to obtain the
DISPID.
wFlags
Flags describing the context of the Invoke call, as follows:

Value Description
DISPATCH_METHOD The member is invoked as a method. If a property has the same na
me, both this and the DISPATCH_PROPERTYGET flag can be set.
DISPATCH_PROPERTYGET The member is retrieved as a property or data member.
DISPATCH_PROPERTYPUT The member is changed as a property or data member.
DISPATCH_PROPERTYPUTREF The member is changed by a reference assignment, rather
than a value assignment. This flag is valid only when the property accepts a ref
erence to an object.
pparams
Pointer to a structure containing an array of arguments, an array of argument DI
SPIDs for named arguments, and counts for number of elements in the arrays.
pvarResult
Pointer to where the result is to be stored, or Null if the caller expects no re
sult. This argument is ignored if DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTRE
F is specified.
pexcepinfo
Pointer to a structure containing exception information. This structure should b
e filled in if DISP_E_EXCEPTION is returned.
puArgErr
The index within rgvarg of the first argument that has an error. Arguments are s
tored in pdispparams->rgvarg in reverse order, so the first argument is the one
with the highest index in the array. This parameter is returned only when the re
sulting return value is DISP_E_TYPEMISMATCH or DISP_E_PARAMNOTFOUND.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
DISP_E_BADPARAMCOUNT The number of elements provided in DISPPARAMS is differe
nt from the number of arguments accepted by the method or property.
DISP_E_BADVARTYPE One of the arguments in DISPPARAMS is not a valid varian
t type.
DISP_E_EXCEPTION The application needs to raise an exception. In this cas
e, 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 s
pecified type.
DISP_E_PARAMNOTFOUND One of the parameter IDs does not correspond to a parame
ter on the method. In this case, puArgErr is set to the first argument that cont
ains the error.
DISP_E_PARAMNOTOPTIONAL A required parameter was omitted.
DISP_E_TYPEMISMATCH One or more of the arguments could not be coerced. The i
ndex of the first parameter with the incorrect type within rgvarg is returned in
puArgErr.
E_INVALIDARG One of the arguments is invalid.
E_OUTOFMEMORY Insufficient memory to complete the operation.
Other return codes Any of the ITypeInfo::Invoke errors can also be returned
.
Comments
The parameter _this is a pointer to an implementation of the interface that is b
eing deferred to. DispInvoke builds a stack frame, coerces parameters using stan
dard coercion rules, pushes them on the stack, and then calls the correct member
function in the VTBL.
Example
The following code from the Lines sample file Lines.cpp implements IDispatch::In
voke using DispInvoke. This function uses m_bRaiseException to signal that an er
ror occurred during the DispInvoke call.
STDMETHODIMP
CLines::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pdispparams,
VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo,
UINT FAR* puArgErr)
{
return DispInvoke(
this, m_ptinfo,
dispidMember, wFlags, pdispparams,
pvarResult, pexcepinfo, puArgErr);
}
See Also
CreateStdDispatch, IDispatch::Invoke
18.5.7 DosDateTimeToVariantTime
int DosDateTimeToVariantTime(
unsigned short wDOSDate,
unsigned short wDOSTime,
double FAR* pvtime
);
Converts the MS-DOS representation of time to the date and time representation s
tored in a 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
The return value obtained from the returned HRESULT is 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 the 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).
18.5.8 GetActiveObject
HRESULT GetActiveObject(
REFCLSID rclsid,
void FAR* pvReserved,
IUnknown FAR* FAR* ppunk
);
Retrieves a pointer to a running object that has been registered with COM.
Parameters
rclsid
Pointer to the class identifier (CLSID) of the active object from the COM regist
ration database.
pvReserved
Reserved for future use. Must be Null.
ppunk
On return, a pointer to the requested active object.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
Other return codes Failure.
18.5.9 RegisterActiveObject
HRESULT RegisterActiveObject(
IUnknown FAR* punk,
REFCLSID rclsid,
DWORD dwFlags,
Unsigned long FAR* pdwRegister
);
Registers an object as the active object for its class.
Parameters
punk
Pointer to the IUnknown interface of the active object.
rclsid
Pointer to the CLSID of the active object.
dwFlags
Flags controlling registration of the object. Possible values are ACTIVEOBJECT_S
TRONG and ACTIVEOBJECT_WEAK.
pdwRegister
On return, a pointer to a handle. This handle must be passed to RevokeActiveObje
ct to end the object ' s active status.
Return Value
The return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
Other return codes Failure.
Comments
The RegisterActiveObject function registers the object to which punk points as t
he active object for the class denoted by rclsid. Registration causes the object
to be listed in the running object table (ROT) of COM, a globally accessible lo
okup table that keeps track of objects that are currently running on the compute
r. (For more information about the running object table, see the OLE Programmer'
s Reference.) The dwFlags parameter specifies the strength or weakness of the re
gistration, which affects the way the object is shut down.
In general, COM objects should behave in the following manner:
· If the object is visible, it should shut down only in response to an explicit us
er command (such as the Exit command on the File menu), or to the equivalent com
mand from an COM client (invoking the Quit or Exit method on the Application obj
ect).
· If the object is not visible, it should shut down only when the last external co
nnection to it is gone.
Strong registration performs an AddRef on the object, incrementing the reference
count of the object (and its associated stub) in the running object table. A st
rongly registered object must be explicitly revoked from the table with RevokeAc
tiveObject. The default is strong registration (ACTIVEOBJECT_STRONG).
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 co
nnection to a weakly registered object disappears, COM releases the object ' s stub, an
d the object itself is no longer available.
To ensure the desired behavior, consider not only the default actions of COM, bu
t also the following:
· Even though code can create an invisible object, the object may become visible a
t some later time. Once the object is visible, it should remain visible and acti
ve until it receives an explicit command to shut down. This can occur after refe
rences from the code disappear.
· Other COM clients may be using the object. If so, the code should not force the
object to shut down.
To avoid possible conflicts, you should always register COM objects with ACTIVEO
BJECT_WEAK, and call CoLockObjectExternal, when necessary, to guarantee the obje
ct remains active. CoLockObjectExternal adds a strong lock, thereby preventing t
he object ' s reference count from reaching zero. For detailed information about this f
unction, refer to the OLE Programmer's Reference.
Most commonly, objects need to call CoLockObjectExternal when they become visibl
e, so they remain active until the user requests the object to shut down. The fo
llowing procedure lists the steps your code should follow to shut down an object
correctly.
To shut down an active object:
1. When the object becomes visible, make the following call to add
a lock for the user:
CoLockObjectExternal(punk, TRUE, TRUE)
The lock remains in effect until a user explicitly requests the object to be shu
t down, such as with a Quit or Exit command.
2. When the user requests the object to be shut down, call CoLockOb
jectExternal 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 CoDisconnectO
bject as follows:
CoDisconnectObject(punk, 0)
This function is described in more detail in the OLE Programmer's Reference.

18.5.10 RevokeActiveObject
HRESULT RevokeActiveObject(
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 return value obtained from the returned HRESULT is one of the following:
Return value Meaning
S_OK Success.
Other return codes Failure.

18.5.11 CompareString
int CompareString(
LCID lcid,
DWORD dwCmpFlags,
LPCWSTR lpString1,
integer cchCount1,
LPWSTR lpString2,
integer cchCount2,
);
Compares two character strings of the same locale according to the supplied LCID
.
Parameters
lcid
Locale context for the comparison. The strings are assumed to be represented in
the default ANSI code page for this locale.
dwCmpFlags
Flags that indicate the character traits to use or ignore when comparing the two
strings. Several flags can be combined , or none can be used. (In the case of t
his function, there are no illegal combinations of flags.) Compare flags include
the following.
Value Meaning
Ignore case. Default is Off.
Ignore Japanese hiragana/katakana character differences. Default is Off.
Ignore nonspacing marks (accents, diacritics, and vowel marks). Default
is Off.
Ignore symbols. Default is Off.
Ignore character width. Default is Off.
lpString1 and lpString2
The two strings to be compared.
cchCount1 and cchCount2
The character counts of the two strings. The count does not include the null-ter
minator (if any). If either cchCount1 or cchCount2 is *1, the corresponding stri
ng is assumed to be null-terminated, and the length is calculated automatically.

Return Value
Value Meaning
Failure.
lpString1 is less than lpString2.
lpString1 is equal to lpString2.
lpString1 is greater than lpString2.
Comments
When used without any flags, this function uses the same sorting algorithm as ls
trcmp in the given locale. When used with NORM_IGNORECASE, the same algorithm as
lstrcmpi is used.
For double-byte character set (DBCS) locales, the flag NORM_IGNORECASE has an ef
fect on all the wide (two-byte) characters as well as the narrow (one-byte) char
acters. This includes the wide Greek and Cyrillic characters.
In Chinese Simplified, the sorting order used to compare the strings is based on
the following sequence: symbols, digit numbers, English letters, and Chinese Si
mplified characters. The characters within each group sort in character-code ord
er.
In Chinese Traditional, the sorting order used to compare the strings is based o
n the number of strokes in the characters. Symbols, digit numbers, and English c
haracters are considered to have zero strokes. The sort sequence is symbols, dig
it numbers, English letters, and Chinese Traditional characters. The characters
within each stroke-number group sort in character-code order.
In Japanese, the sorting order used to compare the strings is based on the Japan
ese 50-on sorting sequence. The Kanji ideographic characters sort in character-c
ode order.
In Japanese, the flag NORM_IGNORENONSPACE has an effect on the daku-on, handaku-
on, chou-on, you-on, and soku-on modifiers, and on the repeat kana/kanji charact
ers.
In Korean, the sort order is based on the sequence: symbols, digit numbers, Jaso
and Hangeul, Hanja, and English. Within the Jaso-Hangeul group, each Jaso chara
cter is followed by the Hangeuls that start with that Jaso. Hanja characters are
sorted in Hangeul pronunciation order. Where multiple Hanja have the same Hange
ul pronunciation, they are sorted in character-code order.
The NORM_IGNORENONSPACE flag only has an effect for the locales in which accente
d characters are sorted in a second pass from main characters. All characters in
the string are first compared without regard to accents and (if the strings are
equal) a second pass over the strings to compare accents is performed. In this
case, this flag causes the second pass to not be performed. Some locales sort ac
cented characters in the first pass, in which case this flag will have no effect
.
If the return value is 2, the two strings are equal in the collation sense, thou
gh not necessarily identical (the case might be ignored, and so on).
If the two strings are of different lengths, they are compared up to the length
of the shortest one. If they are equal to that point, the return value will indi
cate that the longer string is greater.
To maintain the C run-time convention of comparing strings, the value 2 can be s
ubtracted from a non-zero return value. The meaning of < 0, == 0, and > 0 is the
n consistent with the C run-time conventions.
18.5.12 LCMapString
int LCMapString(
LCID lcid,
DWORD dwMapFlags,
LPCWSTR lpSrcStr,
int cchSrc,
LPWSTR lpDestStr,
int cchDest,
);
Transforms the case or sort order of a string.
Parameters
lcid
Locale ID context for mapping. The strings are assumed to be represented in the
default ANSI code page for this locale.
dwMapFlags
Flags that indicate what type of transformation is to occur during mapping. Seve
ral flags can be combined on a single transformation (though some combinations a
re illegal). Mapping options include the following.
Name Meaning
Lowercase.
Uppercase.
Character sort key.
Narrow characters (where applicable).
Wide characters (where applicable).
Hiragana.
Katakana.
Ignore case. Default is Off.
Ignore nonspacing. Default is Off.
Ignore character width. Default is Off.
Ignore Japanese hiragana/katakana character differences. Default is Off.
Ignore symbols. Default is Off.
The latter five options (NORM_IGNORECASE, NORM_IGNORENONSPACE, NORM_IGNOREWIDTH,
NORM_IGNOREKANATYPE, and NORM_IGNORESYMBOLS) are normalization options that can
only be used in combination with the LCMAP_SORTKEY conversion option.
Conversion options can be combined only when they are taken from the following t
hree groups, and then only when there is no more than one option from each group
:
* Casing options (LCMAP_LOWERCASE, LCMAP_UPPERCASE)
* Width options (LCMAP_HALFWIDTH, LCMAP_FULLWIDTH)
* Kana options (LCMAP_HIRAGANA, LCMAP_KATAKANA)
lpSrcStr
Pointer to the supplied string to be mapped.
cchSrc
Character count of the input string buffer. If *1, lpSrcStr is assumed to be nul
l-terminated and the length is calculated automatically.
lpDestStr
Pointer to the memory buffer that stores the resulting mapped string.
cchDest
Character count of the memory buffer pointed to by lpDestStr. If cchDest is 0, t
hen the return value of this function is the number of characters required to ho
ld the mapped string. In this case, the lpDestStr pointer is not referenced.
Return Value
Value Meaning
Failure.
Success.
Comments
LCMapStringA maps one character string to another, performing the specified loca
le-dependent translation.
The flag LCMAP_UPPER produces the same result as AnsiUpper in the given locale.
The flag LCMAP_LOWER produces the same result as AnsiLower. This function always
maps a single character to a single character.
The mapped string is null-terminated if the source string is null-terminated.
When used with LCMAP_UPPER and LCMAP_LOWER, the lpSrcStr and lpDestStr may be th
e same to produce an in-place mapping. When LCMAP_SORTKEY is used, the lpSrcStr
and lpDestStr pointers may not be the same. In this case, an error will result.
The LCMAP_SORTKEY transforms two strings so that when they are compared with the
standard C library function strcmp (by strict numerical valuation of their char
acters), the same order will result, as if the original strings were compared wi
th CompareStringA. When LCMAP_SORTKEY is specified, the output string is a strin
g (without Nulls, except for the terminator), but the character values will not
be meaningful display values. This is similar behavior to the ANSI C function st
rxfrm.
18.5.13 GetLocaleInfo
int GetLocaleInfoA(
LCID lcid,
LCTYPE LCType,
LPSTR lpLCData,
int cchData,
);
Retrieves locale information from the user ' s system.
Parameters
lcid
The locale ID. The returned string is represented in the default ANSI code page
for this locale.
LCType
Flag that indicates the type of information to be returned by the call. See the
listing of constant values defined in this chapter. LOCALE_NOUSEROVERRIDE | LCTY
PE indicates that the desired information will always be retrieved from the loca
le database, even if the LCID is the current one and the user has changed some o
f the values in the Windows 95 Control Panel. If this flag is not specified, the
values in Win.ini take precedence over the database settings when getting value
s for the current system default locale.
lpLCData
Pointer to the memory where GetLocaleInfoA will return the requested data. This
pointer is not referenced if cchData is 0.
cchData
Character count of the supplied lpLCData memory buffer. If cchData is 0, the ret
urn value is the number of characters required to hold the string, including the
terminating null character. In this case, lpLCData is not referenced.
Return Value
Value Meaning
Failure.
Success.
Comments
GetLocaleInfoA returns one of the various pieces of information about a locale b
y querying the stored locale database or Win.ini. The call also indicates how mu
ch memory is necessary to contain the desired information.
The information returned is always a null-terminated string. No integers are ret
urned by this function and numeric values are returned as text. (See the format
descriptions under LCTYPE).
18.5.14 GetStringType
BOOL GetStringTypeA(
LCID lcid,
DWORD dwInfoType,
LPCSTR lpSrcStr,
int cchSrc,
LPWORD lpCharType,
);
Retrieves locale type information about each character in a string.
Parameters
lcid
Locale context for the mapping. The string is assumed to be represented in the d
efault ANSI code page for this locale.
dwInfoType
Type of character information to retrieve. The various types are divided into di
fferent levels. (See the Comments section for a list of information included in
each type). The options are mutually exclusive. The following types are supporte
d:
* CT_CTYPE1
* CT_CTYPE2
* CT_CTYPE3
lpSrcStr
String for which character types are requested. If cchSrc is *1, lpSrcStr is ass
umed to be null-terminated.
cchSrc
Character count of lpSrcStr. If cchSrc is *1, lpSrcStr is assumed to be null-ter
minated. This must also be the character count of lpCharType.
lpCharType
Array of the same length as lpSrcStr (cchSrc). On output, the array contains one
word corresponding to each character in lpSrcStr.
Return Value
Return value Meaning
Failure.
Success.
Comments
The lpSrcStr and lpCharType pointers cannot be the same. In this case, the error
ERROR_INVALID_PARAMETER results.
The character type bits are divided up into several levels. One level ' s information c
an be retrieved by a single call.
This function supports three character types:
* Ctype 1
* Ctype 2
* Ctype 3
Ctype 1 character types support ANSI C and POSIX character typing functions. A b
itwise OR of these values is returned when dwInfoType is set to CT_CTYPE1. For D
BCS locales, the Ctype 1 attributes apply to both narrow characters and wide cha
racters. The Japanese hiragana and katakana characters, and the kanji ideograph
characters all have the C1_ALPHA attribute.
The following table lists the Ctype 1 character types.
Name Value Meaning
0x0001 Uppercase1.
0x0002 Lowercase1.
0x0004 Decimal digits.
0x0008 Space characters.
0x0010 Punctuation.
0x0020 Control characters.
0x0040 Blank characters.
0x0080 Hexadecimal digits.
0x0100 Any letter.
1 The Windows version 3.1 functions IsCharUpper and IsCharLower do not alw
ays produce correct results for characters in the range 0x80-0x9f, so they may p
roduce different results than this function for characters in that range. (For e
xample, the German Windows version 3.1 language driver incorrectly reports 0x9a,
lowercase s hacek, as uppercase).
Ctype 2 character types support the proper layout of text. For DBCS locales, Cty
pe 2 applies to both narrow and wide characters. The directional attributes are
assigned so that the BiDi layout algorithm standardized by Unicode produces the
correct results. For more information on the use of these attributes, see The Un
icode Standard: Worldwide Character Encoding from Addison-Wesley publishers.
Attribute Name Value Meaning
C2_LEFTTORIGHT 0x1 Left to right.
C2_RIGHTTOLEFT 0x2 Right to left.
C2_EUROPENUMBER 0x3 European number, European digit.
C2_EUROPESEPARATOR 0x4 European numeric separator.
C2_EUROPETERMINATOR 0x5 European numeric terminator.
C2_ARABICNUMBER 0x6 Arabic number.
C2_COMMONSEPARATOR 0x7 Common numeric separator.
C2_BLOCKSEPARATOR 0x8 Block separator.
C2_SEGMENTSEPARATOR 0x9 Segment separator.
C2_WHITESPACE 0xA White space.
C2_OTHERNEUTRAL 0xB Other neutrals.
C2_NOTAPPLICABLE 0x0 No implicit direction (for example, cont
rol codes).
Ctype 3 character types are general text-processing information. A bitwise OR of
these values is returned when dwInfoType is set to CT_CTYPE3. For DBCS locales,
the Ctype 3 attributes apply to both narrow characters and wide characters. The
Japanese hiragana and katakana characters, and the kanji ideograph characters a
ll have the C3_ALPHA attribute.
Name Value Meaning
0x1 Nonspacing mark.
0x2 Diacritic nonspacing mark.
0x4 Vowel nonspacing mark.
0x8 Symbol.
0x10 Katakana character.
0x20 Hiragana character.
0x40 Narrow character.
0x80 Wide character.
0x100 Ideograph.
0x8000 Any letter.
0x0 Not applicable.

18.5.15 GetSystemDefaultLangID
LANGID GetSystemDefaultLangID();
Retrieves the default LANGID from a user ' s system.
Return Value
Return value Meaning
Failure.
Success.
Comments
Returns the system default LANGID. For information on how this value is determin
ed, see GetSystemDefaultLCID in the following section..
18.5.16 GetSystemDefaultLCID
LCID GetSystemDefaultLCID();
Retrieves the default LCID from a user ' s system.
Return Value
Return value Meaning
Failure.
Success.

Comments
Returns the system default LCID. The return value is determined by examining the
values of sLanguage and iCountry in Win.ini, and comparing the values to those
in the stored locale database. If no matching values are found, or the required
values cannot be read from Win.ini, or if the stored locale database cannot be l
oaded, the value 0 is returned.
18.5.17 GetUserDefaultLangID
LANGID GetUserDefaultLangID();
Retrieves the default LANGID from a user ' s system.
Return Value
Value Meaning
Failure.
Success.
Comments
Returns the user default LANGID. On single-user systems, the value returned from
this function is always the same as that returned from GetSystemDefaultLangID.
18.5.18 GetUserDefaultLCID
LCID GetUserDefaultLCID();
Retrieves the default LCID from a user ' s system.
Return Value
Return value Meaning
Failure.
Success.
Comments
Returns the user default LCID. On single-user systems, the value returned by thi
s function is always the same as that returned from GetSystemDefaultLCID.
18.5.19 SafeArrayAccessData
HRESULT SafeArrayAccessData (
SAFEARRAY FAR* psa,
void HUGEP* FAR* ppvdata
);
Increments the lock count of an array, and retrieves a pointer to the array data
.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate.
ppvdata
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 sorts a safe array of one dimension that contains BSTRs by
accessing the array elements directly. This approach is faster than using SafeA
rrayGetElement and SafeArrayPutElement.
long i, j, min;
BSTR bstrTemp;
BSTR HUGEP *pbstr;
HRESULT hr;
// Get a pointer to the the elements of the array.
hr = SafeArrayAccessData(psa, (void HUGEP* FAR*)&pbstr);
if (FAILED(hr))
goto error;
// Bubble sort.
cElements = lUBound-lLBound+1;
for (i = 0; i < cElements-1; i++)
{
min = i;
for (j = i+1; j < cElements; j++)
{
if (wcscmp(pbstr[j], pbstr[min]) < 0)
min = j;
}
// Swap array[min] and array[i].
bstrTemp = pbstr[min];
pbstr[min] = pbstr[i];
pbstr[i] = bstrTemp;
}
SafeArrayUnaccessData(psa);
18.5.20 SafeArrayAllocData
HRESULT SafeArrayAllocData(
SAFEARRAY FAR* psa
);
Allocates memory for a safe array, based on a descriptor created with SafeArrayA
llocDescriptor.
Parameter
psa
Pointer to an array descriptor created by SafeArrayAllocDescriptor.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 an
d 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
18.5.21 SafeArrayAllocDescriptor
HRESULT SafeArrayAllocDescriptor(
unsigned int cDims,
SAFEARRAY FAR* FAR* ppsaOut
);
Allocates memory for a safe array descriptor.
Parameters
cDims
The number of dimensions of the array.
ppsaOut
Pointer to a location in which to store the created array descriptor.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 allows the creation of safe arrays that contain elements with data
types other than those provided by SafeArrayCreate. After creating an array des
criptor using SafeArrayAllocDescriptor, set the element size in the array descri
ptor, an call SafeArrayAllocData to allocate memory for the array elements.
Example
The following example creates a safe array using the SafeArrayAllocDescriptor an
d 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
18.5.22 SafeArrayCopy
HRESULT SafeArrayCopy(
SAFEARRAY FAR* psa,
SAFEARRAY FAR* FAR* ppsaOut
);
Creates a copy of an existing safe array.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate.
ppsaOut
Pointer to a location in which to return the new array descriptor.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
E_OUTOFMEMORY Insufficient memory to create the copy.
Comments
SafeArrayCopy calls the string or variant manipulation functions if the array to
copy contains either of these data types. If the array being copied contains ob
ject references, the reference counts for the objects are incremented.
See Also
SysAllocStringLen, VariantCopy, VariantCopyInd
18.5.23 SafeArrayCopyData
HRESULT SafeArrayCopyData(
SAFEARRAY FAR* psaSource,
SAFEARRAY FAR* FAR* ppsaTarget
);
Copies the source array to the target array after releasing any resources in the
target array. This is similar to SafeArrayCopy, except that the target array ha
s to be set up by the caller. The target is not allocated or reallocated.
Parameters
psaSource
The safe array from which to be copied.
ppsaTarget
On exit, the array referred to by ppsaTarget contains a copy of the data in psaS
ource.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
E_OUTOFMEMORY Insufficient memory to create the copy.
Comments
Visual Basic for Applications and Automation use the same set of rules with case
s in which the size or types of source and destination arrays do not match. The
rules of Visual Basic are described in the following comments.
Array Assignment
In general, VBA3 supports array assignment.
Dim lhs(1 To 10) As Integer
Dim rhs(1 To 10) As Integer
lhs = rhs
When the number of dimensions, the size of those dimensions, and the element typ
es match, data types are differentiated based on the following factors:
· Fixed-size, left side. The left side is fixed if the type of the expression on
the left side is a fixed-size array. For example, the following statement is a d
eclaration of a fixed-size array.
Dim x (1 To 10) As Integer
· Matching number of dimensions. The number of dimensions of the left side may or
may not match the number of dimensions of the array on the right side.
· Dimensions match. The dimensions match if, for each dimension, the number of el
ements match. The dimensions can match even if the declarations are slightly dif
ferent, such as when one array is zero-based and another is one-based, but they
have the same number of elements.
The following table shows what happens when the number of dimensions, size of th
e dimension, and element types do not match:
Fixed-size, left side Number of dimensions Dimensions match What hap
pens
No Yes or No Yes or No Success. If necessary, the left side is
resized to the size of the right side.
Yes No Failure.
Yes Yes No Treated in same manner as fixed-length strings.If the ri
ght side has more elements than the left side, the assignment succeeds and the e
xtra elements have no effect. If the left side has more elements than the right
side, the assignment succeeds and the unaffected elements of the left side are z
ero-, null-, or empty-filled, depending on the types of the elements.
Yes Yes Yes Success.
See Also
SysAllocStringLen, VariantCopy, VariantCopyInd
18.5.24 SafeArrayCreate
HRESULT SafeArrayCreate(
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 (the VARTYPE of each element of the array). The VARTY
PE 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. The number cannot be changed after the array
is created.
rgsabound
Pointer 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 for brevity.
LError0:;
return hresult;
}
18.5.25 SafeArrayCreateVector
HRESULT SafeArrayCreateVector(
VARTYPE vt,
long lbound,
unsigned int cElements
);
Creates a one-dimensional array whose lower bound is always zero. A safe array c
reated with SafeArrayCreateVector is a fixed size, so the constant FADF_FIXEDSIZ
E is always set.
Parameters
vt
The base type of the array (the VARTYPE of each element of the array). The VARTY
PE 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.
lbound
The lower bound for the array. Can be negative.
cElements
The number of elements in the array.
Return Value
Points to the array descriptor, or Null if the array could not be created.
Comments
SafeArrayCreateVector allocates a single block of memory containing a SAFEARRAY
structure for a single-dimension array (24 bytes), immediately followed by the a
rray data. All of the existing safe array functions work correctly for safe arra
ys that are allocated with SafeArrayCreateVector.
A SafeArrayCreateVector is allocated as a single block of memory. Both the SafeA
rray descriptor and the array data block are allocated contiguously in one alloc
ation, which speeds up array allocation. However, a user can allocate the descri
ptor and data area separately using the SafeArrayAllocDescriptor and SafeArrayAl
locData calls.
18.5.26 SafeArrayDestroy
HRESULT SafeArrayDestroy(
SAFEARRAY FAR* psa
);
Destroys an existing array descriptor and all of the data in the array. If objec
ts are stored in the array, Release is called on each object in the array.
Parameter
psa
Pointer to an array descriptor created by SafeArrayCreate.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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;
}
18.5.27 SafeArrayDestroyData
HRESULT SafeArrayDestroyData(
SAFEARRAY FAR* psa
);
Destroys all the data in a safe array.
Parameter
psa
Pointer to an array descriptor.

Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 w
ith data types other than variants. If objects are stored in the array, Release
is called on each object in the array.
See Also
SafeArrayAllocData, SafeArrayAllocDescriptor, SafeArrayDestroyDescriptor
18.5.28 SafeArrayDestroyDescriptor
HRESULT SafeArrayDestroyDescriptor(
SAFEARRAY FAR* psa
);
Destroys a descriptor of a safe array.
Parameter
psa
Pointer to a safe array descriptor.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 c
ontains elements with data types other than variants. Destroying the array descr
iptor does not destroy the elements in the array. Before destroying the array de
scriptor, call SafeArrayDestroyData to free the elements.
See Also
SafeArrayAllocData, SafeArrayAllocDescriptor, SafeArrayDestroyData
18.5.29 SafeArrayGetDim
HRESULT SafeArrayGetDim(
unsigned int SafeArrayGetDim(psa),
SAFEARRAY FAR* psa
);
Returns the number of dimensions in the array.
Parameters
psa
Pointer 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 for brevity.
}
18.5.30 SafeArrayGetElement
HRESULT SafeArrayGetElement(
SAFEARRAY FAR* psa,
long FAR* rgIndices,
void FAR* pvData
);
Retrieves a single element of the array.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate.
rgIndices
Pointer to a vector of indexes for each dimension of the array. The right-most (
least significant) dimension is rgIndices[0]. The left-most dimension is stored
at rgIndices[psa->cDims - 1].
pvData
Pointer to the location to place the element of the array.
Comments
This function calls SafeArrayLock and SafeArrayUnlock automatically, before and
after retrieving the element. The caller must provide a storage area of the corr
ect size to receive the data. If the data element is a string, object, or varian
t, the function copies the element in the correct way.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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;
}
18.5.31 SafeArrayGetElemsize
HRESULT SafeArrayGetElemsize(
unsigned int SafeArrayGetElemsize(psa),
SAFEARRAY FAR* psa
);
Returns the size (in bytes) of the elements of a safe array.
Parameter
psa
Pointer to an array descriptor created by SafeArrayCreate.
18.5.32 SafeArrayGetLBound
HRESULT SafeArrayGetLBound(
SAFEARRAY FAR* psa,
unsigned int nDim,
long FAR* plLbound
);
Returns the lower bound for any dimension of a safe array.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate.
nDim
The array dimension for which to get the lower bound.
plLbound
Pointer to the location to return the lower bound.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 for brevity.

}
18.5.33 SafeArrayGetUBound
HRESULT SafeArrayGetUBound(
SAFEARRAY FAR* psa,
unsigned int nDim,
long FAR* plUbound
);
Returns the upper bound for any dimension of a safe array.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate().
nDim
The array dimension for which to get the upper bound.
plUbound
Pointer to the location to return the upper bound.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 here for brevity.
LError0:;
penum->Release();
return hresult;
}
18.5.34 SafeArrayLock
HRESULT SafeArrayLock(
SAFEARRAY FAR* psa
);
Increments the lock count of an array, and places a pointer to the array data in
pvData of the array descriptor.
Parameter
psa
Pointer to an array descriptor created by SafeArrayCreate.
Comments
The pointer in the array descriptor is valid until SafeArrayUnlock is called. Ca
lls to SafeArrayLock can be nested. An equal number of calls to SafeArrayUnlock
are required.
An array cannnot be deleted while it is locked.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
E_UNEXPECTED The array could not be locked.
18.5.35 SafeArrayPtrOfIndex
HRESULT SafeArrayPtrOfIndex(
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.

Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
DISP_E_BADINDEX The specified index was invalid.
Comments
The array should be locked before SafeArrayPtrOfIndex is called. Failing to lock
the array can cause unpredictable results.
18.5.36 SafeArrayPutElement
HRESULT SafeArrayPutElement(
SAFEARRAY FAR* psa,
long FAR* rgIndices,
void FAR* pvData
);
Assigns a single element to the array.
Parameters
psa
Pointer to an array descriptor created by SafeArrayCreate.
rgIndices
Pointer to a vector of indexes for each dimension of the array. The right-most (
least significant) dimension is rgIndices[0]. The left-most dimension is stored
at rgIndices[psa->cDims - 1].
pvData
Pointer to the data to assign to the array. The variant types VT_DISPATCH, VT_UN
KNOWN, and VT_BSTR are pointers, and do not require another level of indirection
.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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.
Comments
This function automatically calls SafeArrayLock and SafeArrayUnlock before and a
fter assigning the element. If the data element is a string, object, or variant,
the function copies it correctly. If the existing element is a string, object,
or variant, it is cleared correctly.
Note Multiple locks can be on an array. Elements can be put into an array while
the array is locked by other operations.
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 = ResultFromScode(E_OUTOFMEMORY);
goto LError0;
}
// Code omitted here for brevity.
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;
}
18.5.37 SafeArrayRedim
HRESULT SafeArrayRedim(
SAFEARRAY FAR* psa,
SAFEARRAYBOUND FAR* psaboundNew
);
Changes the right-most (least significant) bound of a safe array.
Parameters
psa
Pointer to an array descriptor.
psaboundNew
Pointer to a new safe array bound structure that contains the new array boundary
. You can change only the least significant dimension of an array.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 elemen
ts outside the new array boundary. If the bound of an array is increased, SafeAr
rayRedim allocates and initializes the new array elements. The data is preserved
for elements that exist in both the old and new array.
18.5.38 SafeArrayUnaccessData
HRESULT SafeArrayUnaccessData(
SAFEARRAY FAR* psa
);
Decrements the lock count of an array, and invalidates the pointer retrieved by
SafeArrayAccessData.
Parameter
psa
Pointer to an array descriptor created by SafeArrayCreate.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
E_UNEXPECTED The array could not be unlocked.
18.5.39 SafeArrayUnlock
HRESULT SafeArrayUnlock(
SAFEARRAY FAR* psa
);
Decrements the lock count of an array so it can be freed or resized.
Parameter
psa
Pointer to an array descriptor created by SafeArrayCreate.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_INVALIDARG The argument psa was not a valid safe array descriptor.
E_UNEXPECTED The array could not be unlocked.
Comments
This function is called after access to the data in an array is finished.
18.5.40 SysAllocString
BSTR SysAllocString(
OLECHAR FAR* sz
);
Allocates a new string and copies the passed string into it. Returns Null if the
re is insufficient memory, and if Null, Null is passed in.
Parameter
sz
A zero-terminated string to copy. The sz parameter must be a Unicode string in 3
2-bit applications, and an ANSI string in 16-bit applications.
Return Value
If successful, points to a BSTR containing the string. If insufficient memory ex
ists or sz was Null, returns Null.
Comments
You can free strings created with SysAllocString using SysFreeString.
Example
inline void CStatBar::SetText(OLECHAR FAR* sz)
{
SysFreeString(m_bstrMsg);
m_bstrMsg = SysAllocString(sz);
}
18.5.41 SysAllocStringByteLen
BSTR SysAllocStringByteLen(
char FAR* psz,
unsigned int len
);
Takes an ANSI string as input, and returns a BSTR that contains an ANSI string.
Does not perform any ANSI-to-Unicode translation.
Parameters
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, allocat
ing a total of len+1 bytes.
Allocates a new string of len bytes, copies len bytes from the passed string int
o it, and then appends a null character. Valid only for 32-bit systems.
Return Value
Points to a copy of the string, or Null if insufficient memory exists.
Comments
This function is provided to create BSTRs that contain binary data. You can use
this type of BSTR only in situations where it will not be translated from ANSI t
o Unicode, or vice versa.
For example, do not use these BSTRs between a 16-bit and a 32-bit application ru
nning on a 32-bit Windows system. The COM 16-bit to 32-bit (and 32-bit to 16-bit
) interoperability layer will translate the BSTR and corrupt the binary data. Th
e preferred method of passing binary data is to use a SAFEARRAY of VT_UI1, which
will not be translated by COM.
If psz is Null, a string of the requested length is allocated, but not initializ
ed. The string psz can contain embedded null characters, and does not need to en
d with a Null. Free the returned string later with SysFreeString.
18.5.42 SysAllocStringLen
BSTR SysAllocStringLen(
OLECHAR FAR* pch,
unsigned int cch
);
Allocates a new string, copies cch characters from the passed string into it, an
d then appends a null character.
Parameters
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, al
locating a total of cch+1 characters.
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 initializ
ed. The pch string can contain embedded null characters and does not need to end
with a Null. Free the returned string later with SysFreeString.
18.5.43 SysFreeString
void SysFreeString(
BSTR bstr
);
Frees a string allocated previously by SysAllocString, SysAllocStringByteLen, Sy
sReAllocString, SysAllocStringLen, or SysReAllocStringLen.
Parameter
bstr
A BSTR allocated previously, or Null. If Null, the function simply returns.
Return Value
None.
Example
CStatBar::~CStatBar()
{
SysFreeString(m_bstrMsg);
}
18.5.44 SysReAllocString
BOOL SysReAllocString(
BSTR FAR* pbstr,
OLECHAR FAR* sz
);
Allocates a new BSTR and copies the passed string into it, then frees the BSTR r
eferenced by pbstr, and finally resets pbstr to point to the new BSTR.
Parameters
pbstr
Points to a variable containing a BSTR.
sz
A zero-terminated string to copy.
Return Value
Returns False if insufficient memory exists.
18.5.45 SysReAllocStringLen
BOOL SysReAllocStringLen(
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.
Parameters
pbstr
Pointer to a variable containing a BSTR.
pch
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 afterward, all
ocating a total of cch+1 characters.
Return Value
Returns True if the string is reallocated successfully, or False if insufficient
memory exists.
Comments
Allocates a new string, copies cch characters from the passed string into it, an
d then appends a null character. Frees the BSTR referenced currently by pbstr, a
nd resets pbstr to point to the new BSTR. If pch is Null, a string of length cch
is allocated but not initialized.
The pch string can contain embedded null characters and does not need to end wit
h a Null.
18.5.46 SysStringByteLen
unsigned int SysStringByteLen(
BSTR bstr
);
Returns the length (in bytes) of a BSTR. Valid for 32-bit systems only.
Parameter
bstr
A BSTR allocated previously. It cannot be Null.
Return Value
The number of bytes in bstr, not including a terminating null character.
Comments
The returned value may be different from fstrlen(bstr) if the BSTR was allocated
with Sys[Re]AllocStringLen or SysAllocStringByteLen, and the passed-in characte
rs included a null character in the first len characters. For a BSTR allocated w
ith Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns
the number of bytes specified in the len parameter at allocation time.
Example
// Display the status message.
TextOut(
hdc,
rcMsg.left + (m_dxFont / 2),
rcMsg.top + ((rcMsg.bottom - rcMsg.top - m_dyFont) / 2),
m_bstrMsg, SysStringByteLen(m_bstrMsg));
18.5.47 SysStringLen
unsigned int SysStringLen(
BSTR bstr
);
Returns the length of a BSTR.
Parameter
bstr
A BSTR allocated previously. Cannot be Null.
Return Value
The number of characters in bstr, not including a terminating null character.
Comments
The returned value may be different from _fstrlen(bstr) if the BSTR was allocate
d with Sys[Re]AllocStringLen or SysAllocStringByteLen, and the passed-in charact
ers included a null character in the first cch characters. For a BSTR allocated
with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always return
s the number of characters specified in the cch parameter at allocation time.
Example
// Display the status message.
//
TextOut(
hdc,
rcMsg.left + (m_dxFont / 2),
rcMsg.top + ((rcMsg.bottom - rcMsg.top - m_dyFont) / 2),
m_bstrMsg, SysStringLen(m_bstrMsg));
18.5.48 SystemTimeToVariantTime
int SystemTimeToVariantTime(psystime, pvtime)
SYSTEMTIME *psystime
double *vtime
Converts the variant representation of time-to-system time values.
Parameters
psystime
The system time.
vtime
Returned variant time.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Result Meaning
True Success.
False Failure.
Comments
A variant time is stored as an 8-byte real value (double), representing a date b
etween January 1, 1753 and December 31, 2078, inclusive. The value 2.0 represent
s January 1, 1900; 3.0 represents January 2, 1900, and so on. Adding 1 to the va
lue increments the date by a day. The fractional part of the value represents th
e time of day. Therfore, 2.5 represents noon on January 1, 1900; 3.25 represents
6:00 a.m. on January 2, 1900, and so on. Negative numbers represent the dates p
rior to December 30, 1899.
The SYSTEMTIME structure is useful for the following reasons:
· It spans all time/date periods. MS-DOS date/time is limited to representing only
those dates between 1/1/1980 and 12/31/2107.
· The date/time elements are all easily accessible without needing to do any bit d
ecoding.
· The National Data Support data and time formating functions GetDateFormat() and
GetTimeFormat() take a SYSTEMTIME value as input.
18.5.49 VariantChangeType
HRESULT VariantChangeType(
VARIANTARG FAR* pvargDest,
VARIANTARG FAR* pvargSrc,
unsigned short wFlags,
VARTYPE vtNew
);
Converts a variant from one type to another.
Parameters
pvargDest
Pointer to the VARIANTARG to receive the coerced type. If this is the same as pv
argSrc, the variant will be converted in place.
pvargSrc
Pointer to the source VARIANTARG to be coerced.
wFlags
Flags that control the coercion. The only defined flag is VARIANT_NOVALUEPROP, w
hich 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 ne
cessary, because it makes their behavior inconsistent with other applications.
vtNew
The type to coerce to. If the return code is S_OK, the vt field of the *pvargDes
t is always the same as this value.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 obtaining 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, and then calls VariantChangeType to get the value of one or more argu
ments. For example, if the IDispatch call specifies a SetTitle member that takes
one string argument, the implementor would call VariantChangeType to attempt to
coerce the argument to VT_BSTR. If VariantChangeType does not return an error,
the argument could then be obtained directly from the bstrVal field of the VARIA
NTARG. If VariantChangeType returns DISP_E_TYPEMISMATCH, the implementor would s
et *puArgErr to 0 (indicating the argument in error) and return DISP_E_TYPEMISMA
TCH from IDispatch::Invoke.
Arrays of one type cannnot be converted to arrays of another type with this func
tion.
Note The type of a VARIANTARG should not be changed in the rgvarg array in plac
e.
See Also
VariantChangeTypeEx
18.5.50 VariantChangeTypeEx
HRESULT VariantChangeTypeEx(
VARIANTARG FAR* pvargDest,
VARIANTARG FAR* pvargSrc,
LCID lcid,
unsigned short wFlags,
VARTYPE vtNew
);
Converts a variant from one type to another, using a locale ID.
Parameters
pvargDest
Pointer to the VARIANTARG to receive the coerced type. If this is the same as pv
argSrc, the variant will be converted in place.
pvargSrc
Pointer to the source VARIANTARG to be coerced.
lcid
The locale ID for the variant to coerce. The locale ID is useful when the type o
f 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, w
hich 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 ne
cessary, because it makes their behavior inconsistent with other applications.
vtNew
The type to coerce to. If the return code is S_OK, the vt field of the *pvargDes
t is guaranteed to be equal to this value.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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, change the referenced value
to VariantChangeTypeEx. To coerce objects to fundamental types, obtain the valu
e of the Value property.
Typically, the implementor of IDispatch::Invoke determines which member is being
accessed, and then calls VariantChangeType to get the value of one or more argu
ments. For example, if the IDispatch call specifies a SetTitle member that takes
one string argument, the implementor would call VariantChangeTypeEx to attempt
to coerce the argument to VT_BSTR.
If VariantChangeTypeEx does not return an error, the argument could then be obta
ined directly from the bstrVal field of the VARIANTARG. If VariantChangeTypeEx r
eturns DISP_E_TYPEMISMATCH, the implementor would set *puArgErr to 0 (indicating
the argument in error) and return DISP_E_TYPEMISMATCH from IDispatch::Invoke.
Arrays of one type cannot be converted to arrays of another type with this funct
ion.
Note The type of a VARIANTARG should not be changed in the rgvarg array in plac
e.
See Also
VariantChangeType
18.5.51 VariantClear
HRESULT VariantClear(
VARIANTARG FAR* pvarg
);
Clears a variant.
Parameter
pvarg
Pointer to the VARIANTARG to clear.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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
Use this function to clear variables of type VARIANTARG (or VARIANT) before the
memory containing the VARIANTARG is freed (as when a local variable goes out of
scope).
The function clears a VARIANTARG by setting the vt field to VT_EMPTY and the wRe
served 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, t
he object is released. If the vt field has the VT_ARRAY bit set, the array is fr
eed.
In certain cases, it may be preferable to clear a variant in code without callin
g VariantClear. For example, you can change the type of a VT_I4 variant to anoth
er type without calling this function. However, you must call VariantClear if a
VT_type is received but cannot be handled. Using VariantClear in these cases ens
ures that code will continue to work if Automation adds new variant types in th
e future.
Example
for(i = 0; i < celt; ++i)
VariantClear(&rgvar[i]);
18.5.52 VariantCopy
HRESULT VariantCopy(
VARIANTARG FAR* pvargDest,
VARIANTARG FAR* pvargSrc
);
Frees the destination variant and makes a copy of the source variant.
Parameters
pvargDest
Pointer to the VARIANTARG to receive the copy.
pvargSrc
Pointer to the VARIANTARG to be copied.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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, free any memory that is owned by pvargDest, such as VariantClear (pvargDe
st must point to a valid initialized variant, and not simply to an uninitialized
memory location). Then pvargDest receives an exact copy of the contents of pvar
gSrc.
If pvargSrc is a VT_BSTR, a copy of the string is made. If pvargSrc is a VT_ARRA
Y, the entire array is copied. If pvargSrc is a VT_DISPATCH or VT_UNKNOWN, AddRe
f is called to increment the object's reference count.
18.5.53 VariantCopyInd
HRESULT VariantCopyInd(
VARIANT FAR* pvarDest,
VARIANTARG FAR* pvargSrc
);
Frees the destination variant and makes a copy of the source VARIANTARG, perform
ing the necessary indirection if the source is specified to be VT_BYREF.
Parameters
pvarDest
Pointer to the VARIANTARG that will receive the copy.
pvargSrc
Pointer to the VARIANTARG that will be copied.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value 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 a copy of a variant is needed, and to guarantee tha
t it is not VT_BYREF, such as when handling arguments in an implementation of ID
ispatch::Invoke.
For example, if the source is a (VT_BYREF | VT_I2), the destination will be a BY
VAL | 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 VT_BYREF, t
he contained variant is also dereferenced.
This function frees any existing contents of pvarDest.
18.5.54 VariantInit
void VariantInit(
VARIANTARG FAR* pvarg
);
Initializes a variant.
Parameter
pvarg
Pointer to the VARIANTARG that will be initialized.

Comments
The VariantInit function initializes the VARIANTARG by setting the vt field to V
T_EMPTY. Unlike VariantClear, this function does not interpret the current conte
nts of the VARIANTARG. Use VariantInit to initialize new local variables of type
VARIANTARG (or VARIANT).
Example
for(i = 0; i < celt; ++i)
VariantInit(&rgvar[i]);
18.5.55 VariantTimeToDosDateTime
int VariantTimeToDosDateTime(
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 v
alues.
Parameters
vtime
The variant time to convert.
pwDOSDate
Pointer to the location to store the converted MS-DOS date.
pwDOSTime
Pointer to the location to store the converted MS-DOS time.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Result Meaning
True Success.
False Failure.
Comments
A variant time is stored as an 8-byte real value (double), representing a date b
etween January 1, 1753 and December 31, 2078, inclusive. The value 2.0 represent
s January 1, 1900; 3.0 represents January 2, 1900, and so on. Adding 1 to the va
lue increments the date by a day. The fractional part of the value represents th
e time of day. Therefore, 2.5 represents noon on January 1, 1900; 3.25 represent
s 6:00 a.m. on January 2, 1900, and so on. Negative numbers represent the dates
prior to December 30, 1899.
For a description of the MS-DOS date and time formats, see DosDateTimeToVariantT
ime.
18.5.56 VariantTimeToSystemTime
int VariantTimeToSystemTime(
double vtime,
SYSTEMTIME *psystime
);
Converts the variant representation of time-to-system time values.
Parameters
vtime
The variant time that will be converted.
psystime
Pointer to the location where the converted time will be stored.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Result Meaning
True Success.
False Failure.
Comments
A variant time is stored as an 8-byte real value (double), representing a date b
etween January 1, 1753 and December 31, 2078, inclusive. The value 2.0 represent
s January 1, 1900; 3.0 represents January 2, 1900, and so on. Adding 1 to the va
lue increments the date by a day. The fractional part of the value represents th
e time of day. Therefore, 2.5 represents noon on January 1, 1900; 3.25 represent
s 6:00 A.M. on January 2, 1900, and so on. Negative numbers represent the dates
prior to December 30, 1899.
Using the SYSTEMTIME structure is useful because:
· It spans all time/date periods. MS-DOS date/time is limited to representing only
those dates between 1/1/1980 and 12/31/2107.
· The date/time elements are all easily accessible without needing to do any bit d
ecoding.
· The National Language Support data and time formating functions GetDateFormat()
and GetTimeFormat() take a SYSTEMTIME value as input.
18.5.57 VarNumFromParseNum
HRESULT VarNumFromParseNum(
[in] NUMPARSE *pnumprs,,
[in] unsigned char *rgbDig,
[in] unsigned long dwVtBits,
[out] VARIANT *pvar
);
Once the number is parsed, the caller can call VarNumFromParseNum() to convert t
he parse results to a number. The NUMPARSE structure and digit array must be pas
sed in unchanged from the VarParseNumFromStr() call. This function will choose t
he smallest type allowed that can hold the result value with as little precision
loss as possible. The result variant is an [out] parameter, so its contents are
not freed before storing the result.
Parameters
pnumprs
Parsed results.
rgbDig
Array.
dwVtBits
Contains one bit set for each type that is acceptable as a return value (in many
cases, just one bit).
Pvar
Result variant.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
DISP_E_OVERFLOW The number is too large to be represented in an allowed type. Th
ere is no error if precision is lost in the conversion.
The rgbDig array is filled in with the values for the digits in the range 0-7, 0
-9, or 0-15, depending on whether the number is octal, decimal, or hexadecimal.
All leading zeros have been stripped off. For decimal numbers, trailing zeros ar
e also stripped off, unless the number is zero, in which case a single zero digi
t will be present.
For rounding decimal numbers, the digit array must be at least one digit longer
than the maximum required for data types. The maximum number of digits required
for the DECIMAL data type is 29, so the digit array must have room for 30 digits
. There must also be enough digits to accept the number in octal, if that parsin
g options is selected. (Hexadecimal and octal numbers are limited by VarNumFromP
arseNum() to the magnitude of an unsigned long [32 bits], so they need 11 octal
digits.)
18.5.58 VarParseNumFromStr
HRESULT VarParseNumFromStr(
[in] OLECHAR* strIn,
[in] LCID lcid,
[in] unsigned long dwFlags,
[in] NUMPARSE *pnumprs,
[out] unsigned char *rgbDig
);
Parses a string, and creates a type-independent description of the number it rep
resents. The first three parameters are identical to the first three parameters
of VarI2FromStr, VarI4FromStr, VarR8FromStr, and so on. The fourth parameter is
a pointer to a NUMPARSE structure, which contains both input information to the
function as well as the results, as described above. The last parameter is a poi
nter to an array of digits, filled in by the function.
The VarParseNumFromStr function fills in the dwOutFlags element with each corres
ponding feature that was actually found in the string. This allows the caller to
make decisions about what numeric type to use for the number, based on the form
at in which it was entered. For example, one application might want to use the C
URRENCY data type if the currency symbol is used, and others may want to force a
floating point type if an exponent was used.
Parameters
[in] strIn
Input string to be converted to a number.
lcid
Locale identifier
dwFlags
Allows the caller to control parsing, therefore defining the acceptable syntax o
f a number. If this field is set to zero, the input string must contain nothing
but decimal digits. Setting each defined flag bit enables parsing of that syntac
tic feature. Standard Automation parsing (for example, as used by VarI2FromStr)
has all flags set (NUMPRS_STD).
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Internal memory allocation failed. (Used for DBCS only to create
a copy with all wide characters mapped narrow.)
DISP_E_TYPEMISMATCH There is no valid number in the string, or there is no c
losing parenthesis to match an opening one. In the former case, cDig and cchUsed
in the NUMPARSE structure will be zero. In the latter, the NUMPARSE structure a
nd digit array are fully updated, as if the closing parenthesis was present.
DISP_E_OVERFLOW For hexadecimal and octal digists, there are more digits than wi
ll fit into the array. For decimal, the exponent exceeds the maximum possible. I
n both cases, the NUMPARSE structure and digit array are fully updated (for deci
mal, the cchUsed field excludes the entire exponent).
18.5.59 VectorFromBstr
HRESULT VectorFromBstr(
BSTR bstr,
SAFEARRAY FAR* FAR* ppsa
);
Returns a vector, assigning each character in the BSTR to an element of the vect
or.
Parameters
bstr
The BSTR to be converted to a vector.
ppsa
On exit, ppsa points to a one-dimensional safe array containing the characters i
n the BSTR.
Return Value
The return value obtained from the returned HRESULT is one of the following.
Return value Meaning
S_OK Success.
E_OUTOFMEMORY Out of memory.
E_INVALIDARG BSTR is Null.
18.6 Automation Related Structure Definitions
18.6.1 NUMPARSE
The caller of VarParseNumFromStr() must initialize two elements of the passed-in
NUMPARSE structure:
typedef struct {
int cDig;
unsigned long dwInFlags;
unsigned long dwOutFlags;
int cchUsed;
int nBaseShift;
int nPwr10;
} NUMPARSE;
The cDig element is set to the size of the rgbDig array, and dwInFlags is set to
parsing options. All other elements may be uninitialized and are set by the fun
ction, except on error, as described in the following paragraphs. The cDig eleme
nt is also modified by the function to reflect the actual number of digits writt
en to the rgbDig array.
The cchUsed element of the NUMPARSE sturcture is filled in with the number of ch
aracters (from the beginning of the string) that were successfully parsed. This
allows the caller to determine if the entire string was part of the number (as r
equired by functions such as VarI2FromStr), or where to continue parsing the str
ing.
The nBaseShift element gives the number of bits per digit (3 or 4 for octal and
hexadecimal numbers, and zero for decimal).
The following apply only to decimal numbers:
· nPwr10 sets the decimal point position by giving the power of 10 of the least si
gnificant digit.
· If the number is negative, NUMPRS_NEG will be set in dwOutFlags.
· If there are more non-zero decimal digits than will fit into the digit array, th
e NUMPRS_INEXACT flag will be set.
18.6.2 SAFEARRAY
The definition for a safe array varies, depending on the target operating system
platform. On 32-bit Windows systems, both the cbElements and cLocks parameters
are unsigned long integers, and the handle parameter is omitted. On 16-bit Windo
ws systems, cbElements and cLocks are unsigned short integers The handle paramet
er is retained for compatibility with earlier software. For example:
typedef struct FARSTRUCT tagSAFEARRAY {
unsigned short cDims; // Count of dimensions in this array.
unsigned short fFeatures; // Flags used by the SafeArray
// routines docu
mented below.
#if defined(WIN32)
unsigned long cbElements; // Size of an element of the array.
// Does not incl
ude size of
// pointed-to da
ta.
unsigned long cLocks; // Number of times the array has been
// locked withou
t corresponding unlock.
#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;
The array rgsabound is stored with the left-most dimension in rgsabound[0] and t
he right-most dimension in rgsabound[cDims - 1]. If an array was specified in a
C-like syntax as a [2][5], it would have two elements in the rgsabound vector. E
lement 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 can affect how the arra
y 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.
18.6.3 SAFEARRAYBOUND
Represents the bounds of one dimension of the array. The lower bound of the dime
nsion is represented by lLbound, and cElements represents the number of elements
in the dimension. The structure is defined as follows:
typedef struct tagSAFEARRAYBOUND {
unsigned long cElements;
long lLbound;
} SAFEARRAYBOUND;
19. Support for Remote Debugging
The COM Library and the COM Network Protocol provide support for debugging engin
es on the client and the server side of a remote COM invocation to cooperate in
allowing the overall application to be debugged. This section describes the runt
ime infrastructure provided by the Microsoft Windows implementation of the COM L
ibrary by which that is accomplished; other implementations will provide similar
infrastructures, though in practice the details of such support will be highly
sensitive to the mechanisms by which debugging engines are supported on the give
n platform. This section also specifies the standard data formats transmitted be
tween client and server by which this cooperation is carried out.
The following a brief example of the sort of debugging session scenario which ca
n be supported with this infrastructure.
Suppose the programmer is debugging an application with is an OLE document conta
iner, and that the application is presently stopped in the debugger at a point i
n the code where the container is about to invoke a method in some interface on
one of its contained objects, the implementation of which happens to be in anoth
er executable. That is, the pointer that the container has in hand actually poin
ts to an occurrence of part of the remoting infrastructure known as an interface
proxy (see above). Interface proxies and the rest of the remoting infrastructure
are not (normally) part of the programmer s concern when debugging client and serv
er applications, as the whole raison d être of the RPC infrastructure is to be trans
parent, is to make remote object invocations appear to be local ones. Unless the
programmer is debugging the remoting infrastructure himself, this should apply
to debugging as well.
This perspective leads to some of the following scenarios that need to be suppor
table by the debugger. If the programmer Single Steps into the function invocati
on, then the debugger should next stop just inside the real implementation of th
e remote server object, having transparently passed through the RPC infrastructu
re. (Notice that before the Step command is executed, the remote process may not
presently have the debugger attached to it, and so the act of doing the step ma
y need to cause the debugger to attach itself.) The programmer will now be able
to step line by line through the server's function. When he steps past the closi
ng brace of the function, he should wind up back in the debugger of the client p
rocess immediately after the function call.
A similar scenario is one where we skip the incoming single step but instead, ou
t of the blue, hit a breakpoint in the server, then start single stepping. This,
too, should single step over the end of the server function back into the client
process. The twist is that this time, the client debugger may not presently be
running, and therefore may need to be started.
19.1 Implementation
The ability for debuggers to support scenarios such as these is provided by hook
s in the client and server side RPC infrastructure. If requested by the debugger
, at certain important times, these hooks inform the debugger of the fact that a
transmission of a remote call about to be made or that transmission of return v
alues is about to occur. That is, when the COM Library is about to make or retur
n from a call to an object, it notifies the debugger of what is happening, so th
at the debugger can take any special actions it desires.
19.1.1 DllDebugObjectRPCHook
BOOL WINAPI DllDebugObjectRPCHook(BOOL fTrace, LPORPC_INIT_ARGS lpOrpcInitArgs)
This function is to be exported by name from one or more DLLs that wish to be in
formed when from the user s point of view that debugging is engaged. Debuggers wil
l should call this function to inform each of their loaded DLLs that export this
function as to whether they are presently being debugged or not. When the debug
ger wants to enable debugging, it calls DllDebugObjectRpcHook with fTrace=TRUE and
when it wants to disable it, it calls DllDebugObjectRpcHook with fTrace=FALSE
. When enabled, debugging support such as the tracing described herein should be
enabled.
Certain of the COM Library DLLs, for example, implement this function. When debu
gging is enabled, they turn on what is here called COM remote debugging, and whi
ch is the focus of this section.
The second argument points to an ORPC_INIT_ARGS structure whose definition is gi
ven below. The pvPSN member is used only on the Macintosh, where the calling deb
ugger is required in this field to pass the process serial number of the debugge
e s process. On other systems pvPSN should be NULL.
The lpIntfOrpcDebug member is a pointer to an interface. This is used by in-proc
ess debuggers and is discussed in more detail later. Debuggers that are neither
in-process debuggers nor are Macintosh debuggers should pass NULL for lpIntfOrpc
Debug.
typedef struct ORPC_INIT_ARGS {
IOrpcDebugNotify __RPC_FAR * lpIntfOrpcDebug;
void * pvPSN;
// contains ptr to Process Serial No. for Mac COM debugging.
DWORD dwReserved1; // For f
uture use, must be 0.
DWORD dwReserved2; // For f
uture use, must be 0.
} ORPC_INIT_ARGS;
typedef ORPC_INIT_ARGS __RPC_FAR * LPORPC_INIT_ARGS;
interface IOrpcDebugNotify : IUnknown {
VOID ClientGetBufferSize(LPORPC_DBG_ALL);
VOID ClientFillBuffer(LPORPC_DBG_ALL);
VOID ClientNotify(LPORPC_DBG_ALL);
VOID ServerNotify(LPORPC_DBG_ALL);
VOID ServerGetBufferSize(LPORPC_DBG_ALL);
VOID ServerFillBuffer(LPORPC_DBG_ALL);
};
As one would expect, a debugger calls DllDebugObjectRPCHook within the context (
that is, within the process) of the relevant debuggee. Thus, the implementation
of this function most often will merely store the arguments in global DLL-specif
ic state.
Further, as this function is called from the debugger, the function can be calle
d when the DLL in which it is implemented is in pretty well any state; no synchr
onization with other internal DLL state can be relied upon. Thus, it is recommen
ded that the implementation of this function indeed do nothing more than set int
ernal global variables.
Argument Type Description
fTrace BOOL TRUE if debugging is enabled, FALSE otherwise
lpOrpcInitArgs LPORPC_INIT_ARGS typically NULL; see comments abo
ve for MAC COM debuggers or in-process debuggers.
return value BOOL TRUE if the function was successful (the DLL und
erstood and executed the request), FALSE otherwise
19.1.2 Architectural Overview
When COM remote debugging is enabled, there are a total of six notifications tha
t occur in the round-trip of one COM RPC call: three on the client side and thre
e on the server side. The overall sequence of events is as follows.
Suppose the client has an interface pointer pFoo of type IFoo* which happens to
be a proxy for another object in a remote server process.
interface IFoo : IUnknown {
HRESULT Func();
};
IFoo *pFoo;
When the client invokes pFoo->Func(), it executes code in the interface proxy. T
his code is responsible for marshaling the arguments into a buffer, calling the
server, and unmarshaling the return values. To do so, it draws on the services o
f an IRpcChannelBuffer instance with which it was initialized by the COM Library
.
To get the buffer, the interface proxy calls IRpcChannelBuffer::GetBuffer(), pas
sing in (among other things) the requested size for the buffer. Before actually
allocating the buffer, the GetBuffer() implementation (normally) checks to see if
debugging is enabled per DllDebugObjectRPCHook(). If so, then the channel calls
DebugORPCClientGetBufferSize() (see below for details) to inform the debugger that
an COM RPC call is about to take place and to ask the debugger how many bytes of
information it would like to transmit to the remote server debugger. The channel
then, unbeknownst to the interface proxy, allocates a buffer with this many addi
tional bytes in it.
The interface proxy marshals the incoming arguments in the usual way into the bu
ffer that it received, then calls IRpcChannelBuffer::SendReceive(). Immediately o
n function entry, the channel again checks to see if debugging is enabled. If so
, then it calls DebugORPCClientFillBuffer() passing in the pointer to (the debug
ger s part of) the marshaling buffer. The debugger will write some information int
o the buffer, but this need be of no concern to the channel implementation other
than that it is to ferry the contents of the buffer to the server debugger. Onc
e DebugORPCClientFillBuffer() returns, the channel implementation of SendReceive
() proceeds as in the normal case.
We now switch context in our explanation here to the server-side RPC channel. Su
ppose that it has received an incoming call request and has done what it normall
y does just up to the point where it is about to call IRpcStubBuffer::Invoke(),
which when will cause the arguments to be unmarshaled, etc. Just before calling
Invoke(), if there was any debugger information (i.e.: it exists in the incoming
request and is of non-zero size) in the incoming request or if debugging is pre
sently already enabled per DllDebugObjectRPCHook() (irrespective of the presence
or size of the debug info), then the channel is to call DebugORPCServerNotify().
The act of calling this function may in fact start a new debugger if needed and
attach it to this (the server) process; however, this need not be of concern to
the channel implementation. Having made the request, the channel proceeds to ca
ll Invoke() as in the normal case.
The implementation of Invoke() will unmarshal the incoming arguments, then call
the appropriate method on the server object. When the server object returns, Inv
oke() marshals the return values for transmission back to the client. As on the
client side, the marshaling process begins by calling IRpcChannelBuffer::GetBuffer()
to get a marshaling buffer. As on the client side, the server side channel GetB
uffer() implementation when being debugged (per the present setting of DllDebugObje
ctRPCHook(), not per the presence of the incoming debug info) asks the debugger
how many bytes it wishes to transmit back to the client debugger. The channel al
locates the buffer accordingly and returns it to the Invoke() implementation who
marshals the return values into it, then returns to its caller.
The caller of IRpcStubBuffer::Invoke() then checks to see if he is presently bei
ng debugged. If so, then he at this time calls DebugORPCServerFillBuffer(), pass
ing in the pointer to the debug-buffer that was allocated in the (last, should t
here erroneously be more than one) call to GetBuffer() made inside Invoke(); sho
uld no such call exist, and thus there is no such buffer, NULL is passed. The by
tes written into the buffer (if any) by the debugger are ferried to the client s
ide.
We now switch our explanatory context back to the client side. Eventually the cl
ient channel either receives a reply from the server containing the marshaled re
turn values (and possibly debug info), receives an error indication from the ser
ver RPC infrastructure, or decides to stop waiting. That is, eventually the clien
t channel decides that it is about to return from IRpcChannel::SendReceive(). Im
mediately before doing so, it checks to see if it is either already presently be
ing debugged or if in the reply it received any (non-zero sized) information fro
m the server debugger. If so, then it calls DebugORPCClientNotify(), passing in t
he server-debugger s info if it has any; doing so may start and attach the debugge
r if needed. The channel then returns from SendReceive().
19.1.3 Calling Convention for Notifications
The preceding discussion discussed the COM RPC debugging architecture in terms o
f six of debugger-notification APIs (DebugORPC...()). However, rather than being
actual API-entry points in a a static-linked or dynamically-linked library, the
se notifications use an somewhat unusual calling convention to communicate with
the notification implementations, which are found inside debugger products. This
somewhat strange calling convention is used for the following reasons:
Two of the six notifications need to start and attach the debugger if it is not
already attached to the relevant process.
The convention used transitions into the debugger code with the least possible d
isturbance of the debuggee s state and executing the minimal amount of debuggee co
de. This increases robustness of debugging.
The debugger is necessarily equipped to deal with concurrency issues of other th
reads executing in the same process. Therefore, it is important to transition to
the debugger as fast as possible to avoid inadvertent concurrency problems.
The actual calling convention used is by its nature inherently processor and ope
rating-system specific. On Win32 implementations, the default calling conventio
n for notifications takes the form of a software exception, which is raised by a
call to the RaiseException Win32 API:
VOID RaiseException(
DWORD dwExceptionCode, // exception code
DWORD dwExceptionFlags, // continuable exception flag
DWORD cArguments, // number of arguments in array
CONST DWORD * lpArguments // address of array of arguments
);
As used here, the arguments to this raised exception call in order are:
dwExceptionCode: An exception code EXCEPTION_ORPC_DEBUG (0x804F4C45) is used. Th
e debugger should recognize this exception as a special one indicating an COM R
PC debug notification.
dwExceptionFlags: This is zero to indicate a continuable exception.
cArguments: One
lpArguments: The array contains one argument. This argument is a pointer to a st
ructure which contains the notification specific information that the COM RPC sy
stem passes to the debugger. The definition of this structure ORPC_DBG_ALL is gi
ven below. The same structure is used for all the notifications. The structure i
s just the union of the arguments of the six debugger notification APIs. For a p
articular notification not all the fields in the structure are meaningful and th
ose that are not relevant have undefined values; details on this are below:
typedef struct ORPC_DBG_ALL {
BYTE * pSignature;
RPCOLEMESSAGE * pMessage;
const IID * iid;
void* reserved1;
void* reserved2;
void* pInterface;
IUnknown * pUnkObject;
HRESULT hresult;
void * pvBuffer;
ULONG cbBuffer;
ULONG * lpcbBuffer;
void * reserved3;
} ORPC_DBG_ALL;
The pSignature member of this structure points to a sequence of bytes which cont
ains:
a four-byte sanity-check signature of the ASCII characters MARB in increasing memo
ry order.
a 16-byte GUID indicating which notification this is. Each of the six notificati
ons defined here has a different GUID. More notifications and corresponding GUID
s can be defined in the future and be known not to collide with existing notific
ations.
a four-byte value which is reserved for future use. This value is NULL currently
.
The notifications specified here pass their arguments by filling in the appropri
ate structure members. See each notification description for details.
Using software exceptions for COM debugging notifications is inconvenient for in-
process debugging. In-process debuggers can alternately get these notifications v
ia direct calls into the debugger s code. The debugger which wants to be notified
by a direct call passes in an IOrpcDebugNotify interface in the LPORPC_INIT_ARGS a
rgument to DllDebugObjectRPCHook. If this interface pointer is available, COM ma
kes the debug notifications by calling the methods on this interface. The method
s all take an LPORPC_DBG_ALL as the only argument. The information passed in th
is structure is identical to that passed when the notification is done by raisin
g a software exception.
19.1.4 Notifications
What follows is a detailed description of each of the relevant notifications.
Note that in the network case, depending on the notification in question the byt
e order used may be different than that of the local machine. The byte order, et
c., of the incoming data is provided from the dataRep contained the passed RPCOL
EMESSAGE structure.
Though each function is documented here for purely historical reasons as if it w
ere in fact a function call, we have seen above that this is not the case. Unles
s otherwise specified, the name of the argument to the DebugORPC... notification
call is the same as the name of the structure member in ORPC_DBG_ALL used to pa
ss it to the debugger. So for example the pMessage argument of the DebugORPCClie
ntGetBufferSize notification is passed to the debugger in the pMessage structure
member of ORPC_DBG_ALL. We trust that readers will not be too confused by this,
and apologize profusely should this prove not to be the case.
19.1.4.1 DebugORPCClientGetBufferSize
ULONG DebugORPCClientGetBufferSize(pMessage, iid, reserved, pUnkProxyObject)
Called on the client side in IRpcChannel::GetBuffer().
The GUID for this notification is 9ED14F80-9673-101A-B07B-00DD01113F11
GUID __private_to_macro__ = { /* 9ED14F80-9673-101A-B07B-00DD01113F11 */
0x9ED14F80,
0x9673,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x1, 0x11, 0x3F, 0x11}
};
Argument Type Description
pMessage RPCOLEMESSAGE* identification of the method being invoked, etc.
iid REFIID contains the IID of the interface being called.
reserved void * reserved for future use.
pUnkProxyObject IUnknown * an IUnknown (no particular one) on the object in
volved in this invocation. May legally be NULL, though this reduces debugging fu
nctionality. Further, this and like-named parameters must consistently be either
NULL or non-NULL in all notifications in a given client side COM RPC implementat
ion.
return value ULONG the number of bytes that the client debugger wishes to t
ransmit to the server debugger. May legitimately be zero, which indicates that n
o information need be transmitted. The lpcbBuffer field in the ORPC_DBG_ALL stru
cture holds a pointer to a ULONG. The debugger writes the number of bytes it wan
ts to transmit with the packet in that location.
19.1.4.2 DebugORPCClientFillBuffer
void DebugORPCClientFillBuffer(pMessage, iid, reserved, pUnkProxyObject, pvBuffe
r, cbBuffer)
Called on the client side on entry to IRpcChannel::SendReceive(). See the above
overview for further details.
The GUID for this notification is DA45F3E0-9673-101A-B07B-00DD01113F11:
GUID __private_to_macro__ = { /* DA45F3E0-9673-101A-B07B-00DD01113F11 */
0xDA45F3E0,
0x9673,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x01, 0x11, 0x3F, 0x11}
};
Argument Type Description
pMessage RPCOLEMESSAGE* as in DebugORPCClientGetBufferSize().
iid REFIID as in DebugORPCClientGetBufferSize().
reserved void * as in DebugORPCClientGetBufferSize().
pUnkProxyObject IUnknown * as in DebugORPCClientGetBufferSize().
pvBuffer void * the debug-data buffer which is to be filled. Is undefine
d (may or may not be NULL) if cbBuffer is zero.
cbBuffer ULONG the size of the data pointed to by pvBuffer.
19.1.4.3 DebugORPCServerNotify
void DebugORPCServerNotify(pMessage, iid, pChannel, pInterface, pUnkObject, pvBu
ffer, cbBuffer)
Called on the server side immediately before calling IRpcStubBuffer::Invoke() to
inform it that there is an incoming request. Will start the debugger in this pr
ocess if need be. See the above overview for further details.
The GUID for this notification is 1084FA00-9674-101A-B07B-00DD01113F11:
GUID __private_to_macro__ = { /* 1084FA00-9674-101A-B07B-00DD01113F11 */
0x1084FA00,
0x9674,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x01, 0x11, 0x3F, 0x11}
};
On entry, the members of pMessage are set as follows:

Member Name Value on entry to Invoke()


reserved members indeterminate. These members are neither to be read nor
to be changed by the callee.
dataRepresentation this indicates the byte order, etc., of the client debug
ger
pvBuffer points to a buffer which contains the marshaled incoming argumen
ts. In the case that there are no such arguments (i.e.: cbBuffer == 0), pvBuffer
may be NULL, but will not necessarily be so.
cbBuffer the size in bytes of the memory buffer to which pvBuffer points.
If pvBuffer is NULL, then cbBuffer will be zero (but the converse is not necess
arily true, as was mentioned in pvBuffer).
iMethod the zero-based method number in the interface which is being invoked.
rpcFlags indeterminate. Neither to be read nor to be changed by the calle
e.
Argument Type Description
pMessage RPCOLEMESSAGE* as in IRpcStubBuffer::Invoke().
iid REFIID contains the iid of the interface being called.
pChannel IRpcChannelBuffer* as in IRpcStubBuffer::Invoke(). The COM
RPC channel implementation on the server side.
pInterface void * This contains the pointer to the COM interface instance
which contains the pointer to the method that will be invoked by this particular
remote procedure call. Debuggers can use this information in conjunction with t
he iMethod field of the pMessage structure to get to the address of the method t
o be invoked. May not be NULL.
pUnkObject IUnknown * this pointer is currently NULL. In the future th
is might be used to pass the controlling IUnknown of the server object whose met
hod is being invoked.
pvBuffer void * the pointer to the incoming debug information. Is undefi
ned (may or may not be NULL) if cbBuffer is zero.
cbBuffer ULONG the size of the data pointed to by pvBuffer. May be zero
, but as described above, a size of zero can only passed in the case that debugg
ing is already enabled.
19.1.4.4 DebugORPCServerGetBufferSize
ULONG DebugORPCServerGetBufferSize(pMessage, iid, pChannel, pInterface, pUnkObje
ct)
Called on the server side from within IRpcChannelBuffer::GetBuffer(). See the ab
ove overview for further details.
The GUID for this notification is 22080240-9674-101A-B07B-00DD01113F11:
GUID __private_to_macro__ = { /* 22080240-9674-101A-B07B-00DD01113F11 */
0x22080240,
0x9674,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x01, 0x11, 0x3F, 0x11}
};
Argument Type Description
pMessage RPCOLEMESSAGE* as in DebugORPCServerNotify().
iid REFIID as in DebugORPCServerNotify().
pChannel IRpcChannelBuffer* as in DebugORPCServerNotify().
pInterface void * as in DebugORPCServerNotify().
pUnkObject IUnknown * as in DebugORPCServerNotify().
return value ULONG the number of bytes that the client debugger wishes to t
ransmit to the server debugger. May legitimately be zero, which indicates that n
o information need be transmitted. Value is actually returned through lpcbBuffer
member of an ORPC_DBG_ALL.
19.1.4.5 DebugORPCServerFillBuffer
void DebugORPCServerFillBuffer(pMessage, iid, pChannel, pInterface, pUnkObject,
pvBuffer, cbBuffer)
Called on the server side immediately after calling IRpcStubBuffer::Invoke(). Se
e the above overview for further details.
The GUID for this notification is 2FC09500-9674-101A-B07B-00DD01113F11:
GUID __private_to_macro__ = { /* 2FC09500-9674-101A-B07B-00DD01113F11 */
0x2FC09500,
0x9674,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x01, 0x11, 0x3F, 0x11}
};
Argument Type Description
pMessage RPCOLEMESSAGE* as in DebugORPCServerNotify().
iid REFIID as in DebugORPCServerNotify().
pChannel IRpcChannelBuffer* as in DebugORPCServerNotify().
pInterface void * as in DebugORPCServerNotify().
pUnkObject IUnknown * as in DebugORPCServerNotify().
pvBuffer void * the debug-data buffer which is to be filled. Is undefine
d (may or may not be NULL) if cbBuffer is zero.
cbBuffer ULONG the size of the data pointed to by pvBuffer.
19.1.4.6 DebugORPCClientNotify
void DebugORPCClientNotify(pMessage, iid, reserved, pUnkProxyObject, hresult, p
vBuffer, cbBuffer)
Called on the client side immediately before returning from IRpcChannelBuffer::S
endReceive(). See the above overview for further details.
The GUID for this notification is 4F60E540-9674-101A-B07B-00DD01113F11:
GUID __private_to_macro__ = { /* 4F60E540-9674-101A-B07B-00DD01113F11 */
0x4F60E540,
0x9674,
0x101A,
0xB0,
0x7B,
{0x00, 0xDD, 0x01, 0x11, 0x3F, 0x11}
};
Argument Type Description
pMessage RPCOLEMESSAGE* as in DebugORPCClientGetBufferSize().
iid REFIID as in DebugORPCClientGetBufferSize().
reserved void * reserved for future use.
pUnkProxyObject IUnknown * as in DebugORPCClientGetBufferSize().
hresult HRESULT the HRESULT of the RPC call that just happened.
pvBuffer void * the pointer to the incoming debug information. Is undefi
ned (may or may not be NULL) if cbBuffer is zero.
cbBuffer ULONG the size of the data pointed to by pvBuffer.
19.1.5 Special Segments
The COM Library system DLLs have code in specially named segments (sections in C
OFF terminology) to aid debuggers. The remoting code in the COM interface proxy
and interface stub DLLs and other appropriate parts of the runtime are put in se
gments whose name begins with .orpc . These segments are henceforth referred to as
.orpc segments. A transition of the instruction pointer from a non .orpc segment
to a .orpc segment indicates that the program control is entering the RPC layer
. On the client side such a transition implies that a RPC call is about to happe
n. On the server side if a function is returning back to a .orpc segment it impl
ies that the call is going to return back to the client side. Application writer
s who write their own remoting code can also avail of this feature by putting th
eir remoting specific code in a .orpc segment.
Debuggers can use this naming convention regarding which code lies in COM RPC to
aid in their user interface as to what code they choose to show the user and wh
at code they do not. When the debugger reaches the code address after handling t
he DebugOrpcServerNotify() exception it should check if it is still in a .orpc s
egment. This implies that the instruction pointer is still in code that to the p
rogrammer is part of the local-remote transparency magic provided by COM, and so
should be skipped by the debugger.
Similar behavior on the client side after the DebugOrpcClientNotify() exception
is also desirable.
19.1.6 Registry specific information
COM RPC debuggers make use of this mechanism in order to start the debugging of
a client or server application that is not presently being debugged. A common sc
enario is that of a user wanting to step into a RPC call as she is debugging. Th
e client side debugger is notified about the RPC call and sends debugger specifi
c information with the packet. A DebugOrpcServerNotify() notification is raised
in the server process. If the server application is already being debugged, it r
ecognizes this as a COM RPC notification and handles it. However if the server a
pplication is not being debugged, the system will launch the debugger specified
in the AeDebug entry. The debugger will then get the exception notification and
handle it.
To avoid having malicious clients being able to force the debugging of a remote
server, additional safeguards are required. The COM RPC system checks that the r
egistry key DebugObjectRPCEnabled exists on the system. If this key does not exi
st, the debug notifications are disabled. Thus, debugging will only take place i
f explicit action has been taken on a given machine to enable it, and so a remot
e client cannot cause debugging (and thus denial of service) to occur on an othe
rwise secure machine.
The client side debugger should also ensure that the AeDebug\Debugger entry on i
ts machine is set appropriately.
Before sending any notification, COM sets the AeDebug\Auto entry to 1. This is d
one in order that the system does not put up a dialog box to ask the user if she
wants to debug the server application. Instead it directly launches the debugge
r.
The scenario where the user steps out of the server application into to a client
application which is not being debugged currently is symmetrically identical th
e preceding insofar as launch of the debugger is concerned.
19.1.7 Format of Debug Information
This section discusses the format of the debug information which the debugger pu
ts into the buffer in the DebugORPCClientFillBuffer and DebugORPCServerFillBuffe
r calls. The structure of this data is as follows, here specified in an IDL-like
manner. For historical reasons, this structure has 1-byte alignment of its inte
rnal members. Again, for historical reasons, the data is always transmitted in l
ittle-endian byte order.
#pragma pack(1) // this structure defined with 1-byte packing alignment
struct {
DWORD alwaysOrSometimes; // controls spawning of debugger
BYTE verMajor; // major version
BYTE verMinor; // minor version
DWORD cbRemaining; // inclusive of byte count itself
GUID guidSemantic; // semantic of this packet
[switch_is(guidSemantic)] union { // semantic specific information
// case step semantic, guid = 9CADE560-8F43-101A-B07B-00DD01113F11
BOOL fStopOnOtherSide; // should single step or not?
// case general semantic, guid = D62AEDFA-57EA-11ce-A964-00AA006C3706
USHORT wDebuggingOpCode; // should single step or not, etc.
USHORT cExtent; // offset=28
BYTE padding[2]; // offset=30, m.b.z.
[size_is(cExtent)] struct {
ULONG cb; // offset=32
GUID guidExtent; // the semantic of this extent
[size_is(cb)] BYTE *rgbData;
};
};
}
The first DWORD in the debug packet has a special meaning assigned to it. The re
st of the debug packet is treated as a stream of bytes by COM and is simply pass
ed across the channel to the debugger on the other side. If the first DWORD cont
ains the value ORPC_DEBUG_ALWAYS (this is a manifest constant defined in the hea
der files) then COM will always raise the notification on the other side (use of
the four bytes MARB is for historical reasons synonymous with use of ORPC_DEBUG_A
LWAYS). If the first DWORD in the debug packet contains the value ORPC_DEBUG_IF_
HOOK_ENABLED, then the notification is raised on the other side of the channel o
nly if COM debugging has been enabled in that context; that is only if DllDebugO
bjectRPCHook has been called in that process with fTrace = TRUE. It is the debug
ger s responsibility to include enough memory for the first DWORD in its response
to the DebugOrpcClientGetBufferSize or DebugOrpcServerGetBufferSize notifications.
The two bytes immediately following the initial DWORD contain the major and mino
r version numbers of the data format specification.
For packets in the format of the current major version, this is followed by
A DWORD which holds the count of bytes that follow in this data, and which is in
clusive of this byte count itself.
A GUID that identifies the semantic of the packet.
Semantic specific information. The layout of this information is dependent on th
e GUID that specifies the semantic. These are as follows:
Semantic Meaning
Step This semantic indicates that the single stepping is to be performed or n
ot. The GUID of this semantic is 9CADE560-8F43-101A-B07B-00DD01113F11. The data
of this semantic consists of a boolean value which indicates in the step out of a
server case whether execution should continue once the other side is reached or
one should remain stopped.
General This semantic, which has GUID D62AEDFA-57EA-11ce-A964-00AA006C3706, allo
ws for series of tagged bags of data to be passed. Each is byte counted, and has
associated with it a GUID. wDebuggingOpCode allows for one of a series of opera
tions to be specified. Existing-defined opcodes are as follows. Future opcodes a
re to be allocated by a central coordinating body.
Opcode Meaning
0x0000 No operation
0x0001 Single step, stop on the other side, as in the Step seman
tic.
Extents presently defined for use in the General semantic are as follows:
Extent Meaning
Interface pointer This semantic has GUID 53199051-57EB-11ce-A964-00AA006C3
706.
The contents of rgbData for this extent is simply an OBJREF, which is the data s
tructure which describes a marshaled interface pointer, the data that results fr
om calling CoMarshalInterface (OBREFs are described later in this specification)
. Usually, this OBJREF is either the self-enclosed LONGOBJREF variation or a cus
tom-marshaled variation, but this is not required. The LONGOBJREF usually contai
ns a reference count of zero, allowing this information to be freely discared wi
thout a leakage of state. Remember that OBJREFs are always in little-endian byte
order. An OBJREF is converted into its corresponding interface pointer using Co
UnmarshalInterface.
With the Interface Pointer extent, an object can be created in the source debugg
er s space that relates to the call being made. It can then be marshaled, again, i
n the source debugger s process, not the source debuggee; this yields an OBJREF. T
he OBJREF is then transmitted in the course of the call as an extent in the pass
ed debug information. On the destination side, it is conveyed to the destination
debugger, who unmarshals it in its process. The result is a COM remoting connec
tion from the source debuggers process to the destination debugger s process that
is semantically tied to a particular COM call that needs to be debugged. (TBD) I
nterfaces on this object can be then be used to provide stack walk-backs, remote
memory manipulation, or other debugging functionality.
Part III: Component Object Model Protocols and Services

20. Interface Definition Language


As was described previously in this specification, the COM infrastructure is com
pletely divorced from the source-level tools used to create and use COM componen
ts. COM is completely a binary specification, and thus source-level specificatio
ns and standards have no role to play in the fundamental architecture of the sys
tem.
Specifically, and somewhat different than other environments, this includes any
and all forms of interface definition language (IDL). Having an interoperable st
andard for an appropriate IDL (or any other source level tool for that matter) i
s still incredibly valuable and useful, it s just important to understand that thi
s is a tool standard and not a fundamental system standard. Contrast this, for e
xample, with the DCE RPC API specification, where, if only because the fundament
al SendReceive API is not part of the public standard runtime infrastructure, on
e must use IDL to interoperate with the system. People can (and have, out of nec
essity) built COM components with custom COM interfaces without using any interf
ace definition language at all. This clear separation of system standards from t
ools standards is an important point, for without it COM tools vendors cannot in
novate without centralizing their innovations through some central standards bod
y. Innovation is stifled, and the customers suffer a loss of valuable tools in t
he marketplace.
That all being said, as was just mentioned, source-level standards are still use
ful, and DCE IDL is one such standard. The following enhancements to DCE IDL ena
ble it to specify COM interfaces in addition to DCE RPC interfaces.
20.1 Object RPC IDL Extensions
This section needs to be updated with ODL extensions.
20.1.1 Object interface attribute
COM interfaces are signified by a new interface attribute, object . See [CAE RPC],
page 238.
<interface_attributes> ::= <interface_attribute> [ , <interface_attribute> ] ...
<interface_attribute> ::= uuid ( <Uuid_rep> )
| version ( <Integer_literal>[.Integer_literal>])
| endpoint ( <port_spec> [ ,<port_spec> ] ... )
| local
| pointer_default ( <ptr_attr> )
| object
<port_spec> ::= <Family_string> : <[> <Port_string> <]>
The object interface attribute attributed may not be specified with the version
attribute. However, it may be specified with any of the others, though the uuid
attribute is virtually always used and the local attribute is used but rarely.
If this keyword is present, the following extensions are enabled in the interfac
e.
20.1.2 Interface name as a type
The interface name becomes the name of a type, which can then be used as a param
eter in methods. For example:
[object, uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IFoo {
};
causes a typed named "IFoo" to be declared, such that a method
[object, uuid(6A874340-57EB-11ce-A964-00AA006C3706)]
interface IBar {
HRESULT M1([in] short i, [in] IFoo* pfoo);
};
is a legal declaration.
20.1.3 No handle_t required
In methods, no handle_t argument is needed, and it's absence does not indicate a
uto-binding. Instead, a this pointer is used in the C++ binding to indicate the re
mote object being referenced, and an implicit extra first argument is used in C.
For example:
[object, uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IBar {
HRESULT Bar([in] short i, [in] IFoo * pIF);
};
can be invoked from C++ with:
IFoo * pIF;
IBar * pIB;
pIB->Bar(3, pIF);
or from C with the equivalent
pIB->lpVtbl->Bar(pIB, 3, pIF);
20.1.4 Interface inheritance
Single inheritance of interfaces is supported, using the C++ notation for same.
Referring again to [CAE RPC], page 238:
<interface_header> ::=
<[> <interface_attributes> <]> interface <Identifier> [ <:> <Identifier>
]
For example:
[object, uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IBar : IWazoo {
HRESULT Bar([in] short i, [in] IFoo * pIF);
};
cases the first methods in IBar to be the methods of IWazoo.
20.1.5 iid_is and use of void*
The use of void* pointers are permitted, as long as such pointers are qualified wi
th an iid_is pointer attribute. See [CAE RPC], page 253.
<ptr_attr> ::= ref
| ptr
| iid_is ( <attr_var_list> )
| unique
The iid_is construct says that the void* parameter is an interface pointer whose
type is only known at run time, but whose interface ID is the parameter named i
n the iid_is attribute. For example:
[object, uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IBar : IWazoo {
Bar([in] short i, [in, ref] uuid_t *piid, [out, iid_is(piid)] vo
id ** ppvoid);
};
This can be invoked in C++ as:
IFoo* pIF;
pIB->Bar(i, &IID_IFoo, (void*)&pIF);
where IID_IFoo is the name of a constant whose value is the interface ID for IFoo.
20.1.6 All methods must return void or HRESULT
Asynchronous methods (and only asynchronous methods) must return void, all other
s must return HRESULT.
20.1.7 The wire_marshal attribute
typedef [wire_marshal( transmissible_type)] type_specifier user_type
;
This attribute is a type attribute used in the IDL file and is somewhat similar
in syntax and semantic to the transmit_as attribute. Each user-specific type has
a corresponding transmissible type that defines the wire representation.
The user can define his specific type quite freely, (simple types, pointer types
and composite types may be used) although some restrictions apply. The main one
is that the type object needs to have well defined (fixed) memory size. If the
changeable size needs to be accommodated, the type should have a pointer field a
s opposed to a conformant array; or, it can be a pointer to the interesting type
. General restrictions apply as usual. Specific restrictions related to embeddin
g affect the way types can be specified. For more information see the User type v
s. wire type section.
The [wire_marshal] attribute cannot be used with [allocate()] attribute (directl
y or indirectly) as the engine doesn t control the memory allocation for the type.
Also the wire type cannot be an interface pointer (these may be marshaled direc
tly) or a full pointer (we cannot take care of the aliasing).
The following is a list of additional points regarding wire_marshal:
The wire type cannot be an interface pointer.
The wire type cannot be a full pointer.
The wire type cannot have allocate attribute on it (like [allocate(all_nodes)]).
The wire type has to have a well defined memory size (cannot be a conformant str
ucture etc.) as we allocate the top level object for the user as usual.
When the engine delegates responsibility for a wire_marshalable type to the user
supplied routines, everything is up to the user including servicing of the pos
sible embedded types that are defined with wire_marshal, user_marshal, transmit_
as etc.
wire_marshal is mutually exclusive with user_marshal, transmit_as or represent_a
s when applied to the same type.
Two different user types cannot resolve to the same wire type and vice versa.
The user type may or may not be rpc-able.
The user type must be known to MIDL.
20.1.8 The user_marshal attribute
typedef [user_marshal( user_type)] transmissible_type;
This attribute is a type attribute used in the ACF file and is somewhat similar
in syntax and semantic to the represent_as attribute. Each user-specific type ha
s a corresponding transmissible type that defines the wire representation. Simil
ar to represent_as, in the generated files, each usage of the trasmissible_type
name is substituted by the user_type name.
The user can define his specific type quite freely, (simple types, pointer types
and composite types may be used) although some restrictions apply. The main one
is that the type object needs to have well defined (fixed) memory size. If the
changeable size needs to be accommodated, the type should have a pointer field a
s opposed to a conformant array; or, it can be a pointer to the interesting type
. General restrictions apply as usual. Specific restrictions related to embeddin
g affect the way types can be specified. For more information see the User type v
s. wire type section.
The [user_marshal] attribute cannot be used with [allocate()] attribute (directl
y or indirectly) as the engine doesn t control the memory allocation for the type.
Also the wire type cannot be an interface pointer (these may be marshaled direc
tly) or a full pointer (we cannot take care of the aliasing).
Additional points regarding user_marshal:
The wire type cannot be an interface pointer.
The wire type cannot be a full pointer.
The wire type cannot have allocate attribute on it (like [allocate(all_nodes)]).
The wire type has to have a well defined memory size (cannot be a conformant str
ucture etc.) as we allocate the top level object for the user as usual.
When the engine delegates responsibility for a user_marshalable type to the user
supplied routines, everything is up to the user including servicing of the pos
sible embedded types that are defined with user_marshal, transmit_as etc.
user_marshal is mutually exclusive with wire_marshal, transmit_as or represent_a
s when applied to the same type.
Two different wire types cannot resolve to the same user type and vice versa.
The user type may or may not be rpc-able.
The user type may or may not be known to MIDL.
20.1.9 User supplied routines
The routines required by user_marshall have the following prototypes.
<type_name> means a user specific type name. This may be non-rpcable type or eve
n, when used with user_marshal, a type unknown to MIDL at all. The wire type nam
e (the name of transmissible type) is not used here.
unsigned long __RPC_USER <type_name>_UserSize(
unsigned long __RPC_FAR * pFlags,
unsigned long StartingSize,
<type_name> __RPC_FAR * pFoo);
unsigned char __RPC_FAR * __RPC_USER <type_name>_UserMarshal(
unsigned long __RPC_FAR * pFlags,
unsigned char __RPC_FAR * Buffer,
<type_name> __RPC_FAR * pFoo);
unsigned char __RPC_FAR * __RPC_USER <type_name>_UserUnmarshal(
unsigned long __RPC_FAR * pFlags,
unsigned char __RPC_FAR * Buffer,
<type_name> __RPC_FAR * pFoo);
void __RPC_USER <type_name>_UserFree(
unsigned long __RPC_FAR * pFlags,
<type_name> __RPC_FAR * pFoo );
The meaning of the arguments is as follows:
pFlags - pointer to a flag ulong. Flags: local call flag, data rep flag
.
pBuffer - the current buffer pointer,
pFoo - pointer to a user type object
StartingSize - the buffer size (offset) before the object
The return value when sizing, marshaling or unmarshaling is the new offset or bu
ffer position. See the function description below for details.
The flags pointed to by the first argument have the following layout.
31 24 16 8 4 0
Floating point Int Char MSHCTX flags

Ndr data representation Marshal context flags

Upper word: NDR representation flags as defined by DCE: floating point, endianes
s and character representations.
Lower word: marshaling context flags as defined by the COM channel. The flags ar
e defined in the public wtypes.h file (and in wtypes.idl file). Currently the fo
llowing flags are defined:
typedef
enum tagMSHCTX
{ MSHCTX_LOCAL = 0,
MSHCTX_NOSHAREDMEM = 1,
MSHCTX_DIFFERENTMACHINE = 2,
MSHCTX_INPROC = 3
} MSHCTX;
The flags make it possible to differ the behavior of the routines depending on t
he context for the RPC call. For example when a handle is remoted in-process it
could be sent as a handle (a long), while sending it remotely would mean sending
the data related to the handle.
20.1.9.1 _UserSize
The *_UserSize routine is called when sizing the RPC data buffer before the mars
haling on the client or server side. The routine should work in terms of cumula
tive size. The StartingSize argument is the current buffer offset . The routine
should return the cumulative size that includes the possible padding and then th
e data size. The starting size indicates the buffer offset for the user object a
nd it may or may not be aligned properly. User s routine should account for all pa
dding as necessary. In other words, the routine should return a new offset, afte
r the user object. The sizing routine is not called if the wire size can be comp
uted at the compile time. Note that for most unions, even if there are no pointe
rs, the actual size of the wire representation may be determined only at the run
time.
This routine actually can return an overestimate as long as the marshaling routi
ne does not use more than the sizing routine promised and so the marshaling buff
er is not overwritten then or later (by subsequent objects).
20.1.9.2 _UserMarsahal
The *_UserMarshal routine is called when marshaling the data on the client or se
rver side. The buffer pointer may or may not be aligned upon the entry. The rout
ine should align the buffer pointer appropriately, marshal the data and then ret
urn the new buffer pointer position which is at the first free byte after the mars
haled object. For the complications related to pointees see the next chapter.
Please note that the wire type specification is a contract that determines the a
ctual layout of the data in the buffer. For example, if the conversion is needed
and done by the NDR engine, it follows from the wire type definitions how much
data would be processed in the buffer for the type.
20.1.9.3 _UserUnmarshal
The *_UserUnmarshal routine is called when unmarshaling the data on the client o
r server side. The flags indicate if data conversion is needed (if needed, it ha
s been performed by the NDR engine before the call to the routine). The buffer p
ointer may or may not be aligned upon the entry. The routine should align the bu
ffer as appropriate, unmarshal the data and then return the new buffer pointer p
osition, which is at the first free byte after the unmarshaled object. For the com
plications related to pointees see the next chapter
20.1.9.4 _UserFree
The *_UserFree routine is called when freeing the data on the server side. The o
bject itself doesn t get freed as the engine takes care of it. The user shall free
the pointees of the top level objects.
20.1.10 The library keyword
[attributes] library libname {definitions};
The library keyword indicates that a type library (See Chapter 17) should be gen
erated. Below is an example library section.
[
uuid(3C591B22-1F13-101B-B826-00DD01103DE1), // IID_ISome
object
]
interface ISome : IUnknown
{
HRESULT DoSomething(void);
}
[
uuid(3C591B20-1F13-101B-B826-00DD01103DE1), // LIBID_Lines
helpstring("Lines 1.0 Type Library"),
lcid(0x0409),
version(1.0)
]
library Lines
{
importlib("stdole.tlb");
[
uuid(3C591B21-1F13-101B-B826-00DD01103DE1), // CLSID_Lines
helpstring("Lines Class"),
appobject
]
coclass Lines
{
[default] interface ISome;
interface IDispatch;
}
}
20.2 Mapping from ORPC IDL to DCE RPC IDL.
From the above extensions, and the wire representation definitions, one can conc
lude the following rules for converting ORPC IDL files to DCE IDL files:
Remove the [object] attribute from the interface definition.
Insert [in] handle_t h as the first argument of each method, [in] ORPCTHIS *_orpcth
is as the second, and [out] ORPCTHAT *_orpcthat as the third.
Manually insert declarations for the operations that were inherited, if any. You
may want to make the method names unique, unless the EPV invocation style is al
ways going to be used. One way to do this is to prefix each method with the nam
e of the interface. (Note that the IUnknown methods will never be called, as the
IRemUnknown interface is used instead.)
Replace each occurrence of a type name derived from an interface name, or an [ii
d_is] qualified void* with OBJREF. Remove [iid_is] attributes.
20.2.1 An Example
20.2.1.1 Object RPC Style
[object, uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IFoo: IUnknown
{
HRESULT Bar([in] short i, [in] IBozo* pIB, [out] IWaz** ppIW);
HRESULT Zork([in, ref] UUID* iid, [out, iid_is(iid)] void** ppvoid);
};
20.2.1.2 DCE style
[uuid(b5483f00-4f6c-101b-a1c7-00aa00389acb)]
interface IFoo
{
HRESULT IFoo_QueryInterface([in] handle_t h, [in] ORPCTHIS* _orpcthis, [
out] ORPCTHAT* _orpcthat, [in, ref] UUID* iid, [out] OBJREF** ppOR);
ULONG IFoo_AddRef([in] handle_t, [in] ORPCTHIS* _orpcthis, [out] ORPCTHA
T* _orpcthat);
ULONG IFoo_Release([in] handle_t, [in] ORPCTHIS* _orpcthis, [out] ORPCTH
AT* _orpcthat);
HRESULT IFoo_Bar([in] handle_t h, [in] ORPCTHIS* _orpcthis, [out] ORPCTH
AT* _orpcthat, [in, ref] OBJREF* porIB, [out, ref] OBJREF** pporIW);
HRESULT IFoo_Zork([in]handle_t h, [in] ORPCTHIS* _orpcthis, [out] ORPCTH
AT* _orpcthat, [in, ref] UUID* iid, [out] OBJREF** ppvoid);
};
See Chapter 21 Component Object Model Protocol for information on the ORPCTHIS a
nd ORPCTHAT structures and the IRemUnknown interface.
21. Component Object Model Protocol
The Distributed Component Object Model protocol (DCOM) is an application-level p
rotocol for object-oriented remote procedure calls and is thus also called "Obje
ct RPC" or ORPC. The protocol consists of a set of extensions, layered on the di
stributed computing environment (DCE) RPC specification [CAE RPC], with which fa
miliarity is assumed. Familiarity is also assumed with the COM (Component Object
Model) specification [COM].
Object RPC specifies:
How calls are made on an object
How object references are represented, communicated, and maintained
21.1 Purpose
There is a natural tendency in a networked environment to create entirely new ap
plication-level protocols as each new or seemingly unique combination of client,
user agent, and server requirement arises.
While in many situations the definition of a new protocol is useful and justifia
ble, there are numerous features which have eventually been added to or required
from each new protocol (or which become layered above them) as they evolve and
become used in broader contexts.
A design goal of the DCOM protocol is the inherent support of standard features
required by any distributed application communication protocol. In other words,
to act as a framework to facilitate the construction of task-specific communicat
ion paths between distributed applications.
Data Marshaling
A common occurrence among user agents using the HTTP protocol today is the use o
f complex, task-specific Query URL syntax and HTTP POSTs. Also increasingly comm
on is the POSTing and response with custom MIME types to and from resources whic
h interpret the format and reply in same. While workable, there are drawbacks to
this approach including increased complexity and work to produce and consume ea
ch new (and unique) format in the client and server, lessened ability to build t
ask-specific firewalls for administration and security purposes, and in many cas
es definition of platform-centric formats.
DCOM utilizes the Network Data Representation (NDR) for arbitrary data types sup
ported by DCE RPC.
Security
DCOM leverages the authentication, authorization, and message integrity capabili
ties of DCE RPC. An implementation may support any level of DCE RPC security. An
y connection or call can be made as secure or as insecure as negotiated by the c
lient and the server.
Safe Non-Coordinated Versioning of Interfaces
In DCOM versioning of interfaces is done through identifiers which are universal
ly unique (UUID's). To version a published interface, a new interface is defined
with a different UUID to the updated specification. Multiple parties can simult
aneously introduce "revisions" to interfaces by defining related but distinct in
terfaces without fear of colliding with each other's version numbers and without
fear of breaking each other's down-level or up-level clients.
To date, the bulk of task-specific protocols (such as custom POSTs or MIME types
using HTTP) have little or no concept of versioning at all, and simply "narrow"
the incompatibility window by updating clients (typically pages which are being
downloaded anyway) and servers (CGI scripts or other HTTP server infrastructure
) simultaneously.
21.2 Overall Operation
The Object RPC protocol highly leverages the OSF DCE RPC network protocol (see t
he reference [CAE RPC]). This leverage occurs at both the specification level an
d the implementation level: the bulk of the implementation effort involved in im
plementing the DCOM network protocol is in fact that of implementing the DCE RPC
network protocol on which it is built.
21.2.1 Object Calls
An actual COM network remote procedure call (hereinafter referred to as "an ORPC
") is in fact a true DCE remote procedure call (herein termed "a DCE RPC"), a "R
equest PDU" conforming to the specification for such calls per [CAE RPC].
In an ORPC, the object ID field of the invocation header as specified in [CAE RP
C] contains an "IPID". An IPID is a 128-bit identifier known as an interface poi
nter identifier which represents a particular interface on a particular object i
n a particular server. As it is passed in the object ID fields of a DCE RPC, the
static type of an IPID is in fact a UUID. However, IPIDs are scoped not globall
y but rather only relative to the server process which originally allocated th
em; IPIDs do not necessarily use the standard UUID allocation algorithm, but rat
her may use a machine-specific algorithm which can assist with dispatching.
In an ORPC, the interface ID field of the RPC header specifies the IID, and argu
ments are found in the body, as usual. However, when viewed from the DCE RPC per
spective an additional first argument is always present that is absent in the co
rresponding COM interface specification. This argument is of type ORPCTHIS, whic
h is described in Section 3.7. It is placed first in the body of the Request PDU
, before the actual arguments of the ORPC.
It is specifically legal for an ORPC to attempt a call a method number on a give
n interface which is beyond the number of methods believed by the server to be i
n that interface. Such calls should cause a fault.
Similarly, in a reply to an ORPC (a DCE RPC "Response PDU"), when viewed from th
e DCE RPC perspective, an additional first return value is always present that i
s absent in the corresponding COM interface specification. This argument is of t
ype ORPCTHAT, which is described in Section 3.8. It is placed first in the body
of the Response PDU, before the actual return values of the ORPC.
An ORPCTHAT may also be present in a "Fault PDU." In the Connectionless (CL) Fau
lt PDU, it is placed four bytes after the 32- bit fault code which normally comp
rises the entire body of the PDU, thus achieving eight byte alignment for the OR
PCTHAT; the intervening padding bytes are presently reserved and must be zero. T
he PDU body length is of course set to encompass the entire body of the Fault PD
U, including the ORPCTHAT. In the Connection- Oriented (CO) Fault PDU, the ORPCT
HAT is placed in the standard location allocated for the "stub data." In a Fault
PDU of either form that results from an ORPC, if an ORPCTHAT is not present the
n no other data may be substituted in its here-specified location in the PDU.
21.2.2 OXIDs and Object Exporters
Although an IPID from a logical perspective semantically determines the server,
object and interface to which a particular call should be directed, it does not
by itself indicate the binding information necessary to actually carry out an in
vocation.
The protocol represents this "how-to" communication information in a 64-bit val
ue called an object exporter identifier, otherwise known as an OXID. Conceptuall
y, an OXID can be thought of as an implementation scope for an object, which may
be a whole machine, a given process, a thread within that process, or other mor
e esoteric implementation scope, but the exact definition of such scopes has no
bearing on the protocol itself. Data structures in each Object Exporter keep tra
ck of the IPIDs exported and imported by that Object Exporter.
A given machine at any moment may support several OXIDs; however there is always
a unique OXID Resolver service per machine which coordinates the management o
f all the OXIDs on the machine. The OXID Resolver typically (but not necessaril
y) resides at well-known ports (or endpoints, depending on your terminology -- o
ne per protocol, of course) on the machine. It supports a DCE RPC interface know
n as IOXIDResolver, described in Section 5.2.
An OXID is used to determine the RPC string bindings that allow calls to reach t
heir target IPID. Before making a call, the calling process must translate an OX
ID into a set of bindings that the underlying RPC implementation understands. It
accomplishes this by maintaining a cache of these mappings. When the destinatio
n application receives an object reference, it checks to see if it recognizes th
e OXID. If it does not, then it asks the OXID Resolver which scopes the OXID sp
ecified in the object reference for the translation, and saves the resulting se
t of string bindings in a local table that maps OXIDs to string bindings.
Associated with each OXID (ie each Object Exporter) is COM object termed an "OXI
D object." OXID objects implement (at least) the IRemUnknown interface, a COM i
nterface through which remote management of reference counts and requests for in
terfaces are returned.
21.2.3 Marshaled Interface References
The DCOM protocol extends the Network Data Representation (NDR) standard specifi
ed in [CAE RPC] by defining what can be thought of as a new primitive data type
that can be marshaled: that of an interface reference to an object. This is the
only extension to NDR made by the DCOM protocol.
A marshaled interface reference is described by a type known as an OBJREF, which
is described in detail in Section 3.3. An OBJREF in actuality has several varia
tions:
NULL
This is a reference to no object.
STANDARD
A standard remote reference. Known as a STDOBJREF. A STDOBJREF contains:
An IPID, which uniquely specifies the interface and object.
An object ID (OID), which uniquely specifies the identity of the object on which
the IPID is found (scoped to the object exporter with which the object is assoc
iated).
An OXID, which identifies the scope where the implementation of the object is ac
tive, and can be used to reach the interface pointer.
A reference count, indicating the number of references to this IPID that are con
veyed by this marshaling. This count, though typically a value of one, may in fa
ct be zero, one, or more (see the next section).
Some flags, explained later.
CUSTOM
Contains a class ID (CLSID) and class-specific information.
The Custom format gives an object control over the representation of references
to itself. For example, an immutable object might be passed by value, in which c
ase the class-specific information would contain the object's immutable data.
HANDLER
A sub-case of the custom reference in which the class- specific information is s
tandardized.
For example, an object wishes to be represented in client address spaces by a pr
oxy object that caches state. In this case, the class-specific information is ju
st a standard reference to an interface pointer that the handler (proxy object)
will use to communicate with the original object.
Interface references are always marshaled in little-endian byte order, irrespect
ive of the byte order prevailing in the remainder of the data being marshaled.
21.2.4 Reference Counting
In the DCOM protocol, remote reference counting is conducted per interface (per
IPID).
The actual increment and decrement calls are carried out using (respectively) th
e RemAddRef and RemRelease methods in a COM interface known as IRemUnknown found
on the OXID object associated with each OXID, the IPID of which is returned fr
om the function IOXIDResolver::ResolveOxid (section 5.2.1). In contrast to their
analogues in IUnknown, RemAddRef and RemRelease can in one call increment or de
crement the reference count of many different IPIDs by an arbitrary amount; this
allows for greater network efficiency. In the interests of performance, client
COM implementations typically do not immediately translate each local AddRef and
Release into a remote RemAddRef and RemRelease. Rather, the actual remote relea
se of all interfaces on an object is typically deferred until all local referenc
es to all interfaces on that object have been released. Further, one actual remo
te reference count may be used to service many local reference counts; that is,
the client infrastructure may multiplex zero or more local references to an inte
rface into zero or one remote references on the actual IPID.
To prevent a malicious application from calling RemRelease incorrectly, an appli
cation may request secure references. In that case the application must call Re
mAddRef (and RemRelease later on) securely and must request private references.
Private references are stored by client identity so one client cannot release a
nother client s references. DCOM requires that each client make a call to get his
own secure references, rather then receiving a secure reference from someone who
already has one. This reduces the efficiency of interface marshalling because
the client must make a callback.
21.2.5 Pinging
The above reference counting scheme would be entirely adequate on its own if cli
ents never terminated abnormally, but in fact they do, and the system needs to b
e robust in the face of clients terminating abnormally when they hold remote ref
erences. In a DCE RPC, one typically addresses this issue through the use of con
text handles. Context handles are not used, however, by the DCOM protocol, for r
easons of expense. The basic underlying technology used in virtually all protoco
ls for detecting remote abnormal termination is that of periodic pings. Naive us
e of RPC context handles would result in per object per client process pings bei
ng sent to the server. The DCOM protocol includes a pinging infrastructure to si
gnificantly reduce network traffic by relying on the client OXID Resolver imple
mentation to do local management of client liveness detection, and having the ac
tual pings be sent only on a machine by machine basis.
Pinging is carried out on a per-object (per OID), not a per- interface (per-IPID
) basis. Architecturally, at its server machine, each exported object (each expo
rted OID) has associated with it a pingPeriod time value and a numPingsToTimeOut
count which together (through their product) determine the overall amount of ti
me known as the "ping period" that must elapse without receiving a ping on that
OID before all the remote references to IPIDs associated with that OID can be co
nsidered to have expired. Once expiration has occurred, the interfaces behind th
e IPIDs can, as would be expected, be reclaimed solely on the basis of local kno
wledge, though the timeliness with which this is carried out, if at all, is impl
ementation specific detail of the server. If the server COM infrastructure defer
s such garbage collection in this situation (perhaps because it has local refere
nces keeping the interface pointer alive) and it later hears a ping , then it kn
ows a network partition healed. It can consider the extant remote references to
be reactivated and can continue remote operations.
When interface pointers are conveyed from one client to another, such as being p
assed as either [in] or [out] parameters to a call, the interface pointer is mar
shaled in one client and unmarshaled in the other. In order to successfully unma
rshal the interface, the destination client must obtain at least one reference c
ount on the interface. This is usually accomplished by passing in the marshaled
interface STDOBJREF a cPublicRefs of (at least) one; the destination client then
takes ownership of that many (more) reference counts to the indicated IPID, and
the source client then owns that many fewer reference counts on the IPID. It is
legal, however, for zero reference counts to be passed in the STDOBJREF; here,
the destination client must (if it does not already have access to that IPID and
thus have a non-zero reference count for it) before it successfully unmarshals
the interface reference (concretely, e.g., before CoUnmarshalInterface returns)
call to the object exporter using IRemUnknown::RemAddRef to obtain a reference c
ount for it. If the destination client is in fact the object's server, then spec
ial processing is required by the destination client. The remote reference count
s being passed to it should, in effect, be "taken out of circulation," as what w
here heretofore remote references are being converted into local references. Thu
s, the reference counts present in the STDOBJREF are in fact decremented from th
e remote reference count for the IPID in question.
Some objects have a usage model such that they do not need to be pinged at all;
such objects are indicated by the presence of a flag in a STDOBJREF to an interf
ace on the object. Objects which are not pinged in fact need not be reference co
unted either, though it is legal (but pointless) for a client to reference count
the IPIDs of such objects.
For all other objects, assuming a non-zero ping period, it is the responsibility
of the holder of an interface reference on some object to ensure that pings rea
ch the server frequently enough to prevent expiration of the object. The frequen
cy used by a client depends on the ping period, the reliability of the channel b
etween the client and the server, and the probability of failure (no pings getti
ng through and possible premature garbage-collection) that the client is willing
to tolerate. The ping packet and / or its reply may both request changes to the
ping period. Through this mechanism, network traffic may be reduced in the face
of slow links to busy servers.
21.2.5.1 Delta Pinging
Without any further refinements, ping messages could be quite hefty. If machine
A held 1024 remote object references (OIDs) on machine B, then it would send 16K
byte ping messages. This would be annoying if the set of remote objects was rel
atively stable and the ping messages were the same from ping to ping.
The delta mechanism reduces the size of ping messages. It uses a ping-set interf
ace that allows the pinging of a single set to replace the pinging of multiple O
IDs.
Instead of pinging each OID, the client defines a set. Each ping contains only t
he set id and the list of additions and subtractions to the set. Objects that co
me and go within one ping period are removed from the set without ever having be
en added.
The pinging protocol is carried out using two methods in the (DCE RPC) interface
IOXIDResolver on the OXID Resolver: ComplexPing, and SimplePing. ComplexPing is
used by clients to group the set of OIDs that they must ping into sets known t
o the server. These entire sets of OIDs can then be subsequently pinged with a s
ingle, short, call to SimplePing.
21.2.6 QueryInterface
The IRemUnknown interface on the OXID object, in addition to servicing reference
counting as described above also services QueryInterface calls for remote clien
ts for IPIDs managed by that object exporter. IRemUnknown::RemQueryInterface dif
fers from IUnknown::QueryInterface in much the same way as RemAddRef and RemRele
ase differ from AddRef and Release, in that it is optimized for network access b
y being able to retrieve many interfaces at once.
21.2.7 Causality ID
Each ORPC carries with it a UUID known as the causality id that connects togethe
r the chain of ORPC calls that are causally related. If an outgoing ORPC is made
while servicing an incoming ORPC, the outgoing call is to have the same causali
ty id as the incoming call. If an outgoing ORPC is made while not servicing an i
ncoming ORPC, then a new causality id is allocated for it.
Causality ids may in theory be reused as soon as it is certain that no transitiv
ely outstanding call is still in progress which uses that call. In practice, how
ever, in the face of transitive calls and the possibility of network failures in
the middle of such call chains, it is difficult to know for certain when this o
ccurs. Thus, pragmatically, causality ids are not reusable.
The causality id can be used by servers to understand when blocking or deferring
an incoming call (supported in some COM server programming models) is very high
ly probable to cause a deadlock, and thus should be avoided.
The causality id for maybe, idempotent, and broadcast calls must be set to null
(e.g., all zeros). If a server makes a ORPC call while processing such a call, a
new causality id must be generated as if it were a top level call.
21.3 Data Types and Structures
This following several sections present the technical details of the DCOM protoc
ol.
21.3.1 DCE Packet Headers
Object RPC sits entirely on top of DCE RPC. The following list describes the ele
ments of ORPC that are specified above and beyond DCE RPC.
The object id field of the header must contain the IPID.
The interface id of the RPC header must contain the IID, even though it is not n
eeded given the IPID. This allows ORPC to sit on top of DCE RPC. An unmodified D
CE RPC implementation will correctly dispatch based on IID and IPID. An optimize
d RPC need only dispatch based on IPID.
An IPID uniquely identifies a particular interface on a particular object on a m
achine. The converse is not true; a particular interface on a particular object
may be represented by multiple IPIDs. IPIDs are unique on their OXID. IPIDs may
be reused, however reuse of IPIDs should be avoided.
Datagram, maybe, and idempotent calls are all allowed in ORPC.
Interface pointers may not be passed on maybe or idempotent calls.
Datagram broadcasts are not allowed in ORPC.
Faults are returned in the stub fault field of the DCE RPC fault packet. Any 32
bit value may be returned. Only RPC_E_VERSION_MISMATCH is pre-specified:
DCE RPC cancel is supported.
All interface version numbers must be 0.0.
21.3.2 ORPC Base Definitions
There are several fundamental data types and structures on which the COM network
protocol is built. These types are shown here in standard C header format.
////////////////////////////////////////////////////////////
// Basic Definitions
////////////////////////////////////////////////////////////
typedef unsiged long HRESULT; // 32-bit integer: success/failure
typedef t_uuid UUID; // rename DCE-RPC type
typedef UUID GUID; // Globally Unique IDentifier
typedef unsigned hyper ID; // 64-bit integer
typedef ID OXID; // Object Exporter Identifier
typedef ID OID; // Object Identifer
typedef ID SETID; // Ping Set Identifier
typedef GUID IPID; // Interface Pointer Identifier
typedef GUID* REFIPID;
typedef GUID CID; // Causality Identifier
#define CID_NULL uuid_null; // All zeros
//////////////////////////////////////////////////////////////////
// ORPC Call Packet Format
//////////////////////////////////////////////////////////////////
const unsigned short COM_MAJOR_VERSION = 5;
const unsigned short COM_MINOR_VERSION = 1;
typedef struct tagCOMVERSION {
unsigned short MajorVersion; // Major version number
unsigned short MinorVersion; // Minor version number
} COMVERSION;
const unsigned long ORPCF_NULL = 0; // no additional info
// in packet
const unsigned long ORPCF_LOCAL = 1; // call is local to this
// machine
const unsigned long ORPCF_RESERVED1 = 2; // reserved for local use
const unsigned long ORPCF_RESERVED2 = 4; // reserved for local use
const unsigned long ORPCF_RESERVED3 = 8; // reserved for local use
const unsigned long ORPCF_RESERVED4 = 16; // reserved for local use
// Extension to implicit parameters.
typedef struct tagORPC_EXTENT {
GUID id; // Extension identifier
unsigned long size; // Extension size
byte data[]; // [size_is((size+7)&~7)]
} ORPC_EXTENT;
// Array of extensions.
typedef struct tagORPC_EXTENT_ARRAY {
unsigned long size; // Num extents.
unsigned long reserved; // Must be zero.
ORPC_EXTENT** extent; // [size_is((size+1)&~1), unique]
} ORPC_EXTENT_ARRAY;
// implicit 'this' pointer which is the first [in] parameter on
// every ORPC call.
typedef struct tagORPCTHIS {
COMVERSION version; // COM version number
unsigned long flags; // ORPCF flags for presence of
// other data
unsigned long reserved1; // set to zero
CID cid; // causality id of caller
ORPC_EXTENT_ARRAY* extensions; // [unique] extensions
} ORPCTHIS;
// implicit 'that' pointer which is the first [out] parameter on
// every ORPC call.
typedef struct tagORPCTHAT {
unsigned long flags; // ORPCF flags for presence
// of other data
ORPC_EXTENT_ARRAY *extensions; // [unique] extensions
} ORPCTHAT;
//////////////////////////////////////////////////////////////////
// Marshaled COM Interface Wire Format
//////////////////////////////////////////////////////////////////
typedef enum tagMSHLFLAGS {
MSHLFLAGS_NORMAL = 0,
MSHLFLAGS_TABLESTRONG = 1,
MSHLFLAGS_TABLEWEAK = 2,
} MSHLFLAGS;
// Tower IDs for common protocols
const unsigned short NCADG_IP_UDP = 0x08;
const unsigned short NCACN_IP_TCP = 0x07;
const unsigned short NCADG_IPX = 0x0E;
const unsigned short NCACN_SPX = 0x0C;
const unsigned short NCACN_NB_NB = 0x12;
const unsigned short NCACN_NB_IPX = 0x0D;
const unsigned short NCACN_DNET_NSP = 0x04;
const unsigned short NCALRPC = 0x10;
// This is the return type for arrays of string bindings or protseqs
// used by many ORPC interfaces.
typedef struct tagSTRINGBINDING {
unsigned short wTowerId; // Cannot be zero.
unsigned short aNetworkAddr; // Zero terminated.
} STRINGBINDING;
// this value (invalid in DCE RPC) indicates to use default authz
const unsigned short COM_C_AUTHZ_NONE = 0xffff;
typedef struct tagSECURITYBINDING {
unsigned short wAuthnSvc; // Must not be zero
unsigned short wAuthzSvc; // Must not be zero
unsigned short aPrincName; // NULL terminated
} SECURITYBINDING;
// DUALSTRINGARRAYS are the return type for arrays of network
// addresses, arrays of endpoints and arrays of both used in
// many ORPC interfaces
typedef struct tagDUALSTRINGARRAY {
unsigned short wNumEntries; // # of entries in array
unsigned short wSecurityOffset; // Offset of security info
// The array contains two parts, a set of STRINGBINDINGs
// and a set of SECURITYBINDINGs. Each set is terminated by an
// extra zero. The shortest array contains four zeros.
unsigned short aStringArray[]; // [size_is(wNumEntries)]
} DUALSTRINGARRAY;
// arbitrary value to help ensure validity
const unsigned long OBJREF_SIGNATURE = 0x574f454d;
const unsigned long OBJREF_STANDARD = 0x1;
const unsigned long OBJREF_HANDLER = 0x2;
const unsigned long OBJREF_CUSTOM = 0x4;
// Flag values for a STDOBJREF (standard part of an OBJREF).
// SORF_OXRES1 - SORF_OXRES8 are reserved for the object exporters
// use only, object importers must ignore them and must not enforce
// MBZ.
const unsigned long SORF_NULL = 0x0000; // convenient for init
const unsigned long SORF_OXRES1 = 0x0001; // reserved by exporter
const unsigned long SORF_OXRES2 = 0x0020; // reserved by exporter
const unsigned long SORF_OXRES3 = 0x0040; // reserved by exporter
const unsigned long SORF_OXRES4 = 0x0080; // reserved by exporter
const unsigned long SORF_OXRES5 = 0x0100; // reserved by exporter
const unsigned long SORF_OXRES6 = 0x0200; // reserved by exporter
const unsigned long SORF_OXRES7 = 0x0400; // reserved by exporter
const unsigned long SORF_OXRES8 = 0x0800; // reserved by exporter
const unsigned long SORF_NOPING = 0x1000; // Pinging not required
typedef struct tagSTDOBJREF {
unsigned long flags; // SORF_ flags (see above)
unsigned long cPublicRefs; // count of references passed
OXID oxid; // oxid of server with this oid
OID oid; // oid of object with this ipid
IPID ipid; // ipid of Interface
} STDOBJREF;
// although this structure is conformant, it is always marshaled
// in little-endian byte-order.
typedef struct tagOBJREF {
unsigned long signature; // must be OBJREF_SIGNATURE
unsigned long flags; // OBJREF flags (see above)
GUID iid; // interface identifier
union { // [switch_is(flags), switch_type(unsigned long)]
struct { // [case(OBJREF_STANDARD)]
STDOBJREF std; // standard objref
DUALSTRINGARRAY saResAddr; // resolver address
} u_standard;
struct { // [case(OBJREF_HANDLER)]
STDOBJREF std; // standard objref
CLSID clsid; // Clsid of handler code
DUALSTRINGARRAY saResAddr; // resolver address
} u_handler;
struct { // [case(OBJREF_CUSTOM)]
CLSID clsid; // Clsid of unmarshaling code
unsigned long cbExtension; // size of extension data
unsigned long size; // size of data that follows
byte *pData; // extension +
// class specific data
// [size_is(size), ref]
} u_custom;
} u_objref;
} OBJREF;
// wire representation of a marshalled interface pointer,
// always the little-endian form of an OBJREF
typedef struct tagMInterfacePointer {
ULONG ulCntData; // size of data
byte abData[]; // [size_is(ulCntData)] data
} MInterfacePointer, *PMInterfacePointer;
// OXID Resolver information associated with each OXID.
typedef struct tagOXID_INFO {
DWORD dwTid; // thread id of object exporter
DWORD dwPid; // process id of obj exporter
IPID ipidRemUnknown; // IRemUnknown IPID for object exporter
DWORD dwAuthnHint;
DUALSTRINGARRAY *psa; // protocol and security info
} OXID_INFO;
//////////////////////////////////////////////////////////////////
21.3.3 OBJREF
An OBJREF is the data type used to represent an actual marshaled object referenc
e. An OBJREF can either be empty or assume one of three variations, depending on
the degree to which the object being marshaled uses the hook architecture (IMar
shal, etc.) in the marshaling infrastructure. The OBJREF structure is a union co
nsisting of a switch flag followed by the appropriate data.
21.3.3.1 OBJREF_STANDARD
Contains one interface of an object marshaled in standard form. Contains a stand
ard reference, along with a set of protocol sequences and network addresses that
can be used to bind to an OXID resolver that is able to resolve the OXID in the
STDOBJREF. This is useful when marshaling a proxy to give to another machine (a
.k.a. the "middleman" case). The marshaling machine specifies the saResAddr for
the OXID Resolver on the server machine, eliminating the need for the unmarshale
r to call the marshaler (middleman) back to get this information. Further, the
marshaler does not need to keep the OXID in its cache beyond the lifetime of its
own references in order to satisfy requests from parties that it just gave the
OBJREF to.

Member Type Semantic


Signature unsigned long must be OBJREF_SIGNATURE
flags unsigned long OBJREF flags (section 3.5)
GUID iid Interface identifier
std STDOBJREF A standard object reference used to connect to the sourc
e object (Section 3.4).
SaResAddr STRINGARRAY The resolver address.
21.3.3.2 OBJREF_HANDLER
A marshaling of an object that wishes to use handler marshaling. For example, an
object wishes to be represented in client address spaces by a proxy object that
caches state. In this case, the class- specific information is just a standard
reference to an interface pointer that the handler (proxy object) will use to co
mmunicate with the original object. See the IStdMarshalInfo interface.

Member Type Semantic


signature unsigned long must be OBJREF_SIGNATURE
flags unsigned long OBJREF flags (section 3.5)
GUID iid Interface identifier
std STDOBJREF A standard object reference used to connect to the sourc
e object (Section 3.4).
clsid CLSID The CLSID of handler to create in the destination client.
SaResAddr STRINGARRAY The resolver address.
21.3.3.3 OBJREF_CUSTOM
A marshaling of an object which supports custom marshaling. The Custom format gi
ves an object control over the representation of references to itself. For examp
le, an immutable object might be passed by value, in which case the class-specif
ic information would contain the object's immutable data. See the IMarshal inter
face.

Member Type Semantic


signature unsigned long must be OBJREF_SIGNATURE
flags unsigned long OBJREF flags (section 3.5)
GUID iid Interface identifier
clsid CLSID The CLSID of the object to create in the destination client.
cbExtension unsigned long The size of the extension data.
Size unsigned long The size of the marshaled data provided by the source ob
ject, plus the size of the extension data, and passed in pData.
PData byte* The data bytes that should be passed to IMarshal::UnmarshalInter
face on a new instance of class clsid in order to initialize it and complete the
unmarshal process (class specific data).
The first cbExtension bytes are the reserved for future extensions to the protoc
ol, and should not be passed into the custom unmarshaler. CoUnmarshalInterface s
hould skip the extension data, and the data starting at pData+cbExtension should
be given to the custom unmarshaler.
21.3.4 STDOBJREF
An instance of a STDOBJREF represents a COM interface pointer that has been mars
haled using the standard COM network protocol.
The members and semantics of the STDOBJREF structure are as follows:

Member Semantic
flags Flag values taken from the enumeration SORFFLAGS. These are described in
Section 3.5.
cPublicRefs The number of reference counts on ipid that are being transferre
d in this marshaling.
oxid The OXID of the server that owns this OID.
oid The OID of the object to which ipid corresponds.
ipid The IPID of the interface being marshaled.
21.3.5 SORFLAGS
The various SORFLAGS values have the following meanings. The SORF_OXRESxxx bit f
lags are reserved for the object exporter's use only, and must be ignored by obj
ect importers. They need not be passed through when marshaling an interface prox
y.

Flag Value Meaning


SORF_NULL 0 Convenient for initialization.
SORF_OXRES1 1 Reserved for exporter.
SORF_OXRES2 32 Reserved for exporter.
SORF_OXRES3 64 Reserved for exporter.
SORF_OXRES4 128 Reserved for exporter.
SORF_OXRES5 256 Reserved for exporter.
SORF_OXRES6 512 Reserved for exporter.
SORF_OXRES7 1024 Reserved for exporter.
SORF_OXRES8 2048 Reserved for exporter.
SORF_NOPING 4096 This OID does not require pinging. Further, all interfac
es on this OID, including this IPID, need not be reference counted. Pinging and
reference counting on this object and its interfaces are still permitted, howeve
r, though such action is pointless.
21.3.6 ORPCINFOFLAGS
The various ORPCINFOFLAGS have the following meanings.

Flag Meaning
ORPCF_NULL (Not a real flag. Merely a defined constant indicating the absen
ce of any flag values.)
ORPCF_LOCAL The destination of this call is on the same machine on which it
originates. This value is never to be specified in calls which are not in fact l
ocal.
ORPCF_RESERVED1 If ORPCF_LOCAL is set, then reserved for local use; othe
rwise, reserved for future use.
ORPCF_RESERVED2 If ORPCF_LOCAL is set, then reserved for local use; othe
rwise, reserved for future use.
ORPCF_RESERVED3 If ORPCF_LOCAL is set, then reserved for local use; othe
rwise, reserved for future use.
ORPCF_RESERVED4 If ORPCF_LOCAL is set, then reserved for local use; othe
rwise, reserved for future use.
Implementations may use the local and reserved flags to indicate any extra infor
mation needed for local calls. Note that if the ORPCF_LOCAL bit is not set and a
ny of the other bits are set then the receiver should return a fault.
21.3.7 ORPCTHIS
In every Request PDU that is an ORPC, the body (CL case) or the stub data (CO ca
se) which normally contains the marshaled arguments in fact begins with an insta
nce of the ORPCTHIS structure. The marshaled arguments of the COM interface invo
cation follow the ORPCTHIS; thus, viewed at the DCE RPC perspective, the call ha
s an additional first argument. The ORPCTHIS is padded with zero-bytes if necess
ary to achieve an overall size that is a multiple of eight bytes; thus, the rema
ining arguments are as a whole eight byte aligned.
As in regular calls, the causality id must be propagated. If A calls ComputePi o
n B, B calls Release on C (which gets converted to RemRelease), and C calls Add
on A, A will see the same causality id that it called B with.

Member Type Semantic


version COMVERSION The version number of the COM protocol used to m
ake this particular ORPC. The initial value will be 5.1. Each packet contains th
e sender's major and minor ORPC version numbers. The client's and server's major
versions must be equal. Backward compatible changes in the protocol are indicat
ed by higher minor version numbers. Therefore, a server's minor version must be
greater than or equal to the client's. However, if the server's minor version ex
ceeds the client's minor version, it must return the client's minor version and
restrict its use of the protocol to the minor version specified by the client. A
protocol version mismatch causes the RPC_E_VERSION_MISMATCH ORPC fault to be re
turned.
flags unsigned long Flag values taken from the enumeration ORPCINFOFLAGS (se
ction 3.6). Reserved unsigned long Must be set to zero.
reserved1 unsigned long Set to zero.
cid CID The causality id of this ORPC.
extensions ORPC_EXTENT_ARRAY* The body extensions, if any, passed with
this call. Body extensions are GUID-tagged blobs of data which are marshaled as
an array of bytes. Extensions are always marshaled with initial eight byte alig
nment. Body extensions which are presently defined are described in Section 3.10
.
The cid field contains the causality id. Each time a client makes a unique call,
a new causality id is generated. If a server makes a call while processing a re
quest from a client, the new call must have the same causality id as the call cu
rrently being processed. This allows simple servers to avoid working on more the
n one thing at a time (for example A calls B calls A again, meanwhile C tries to
call A with a new causality id). It tells the server that he is being called be
cause he asked someone to do something for him. There are several interesting ex
ceptions.
The causality id for maybe and idempotent calls must be set to CID_NULL. If a se
rver makes a ORPC call while processing such a call, a new causality id must be
generated.
In the face of network failures, the same causality id may end up in use by two
independent processes at the same time. If A calls B calls C calls D and C fails
, both B and D can independently, simultaneously make calls to E with the same c
ausality id.
The extensions field contains extensions to the channel header, described in Sec
tion 3.10. Note that in order to force the ORPCTHIS header to be 8 byte aligned
an even number of extensions must be present and the size of the extension data
must be a multiple of 8.
21.3.8 ORPCTHAT
In every Response PDU that is an ORPC, the body (CL case) or the stub data (CO c
ase) which normally contains the marshaled output parameters in fact begins with
an instance of the ORPCTHAT structure. The marshaled output parameters of the C
OM interface invocation follow the ORPCTHAT; thus, viewed at the DCE RPC perspec
tive, the call has an additional output parameters. The ORPCTHAT is padded with
zero-bytes if necessary to achieve an overall size that is a multiple of eight b
ytes; thus, the remaining output parameters as a whole are eight byte aligned.

Member Type Semantic


flags unsigned long Flag values taken from the enumeration ORPCINFOFLAGS (se
ction 3.6).
extensions ORPC_EXTENT_ARRAY* The body extensions, if any, returned by
this call. See Section 3.10 for a general description of body extensions as wel
l as a description of existing well-known extensions.
21.3.9 HRESULTs
HRESULTs are the 32-bit return value from all ORPC methods. The following is a p
artial list of already defined HRESULTs.

S_OK Success. (0x00000000)


E_OUTOFMEMORY Insufficient memory to complete the call. (0x80000002)
E_INVALIDARG One or more arguments are invalid. (0x80000003)
E_NOINTERFACE No such interface supported (0x80000004)
E_ACCESSDENIED A secured operation has failed due to (0x80070005) inadequate se
curity privileges.
E_UNEXPECTED Unknown, but relatively catastrophic (0x8000FFFF) error.
RPC_E_INVALID_OXID The object exporter was not found. (0x80070776)
RPC_E_INVALID_OID The object specified was not found or (0x80070777) recog
nized.
RPC_E_INVALID_SET The object exporter set specified was (0x80070778) not f
ound.
RPC_E_INVALID_OBJECT The requested object does not exist
Further details TBS.
21.3.10 Body Extensions
Body Extensions are UUID-tagged blocks of data which are useful for conveying ad
ditional, typically out-of-band, information on incoming invocations (within ORP
CTHIS, Section 3.7) and in replies (within ORPCTHAT, Section 3.8).
Any implementations of the DCOM protocol may define its own extensions with thei
r own UUIDs. Implementations should skip over extensions which they do not recog
nize or do not wish to support.
Body Extensions are marshaled as an array of bytes with initial eight byte align
ment. The following sections describe several existing body extensions.
21.3.10.1 Debugging Extension
{f1f19680-4d2a-11ce-a66a-0020af6e72f4}
This extension aids in debugging ORPC. In particular it is designed to allow sin
gle stepping over an ORPC call into the server and out of the server into the cl
ient.
Further details TBS.
21.3.10.2 Extended Error Extension
{f1f19681-4d2a-11ce-a66a-0020af6e72f4}
The extended error information body extension conveys extended error information
concerning the original root cause of a error back to a caller so that the call
er can deal with it. This extension is only semantically useful in Response and
Fault PDUs.
It is intended that this error information is suitable for displaying informatio
n to a human being who is the user; this information is not intended to be the b
asis for logic decisions in a piece of client code, for doing so couples the cli
ent code to the implementation of the server. Rather, client code should act sem
antically only on the information returned through the interface that it invokes
.
Further details TBS.
21.4 IRemUnknown interface
The IRemUnknown interface is used by remote clients for manipulating reference c
ounts on the IPIDs that they hold and for obtaining additional interfaces on the
objects on which those IPIDs are found.
References are kept per interface rather then per object.
This interface is implemented by the COM "OXID object" associated with each OXID
( ie each Object Exporter). The IPID for the IRemUnknown interface on this obje
ct is returned from IOXIDResolver::ResolveOxid; see Section 5.2.1. An OXID objec
t need never be pinged; its interfaces (this IPID included) need never be refere
nce counted. IRemUnknown is specified as follows:
// The remote version of IUnknown. This interface exists on every
// OXID (whether an OXID represents either a thread or a process is
// implementation specific). It is used by clients to query for new
// interfaces, get additional references (for marshaling), and release
// outstanding references.
// This interface is passed along during OXID resolution.
//
[
object,
uuid(00000131-0000-0000-C000-000000000046)
]
interface IRemUnknown : IUnknown
{
typedef struct tagREMQIRESULT
{
HRESULT hResult; // result of call
STDOBJREF std; // data for returned interface
} REMQIRESULT;
HRESULT RemQueryInterface
(
[in] REFIPID ripid, // interface to QI on
[in] unsigned long cRefs, // count of AddRefs requested
[in] unsigned short cIids, // count of IIDs that follow
[in, size_is(cIids)]
IID* iids, // IIDs to QI for
[out, size_is(,cIids)]
REMQIRESULT** ppQIResults // results returned
);
typedef struct tagREMINTERFACEREF
{
IPID ipid; // ipid to AddRef/Release
unsigned long cPublicRefs;
unsigned long cPrivateRefs;
} REMINTERFACEREF;
HRESULT RemAddRef
(
[in] unsigned short cInterfaceRefs,
[in, size_is(cInterfaceRefs)]
REMINTERFACEREF InterfaceRefs[],
[out, size_is(cInterfaceRefs)]
HRESULT* pResults
);
HRESULT RemRelease
(
[in] unsigned short cInterfaceRefs,
[in, size_is(cInterfaceRefs)]
REMINTERFACEREF InterfaceRefs[]
);
}
21.4.1 IRemUnknown::RemQueryInterface
QueryInterface for and return the result thereof for zero or more interfaces fro
m the interface behind the IPID ipid. ipid must designate an interface derived f
rom IUnknown (recall that all remoted interfaces must derive from IUnknown). The
QueryInterface calls on the object that are used to service this request are co
nducted on the IUnknown interface of the object.
Argument Type Semantic
ripid REFIPID The interface on an object from whom more interfaces are
desired.
CRefs unsigned long The number of references sought on each of the returned
IIDs.
CIids unsigned short The number of interfaces being requested.
iids IID* The list of IIDs that name the interfaces sought on this object.
ppQIResults REMQIRESULT** The place at which the array of the results of t
he various QueryInterface calls are returned.
ReturnValue Meaning
S_OK Success. An attempt was made to retrieve each of the requested interface
s from the indicated object; that is, QueryInterface was actually invoked for ea
ch IID. QueryInterface returned S_OK for every IID specified.
S_FALSE Success. An attempt was made to retrieve each of the requested interface
s from the indicated object; that is, QueryInterface was actually invoked for ea
ch IID. QueryInterface returned a failure code for at least one of the IIDs spec
ified.
E_NOINTERFACE QueryInterface returned a failure code for every IID specifed.
E_INVALIDARG One or more arguments (likely ipid) were invalid. No result valu
es are returned.
E_UNEXPECTED An unspecified error occurred.
E_OUTOFMEMORY Insufficient memory to complete the call.
RPC_E_INVALID_OBJECT The requested object does not exist. No result values ar
e returned.
21.4.1.1 REMQIRESULT
The REMQIRESULT structure contains the following members:

Member Type Semantic


hResult HRESULT The result code from the QueryInterface call mad
e for the requested IID.
std STDOBJREF The data for the returned interface. Note that if hResul
t indicates failure then the contents of STDOBJREF are undefined.
21.4.2 IRemUnknown::RemAddRef
Obtain and grant ownership to the caller of one or more reference counts on one
or more IPIDs managed by the corresponding OXID.

Argument Type Semantic


cInterfaceRefs unsigned short The size of the rgRefs array.
InterfaceRefs REMINTERFACEREF[] An array of REMINTERFACEREFs, cRefs larg
e. Each IPID indicates an interface managed by this OXID on whom more reference
counts are sought. The corresponding reference count (cInterfaceRefs), which may
not be zero (and thus is one or more), indicates the number of reference counts
sought on that IPID.
PResults HRESULT* An array of HRESULTs cInterfaceRefs large, each
containing the result of attempting an AddRef on the ipid in the corresponding R
EMINTERFACREF.
Return Value Meaning
S_OK Success. An attempt was made to retrieve each of the requested interface
references.
E_INVALIDARG One or more of the IPIDs indicated were not in fact managed by t
his OXID, or one or more of the requested reference counts was zero. None of the
requested reference counts have been granted to the caller; the call is a no-op
.
E_UNEXPECTED An unspecified error occurred. It is unknown whether any or all
of the requested reference counts have been granted.
CO_E_OBJNOTREG Object is not registered.
A useful optimization is for a caller to RemAddRef more than needed.
When a process receives an out marshaled interface, it receives one reference co
unt. If the process wishes to pass that interface as an out parameter, it must g
et another reference to pass along. Instead, the process (or middleman) should g
et a large number of references. Then if the interface is passed out multiple ti
mes, no new remote calls are needed to gain additional references.
A marshaler may optionally specify more than one reference in the STDOBJREF when
marshaling an interface. This allows the middle man case to pre-fill its cache
of references without making an extra RemAddRef call. The number of references p
assed is always specified in the STDOBJREF field.
If cPrivateRefs is not zero for all IPIDs, the call to RemAddRef must be made se
curely. DCOM on the server remembers the name of the client and the authenticat
ion and authorization service used to make to RemAddRef call.
21.4.2.1 REMINTERFACEREF
Member Type Semantic
IPID ipid ipid to AddRef/Release.
Unsigned long cPublicRefs Number of public references granted .
unsigned long cPrivateRefs Number of private references granted. Private r
eferences belong only to this client and can not be passed to other clients when
marshaling the proxy. If a client has only private references and wishes to pas
s the proxy to some other client, it must first obtain some public references vi
a IRemUnknown::RemAddRef and then pass one or more of those references in the ST
DOBJREF cPublicRefs field of the marshaled interface.
21.4.3 IRemUnknown::RemRelease
Release ownership of one or more reference counts on one or more IPIDs managed b
y the corresponding OXID.
If cPrivateRefs is not zero for all IPIDs, the call to RemRelease must be made s
ecurely. For each IPID, DCOM maintains a table of reference counts indexed by t
he client identity (name, authn svc, authz svc). All public references are stor
ed in one entry. Any call to RemRelease can release public references. Private
references can only be released by the client that added them.

Argument Type Semantic


cRefs unsigned short The size of the rgRefs array.
rgRefs REMINTERFACEREF[] An array of REMINTERFACEREFs, cRefs large. Each
IPID indicates an interface managed by this OXID on whom more reference counts a
re being returned. The corresponding reference count, which may not be zero (and
thus is one or more), indicates the number of reference counts returned on that
IPID.
Return Value Meaning
S_OK Success. An attempt was made to release each of the requested interface
references.
E_INVALIDARG One or more of the IPIDs indicated were not in fact managed by t
his OXID, or one or more of the requested reference counts was zero. None of the
offered reference counts have been accepted by the server; the call is a no-op.
E_UNEXPECTED An unspecified error occurred. It is unknown whether any or all
of the offered reference counts have been accepted.
21.5 The OXID Resolver
Each machine that supports the COM network protocol supports a one- per-machine
service known as the machine's `OXID Resolver.' Communication with an OXID Reso
lver is via a DCE RPC, not an ORPC.
The OXID Resolver performs several services:
It caches and returns to clients when asked the string bindings necessary to con
nect to OXIDs of exported objects for which this machine is either itself a clie
nt or is the server. Note that it typically returns only to client processes on
the same machine as itself, the OXIDs for which it is a client.
It receives pings from remote client machines to keep its own objects alive.
May do lazy protocol registration in the servers which it scopes.
These services are carried out through an RPC interface (not a COM interface) kn
own as IOXIDResolver. An OXID Resolver may be asked for the information require
d to connect to one of two different kinds of OXIDs, either the OXIDs associated
with its own objects, or the OXIDs associated with objects for which it is itse
lf a client The second case occurs when two or more client processes on the sa
me machine ask their local OXID Resolver to resolve a given OXID. The client OXI
D Resolver in this case can cache the OXID information an return it to local cli
ents without having to contact the server s OXID Resolver again.
21.5.1 OXID Resolver Ports/Endpoints
The OXID Resolver resides at different endpoints (ports) depending on the tran
sport being used. The OXID Resolver optimally resides at the same endpoints as t
he DCE RPC Endpoint Mapper (EPM). To accommodate systems where DCOM will coexist
with existing DCE RPC installations (i.e., where an EPM and presumably a comple
te DCE RPC runtime already exists), the DCOM implementation on that system will
register its interfaces with the DCE EPM and all DCOM implementations must be ab
le to fall back if they make DCOM-specific calls on the DCE EPM endpoint which f
ail.

Protocol String Name(s) Description Endpoint


ncadg_ip_udp, ip CL over UDP/IP 135
ncacn_ip_tcp CO over TCP/IP 135
ncadg_ipx CL over IPX TBD
ncacn_spx CO over SPX TBD
ncacn_nb_nb CO over NetBIOS over NetBEUI TBD
ncacn_nb_tcp CO over NetBIOS over TCP/IP 135
ncacn_np CO over Named Pipes TBD
ncacn_dnet_nsp CO over DECNet Network Services 96 Protocol (DECNet Phase IV)
ncacn_osi_dna CO over Open Systems 69 Interconnection (DECNet Phase V)

ncadg_dds, dds CL over Domain Datagram Service 12


ncahttp Hybrid over HTTP (TBS) 80
21.5.2 The IOXIDResolver Interface
IOXIDResolver (in earlier DCOM documentation this interface was named IObjectExp
orter) is defined as follows:
[
uuid(99fcfec4-5260-101b-bbcb-00aa0021347a),
pointer_default(unique)
]
interface IOXIDResolver
{
// Method to get the protocol sequences, string bindings
// and machine id for an object server given its OXID.
[idempotent] error_status_t ResolveOxid
(
[in] handle_t hRpc,
[in] OXID *pOxid,
[in] unsigned short cRequestedProtseqs,
[in, ref, size_is(cRequestedProtseqs)]
unsigned short arRequestedProtseqs[],
[out, ref] DUALSTRINGARRAY **ppdsaOxidBindings,
[out, ref] IPID *pipidRemUnknown,
[out, ref] DWORD *pAuthnHint
);
// Simple ping is used to ping a Set. Client machines use this
// to inform the object exporter that it is still using the
// members of the set.
// Returns S_TRUE if the SetId is known by the object exporter,
// S_FALSE if not.
[idempotent] error_status_t SimplePing
(
[in] handle_t hRpc,
[in] SETID *pSetId // Must not be zero
);
// Complex ping is used to create sets of OIDs to ping. The
// whole set can subsequently be pinged using SimplePing,
// thus reducing network traffic.
[idempotent] error_status_t ComplexPing
(
[in] handle_t hRpc,
[in, out] SETID *pSetId, // In of 0 on first
// call for new set.
[in] unsigned short SequenceNum,
[in] unsigned short cAddToSet,
[in] unsigned short cDelFromSet,
[in, unique, size_is(cAddToSet)] OID AddToSet[],
// add these OIDs to the set
[in, unique, size_is(cDelFromSet)] OID DelFromSet[],
// remove these OIDs from the set
[out] unsigned short *pPingBackoffFactor
// 2^factor = multipler
);
// In some cases the client maybe unsure that a particular
// binding will reach the server. (For example, when the oxid
// bindings have more then one TCP/IP binding) This call
// can be used to validate the binding
// from the client.
[idempotent] error_status_t ServerAlive
(
[in] handle_t hRpc
);
}
21.5.2.1 IOXIDResolver::ResolveOxid
Return the string bindings necessary to connect to a given OXID object.
On entry, arRequestedProtseqs contains the protocol sequences the client is will
ing to use to reach the server. These should be in decreasing order of protocol
preference, with no duplicates permitted. Local protocols (such as "ncalrpc") ar
e not permitted.
On exit, psaOxidBindings contains the string bindings that may be used to connec
t to the indicated OXID; if no such protocol bindings exist which match the requ
ested protocol sequences, NULL may be returned. The returned string bindings are
in decreasing order of preference of the server, with duplicate string bindings
permitted (and not necessarily of the same preferential priority), though of co
urse duplicates are of no utility. Local protocol sequences may not be present;
however, protocol sequences that were not in the set of protocol sequences reque
sted by the client may be. The string bindings returned need not contain endpoin
ts; the endpoint mapper will be used as usual to obtain these dynamically.

Argument Type Description


hRpc handle_t An RPC binding handle used to make the request.
pOxid OXID* The OXID for whom string bindings are requested.
cRequestedProtseqs unsigned short The number of protocol sequences request
ed.
arRequestedProtseqs unsigned short[] arRequestedProtseqs must be init
ialized with all the protocol id's the client is willing to use to reach the ser
ver. It cannot contain local protocol sequences. The object exporter must take c
are of local lookups privately. The protocol sequences are in order of preferenc
e or random order. No duplicates are allowed. See the Lazy Protocol Registrati
on section for more details.
psaOxidBindings STRINGARRAY** The string bindings supported by this OX
ID, in preferential order. Note that these are Unicode strings.
pipidRemUnknown IPID* The IPID to the IRemUnknown interface the OXID o
bject for this OXID.
pdwAuthnHint unsigned long* A value taken from the RPC_C_AUTHN constants. A
hint to the caller as to the minimum authentication level which the server will
accept.

Return Value Meaning


S_OK Success. The requested information was returned.
RPC_E_INVALID_OXID This OXID is unknown to this OXID Resolver, and thus no
information was returned.
E_UNEXPECTED An unspecified error occurred. Some of the requested information
may not be returned.
Object references are transient things. They are not meant to be stored in files
or otherwise kept persistently.
Conversely, since object references are aged, it is the responsibility of each c
lient to unmarshal them and begin pinging them in a timely fashion.
The basic use of the ResolveOxid method is to translate an OXID to string bindin
gs. Put another way, this method translates an opaque process and machine identi
fier to the information needed to reach that machine and process. There are four
interesting cases:
Looking up an OXID the first time an interface is unmarshaled on a machine,
Looking up an OXID between a pair of machines that already have connections,
Looking up an OXID from a middleman, and
Looking up string bindings with unresolved endpoints (lazy use protseq).
Another interesting topic is garbage collection of stored string binding vectors
.
21.5.2.1.1 Lookup Between Friends
Consider the case with two machines A and B. Machine A has OXID Resolver process
C and client process D. Machine B has OXID Resolver process E and server proces
s F.
Server process F, when it starts up, registers it s RPC string bindings with it s lo
cal OXID Resolver process E, and creates an OBJREF to some object inside process
F. At some future time client process D receives that OBJREF (Note: the mechani
sm used to acquire this OBJREF is not relevant to this discussion, it may have c
ome through the object activation protocol (beyond the scope of this document) o
r as an [out] interface parameter in some other ORPC call, or through some other
mechanism). The OBJREF contains the OXID for process F, and the string bindings
for the Resolver process E on the server machine.
Client Process D asks it s local OXID Resolver to resolve the OXID for F. It passe
s it the OXID and the string bindings for OXID Resolver E. If Resolver D has nev
er seen the OXID before, it calls the OXID Resolver E to ask it to resolve the O
XID. Resolver E looks up the OXID and returns the string bindings for server pro
cess F. Resolver D then caches this information for future use, and returns the
string bindings to client process C. Client process C then binds to the string b
indings and is now able to make ORPC calls directly to the server process F.
If other client processes on machine A receive an OBJREF for process F, the OXID
resolve can be handled completely by the Resolver process D on machine A. There
is no need to contact Resolver process E on the server again.
If machine A gives an OBJREF for F to a client process on another machine G, the
n the Resolver process on G will repeat the same steps as Resolver process D did
to resolve the OXID.
+============+============+ +===========+===========+
| Machine A | | Machine B |
+============+============+ +===========+===========+
+============+============+ +===========+===========+
| Process C | Resolver D | | Resolver E| Process F |
+============+============+ +===========+===========+
| | | | | register |
| | | | | endpoints |
| | | | | with local|
| | | | | Resolver E|
+------------+------------+ +-----------+-----------+
| | | | cache F | |
| | | | and it s | |
| | | | endpoints | |
| | | | | |
+------------+------------+ +-----------+-----------+
| receive | | | | |
| OBJREF | | | | |
| to F | | | | |
| | | | | |
+------------+------------+ +-----------+-----------+
| ask local | | | | |
| Resolver C | | | | |
| to resolve | | | | |
| F | | | | |
+------------+------------+ +-----------+-----------+
| | ask remote | | | |
| | Resolver | | | |
| | E to | | | |
| | resolve F | | | |
+------------+------------+ +-----------+-----------+
| | | | lookup F | |
| | | | and | |
| | | | return | |
| | | | endpoints | |
+------------+------------+ +-----------+-----------+
| | cache | | | |
| | endpoints | | | |
| | to F and | | | |
| | return to D| | | |
+------------+------------+ +-----------+-----------+
| bind to | | | | |
| endpoint | | | | |
| for F | | | | |
| | | | | |
+------------+------------+ +-----------+-----------+
| invoke | | | | |
| method on | | | | |
| F | | | | |
| | | | | |
+------------+------------+ +-----------+-----------+
21.5.2.1.2 Lazy Protocol Registration
In a homogeneous network, all machines communicate via the same protocol sequenc
e. In a heterogeneous network, machines may support multiple protocol sequences.
Since it is often expensive in resources to allocate endpoints (RpcServerUsePro
tseq) for all available protocol sequences, ORPC provides a mechanism where they
may be allocated on demand. To implement this extension fully, there are some c
hanges in the server. However, changes are optional. If not implemented, ORPC wi
ll still work correctly if less optimally in heterogeneous networks.
There are two cases: the server implements lazy protocol registration or it doe
s not.
If the server is using lazy protocol registration, the implementation of Resol
veOxid is modified slightly. When the client OXID Resolver calls the server OXID
Resolver, it passes the requested protocol sequence vector. If none of the requ
ested protocol sequences have endpoints allocated in the server, the server OXID
Resolver allocates them according to its own endpoint allocation mechanisms.
If the server does not implement the lazy protocol registration, then all proto
col sequences are registered by the server at server initialization time.
When registering protocol sequences, the server may register endpoints and the s
erver s string bindings will contain the complete endpoints. However, if the serv
er chooses not register endpoints when it registers protocol sequences the endpo
int mapper process can be used to forward calls to the server. Using the endpoin
t mapper requires that all server IIDs be registered in the endpoint mapper. It
also allows a different lazy protocol registration mechanism. The endpoint mapp
er can perform some local magic to force the server to register the protocol se
quences. .
The client will always pass in a vector of requested protocol sequences which th
e server can ignore if it does not implement lazy protocol registration.
21.5.2.2 IOXIDResolver::SimplePing
Pings provide a mechanism to garbage collect interfaces. If an interface has ref
erences but is not being pinged, it may be released. Conversely, if an interface
has no references, it may be released even though it has recently been pinged.
SimplePing just pings the contents of a set. The set must be created with Comple
xPing (section 5.2.3).
Ping a set, previously created with IOXIDResolver::ComplexPing, of OIDs owned by
this OXID Resolver. Note that neither IPIDs nor OIDs may be pinged, only explic
itly created SETIDs.

Argument Type Description


hRpc handle_t An RPC binding handle used to make the request.
PSetId SETID* A SETID previously created with IOXIDResolver::ComplexPing on th
is same OXID Resolver.
Return Value Meaning
S_OK Success. The set was pinged.
RPC_E_INVALID_SET This SETID is unknown to this OXID Resolver, and thus th
e ping did not occur.
E_UNEXPECTED An unspecified error occurred. It is not known whether the ping
was done or not.
21.5.2.3 IOXIDResolver::ComplexPing
Ping a ping set. Optionally, add and/or remove some OIDs from the set. Optionall
y, adjust some ping timing parameters associated with the set. After a set is de
fined, a SimplePing will mark the entire contents of the set as active. After a
set is defined, SimplePing should be used to ping the set. ComplexPing need only
be used to adjust the contents of the set (or to adjust the time-out).
Ping set ids (SETIDs) are allocated unilaterally by the server OXID Resolver. T
he client OXID Resolver then communicates with the server OXID Resolver to add (
and later remove) OIDs from the ping set..
Each OID owned by a server OXID Resolver may be placed in zero or more ping sets
by the various clients of the OID. The client owner of each such set will set a
ping period and a ping time-out count for the set, thus determining an overall
time-out period for the set as the product of these two values. The time-out per
iod is implicitly applied to each OID contained in the set and to future OIDs th
at might add be added to it. The server OXID Resolver is responsible for ensurin
g that an OID that it owns does not expire until at least a period of time t has
elapsed without that OID being pinged, where t is the maximum time-out period o
ver all the sets which presently contain the given OID, or, if OID is not presen
tly in any such sets but was previously, t is the time-out period for the last s
et from which OID was removed at the instant that that removal was done; otherwi
se, OID has never been in a set, and t is a default value (ping period equals 12
0 seconds, ping time-out count equals three (3), t equals 360 seconds, or six (6
) minutes).
Clients are responsible for pinging servers often enough to ensure that they do
not expire given the possibility of network delays, lost packets, and so on. If
a client only requires access to a given object for what it would consider less
than a time-out period for the object (that is, it receives and release the obje
ct in that period of time), then unless it is certain it has not itself passed t
he object to another client, it must be sure to nevertheless ping the object (a
ComplexPing that both adds and removes the OID will suffice). This ensures that
an object will not expire as it is passed through a chain of calls from one clie
nt to another.
An OID is said to be pinged when a set into which it was previously added and pr
esently still resides is pinged with either a SimplePing or a ComplexPing, or wh
en it is newly added to a set with ComplexPing. Note that these rules imply that
a ComplexPing that removes an OID from a set still counts as a ping on that OID
. In addition to pinging the set SETID, this call sets the time-out period of th
e set as the product of a newly-specified ping period and a newly-specified "pin
g count to expiration;" these values take effect immediately. Ping periods are s
pecified in tenths of a second, yielding a maximum allowable ping period of abou
t 1 hr 50 min.
Adjustment of the time-out period of the set is considered to happen before the
addition of any new OIDs to the set, which is in turn considered to happen befor
e the removal of any OIDs from the set. Thus, an OID that is added and removed i
n a single call no longer resides in the set, but is considered to have been pin
ged, and will have as its time-out at least the time-out period specified in tha
t ComplexPing call.
On exit, the server may request that the client adjust the time-out period; that
is, ask it to specify a different time-out period in subsequent calls to Comple
xPing. This capability can be used to reduce traffic in busy servers or over slo
w links. The server indicates its desire through the values it returns through t
he variables pReqSetPingPeriod and pReqSetNumPingsToTimeOut. If the server seeks
no change, it simply returns the corresponding values passed by the client; if
it wishes a longer time-out period, it indicates larger values for one or both o
f these variables; if it wishes a smaller period, it indicates smaller values. W
hen indicating a larger value, the server must start immediately acting on that
larger value by adjusting the time-out period of the set. However, when indicati
ng a smaller value, it must consider its request as purely advice to the client,
and not take any action: if the client wishes to oblige, it will do so in a sub
sequent call to ComplexPing by specifying an appropriate time-out period.

Argument Type Description


hRpc handle_t An RPC binding handle used to make the request.

pSetId SETID The SETID being manipulated. SequenceNum unsigned short The sequ
ence number allows the object exporter to detect duplicate packets. Since the ca
ll is idempotent, it is possible for duplicates to get executed and for calls to
arrive out of order when one ping is delayed.
cAddToSet unsigned short The size of the array AddToSet.
cDelFromSet unsigned short The size of the array DelFromSet.
AddToSet OID[] The list of OIDs which are to be added to this set. Addi
ng an OID to a set in which it already exists is permitted; such an action, as w
ould be expected, is considered to ping the OID.
DelFromSet OID[] The list of OIDs which are to be removed from this set.
Removal counts as a ping. An OID removed from a set will expire after the number
of ping periods has expired without any pings (not the number of ping periods -
1). If an id is added and removed from a set in the same ComplexPing, the id is
considered to have been deleted.
PPingBackoffFactor unsigned short* Acts as a hint (only) from the s
erver to the client in order to reduce ping traffic. Clients are requested to no
t ping more often than (1<<*pPingBackoffFactor)* (BasePingInterval=120) seconds,
and the number of pings until timeout remains unchanged at the default of 3. Cl
ients may choose to assume that this parameter is always zero.

Return Value Meaning


S_OK Success. The set was pinged, etc.
RPC_E_INVALID_OID Indicates that some OID was not recognized. There is no
recovery action for this error, it is informational only.
E_ACCESSDENIED Access is denied.
E_OUTOFMEMORY There was not enough memory to service the call. The caller may
retry adding OIDs to the set on the next ping.
E_UNEXPECTED An unspecified error occurred. It is not known whether the ping
or any of the other actions were done or not.

21.6 Wrapping DCE RPC calls to interoperate with ORPC


This is an example of a server side wrapper for the Bar method. It assumes the e
xistence of small helper functions to import and export object references and lo
okup previously exported object references.
RPC_STATUS Bar(handle_t h, short i, OBJREF * prIB, OBJREF ** pprIW) {
UUID ipid;
RPC_STATUS status;
IFoo * pIF;
IBar * pIB;
IWaz * PIW;
HRESULT hR;
status = RpcBindingInqObject(h, &ipid);
if (status) return(SOMETHING);
status = ORpcLookupIPID(ipid, &pIF);
if (status) return(SOMETHING);
status = ORpcImportObjRef(prIB, &pIB);
if (status) return(SOMETHING);
hR = pIF->Bar(i, pIB, &pIW); // actual call to the me
thod!
pIB->Release();
status = ORpcExportObjRef(pIW, pprIW);
return(hR ? hR : SOMETHING);
};
This is an example of the client side wrapper for Bar:
// assume some class CFoo that implements Bar method
class CFoo : IUnknown, IFoo {
UUID ipid; // one for each interface?
handle_t h;
virtual HRESULT QueryInterface(UUID iid, void **ppvoid);
virtual HRESULT AddRef();
virtual HRESULT Release();
virtual HRESULT Bar(short i, IFoo * pIF, IWaz ** ppIW);
};
HRESULT CFoo::Bar(short i, IFoo * pIF, IWaz ** ppIW) {
OBJREF * prIF;
OBJREF * prIW;
HRESULT hR;
RPC_STATUS status;
status = RpcBindingSetObject(this->h, this->ipid);
if (status) return(SOMETHING);
status = ORpcExportObjRef(pIF, &prIF);
if (status) return(SOMETHING);
hR = Bar(this->h, i, prIF, &prIW);
status = ORpcImportObjRef(prIW, ppIW);
ORpcFreeObjRef(prIF);
ORpcFreeObjRef(prIW);
return(hR ? hR : SOMETHING);
};
21.6.1 Implementing ORPC in RPC
Since the implicit parameters are specified as IDL, the ORPC header received by
RPC will contain many fields inserted by MIDL. Here are the definitions for the
header on the wire.
/*
An inbound header would be laid out as follows where the
extent array is optional and there may be zero of more extents.
An outbound header is laid out similarly.
ORPCTHIS_WITHNOEXTENSIONS
[
ORPC_EXTENT_ARRAY
[ORPC_EXTENT]*
]
*/
typedef struct
{
COMVERSION version; // COM version number
unsigned long flags; // INFO flags for presence of other data
unsigned long reserved1; // set to zero
LTID ltid; // logical thread id of caller
unsigned long unique; // tag to indicate presence of extensions
} ORPCTHIS_WITHNOEXTENSION;
typedef struct
{
unsigned long rounded_size; // Actual number of extents.
uuid_t id; // Extension identifier.
unsigned long size; // Extension size.
byte data[]; // Extension data.
} ORPC_EXTENT;

// Array of extensions.
typedef struct
{
unsigned long rounded_size; // Actual number of extents
unsigned long size; // Number of extents
unsigned long unique_flag[]; // Flags to indicate presense of ORPC_EXTENTs
} ORPC_EXTENT_ARRAY;
typedef struct
{
unsigned long flags; // INFO flags for presence of other data
unsigned long unique; // tag to indicate presence of extensions
} ORPCTHAT_WITH_NOEXTENSIONS;

Part IV: Other COM Core Technologies


22. MSRPC

MS-RPC is an implementation of the Open Software Foundation's (OSF) Distributed


Computing Environment (DCE) Remote Procedure Call (RPC) system.
See the X/Open CAE for DCE RPC.
23. Registry
The registry is a database that COM components use to store and retrieve configu
ration data.
The registry stores data in binary files. To manipulate registry data, an applic
ation must use the registry functions. This chapter describes the registry and t
he functions that applications use to access and manipulate the data stored ther
e. The data stored in the registry varies according to the platform that is used
. This chapter contains a description of registry entries that can exist on any
platform.
23.1 Structure of the Registry
The registry stores data in a hierarchically structured tree. Each node in the t
ree is called a key. Each key can contain both subkeys and data entries called v
alues. Sometimes, the presence of a key is all the data that an application requ
ires; other times, an application opens a key and uses the values associated wit
h the key. A key can have any number of values, and the values can be in any for
m.
Each key has a name consisting of one or more printable ANSI characters that is,
characters ranging from values 32 through 127. Key names cannot include a space
, a backslash (\), or a wildcard character (* or ?). Key names beginning with a
period (.) are reserved. The name of each subkey is unique with respect to the k
ey that is immediately above it in the hierarchy. Key names are not localized in
to other languages, although values may be.
23.2 Registry Storage Space
Although there are few technical limits to the type and size of data an applicat
ion can store in the registry, certain practical guidelines exist to promote sys
tem efficiency. An application should store configuration and initialization dat
a in the registry, but other kinds of data should be stored elsewhere.
Generally, data consisting of more than one or two kilobytes (K) should be store
d as a file and referred to by using a key in the registry rather than being sto
red as a value. Instead of duplicating large pieces of data in the registry, an
application should save the data as a file and refer to the file. Executable bin
ary code should never be stored in the registry.
A value entry uses much less registry space than a key. To save space, an applic
ation should group similar data together as a structure and store the structure
as a value rather than storing each of the structure members as a separate key.
(Storing the data in binary form allows an application to store data in one valu
e that would otherwise be made up of several incompatible types.)
23.3 Predefined Keys
An application must open a key before it can add data to the registry. To open a
key, an application must supply the handle of another key in the registry that
is already open. The system defines standard handles that are always open. An ap
plication can use these predefined handles as entry points to the registry.
The system provides two predefined keys at the root of the registry: HKEY_LOCAL_
MACHINE and HKEY_USERS. In addition, the system defines two subkeys: HKEY_CLASSE
S_ROOT (a subkey of HKEY_LOCAL_MACHINE) and HKEY_CURRENT_USER (a subkey of HKEY_
USERS). These registry handles are valid for all Win32 implementations of the re
gistry, although the use of the handles may vary from platform to platform.
Predefined keys help an application navigate in the registry and make it possibl
e to develop tools that allow a system administrator to manipulate categories of
data. Applications that add data to the registry should always work within the
framework of predefined keys, so administrative tools can find and use the new d
ata.
These predefined keys are used as entry points to the registry.
Entry point Use
HKEY_CLASSES_ROOT Registry entries subordinate to this key define types (o
r classes) of documents and the properties associated with those types. Data sto
red under this key is used by COM components.
HKEY_CURRENT_USER Registry entries subordinate to this key define the pref
erences of the current user.
HKEY_LOCAL_MACHINE Registry entries subordinate to this key define the phys
ical state of the computer.
HKEY_USERS Registry entries subordinate to this key define the default user
configuration for new users on the local computer and the user configuration fo
r the current user.
The use of HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and HKEY_USERS varies dependin
g on the implementation of the registry. In addition, other predefined handles h
ave been defined for specific Windows platforms.
23.4 Opening, Creating, and Closing Keys
Before an application can add data to the registry, it must create or open a key
. To create or open a key, an application always refers to the key as a subkey o
f a currently open key. The four predefined keys (HKEY_LOCAL_MACHINE, HKEY_CLASS
ES_ROOT, HKEY_USERS, and HKEY_CURRENT_USER) are always open. An application uses
the RegOpenKeyEx function to open a key and the RegCreateKeyEx function to crea
te a key.
An application can use the RegCloseKey function to close a key and write the dat
a it contains into the registry. RegCloseKey does not necessarily write the data
to the registry before returning; it can take as much as several seconds for th
e cache to be flushed to the hard disk. If an application must explicitly write
registry data to the hard disk, it can use the RegFlushKey function. RegFlushKey
, however, uses many system resources and should be called only when absolutely
necessary.
23.5 Writing and Deleting Registry Data
An application can use either the RegSetValue or RegSetValueEx function to assoc
iate a value and its data with a key. RegSetValue works only with strings (value
s having the REG_SZ type). RegSetValueEx, however, can write values with any typ
e of data. Either of these functions can create a key and its value at the same
time.
To delete a value from a key, an application can use the RegDeleteValue function
. To delete a key, it can use the RegDeleteKey function. A deleted key is not re
moved until the last handle to it has been closed. Subkeys and values cannot be
created under a deleted key.
To change a key s security information, an application can use the RegSetKeySecuri
ty function.
23.6 Retrieving Data from the Registry
To retrieve data from the registry, an application typically enumerates the subk
eys of a key until it finds a particular one and then retrieves data from the va
lue or values associated with it. An application can call the RegEnumKeyEx funct
ion to enumerate the subkeys of a given key. RegEnumKeyEx returns a subkey and i
ts class.
To retrieve detailed data about a particular subkey, an application can call the
RegQueryInfoKey function. The RegGetKeySecurity function retrieves a copy of th
e security descriptor protecting a key.
An application can use the RegEnumValue function to enumerate the values for a g
iven key, and the RegQueryValueEx function to retrieve a particular value for a
key. An application typically calls RegEnumValue to determine the value names an
d then RegQueryValueEx to retrieve the data for the names.
RegQueryValueEx differ in how they treat . If an unnamed value contains an unexp
anded environment variable (for example, %PATH%), RegQueryValue expands the vari
able into the storage buffer provided as one of its parameters. RegQueryValueEx
does not expand unexpanded references to environment variables
23.7 Registry Files
Applications can save part of the registry in a file and then load the contents
of the file back into the registry. A registry file is useful when a large amoun
t of data is being manipulated, when many entries are being made in the registry
, or when the data is transitory and must be loaded and then unloaded again. App
lications that back up and restore parts of the registry are likely to use regis
try files.
To save a key and its subkeys and values to a registry file, an application can
call the RegSaveKey function. To write the registry file back to the registry, a
n application can use the RegLoadKey, RegReplaceKey, or RegRestoreKey function.
RegLoadKey loads registry data from a specified file into a specified subkey und
er HKEY_USERS or HKEY_LOCAL_MACHINE on the calling application s computer or on a
remote computer. The function creates the specified subkey if it does not alread
y exist. After calling this function, an application can use the RegUnLoadKey fu
nction to restore the registry to its previous state.
RegReplaceKey replaces a key and all its subkeys and values in the registry with
the data contained in a specified file. The new data takes effect the next time
the system is started.
RegRestoreKey loads registry data from a specified file into a specified key on
the calling application s computer or on a remote computer. This function replaces
the subkeys and values below the specified key with the subkeys and values that
follow the top-level key in the file.
23.8 The Registry API Descriptions
23.9 RegCloseKey
The RegCloseKey function releases the handle of the specified key.
LONG RegCloseKey(
HKEY hKey // handle of key to close
);
Parameters
hKey
Identifies the open key to close.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The handle for a specified key should not be used after it has been closed, beca
use it will no longer be valid. Key handles should not be left open any longer t
han necessary.
The RegCloseKey function does not necessarily write information to the registry
before returning; it can take as much as several seconds for the cache to be flu
shed to the hard disk. If an application must explicitly write registry informat
ion to the hard disk, it can use the RegFlushKey function. RegFlushKey, however,
uses many system resources and should be called only when necessary.
See Also
RegCreateKeyEx, RegDeleteKey, RegFlushKey, RegOpenKeyEx, RegSetValueEx
23.10 RegConnectRegistry
The RegConnectRegistry function establishes a connection to a predefined registr
y handle on another computer.
LONG RegConnectRegistry(
LPTSTR lpMachineName, // address of name of remote computer
HKEY hKey, // predefined registry handle
PHKEY phkResult // address of buffer for remote registry handle
);
Parameters
lpMachineName
Points to a null-terminated string containing the name of the remote computer. T
he string has the following form:
\\computername
If lpMachineName is NULL, the local computer name is used.
hKey
Specifies the one of the following predefined registry handles on the remote com
puter.
HKEY_LOCAL_MACHINE
HKEY_USERS
You cannot specify the HKEY_CLASSES_ROOT or HKEY_CURRENT_USER value for this par
ameter.
phkResult
Points to a variable that receives a key handle identifying the predefined handl
e on the remote computer.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
When a handle returned by RegConnectRegistry is no longer needed, it should be c
losed by calling RegCloseKey.
See Also
RegCloseKey
23.11 RegCreateKeyEx
The RegCreateKeyEx function creates the specified key. If the key already exists
in the registry, the function opens it.
LONG RegCreateKeyEx(
HKEY hKey, // handle of an open key
LPCTSTR lpSubKey, // address of subkey name
DWORD Reserved, // reserved
LPTSTR lpClass, // address of class string
DWORD dwOptions, // special options flag
REGSAM samDesired, // desired security access
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of key security struc
ture
PHKEY phkResult, // address of buffer for opened handle
LPDWORD lpdwDisposition // address of disposition value buffer
);
Parameters
hKey
Identifies a currently open key or one of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
The key opened or created by the RegCreateKeyEx function is a subkey of the key
identified by the hKey parameter.
lpSubKey
Points to a null-terminated string specifying the name of a subkey that this fun
ction opens or creates. The subkey specified must be a subkey of the key identif
ied by the hKey parameter. This subkey must not begin with the backslash charact
er ( \ ). This parameter cannot be NULL.
Reserved
Reserved; must be zero.
lpClass
Points to a null-terminated string that specifies the class (object type) of thi
s key. This parameter is ignored if the key already exists.
dwOptions
Specifies special options for the key. This parameter can be one of the followin
g values.
Value Meaning
REG_OPTION_NON_VOLATILE This key is not volatile; this is the default. The infor
mation is stored in a file and is preserved when the system is restarted. The Re
gSaveKey function saves keys that are not volatile.

samDesired
Specifies an access mask that specifies the desired security access for the new
key. This parameter can be a combination of the following values:

Value Meaning
KEY_ALL_ACCESS Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, KEY_NOTI
FY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and KEY_SET_VALUE access.
KEY_CREATE_LINK Permission to create a symbolic link.
KEY_CREATE_SUB_KEY Permission to create subkeys.
KEY_ENUMERATE_SUB_KEYS Permission to enumerate subkeys.
KEY_EXECUTE Permission for read access.
KEY_NOTIFY Permission for change notification.
KEY_QUERY_VALUE Permission to query subkey data.
KEY_READ Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_
NOTIFY access.
KEY_SET_VALUE Permission to set subkey data.
KEY_WRITE Combination of KEY_SET_VALUE and KEY_CREATE_SUB_KEY access.

lpSecurityAttributes
Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned
handle can be inherited by child processes. If lpSecurityAttributes is NULL, the
handle cannot be inherited.
phkResult
Points to a variable that receives the handle of the opened or created key.
lpdwDisposition
Points to a variable that receives one of the following disposition values:
Value Meaning
REG_CREATED_NEW_KEY The key did not exist and was created.
REG_OPENED_EXISTING_KEY The key existed and was simply opened without being chan
ged.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The key that the RegCreateKeyEx function creates has no values. An application c
an use the RegSetValue or RegSetValueEx function to set key values.
The key identified by the hKey parameter must have been opened with KEY_CREATE_S
UB_KEY access. To open the key, use the RegCreateKeyEx or RegOpenKeyEx function.
An application cannot create a key under HKEY_USERS or HKEY_LOCAL_MACHINE.
An application can use RegCreateKeyEx to temporarily lock a portion of the regis
try. When the locking process creates a new key, it receives the disposition val
ue REG_CREATED_NEW_KEY, indicating that it owns the lock. Another process attempti
ng to create the same key receives the disposition value REG_OPENED_EXISTING_KEY
, indicating that another process already owns the lock.
See Also
RegCloseKey, RegDeleteKey, RegOpenKeyEx, RegSaveKey, SECURITY_ATTRIBUTES
23.12 RegDeleteKey
writer TODO note: need to tweak this page s use of key to distinguish between the sub
key that s being deleted and the parent key. v-alans, 2/15/95: Something like this
for the opening paragraph: 4/11/95- - this is Gary- - -brad s update doc requires
the following separation for Win95. The RegDeleteKey function deletes a named s
ubkey from the specified registry key. The subkey to be deleted cannot have any
subkeys.
LONG RegDeleteKey(
HKEY hKey, // handle of open key
LPCTSTR lpSubKey // address of name of subkey to delete
);
Parameters
hKey
Identifies a currently open key or one of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
The key specified by the lpSubKey parameter must be a subkey of the key identifi
ed by hKey.
lpSubKey
Points to a null-terminated string specifying the name of the key to delete. Thi
s parameter cannot be NULL.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If the function succeeds, RegDeleteKey removes the specified key from the regist
ry. The entire key, including all of its values, is removed.
To open the key, use the RegCreateKeyEx or RegOpenKeyEx function.
See Also
RegCloseKey, RegCreateKeyEx, RegOpenKeyEx
23.13 RegDeleteValue
The RegDeleteValue function removes a named value from the specified registry ke
y.
LONG RegDeleteValue(
HKEY hKey, // handle of key
LPCTSTR lpValueName // address of value name
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpValueName
Points to a null-terminated string that names the value to remove. If this param
eter is NULL or points to an empty string, the value set by the RegSetValue func
tion is removed.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The key identified by the hKey parameter must have been opened with KEY_SET_VALU
E access (KEY_WRITE access includes KEY_SET_VALUE access).
See Also
RegSetValueEx
23.14 RegEnumKeyEx
The RegEnumKeyEx function enumerates subkeys of the specified open registry key.
The function retrieves information about one subkey each time it is called. Unl
ike the RegEnumKey function, RegEnumKeyEx retrieves the class name of the subkey
and the time it was last modified.
LONG RegEnumKeyEx(
HKEY hKey, // handle of key to enumerate
DWORD dwIndex, // index of subkey to enumerate
LPTSTR lpName, // address of buffer for subkey name
LPDWORD lpcbName, // address for size of subkey buffer
LPDWORD lpReserved, // reserved
LPTSTR lpClass, // address of buffer for class string
LPDWORD lpcbClass, // address for size of class buffer
PFILETIME lpftLastWriteTime // address for time key last written to
);
Parameters
hKey
Identifies a currently open key or one of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
The enumerated keys are subkeys of the key identified by hKey.
dwIndex
Specifies the index of the subkey to retrieve. This parameter should be zero for
the first call to the RegEnumKeyEx function and then incremented for subsequent
calls.
Because subkeys are not ordered, any new subkey will have an arbitrary index. Th
is means that the function may return subkeys in any order.
lpName
Points to a buffer that receives the name of the subkey, including the terminati
ng null character. The function copies only the name of the subkey, not the full
key hierarchy, to the buffer.
lpcbName
Points to a variable that specifies the size, in characters, of the buffer speci
fied by the lpName parameter. This size should include the terminating null char
acter. When the function returns, the variable pointed to by lpcbName contains t
he number of characters stored in the buffer. The count returned does not includ
e the terminating null character.
lpReserved
Reserved; must be NULL.
lpClass
Points to a buffer that contains the class of the enumerated subkey when the fun
ction returns. This parameter can be NULL if the class is not required.
lpcbClass
Points to a variable that specifies the size, in characters, of the buffer speci
fied by the lpClass parameter. The size should include the terminating null char
acter. When the function returns, lpcbClass contains the number of characters st
ored in the buffer. The count returned does not include the terminating null cha
racter. This parameter can be NULL only if lpClass is NULL.
lpftLastWriteTime
Points to a variable that receives the time the enumerated subkey was last writt
en to.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
To enumerate subkeys, an application should initially call the RegEnumKeyEx func
tion with the dwIndex parameter set to zero. The application should then increme
nt the dwIndex parameter and call RegEnumKeyEx until there are no more subkeys (
until the function returns ERROR_NO_MORE_ITEMS).
The application can also set dwIndex to the index of the last subkey on the firs
t call to the function and decrement the index until the subkey with the index 0
is enumerated. To retrieve the index of the last subkey, use the RegQueryInfoKe
y function.
While an application is using the RegEnumKeyEx function, it should not make call
s to any registration functions that might change the key being enumerated.
The key identified by hKey must have been opened with KEY_ENUMERATE_SUB_KEYS acc
ess (KEY_READ includes KEY_ENUMERATE_SUB_KEYS). Use the RegCreateKeyEx or RegOpe
nKeyEx function to open the key.
See Also
RegCreateKeyEx, RegDeleteKey, RegOpenKeyEx, RegQueryInfoKey
23.15 RegEnumValue
The RegEnumValue function enumerates the values for the specified open registry
key. The function copies one indexed value name and data block for the key each
time it is called.
LONG RegEnumValue(
HKEY hKey, // handle of key to query
DWORD dwIndex, // index of value to query
LPTSTR lpValueName, // address of buffer for value string
LPDWORD lpcbValueName, // address for size of value buffer
LPDWORD lpReserved, // reserved
LPDWORD lpType, // address of buffer for type code
LPBYTE lpData, // address of buffer for value data
LPDWORD lpcbData // address for size of data buffer
);
Parameters
hKey
Identifies a currently open key or one of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
The enumerated values are associated with the key identified by hKey.
dwIndex
Specifies the index of the value to retrieve. This parameter should be zero for
the first call to the RegEnumValue function and then be incremented for subseque
nt calls.
Because values are not ordered, any new value will have an arbitrary index. This
means that the function may return values in any order.
lpValueName
Points to a buffer that receives the name of the value, including the terminatin
g null character.
lpcbValueName
Points to a variable that specifies the size, in characters, of the buffer point
ed to by the lpValueName parameter. This size should include the terminating nul
l character. When the function returns, the variable pointed to by lpcbValueName
contains the number of characters stored in the buffer. The count returned does
not include the terminating null character.
lpReserved
Reserved; must be NULL.
lpType
Points to a variable that receives the type code for the value entry. The type c
ode can be one of the following values:
Value Meaning
REG_BINARY Binary data in any form.
REG_DWORD A 32-bit number.
REG_DWORD_LITTLE_ENDIAN A 32-bit number in little-endian format (same as REG_DWO
RD). In little-endian format, the most significant byte of a word is the high-or
der byte. This is the most common format for computers running
REG_DWORD_BIG_ENDIAN A 32-bit number in big-endian format. In big-endian form
at, the most significant byte of a word is the low-order byte.
REG_EXPAND_SZ A null-terminated string that contains unexpanded references to
environment variables (for example, %PATH% ). It will be a Unicode or ANSI string d
epending on whether you use the Unicode or ANSI functions.
REG_LINK A Unicode symbolic link.
REG_MULTI_SZ An array of null-terminated strings, terminated by two null char
acters.
REG_NONE No defined value type.
REG_RESOURCE_LIST A device-driver resource list.
REG_SZ A null-terminated string. It will be a Unicode or ANSI string, depending
on whether you use the Unicode or ANSI functions.
The lpType parameter can be NULL if the type code is not required.
lpData
Points to a buffer that receives the data for the value entry. This parameter ca
n be NULL if the data is not required.
lpcbData
Points to a variable that specifies the size, in bytes, of the buffer pointed to
by the lpData parameter. When the function returns, the variable pointed to by
the lpcbData parameter contains the number of bytes stored in the buffer. This p
arameter can be NULL, only if lpData is NULL.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
To enumerate values, an application should initially call the RegEnumValue funct
ion with the dwIndex parameter set to zero. The application should then incremen
t dwIndex and call the RegEnumValue function until there are no more values (unt
il the function returns ERROR_NO_MORE_ITEMS).
The application can also set dwIndex to the index of the last value on the first
call to the function and decrement the index until the value with index 0 is en
umerated. To retrieve the index of the last value, use the RegQueryInfoKey funct
ion.
While using RegEnumValue, an application should not call any registration functi
ons that might change the key being queried.
The key identified by the hKey parameter must have been opened with KEY_QUERY_VA
LUE access. To open the key, use the RegCreateKeyEx or RegOpenKeyEx function.
To determine the maximum size of the name and data buffers, use the RegQueryInfo
Key function.
See Also
RegCreateKeyEx, RegEnumKeyEx, RegOpenKeyEx, RegQueryInfoKey
23.16 RegFlushKey
The RegFlushKey function writes all the attributes of the specified open key int
o the registry.
LONG RegFlushKey(
HKEY hKey // handle of key to write
);
Parameters
hKey
Identifies a currently open key or one of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS

Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
It is not necessary to call RegFlushKey to change a key. Registry changes are fl
ushed to disk by the registry using its lazy flusher. Registry changes are also
flushed to disk at system shutdown.
Unlike RegCloseKey, the RegFlushKey function returns only when all the data has
been written to the registry.
The RegFlushKey function may also write out parts of or all of the other keys. C
alling this function excessively can have a negative effect on an application s pe
rformance.
An application should only call RegFlushKey if it requires absolute certainty th
at registry changes are on disk. In general, RegFlushKey rarely, if ever, need b
e used.
See Also
RegCloseKey, RegDeleteKey
23.17 RegGetKeySecurity
The RegGetKeySecurity function retrieves a copy of the security descriptor prote
cting the specified open registry key.
LONG RegGetKeySecurity(
HKEY hKey, // open handle of key to set
SECURITY_INFORMATION SecurityInformation, // descriptor contents
PSECURITY_DESCRIPTOR pSecurityDescriptor, // address of descriptor for key
LPDWORD lpcbSecurityDescriptor // address of size of buffer and descrip
tor
);
Parameters
hKey
Identifies an open key for which to retrieve the security descriptor.
SecurityInformation
Specifies a SECURITY_INFORMATION structure that indicates the requested security
information.
The SECURITY_INFORMATION structure has the following form:
typedef DWORD SECURITY_INFORMATION;
For a full description of this structure, see the Microsoft Win32 Programmer's R
eference, Volume 5.
pSecurityDescriptor
Points to a buffer that receives a copy of the requested security descriptor.
lpcbSecurityDescriptor
Points to a variable that specifies the size, in bytes, of the buffer pointed to
by the pSecurityDescriptor parameter. When the function returns, the variable c
ontains the number of bytes written to the buffer.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If the buffer specified by the pSecurityDescriptor parameter is too small, the f
unction returns ERROR_INSUFFICIENT_BUFFER and the lpcbSecurityDescriptor paramet
er contains the number of bytes required for the requested security descriptor.
To read the security descriptor for the specified key, the calling process must
have been granted READ_CONTROL access when the key was opened, or it must be the
owner of the key. (READ_CONTROL access is granted by the KEY_READ, KEY_WRITE, K
EY_EXECUTE, and KEY_ALL_ACCESS access rights.) In addition, the caller must have
the SE_SECURITY_NAME privilege to read the system access-control list (SACL).
See Also
RegDeleteKey, RegOpenKeyEx, RegSetKeySecurity, SECURITY_INFORMATION
23.18 RegLoadKey
The RegLoadKey function creates a subkey under HKEY_USER or HKEY_LOCAL_MACHINE a
nd stores registration information from a specified file into that subkey. This
registration information is in the form of a hive. A hive is a discrete body of
keys, subkeys, and values that is rooted at the top of the registry hierarchy. A
hive is backed by a single file and .LOG file.
LONG RegLoadKey(
HKEY hKey, // handle of open key
LPCTSTR lpSubKey, // address of name of subkey
LPCTSTR lpFile // address of filename for registry information
);
Parameters
hKey
Specifies the key where the subkey will be created. This can be a predefined res
erved handle value, or a handle returned by a call to RegConnectRegistry. The pr
edefined reserved handle values are:
HKEY_LOCAL_MACHINE
HKEY_USERS
This function always loads information at the top of the registry hierarchy. The
HKEY_CLASSES_ROOT and HKEY_CURRENT_USER handle values cannot be specified for t
his parameter, because they represent subsets of the HKEY_LOCAL_MACHINE and HKEY
_USERS handle values, respectively.
lpSubKey
Points to a null-terminated string that specifies the name of the key to be crea
ted under hKey. This subkey is where the registration information from the file
will be loaded.
lpFile
Points to a null-terminated string containing the name of a file that has regist
ration information. This file must have been created with the RegSaveKey functio
n. Under the file allocation table (FAT) file system, the filename may not have
an extension.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If hKey is a handle returned by RegConnectRegistry, then the path specified in l
pFile is relative to the remote computer.
See Also
RegConnectRegistry, RegDeleteKey, RegReplaceKey, RegRestoreKey, RegSaveKey, RegU
nloadKey
23.19 RegNotifyChangeKeyValue
The RegNotifyChangeKeyValue function notifies the caller about changes to the at
tributes or contents of a specified registry key. Note that the function does no
t notify the caller if the specified key is deleted.
LONG RegNotifyChangeKeyValue(
HKEY hKey, // handle of key to watch
BOOL bWatchSubtree, // flag for subkey notification
DWORD dwNotifyFilter, // changes to be reported
HANDLE hEvent, // handle of signaled event
BOOL fAsynchronous // flag for asynchronous reporting
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
bWatchSubtree
Specifies a flag that indicates whether to report changes in the specified key a
nd all of its subkeys or only in the specified key. If this parameter is TRUE, t
he function reports changes in the key and its subkeys. If the parameter is FALS
E, the function reports changes only in the key.
dwNotifyFilter
Specifies a set of flags that control which changes should be reported. This par
ameter can be a combination of the following values:
Value Meaning
REG_NOTIFY_CHANGE_NAME Notify the caller if a subkey is added or deleted.
REG_NOTIFY_CHANGE_ATTRIBUTES Notify the caller of changes to the attributes o
f the key, such as the security descriptor information.
REG_NOTIFY_CHANGE_LAST_SET Notify the caller of changes to a value of the k
ey. This can include adding or deleting a value, or changing an existing value.
REG_NOTIFY_CHANGE_SECURITY Notify the caller of changes to the security des
criptor of the key.
hEvent
Identifies an event. If the fAsynchronous parameter is TRUE, the function return
s immediately and changes are reported by signaling this event. If fAsynchronous
is FALSE, hEvent is ignored.
fAsynchronous
Specifies a flag that indicates how the function reports changes. If this parame
ter is TRUE, the function returns immediately and reports changes by signaling t
he specified event. When this parameter is FALSE, the function does not return u
ntil a change has occurred.
If hEvent does not specify a valid event, the fAsynchronous parameter cannot be
TRUE.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If the key identified by the hKey parameter is closed, the event is signaled. Th
is means that an application should not depend on the key being open after retur
ning from a wait operation on the event.
RegNotifyChangeKeyValue does not work with remote handles. If RegNotifyChangeKey
Value is called with an hKey value that is a remote handle, it returns ERROR_INV
ALID_HANDLE.
See Also
RegDeleteKey, RegEnumKeyEx, RegEnumValue, RegQueryInfoKey, RegQueryValueEx
23.20 RegOpenKeyEx
The RegOpenKeyEx function opens the specified key.
LONG RegOpenKeyEx(
HKEY hKey, // handle of open key
LPCTSTR lpSubKey, // address of name of subkey to open
DWORD ulOptions, // reserved
REGSAM samDesired, // security access mask
PHKEY phkResult // address of handle of open key
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpSubKey
Points to a null-terminated string containing the name of the subkey to open. If
this parameter is NULL or a pointer to an empty string, the function will open
a new handle of the key identified by the hKey parameter. In this case, the func
tion will not close the handles previously opened.
ulOptions
Reserved; must be zero.
samDesired
Specifies an access mask that describes the desired security access for the new
key. This parameter can be a combination of the following values:
Value Meaning
KEY_ALL_ACCESS Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, KEY_NOTI
FY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and KEY_SET_VALUE access.
KEY_CREATE_LINK Permission to create a symbolic link.
KEY_CREATE_SUB_KEY Permission to create subkeys.
KEY_ENUMERATE_SUB_KEYS Permission to enumerate subkeys.
KEY_EXECUTE Permission for read access.
KEY_NOTIFY Permission for change notification.
KEY_QUERY_VALUE Permission to query subkey data.
KEY_READ Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_
NOTIFY access.
KEY_SET_VALUE Permission to set subkey data.
KEY_WRITE Combination of KEY_SET_VALUE and KEY_CREATE_SUB_KEY access.

phkResult
Points to a variable that receives the handle of the opened key.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
Unlike the RegCreateKeyEx function, the RegOpenKeyEx function does not create th
e specified key if the key does not exist in the registry.
See Also
RegCloseKey, RegCreateKeyEx, RegDeleteKey,
23.21 RegQueryInfoKey
The RegQueryInfoKey function retrieves information about a specified registry ke
y.
LONG RegQueryInfoKey (
HKEY hKey, // handle of key to query
LPTSTR lpClass, // address of buffer for class string
LPDWORD lpcbClass, // address of size of class string buffer
LPDWORD lpReserved, // reserved
LPDWORD lpcSubKeys, // address of buffer for number of subkeys
LPDWORD lpcbMaxSubKeyLen, // address of buffer for longest subkey name len
gth
LPDWORD lpcbMaxClassLen, // address of buffer for longest class string le
ngth
LPDWORD lpcValues, // address of buffer for number of value entries
LPDWORD lpcbMaxValueNameLen, // address of buffer for longest value n
ame length
LPDWORD lpcbMaxValueLen, // address of buffer for longest value data leng
th
LPDWORD lpcbSecurityDescriptor, // address of buffer for security descri
ptor length
PFILETIME lpftLastWriteTime // address of buffer for last write time

);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpClass
Points to a buffer that receives the key s class name. This parameter can be NULL.
lpcbClass
Points to a variable that specifies the size, in characters, of the buffer point
ed to by the lpClass parameter. This size should include the terminating null ch
aracter. When the function returns, this variable contains the length of the cla
ss string stored in the buffer. The count returned does not include the terminat
ing null character. If the buffer is not big enough, the function returns ERROR_
MORE_DATA, and the variable contains the size of the string, in characters, with
out counting the null character.
If lpClass is NULL, lpcbClass can be NULL.
lpReserved
Reserved; must be NULL.
lpcSubKeys
Points to a variable that receives the number of subkeys contained by the specif
ied key. This parameter can be NULL.
lpcbMaxSubKeyLen
Points to a variable that receives the length, in characters, of the key s subkey
with the longest name. The count returned does not include the terminating null
character. This parameter can be NULL.
lpcbMaxClassLen
Points to a variable that receives the length, in characters, of the longest str
ing specifying a subkey class. The count returned does not include the terminati
ng null character. This parameter can be NULL.
lpcValues
Points to a variable that receives the number of values associated with the key.
This parameter can be NULL.
lpcbMaxValueNameLen
Points to a variable that receives the length, in characters, of the key s longest
value name. The count returned does not include the terminating null character.
This parameter can be NULL.
lpcbMaxValueLen
Points to a variable that receives the length, in bytes, of the longest data com
ponent among the key s values. This parameter can be NULL.
lpcbSecurityDescriptor
Points to a variable that receives the length, in bytes, of the key s security des
criptor. This parameter can be NULL.
lpftLastWriteTime
Pointer to a FILETIME structure. This parameter can be NULL.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The key identified by the hKey parameter must have been opened with KEY_QUERY_VA
LUE access (KEY_READ access includes KEY_QUERY_VALUE access).
See Also
FILETIME, RegDeleteKey, RegEnumKeyEx, RegEnumValue, RegQueryValueEx
23.22 RegQueryMultipleValues
The RegQueryMultipleValues function retrieves the type and data for a list of va
lue names associated with an open registry key.
LONG RegQueryMultipleValues(
HKEY hKey, // handle of key to query
PVALENT val_list, // address of array of value entry structures
DWORD num_vals, // size of array of value entry structures
LPTSTR lpValueBuf, // address of buffer for value information
LPDWORD ldwTotsize // address of size of value buffer
);
Parameters
hKey
Identifies a currently open key or any of the pre-defined reserved handle values
:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
val_list
Address of an array of VALENT structures that describe one or more value entries
. On input, the ve_valuename member of each structure must contain a pointer to
the name of a value to retrieve. The function fails if any of the specified valu
es do not exist in the specified key.
If the function succeeds, each element of the array contains the information for
the specified value.
num_vals
Specifies the number of elements in the val_list array.
lpValueBuf
Pointer to a buffer. If the function succeeds, the buffer receives the data for
each value.
If lpValueBuf is NULL, the function returns success, and ldwTotsize returns the
required size, in bytes, of the buffer.
ldwTotsize
Pointer to a value that specifies the size, in bytes, of the buffer pointed to b
y the lpValueBuf parameter. If the function succeeds, ldwTotsize returns the num
ber of bytes copied to the buffer. If the function fails because the buffer is t
oo small, ldwTotsize receives the required size, in bytes.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is one of the following error codes:
Value Meaning
ERROR_CANTREAD RegQueryMultipleValues cannot instantiate or access the provider
of the dynamic key.
ERROR_MORE_DATA The buffer pointed to by lpValueBuf was too small. In this case,
ldwTotsize returns the required buffer size.
ERROR_TRANSFER_TOO_LONG The total length of the requested data (size of the val_
list array + ldwTotSize) is more than the system limit of one megabyte.
Remarks
The RegQueryMultipleValues function allows an application to query one or more v
alues of a static or dynamic key. If the target key is a static key, the system
provides all of the values in an atomic fashion. To prevent excessive serializat
ion, the aggregate data returned by the function can not exceed one megabyte.
If the target key is a dynamic key, its provider must provide all the values in
an atomic fashion. This means the provider should fill the results buffer synchr
onously, providing a consistent view of all the values in the buffer while avoid
ing excessive serialization. The provider can provide at most one megabyte of to
tal output data during an atomic call to this function.
RegQueryMultipleValues is supported remotely; that is, the hKey parameter passed
to the function can refer to a remote computer.
See Also
VALENT
23.23 RegQueryValueEx
The RegQueryValueEx function retrieves the type and data for a specified value n
ame associated with an open registry key.
LONG RegQueryValueEx(
HKEY hKey, // handle of key to query
LPTSTR lpValueName, // address of name of value to query
LPDWORD lpReserved, // reserved
LPDWORD lpType, // address of buffer for value type
LPBYTE lpData, // address of data buffer
LPDWORD lpcbData // address of data buffer size
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpValueName
Points to a null-terminated string containing the name of the value to be querie
d.
lpReserved
Reserved; must be NULL.
lpType
Points to a variable that receives the key s value type. The value returned throug
h this parameter will be one of the following:
Value Meaning
REG_BINARY Binary data in any form.
REG_DWORD A 32-bit number.
REG_DWORD_LITTLE_ENDIAN A 32-bit number in little-endian format (same as REG_DWO
RD). In little-endian format, the most significant byte of a word is the high-or
der byte. This is the most common format for computers running
REG_DWORD_BIG_ENDIAN A 32-bit number in big-endian format. In big-endian form
at, the most significant byte of a word is the low-order byte.
REG_EXPAND_SZ A null-terminated string that contains unexpanded references to
environment variables (for example, %PATH% ). It will be a Unicode or ANSI string d
epending on whether you use the Unicode or ANSI functions.
REG_LINK A Unicode symbolic link.
REG_MULTI_SZ An array of null-terminated strings, terminated by two null char
acters.
REG_NONE No defined value type.
REG_RESOURCE_LIST A device-driver resource list.
REG_SZ A null-terminated string. It will be a Unicode or ANSI string depending
on whether you use the Unicode or ANSI functions.
The lpType parameter can be NULL if the type is not required.
lpData
Points to a buffer that receives the value s data. This parameter can be NULL if t
he data is not required.
lpcbData
Pointer to a variable that specifies the size, in bytes, of the buffer pointed t
o by the lpData parameter. When the function returns, this variable contains the
size of the data copied to lpData.
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, then lpcbData wi
ll also include the size of the terminating null character.
The lpcbData parameter can be NULL only if lpData is NULL.
If the buffer specified by lpData parameter is not large enough to hold the data
, the function returns the value ERROR_MORE_DATA, and stores the required buffer
size, in bytes, into the variable pointed to by lpcbData.
If lpData is NULL, and lpcbData is non-NULL, the function returns ERROR_SUCCESS,
and stores the size of the data, in bytes, in the variable pointed to by lpcbDa
ta. This lets an application determine the best way to allocate a buffer for the
value s data.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The key identified by hKey must have been opened with KEY_QUERY_VALUE access. To
open the key, use the RegCreateKeyEx or RegOpenKeyEx function.
This function does not expand the environment-variable names in the value data w
hen the value type is REG_EXPAND_SZ
If the value data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, and the AN
SI version of this function is used (either by explicitly calling RegQueryValueE
xA or by not defining UNICODE before including the WINDOWS.H file), this functio
n converts the stored Unicode string to an ANSI string before copying it to the
buffer pointed to by lpData.
See Also
RegCreateKeyEx, RegEnumKeyEx, RegOpenKeyEx, RegQueryInfoKey,
23.24 RegReplaceKey
The RegReplaceKey function replaces the file backing a key and all its subkeys w
ith another file, so that when the system is next started, the key and subkeys w
ill have the values stored in the new file.
LONG RegReplaceKey(
HKEY hKey, // handle of open key
LPCTSTR lpSubKey, // address of name of subkey
LPCTSTR lpNewFile, // address of filename for file with new data
LPCTSTR lpOldFile // address of filename for backup file
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpSubKey
Points to a null-terminated string containing the name of a key whose subkeys an
d values are replaced by this function. This key must be a subkey of the key ide
ntified by the hKey parameter. This parameter can be NULL.
The selected key must be the root of a hive; that is, it must be an immediate de
scendent of HKEY_LOCAL_MACHINE or HKEY_USERS.
lpNewFile
Points to a null-terminated string containing the name of the file with registra
tion information. This file is typically created by using the RegSaveKey functio
n. Under the file allocation table (FAT) file system, the filename may not have
an extension.
lpOldFile
Points to a null-terminated string containing the name of a file that receives a
backup copy of the registry information being replaced. If this file is created
under the FAT file system, it should not have an extension.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
The file specified by the lpNewFile parameter remains open until the system is r
estarted.
If hKey is a handle returned by RegConnectRegistry, then the paths specified in
lpNewFile and lpOldFile are relative to the remote computer.
See Also
RegConnectRegistry, RegDeleteKey, RegLoadKey, RegRestoreKey
23.25 RegRestoreKey
The RegRestoreKey function reads the registry information in a specified file an
d copies it over the specified key. This registry information may be in the form
of a key and multiple levels of subkeys.
LONG RegRestoreKey(
HKEY hKey, // handle of key where restore begins
LPCTSTR lpFile, // address of filename containing saved tree
DWORD dwFlags // optional flags
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
Any information contained in this key and its descendent keys is overwritten by
the information in the file pointed to by the lpFile parameter.
lpFile
Points to a null-terminated string containing the name of the file with registry
information. This file is typically created by using the RegSaveKey function. U
nder the file allocation table (FAT) file system, the filename may not have an e
xtension.
dwFlags
Specifies a flag indicating whether the key is volatile. (A volatile key is vali
d only until the next time the system is started.) This parameter is optional; i
f no value is specified, the key is not volatile.
This parameter can be the REG_WHOLE_HIVE_VOLATILE flag set. Instead of restoring
the given key, this flag causes a new, volatile (memory only) set of registry i
nformation to be created. (A hive is a large set of registry information, typica
lly containing all of the pertinent information for part of the system. For exam
ple, HKEY_LOCAL_MACHINE\Hardware is a volatile hive.)
If REG_WHOLE_HIVE_VOLATILE is specified, the key identified by the hKey paramete
r must be either the HKEY_USERS or HKEY_LOCAL_MACHINE value.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If any subkeys of the hKey parameter are open, RegRestoreKey fails. The function
also fails if the calling process does not have the SE_RESTORE_NAME privilege.
For more information about privileges, see Privileges.
This function replaces the keys and values below the specified key with the keys
and values that are subsidiary to the top-level key in the file, no matter what
the name of the top-level key in the file might be. For example, hKey might ide
ntify a key A with subkeys B and C, while the lpFile parameter specifies a file
containing key X with subkeys Y and Z. After a call to RegRestoreKey, the regist
ry would contain key A with subkeys Y and Z. The value entries of A would be rep
laced by the value entries of X.
The new information in the file specified by lpFile overwrites the contents of t
he key specified by the hKey parameter, except for the key name.
If hKey represents a key in a remote computer, the path described by lpFile is r
elative to the remote computer.
See Also
RegDeleteKey, RegLoadKey, RegReplaceKey, RegSaveKey
23.26 RegSaveKey
The RegSaveKey function saves the specified key and all of its subkeys and value
s to a new file.
LONG RegSaveKey(
HKEY hKey, // handle of key where save begins
LPCTSTR lpFile, // address of filename to save to
LPSECURITY_ATTRIBUTES lpSecurityAttributes // address of security structure
);
Parameters
hKey
Specifies a handle of the key where the save operation is to begin, or any of th
e following predefined reserved handle values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpFile
Points to a null-terminated string containing the name of the file in which the
specified key and subkeys are saved.
If this filename includes an extension, it cannot be used on file allocation tab
le (FAT) file systems by the RegLoadKey, RegReplaceKey, or RegRestoreKey functio
n.

lpSecurityAttributes
This parameter is ignored.

Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
If hKey represents a key on a remote computer, the path described by lpFile is r
elative to the remote computer.
The RegSaveKey function saves only nonvolatile keys. It does not save volatile k
eys. A key is made volatile or nonvolatile at its creation; see RegCreateKeyEx.

See Also
RegCreateKeyEx, RegDeleteKey, RegLoadKey, RegReplaceKey, RegRestoreKey, SECURITY
_ATTRIBUTES
23.27 RegSetKeySecurity
The RegSetKeySecurity function sets the security of an open registry key.
LONG RegSetKeySecurity(
HKEY hKey, // open handle of key to set
SECURITY_INFORMATION SecurityInformation, // descriptor contents
PSECURITY_DESCRIPTOR pSecurityDescriptor // address of descriptor for key
);
Parameters
hKey
Identifies an open key for which the security descriptor is set.
SecurityInformation
Specifies a SECURITY_INFORMATION structure that indicates the contents of the su
pplied security descriptor.
Because subkeys are not ordered, any new subkey will have an arbitrary index. Th
is means the function may return subkeys in any order.
pSecurityDescriptor
Points to a SECURITY_DESCRIPTOR structure that specifies the security attributes
to set for the specified key.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
This function succeeds only if the following conditions are met:
· If the key s owner or group is being set, the caller must have WRITE_OWNER permiss
ion or have the SE_TAKE_OWNERSHIP_NAME privilege.
· If the key s discretionary access-control list (DACL) is being set, the caller mus
t have WRITE_DAC permission or be the object s owner.
· If the key s system access-control list (SACL) is being set, the caller must have
the SE_SECURITY_NAME privilege.
If hKey is one of the predefined keys, the predefined key should be closed with
RegCloseKey. That ensures that the new security information is in effect the nex
t time the predefined key is referenced.
See Also
RegCloseKey, RegDeleteKey, RegGetKeySecurity, SECURITY_INFORMATION
23.28 RegSetValueEx
The RegSetValueEx function stores data in the value field of an open registry ke
y. It can also set additional value and type information for the specified key.
LONG RegSetValueEx(
HKEY hKey, // handle of key to set value for
LPCTSTR lpValueName, // address of value to set
DWORD Reserved, // reserved
DWORD dwType, // flag for value type
CONST BYTE *lpData, // address of value data
DWORD cbData // size of value data
);
Parameters
hKey
Identifies a currently open key or any of the following predefined reserved hand
le values:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpValueName
Points to a string containing the name of the value to set. If a value with this
name is not already present in the key, the function adds it to the key.
If this parameter is NULL or points to an empty string and the dwType parameter
is the REG_SZ type, this function sets the same value the RegSetValuefunction wo
uld set.
Reserved
Reserved; must be zero.
dwType
Specifies the type of information to be stored as the value s data. This parameter
can be one of the following values:
Value Meaning
REG_BINARY Binary data in any form.
REG_DWORD A 32-bit number.
REG_DWORD_LITTLE_ENDIAN A 32-bit number in little-endian format (same as REG_DWO
RD). In little-endian format, the most significant byte of a word is the high-or
der byte. This is the most common format for computers running
REG_DWORD_BIG_ENDIAN A 32-bit number in big-endian format. In big-endian form
at, the most significant byte of a word is the low-order byte.
REG_EXPAND_SZ A null-terminated string that contains unexpanded references to
environment variables (for example, %PATH% ). It will be a Unicode or ANSI string d
epending on whether you use the Unicode or ANSI functions.
REG_LINK A Unicode symbolic link.
REG_MULTI_SZ An array of null-terminated strings, terminated by two null char
acters.
REG_NONE No defined value type.
REG_RESOURCE_LIST A device-driver resource list.
REG_SZ A null-terminated string. It will be a Unicode or ANSI string depending
on whether you use the Unicode or ANSI functions.
lpData
Points to a buffer containing the data to be stored with the specified value nam
e.
cbData
Specifies the size, in bytes, of the information pointed to by the lpData parame
ter. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must
include the size of the terminating null character.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
Value lengths are limited by available memory. Long values (more than 2048 bytes
) should be stored as files with the filenames stored in the registry. This help
s the registry perform efficiently. Application elements such as icons, bitmaps,
and executable files should be stored as files and not be placed in the registr
y.
The key identified by the hKey parameter must have been opened with KEY_SET_VALU
E access. To open the key, use the RegCreateKeyEx or RegOpenKeyEx function.
If dwType is the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type and the ANSI version
of this function is used (either by explicitly calling RegSetValueExA or by not
defining UNICODE before including the WINDOWS.H file), the data pointed to by t
he lpData parameter must be an ANSI character string. The string is converted to
Unicode before it is stored in the registry.
See Also
RegCreateKeyEx, RegFlushKey, RegOpenKeyEx, RegQueryValueEx,
23.29 RegUnLoadKey
The RegUnLoadKey function unloads the specified key and subkeys from the registr
y.
LONG RegUnLoadKey(
HKEY hKey, // handle of open key
LPCTSTR lpSubKey // address of name of subkey to unload
);
Parameters
hKey
Specifies the key to be unloaded. This can be a predefined reserved handle value
, or a handle returned by a call to RegConnectRegistry. The predefined reserved
handle values are:
HKEY_LOCAL_MACHINE
HKEY_USERS
lpSubKey
Points to a null-terminated string containing the name of the subkey to be unloa
ded. The key referred to by the lpSubKey parameter must have been created by usi
ng the RegLoadKey function.
Return Values
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in WINER
ROR.H. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTE
M flag to get a generic description of the error.
Remarks
This function removes a hive from the registry but does not modify the file cont
aining the registry information. A hive is a discrete body of keys, subkeys, and
values that is rooted at the top of the registry hierarchy.
See Also
RegConnectRegistry, RegDeleteKey, RegLoadKey, RegRestoreKey
23.30 Registry Structures
23.31 VALENT
The VALENT structure contains information about a registry value. The RegQueryMu
ltipleValues function uses this structure.
typedef struct value_ent {
LPTSTR ve_valuename;
DWORD ve_valuelen;
DWORD ve_valueptr;
DWORD ve_type;
}VALENT;
Members
ve_valuename
Pointer to a null-terminated string. Before calling RegQueryMultipleValues, set
this member to point to the name of a value to retrieve.
ve_valuelen
Specifies the size, in bytes, of the data pointed to by ve_valueptr.
ve_valueptr
Pointer to the data for the value entry. This is a pointer to the value s data ret
urned in the lpValueBuf buffer filled in by RegQueryMultipleValues.
ve_type
Specifies the type code for the value entry. The type code can be one of the fol
lowing values:
Value Meaning
REG_BINARY Binary data in any form.
REG_DWORD A 32-bit number.
REG_DWORD_LITTLE_ENDIAN A 32-bit number in little-endian format (same as REG_DWO
RD). In little-endian format, the most significant byte of a word is the high-or
der byte. This is the most common format for computers running
REG_DWORD_BIG_ENDIAN A 32-bit number in big-endian format. In big-endian form
at, the most significant byte of a word is the low-order byte.
REG_EXPAND_SZ A null-terminated string that contains unexpanded references to
environment variables (for example, %PATH% ). It will be a Unicode or ANSI string d
epending on whether you use the Unicode or ANSI functions.
REG_LINK A Unicode symbolic link.
REG_MULTI_SZ An array of null-terminated strings, terminated by two null char
acters.
REG_NONE No defined value type.
REG_RESOURCE_LIST A device-driver resource list.
REG_SZ A null-terminated string. It will be a Unicode or ANSI string, depending
on whether you use the Unicode or ANSI functions.
See Also
RegQueryMultipleValues
24. Security Support Provider Iinterface
The Security Support Provider Interface (SSPI) provides a common interface betwe
en transport-level applications and security providers. SSPI provides a mechanis
m by which a distributed application can call one of several security providers
to obtain an authenticated connection without knowledge of the details of the se
curity protocol.
SSPI consists of following APIs:
Credential Management APIs Credential Management APIs provide access to credential
s (password data, tickets, and so on) of a principal or free such access. The AP
Is are:
AcquireCredentialsHandle This method acquires a handle to the reference credential
s.
FreeCredentialsHandle This method releases a credential handle and associated reso
urces.
QueryCredentialAttributes This method allows queries on various credential attribu
tes like associated name, domain name, and so forth.
Context Management APIs Context management APIs provide methods for creating and u
sing security contexts. The contexts are created on both the client and the serv
er side of a communication link. These contexts can then be used later with the
message support APIs. The APIs are:
InitializeSecurityContext Initiates a security context by generating an opaque mes
sage (security token) that can be passed to the server.
AcceptSecurityContext Creates a security context using the opaque message received
from the client.
DeleteSecurityContext Frees a security context and associated
resources.
QueryContextAttributes Allows queries on various context attributes.
ApplyControlToken Applies a supplemental security message to an existing security
context.
CompleteAuthToken Completes an authentication token, since some protocols, like DC
E RPC, need to revise the security information once the transport has updated so
me message fields.
ImpersonateSecurityContext Attaches the client s security context as an impersonatio
n token to the calling thread.
RevertSecurityContext Ceases impersonation and defaults the calling thread to its
primary token.
Message Support APIs Message support APIs provide communication integrity and priv
acy services based on a security context. The APIs are:
MakeSignature Generates a secure signature based on a message and a security conte
xt.
VerifySignature Verifies that the signature matches a received message.
Package Management APIs Package Managment APIs provide services for different secu
rity packages that the security provider supports. The APIs are:
EnumerateSecurityPackages Lists available security packages and their capabilities
.
QuerySecurityPackageInfo Queries an individual security package for its capabiliti
es.
SSPI does not currently provide any public interfaces for encryption/decryption
functionality. Future versions of the SSPI will make message support routines fo
r encryption available.
A security provider is a dynamic-link library that implements the Security Suppo
rt Provider Interface and makes one or more security packages available to appli
cations. A security package maps the SSPI functions to an implementation of the
security protocol specific to that package, such as NTLM, Kerberos, or SSL. Secu
rity packages are sometimes referred to as SSPs, such as the NTLM SSP. The name of t
he security package is used in the initialization step to identify a specific pa
ckage.
The Security Support Provider Interface allows an application to use any of the
available security packages on a system without changing the interface to use se
curity services. SSPI does not establish logon credentials because that is gener
ally a privileged operation handled by the operating system.
An application can use the package management functions to list the security pac
kages available and select one to support its needs. The application then uses t
he credential management functions to obtain a handle to the credentials of the
user on whose behalf they are executing. With this handle, the application can u
se the context management functions to create a security context to a service. A
security context is an opaque data structure that contains the security data re
levant to a connection, such as a session key, the duration of the session, and
so on. Finally, the application uses the security context with the message suppo
rt functions to ensure message integrity and privacy during the connection.
24.1 Security Package Capabilities
The capabilities of the security package determine what services it provides to
the application. These capabilities include, for example, support for client-onl
y authentication or mutual authentication, or support for message integrity and
message privacy. In addition, some packages are designed for use only on reliabl
e transport protocols and are not designed for use on datagram transports.
The security package capabilities available by a specific package are obtained u
sing the QuerySecurityPackageInfo API. The following lists show the security pac
kage capabilities:
Authentication-related capabilities:
Client-only authentication
Multileg authentication required
Transport-related capabilities:
Datagram-style transports
Connection-oriented transports
Data stream connection semantics
Message-related capabilities
Supports message integrity
Supports message privacy
Applications will typically select security packages based on the type of securi
ty capabilities available to meet the application needs. More discussion on secu
rity package capabilities can be found in the section below on Security Context
Semantics.
24.2 Initializing the Security Provider
This section describes how applications-level protocols initialize and use the S
ecurity Support Provider Interface. The section describes various stages of a se
cure network connection setup. The stages include:
Initializing the SSPI
Establishing an authenticated connection
Ensuring communication integrity during message exchange
Security quality of service to service a client request
These stages are described in the following sections.
24.2.1 Initializing the SSPI
Both the client and server use the same sequence of operations to initialize the
security provider and select the appropriate security package.
Initializing the security interface involves the following steps:
Load the security provider DLL
Get a pointer to the provider initialization function
Use the initialization function to get a reference to the provider s security func
tion table
Get specific information about the security package, such as the maximum token s
ize
The security function table contains the SSPI entry points for the security pack
age. The function table is used to invoke the calls implemented by the security
package.
24.3 Loading the Security Provider DLL
In order to initialize security, we need to load the provider. In all our discussi
ons it will be assumed that the client side of the provider is a DLL..
The provider is loaded using a call to the LoadLibrary function, shown in the ex
ample below:
void * DllHandle;
//loading NTLM SSP
DllHandle = (void *)LoadLibrary(TEXT( security.dll );
if(!DllHandle)
{
//
// DLL did not get loaded.
//
Status = GetLastError();
return Status;
}
//
// DllHandle is valid
//
NTLM
MSN
Schannel (SSL/ Private Communications Technology [PCT])
24.4 Provider Initialization
Once the provider has been loaded successfully, you need to perform some setup t
o use the security interface conveniently in the rest of the application. First,
you need to get a pointer to the initialization function for the provider. Then
you will use the initialization function to get a reference to the provider s sec
urity function table. Finally, you can get information from the provider about t
he security packages, or protocols, supported by this security provider. Each se
curity package may have unique capabilities of interest to the application. Howe
ver, in most cases, applications use security packages that support default or c
ommon capabilities.
The example below shows how to initialize the security provider.
//
// Initial provider setup.
//
INIT_SECURITY_INTERFACE InitSecurityInterface;
PSecurityFunctionTable SecurityInterface = 0;
SecPkgInfo PAPI * SecurityPackages;
DWORD NumOfPkgs;
SECURITY_PROVIDER_INFO PAPI * List;
InitSecurityInterface = GetProcAddress(DllHandle, SECURITY_ENDPOINT);
if(!InitSecurityInterface)
{
//
// Something is amiss..
//
}
//
// We got the InitSecurityInterface!
// Now use it to get the function table.
//
SecurityInterface = (*InitSecurityInterface)();
if(!SecurityInterface)
{
//
// we have a problem
//
}
//
// Lets find out the security packages supported by the provider.
//
Status = (*SecurityInterface->EnumerateSecurityPackages)( &NumOfPkgs, &SecurityP
ackages);
//
// Now using the capabilities information figure out which package you want to u
se.
//
PkgToUseIndex = -1;
for(I=0;I<NumOfPackages;I++)
{
//
// for example, if app needs integrity & privacy on messages, it checks
//
if(SecurityPackages[I].fCapabilities & (SECPKG_FLAG_INTEGRITY | SECPKG_F
LAG_PRIVACY))
{
PkgToUseIndex = I;
break;
}
}
if(PkgToUseIndex > 0)
{
//
// Find out the maximum token size for this package
//
g_MaxToken = SecurityPackages[I].cbMaxToken;
}
Both the client and server need to agree on the security package they will use b
efore the SSPI initialization steps shown above.
At this point the application has successfully initialized a security support pr
ovider and chosen a security package with sufficient capabilities needed by the
application protocol. The SecurityInterface points to an array of function point
ers as defined by SSPI.
Notice that the call to EnumerateSecurityPackages initializes the reference poin
ter SecurityPackages, with return data. Some SSPI functions have return output p
arameters, such as security package information. For the output data parameters,
the caller passes in a pointer to a pointer to the return structure type, and t
he security provider allocates memory and returns the data to the caller by assi
gning the address of the return data buffer to the argument. The convention used
by SSPI to return data is the following:
The security package allocates, and the caller frees.
Therefore, the calling program will use FreeContextBuffer to free the memory con
taining data allocated by the security provider when it is done referencing the
data. The examples below will continue to reference SecurityPackages information
, so it must be freed later.
24.5 Security Function Table
The Security Function Table is an array of function pointers which are defined i
n the include file, SSPI.H. The function names correspond to the interface speci
fication for SSPI.
The definition of the Security Function Table is shown below:
typedef struct _SECURITY_FUNCTION_TABLE_W {
unsigned long dwVersion;
ENUMERATE_SECURITY_PACKAGES_FN_W EnumerateSecurityPackagesW;
void SEC_FAR * Reserved1;
// QUERY_CREDENTIALS_ATTRIBUTES_FN_W QueryCredentialsAttributesW;
ACQUIRE_CREDENTIALS_HANDLE_FN_W AcquireCredentialsHandleW;
FREE_CREDENTIALS_HANDLE_FN FreeCredentialHandle;
void SEC_FAR * Reserved2;
INITIALIZE_SECURITY_CONTEXT_FN_W InitializeSecurityContextW;
ACCEPT_SECURITY_CONTEXT_FN AcceptSecurityContext;
COMPLETE_AUTH_TOKEN_FN CompleteAuthToken;
DELETE_SECURITY_CONTEXT_FN DeleteSecurityContext;
APPLY_CONTROL_TOKEN_FN ApplyControlToken;
QUERY_CONTEXT_ATTRIBUTES_FN_W QueryContextAttributesW;
IMPERSONATE_SECURITY_CONTEXT_FN ImpersonateSecurityContext;
REVERT_SECURITY_CONTEXT_FN RevertSecurityContext;
MAKE_SIGNATURE_FN MakeSignature;
VERIFY_SIGNATURE_FN VerifySignature;
FREE_CONTEXT_BUFFER_FN FreeContextBuffer;
QUERY_SECURITY_PACKAGE_INFO_FN_W QuerySecurityPackageInfoW;
void SEC_FAR * Reserved3;
void SEC_FAR * Reserved4;
QUERY_SECURITY_CONTEXT_TOKEN_FN QuerySecurityContextToken;
} SecurityFunctionTableW, SEC_FAR * PSecurityFunctionTableW;
24.5.1 Memory Use, Security Buffers, and Descriptor
Most of the SSPI functions have variable length arguments for the caller (applic
ation) to provide message data to the security package and for the security pack
age to return security data to the caller. SSPI APIs use a parameter type, Buffe
rDescriptor, to define the size and location of the variable length data. Securi
ty buffers are used by the caller, for example, to pass message data to the secu
rity package, or to receive an output security token.
Security buffers can be passed in as an array of buffers. The security buffer de
scriptor identifies the number of buffers and starting address of the buffer arr
ay. Each security buffer also has a buffer type field to identify the contents o
f the buffer.
The definition of security buffers, buffer descriptors, and buffer data types fr
om SSPI.H are shown below:
//
// SecBuffer
//
// Generic memory descriptors for buffers passed in to the security
// API
//
typedef struct _SecBuffer {
unsigned long cbBuffer; // Size of the buffer, in bytes
unsigned long BufferType; // Type of the buffer (below)
void SEC_FAR * pvBuffer; // Pointer to the buffer
} SecBuffer, SEC_FAR * PSecBuffer;
typedef struct _SecBufferDesc {
unsigned long ulVersion; // Version number
unsigned long cBuffers; // Number of buffers
#ifdef MIDL_PASS
[size_is(cBuffers)]
#endif
PSecBuffer pBuffers; // Pointer to array of buffers
} SecBufferDesc, SEC_FAR * PSecBufferDesc;
#define SECBUFFER_VERSION 0
#define SECBUFFER_EMPTY 0 // Undefined, replaced by provider
#define SECBUFFER_DATA 1 // Packet data
#define SECBUFFER_TOKEN 2 // Security token
#define SECBUFFER_PKG_PARAMS 3 // Package specific parameters
#define SECBUFFER_MISSING 4 // Missing Data indicator
#define SECBUFFER_EXTRA 5 // Extra data
#define SECBUFFER_STREAM_TRAILER 6 // Security Trailer
#define SECBUFFER_STREAM_HEADER 7 // Security Header
#define SECBUFFER_ATTRMASK 0xF0000000
#define SECBUFFER_READONLY 0x80000000 // Buffer is read-only
Each time a security API is called that takes a SecBufferDesc parameter, it shou
ld be setup with one or more SecBuffers. For example, there can be two security
buffers, one that contains input message data and the other for the output opaqu
e security token returned by the security package. The order of security buffers
in the security buffer descriptor is not important but they should be tagged wi
th appropriate type. Also, an input buffer that can not be modified by the secur
ity package should additionally be tagged as read only.
The size of the output buffer that is expected to contain the security token is
important. An application can find the maximum token size for a security package
during initial setup. The call to EnumerateSecurityPackages returns an array of
pointers to security package information. The security package information stru
cture contains maximum token size value. In the example code, the information is
in SecPkgInfo.cbMaxToken. It can also be obtained later on using QuerySecurityP
ackageInfo.
The application initializes the buffer pointers and sizes in the buffer descript
ion to indicate where message data and other information may be found.
The example below shows how to initialize an array of security buffers. This par
ticular case shows how input security buffers are initialized by the server-side
of a connection in a call to AcceptSecurityContext. Note that the last buffer c
ontains the opaque security token received by the client and the SECBUFFER_READO
NLY flag is also set.
SecBuffer Buffers[3];
SecBufferDesc BufferDesc;
...
BufferDesc.ulVersion = SECBUFFER_VERSION;
BufferDesc.cBuffers = 3;
BufferDesc.pBuffers = &Buffers;
Buffers[0].cbBuffer = sizeof(Protocol_Header);
Buffers[0].BufferType = SECBUFFER_READONLY | SECBUFFER_DATA;
Buffers[0].pvBuffer = pHeader;
Buffers[1].cbBuffer = pHeader->MessageSize;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[1].pvBuffer = pMessage;
Buffers[2].cbBuffer = pHeader->TrailerSize;
Buffers[2].BufferType = SECBUFFER_READONLY | SECBUFFER_TOKEN;
Buffers[2].pvBuffer = pSecurityTrailer;
24.6 Establishing an Authenticated Connection
In a client/server application protocol, a server typically binds to a well know
n communication port (for example, a socket, RPC interface, and so forth) and wa
its for clients to connect and request service. The role of security at connecti
on setup is two fold:
Server should be able to authenticate the client.
Client should be able to authenticate the server.
Associated with these two basic requirements are other security issues, such as,
the authentication information should not be prone to replay, corruption, and s
o on. The application does not need to worry about how these are handled. It can
simply request it from the chosen provider which will encapsulate the underlyin
g security protocol.
The protocol used to establish an authenticated connection involves the exchange
of one or more security tokens between the security providers on each side. These
tokens are sent as opaque messages by the two sides along with any other applicat
ion protocol specific information. The application level protocol strips the sec
urity token out of the received message and passes on to the security package on
their side to figure out if authentication is complete or if further exchange o
f tokens is required. Theoretically, the exchange of security tokens can continu
e ad infinitum, however, in practice it contains one to three legs of message ex
change.
For example, NTLM authentication is based on the challenge/response scheme, and
uses three legs to authenticate a client to the server, as shown in the figure b
elow.
Figure 24-1 : Using NTLM Challenge Response Authentication Protocol via SSPI
24.6.1 Client Context Initialization
To establish a secure connection, the client needs to acquire an outbound creden
tials handle so that it can send over an authentication request to the server. T
he server creates a security context for the client from the authentication requ
est. There are two client-side SSPI functions involved in authentication setup:
AcquireCredentialsHandle to obtain a reference to previously obtained logon cred
entials
InitializeSecurityContext to create the initial authentication request security
tokens
Using the reference to the Security Function Table initialized during the securi
ty provider setup stage, the client calls AcquireCredentialsHandle as follows:
//
// Acquire an out-bound Credentials handle using the chosen security package.
//
SecurityStatus = (*SecurityInterface->AcquireCredentialsHandle)(
0,
SecurityPackages[PkgToUseIndex].Name,
SECPKG_CRED_OUTBOUND,
0,
0,
0,
0,
&Credentials,
&TimeStamp
);
The arguments to AcquireCredentialHandle are the following:
Arg1 = Principal Name, set to NULL here to let the security package use the defa
ult.
Arg2 = Security Package Name, set to the one that was selected during package se
tup.
Arg3 = Type of credential, the client will use outbound credentials.
Arg4 = Pointer to LogonID, set to NULL to let the security package use the defau
lt.
Arg5 =AuthIdentity, set to NULL to use the process s default credentials. This par
ameter may be used to provide. package specific data. For an NTLM security packa
ge it may contain a pointer to the SEC_WINNT_AUTH_IDENTIY structure that contain
s the username, domainname, and password. This feature is used, for example, by
file system redirectors to allow users to specify an alternate account name than
the one they are currently logged in as when connecting to a remote file server
.
Arg6 = GetKey function, set to NULL, not used.
Arg7 = Any argument to the GetKey function, also set to NULL.
Arg8 = returned Credentials Handle, used for additional SSPI calls.
Arg9 =returned TimeStamp which indicates the lifespan of the credentials handle.
Once the client has acquired an outbound credentials handle, it is ready to star
t the authentication protocol to establish a connection with the server. The app
lication client calls the security package again to initialize the security cont
ext.
To initiate the first leg of the authentication, the client calls InitializeSecu
rityContext to obtain an initial security token that will be sent in a connectio
n request message to the server.
The example of the client call to InitializeSecurityContext is shown below:
//
// Set up the Buffer Descriptor.
//
OutBufferDesc.ulVersion = 0;
OutBufferDesc.cBuffers = 1;
OutBufferDesc.pBuffers = &OutSecBuffer;
OutSecBuffer.cbBuffer = BufferLen;
OutSecBuffer.BufferType = SECBUFFER_TOKEN;
OutSecBuffer.pvBuffer = Buffer;
//
// Lets get the authentication token from the security package
// to send to the server to request an authenticated connection.
//
SecurityStatus = (*SecurityInterface->InitializeSecurityContext(
Credentials,
0,
ServerPrincipalName,
ISC_REQ_USE_DCE_STYLE | ISC_REQ_DELEGATE |
ISC_REQ_MUTUAL_AUTH |ISC_REQ_REPLAY_DETECT |
ISC_REQ_SEQUENCE_DETECT |ISC_REQ_CONFIDENTIALITY
|
ISC_REQ_CONNECTION,
0,
0,
0,
0,
&SecurityContext,
BufferDescriptor,
&ContextAttributes,
&TimeStamp
);
The arguments to InitializeSecurityContext are the following:
Arg1 = Credentials handle received from AcquireCredentialsHandle call.
Arg2 = Old Context handle if any.
Arg3 = Target server name, which is ignored by NTLM SSP.
Arg4 = Context Attributes Requested (See SSPI.H for valid values).
Arg5 = Reserved Parameter.
Arg6 = Data Representation (see SSPI.H for valid values).
Arg7 = Input Buffer Descriptor (if there is one received from the server).
Arg8 = Reserved Parameter.
Arg9 = New Context Handle.
Arg10 = Output Buffer Descriptor (contains what will be sent to the server).
Arg11 = Context Attributes that are supported by the provider.
Arg12 = TimeStamp for the lifespan of context validity.
The client then uses the security token information received in the output buffe
r descriptor to generate a message to send to the server. The construction of th
e message in terms of placement of various buffers and so forth, is part of the
application protocol and should be understood between the two parties.
The client checks the return status from InitializeSecurityContext to see if aut
hentication will complete in a single call. Otherwise it expects to receive a se
rver-side authentication token in a response message to continue the security pr
otocol. The return status SEC_I_CONTINUE_NEEDED, indicates the security protocol
requires multiple authentication messages.
24.6.2 Server Context Initialization
To establish an authenticated connection, the server needs to acquire a credenti
als handle so that it can receive an incoming authentication request from the cl
ient. The server s credentials may be used to authenticate the server in security
protocols that support server authentication or mutual authentication. When a co
nnection request is received, the server creates a local security context to rep
resent the client. The server uses the security context to carry out future requ
ests by the same client.
First, the server obtains a handle to its credentials, which may be defined by t
he service account used to start the server. It does so by calling AcquireCreden
tialsHandle as follows:
//
// Acquire an out-bound Credentials handle using the chosen security package.
//
SecurityStatus = (*SecurityInterface->AcquireCredentialsHandle)(
0,
SecurityPackages[PkgToUseIndex].Name,
SECPKG_CRED_INBOUND,
0,
0,
0,
0,
&Credentials,
&TimeStamp
);
The arguments to the server-side call to AcquireCredentialHandle are as follows:
Arg1 = Principal Name, set to NULL here to let the security package use the defa
ult
Arg2 = Security Package Name, set to the one that was selected at initialization
Arg3 = Type of credentials, inbound for a server, use SECPKG_CRED_BOTH if this s
erver is going to be a client to another server.
Arg4 = Pointer to LogonID (set to NULL to let the security package use the defau
lt).
Arg5 = AuthIdentity Package specific authentication data. Since NTLM does not su
pport server authentication, this can be NULL. For other security providers, thi
s can be server authentication data, such as public key credentials.
Arg6 = GetKey function (set to NULL)
Arg7 = Any argument to the GetKey function (also set to NULL)
Arg8 = Returned Credentials Handle.
Arg9 = Returned TimeStamp which indicates the life span of the credentials handl
e.
The returned Credentials Handle should be assigned to a global variable that is
used for the lifetime of the server process. The returned TimeStamp is a tempora
ry variable.
The server can wait (in a listen state) until a connection request arrives befor
e acquiring an inbound credentials handle or it may acquire the handle and then
go into a listen state.
When the server receives a connection request message from a client, it creates
a security context for the client using AcceptSecurityContext. The server initia
lizes the SecurityBufferDescriptors to refer to sections of the data message rec
eived, rather than copying data to an alternate buffer.
The following example shows the call to AcceptSecurityContext.
//
// Set up the Input and OutputBuffer Descriptor using the information from messa
ge received
// from the client.
//
OutBufferDesc.ulVersion = 0;
OutBufferDesc.cBuffers = 1;
OutBufferDesc.pBuffers = &OutSecBuffer;
OutSecBuffer.cbBuffer = BufferLen;
OutSecBuffer.BufferType = SECBUFFER_TOKEN;
OutSecBuffer.pvBuffer = Buffer;
InBufferDesc.ulVersion = 0;
InBufferDesc.cBuffers = 1;
InBufferDesc.pBuffers = &InSecBuffer;
InSecBuffer.cbBuffer = InBufferLen;
InSecBuffer.BufferType = SECBUFFER_TOKEN;
InSecBuffer.pvBuffer = InBuffer;

//
// Lets initialize client s context from the SSP and see if
// we need to send anything back to the client
//
SecurityStatus = (*SecurityInterface->AcceptSecurityContext(
Credentials,
0,
InputBufferDescriptor,
ISC_REQ_USE_DCE_STYLE | ISC_REQ_DELEGATE |
ISC_REQ_MUTUAL_AUTH |ISC_REQ_REPLAY_DETECT |
ISC_REQ_SEQUENCE_DETECT |ISC_REQ_CONFIDENTIALITY
|
ISC_REQ_CONNECTION,
DataRepresentation,
&SecurityContext,
OutputBufferDescriptor,
&ContextAttributes,
&TimeStamp
);
The arguments to AcceptSecurityContext are as follows:
Arg1 = Credentials handle returned from the AcquireCredentialsHandle call.
Arg2 = Old Context handle if any.
Arg3 = Input Buffer Descriptor (if there is one received from client).
Arg4 = Context Attributes Requested (See the Security Context Details section be
low for for more information).
Arg5 = Data Representation (see SSPI.H for valid values).
Arg6 = New Context Handle.
Arg7 = Output Buffer Descriptor, containing what will be sent back to the client
.
Arg8 = Context Attributes that are supported by the provider.
Arg9 = TimeStamp for the lifespan of context validity.
The server checks the return status and output buffer descriptor to ensure there
are no errors so far, otherwise it rejects the connection request. If there is
information in the output buffer it bundles it into a response message to the cl
ient as per the application protocol.
If the return status requires the protocol to continue (SEC_I_CONTINUE_NEEDED or
SEC_I_COMPLETE_AND_CONTINUE), then another message exchange with the client is
required. Otherwise the authentication is complete. For third leg, the server wa
its for the client to respond with another message. Note that this wait maybe ti
med out so as to avoid a denial of service attack (a malicious client may never
respond hanging this server thread, and soon it will hang all server threads!!).
24.6.3 Client Continuation
On receipt of the response from the server, the client decomposes the message an
d, using the continue status from the previous call, it calls InitializeSecurity
Context again:
if(SecurityStatus == SEC_I_CONTINUE_NEEDED || SecurityStatus = SEC_I_COMPLETE_AN
D_CONTINUE)
{
//
// Set up the Input and OutputBuffer Descriptor using the information from messa
ge
// received from the server.
//
OutBufferDesc.ulVersion = 0;
OutBufferDesc.cBuffers = 1;
OutBufferDesc.pBuffers = &OutSecBuffer;
OutSecBuffer.cbBuffer = BufferLen;
OutSecBuffer.BufferType = SECBUFFER_TOKEN;
OutSecBuffer.pvBuffer = Buffer;
InBufferDesc.ulVersion = 0;
InBufferDesc.cBuffers = 1;
InBufferDesc.pBuffers = &InSecBuffer;
InSecBuffer.cbBuffer = InBufferLen;
InSecBuffer.BufferType = SECBUFFER_TOKEN;
InSecBuffer.pvBuffer = InBuffer;
//
//
SecurityStatus = (*SecurityInterface->InitializeSecurityContext(
0,
&SecurityContext,
0,
0,
0,
DataRepresentation,
InputBufferDescriptor,
0,
&SecurityContext,
OutputBufferDescriptor,
&ContextAttributes,
&TimeStamp
);
}
The client checks the return status from this call and may be required to contin
ue for another leg. It uses the information in the OutputBufferDescriptor to con
struct a message and sends it to the server.
24.6.4 Server Continuation
The server should be waiting for the response based on the return code from prev
ious call to AcquireSecurityContext. To continue the authentication protocol, th
e server also calls AcceptSecurityContext again.
if(SecurityStatus = SEC_I_CONTINUE_NEEDED || SecurityStatus = SEC_I_COMPLETE_AND
_CONTINUE)
{
//
// Set up the Input and OutputBuffer Descriptor using the information from messa
ge
// receivedfrom the client.
//
OutBufferDesc.ulVersion = 0;
OutBufferDesc.cBuffers = 1;
OutBufferDesc.pBuffers = &OutSecBuffer;
OutSecBuffer.cbBuffer = BufferLen;
OutSecBuffer.BufferType = SECBUFFER_TOKEN;
OutSecBuffer.pvBuffer = Buffer;
InBufferDesc.ulVersion = 0;
InBufferDesc.cBuffers = 1;
InBufferDesc.pBuffers = &InSecBuffer;
InSecBuffer.cbBuffer = InBufferLen;
InSecBuffer.BufferType = SECBUFFER_TOKEN;
InSecBuffer.pvBuffer = InBuffer;

//
// Lets do the next leg of client s context initialization from the security packa
ge and see if we need
// to send anything back to the client
//
SecurityStatus = (*SecurityInterface->AcceptSecurityContext(
0,
&SecurityContext,
InputBufferDescriptor,
0,
DataRepresentation,
&SecurityContext,
OutputBufferDescriptor,
&ContextAttributes,
&TimeStamp
);
}
The return status is checked to see if the server needs to wait for another leg
from the client. In most existing authentication protocols this is the maximum e
ven for mutual authentication. NTLM security package performs client authenticat
ion and Kerberos security package does mutual authentication in three legs.
24.7 Secure Message Exchange
Microsoft SSPI provides message APIs that can be used to ensure application prot
ocol message integrity. Message privacy APIs (data encryption) are not exposed d
irectly but a particular provider may expose them and document them separately.
If the application wants to generate signed messages, the client must have speci
fied the ISC_REQ_REPLAY_DETECT or ISC_REQ_SEQUENCE_DETECT flag as the Context At
tributes argument in the first call to the InitializeSecurityContext function.
After an authenticated connection has been established, the security support pro
viders on each side establish a common session key that is used to sign messages
on the sending side and to verify messages on the receiving side. The algorithm
s used in message signatures are private to the security package.
The SSPI message APIs are the following:
MakeSignature Generates a secure signature based on a message and a security conte
xt.
VerifySignature Verifies that the signature matches a received message.
The message APIs provide integrity for application data messages. MakeSignature
generates a checksum of the message and also includes sequencing information to
prevent message loss or insertion. The next sections show how the sender and rec
eiver use the SSPI Message APIs.
24.7.1 Sender
The sender of a message calls MakeSignature API to get a signature for the messa
ge and appends it to the message at an appropriate place so that the receiver is
able to extract it on receipt:
//
// Setup the Buffer Descriptors.
//
OutBufferDesc.ulVersion = 0;
OutBufferDesc.cBuffers = 2;
OutBufferDesc.pBuffers = &OutSecBuffer;
OutSecBuffer[0].cbBuffer = MessageLen;
OutSecBuffer[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
OutSecBuffer[0].pvBuffer = Message;
OutSecBuffer[1].cbBuffer = SignatureLen;
OutSecBuffer[1].BufferType = SECBUFFER_EMPTY;
OutSecBuffer[1].pvBuffer = (Message + MessageLen); // just after the message

//
// Now call MakeSignature API to get it signed.
//
SecurityStatus = (*SecurityInterface->MakeSignature)(
&SecurityContext,
0,
BufferDescriptor,
Sequence
);
The arguments to MakeSignature are the following:
Arg1 = Context Handle for the active security context
Arg2 = Quality of protection
Arg3 = Buffer descriptor containing the message for signing.
Arg4 = Sequence number of the message if sequence detection is on.
The sender then uses the buffer descriptor (including the signature) to construc
t a message to send to the receiver.
The quality of protection value allows applications to select different cryptogr
aphic algorithms supported by the security package. By default NTLM does not sup
port this parameter. Other security packages, however, may provide different qua
lity of protection options.
24.7.2 Receiver
The receiver takes the message and breaks it down to create the buffer descripto
r as before. It then passes this buffer descriptor on to the VerifySignature API
to verify the message integrity.
//
// Setup the Buffer Descriptors.
//
InBufferDesc.ulVersion = 0;
InBufferDesc.cBuffers = 2;
InBufferDesc.pBuffers = &InSecBuffer;
InSecBuffer[0].cbBuffer = MessageLen;
InSecBuffer[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
InSecBuffer[0].pvBuffer = Message;
InSecBuffer[1].cbBuffer = SignatureLen;
InSecBuffer[1].BufferType = SECBUFFER_TOKEN;
InSecBuffer[1].pvBuffer = (Message + MessageLen); // just after the message
//
// Now call MakeSignature API to get it signed.
//
SecurityStatus = (*SecurityInterface->VerifySignature)(
&SecurityContext,
BufferDescriptor,
Sequence,
&QualityOfProtection
);
The arguments to VerifySignature are the following:
Arg1 = Context Handle for the active context
Arg2 = Buffer descriptor containing received message
Arg3 = Sequence number expected for the received message
Arg4 = Quality of protection on the message (if any)
Once the receiver is ensured of the authenticity and integrity of the message, t
he receiver is free to use it as per the application protocol.
24.7.4 Impersonation
An important aspect of client/server communication besides authentication and me
ssage exchange is the ability of a server to determine whether it should service
the client s request. A large number of servers run under system s context and ther
efore have far more privileges and abilities than a typical client requesting se
rvice. An example of this is a network file server which has full access to all
files, whereas requesting users may not. Therefore, the server should carry out
a client request if and only if the client has sufficient access rights for the
requested service.
There are two approaches for determining whether a client has sufficient access
rights for the operation: an access check by the server, or an access check by t
he system. The brute force approach builds the logic of doing authorization chec
ks for client access into the server. The server code uses authorization informa
tion, for example, from a separate authorization file, and determines if the cli
ent has sufficient rights to perform the requested operation.
SSPI provides an API, ImpersonateSecurityContext, that allows a server to impers
onate the client s security context as well as to revert back to it s own security c
ontext (RevertSecurityContext) when done servicing.
The example below shows how to use ImpersonateSecurityContext and RevertSecurity
Context APIs:
//
// When accessing a resource on behalf the client, we need to
// impersonate the client so that appropriate access check is done.
//
SecurityStatus = (*SecurityInterface->ImpersonateSecurityContext) (&SecurityCont
ext);
if(SecurityStatus != SEC_E_OK)
{
//
// We have a problem
// This security context is not at least impersonation level.
//
return error;
}
//
// At this point the calling thread is under an impersonation token with client s
credentials
//
//
// Process the request..
//
. . .
//
// Revert to primary token once we are done.
//
SecurityStatus = (*SecurityInterface->RevertSecurityContext)(&SecurityContext);
if(SecurityStatus != SEC_E_OK)
{
//
// check for any errors
//
return error;
}
//
// The server thread is back to its original context.
//
24.7.5 Using Delegation in Kerberos
The Kerberos authentication protocol supports delegation. When delegation is sup
ported, the impersonating server can use the client s delegation level credentials
to initialize a security context with a remote server to request a service on t
he client s behalf.
The diagram below shows how the client s security context, identified by C, is est
ablished on Server 1. When Server 1 impersonates the client, the impersonation c
ontext on Server 1 is identified as C/S1. Server 1 makes an off-machine connecti
on to Server 2. Through the use of delegation, Server 2 is also able to imperson
ate the client s security context. Server 2 s impersonation of the client is identif
ied as C/S2.
Figure 24-2: Delegation of Security
The following example shows how delegation can be accomplished using SSPI:
//
// When accessing a resource on behalf the client, we need to impersonate the cl
ient so that
// appropriate access check is done.
//
SecurityStatus = (*SecurityInterface->ImpersonateSecurityContext)(&SecurityConte
xt);
if(SecurityStatus != SEC_E_OK)
{
//
// We have a problem
// This security context is not at least impersonation level.
//
return error;
}
//
// At this point the calling thread is under an impersonation token with client s
credentials
//
//
// Now we can call InitializeSecurityContext to get an authentication token with
current
// credentials (client s) to send to another remote server:
//
// Set up security buffers and the descriptor.
//
//
// Call InitializeSecurityContext
// If this fails, then the client security context is not delegation level,
// WATCH OUT FOR THIS, and handle according to the application protocol.
//

//
// construct a message with the auth token from the SSP to send to the remote se
rver.
// and send the message.
//
// Wait for the reply from the server.
//
//
// If SEC_I_CONTINUE_NEEDED is returned by the first call to Initialize, use
// the auth token returned by the remote server to call InitializeSecurityContex
t again.
//
if(SecurityStatus == SEC_I_CONTINUE_NEEDED || SecurityStatus = SEC_I_COMPLETE_AN
D_CONTINUE)
{
//
// Fill up Input security buffers and setup output security buffers.
//
//
// call InitializeSecurityContext.
//
//
// Convert the security buffers into a message.
// and send the message.
//
//
// wait for reply.
//
}
//
// At this point the connection is established.
//
//
// Request service from the remote server by exchanging messages. Note that
// the remote server will process these assuming that they are coming from the c
lient.
//
. . .
//
// Once done, tear down the connection.
//
//
// Now, you may revert to primary token.
//
SecurityStatus = (*SecurityInterface->RevertSecurityContext)(&SecurityContext);
if(SecurityStatus != SEC_E_OK)
{
//
// check for any errors
//
return error;
}
//
// The server thread is back to its original context.
//
24.7.6 Security Context Details
The Security Support Provider Interface model supports three types of security c
ontexts, which are summarized in the following table.
Type Description
Connection A connection-oriented context is the most common security contex
t, and the simplest to use. The caller is responsible for the overall message fo
rmat. The caller is responsible for the location of the data in the message. The
caller is also responsible for the location of the security-relevant fields wit
hin a message, such as the location of the signature data.
Datagram A datagram-oriented context has extra support for DCE RPC style
datagram communication. It can also be used generically for a datagram-oriented
transport application.
Stream A stream-oriented context is responsible for the blocking and message fo
rmatting within the security package. The caller is not interested in formatting
, but rather a raw stream of data.
24.7.6.1 Connection-Oriented Contexts
With a connection-oriented context, the caller of the function is responsible fo
r formatting messages. The caller also relies on the security provider to authen
ticate connections, and to ensure the integrity of specific parts of the message
. Most of the range of context options are available to connection-oriented cont
exts. These options include mutual authentication, replay detection, and sequenc
e detection, as described in Context Requirements.
A security package sets the SECPKG_FLAG_CONNECTION flag to indicate that it supp
orts connection-oriented semantics.
24.7.6.2 Datagram Contexts
Datagram, or connectionless, contexts have slightly different semantics from con
nection-oriented contexts. A connectionless context implies that the server has
no way of determining when the client has shut down or otherwise terminated the
connection. In other words, no termination notice is passed from the transport a
pplication to the server, as would occur in a connection context. To better supp
ort some models, particularly DCE-style RPC, the following rules apply when the
client specifies the ISC_REQ_DATAGRAM flag in its call to the InitializeSecurity
Context function:
The security package does not produce an authentication blob (binary large objec
t) on the first call to the InitializeSecurityContext function. However, the cli
ent can immediately use the returned security context in a call to the MakeSigna
ture function to generate a signature for a message.
The security package must allow for the context to be re-established multiple ti
mes to allow the server to drop the connection without notice. This also implies
that any keys used in the MakeSignature and VerifySignature functions can be re
set to a consistent state.
The security package must allow for the caller to specify sequence information,
and must provide it back again at the other end. This is not exclusive of any se
quence information maintained by the package and can be viewed as a special payl
oad.
A security package sets the SECPKG_FLAG_DATAGRAM flag to indicate that it suppor
ts datagram semantics.
24.7.6.3 Stream Contexts
Stream contexts are quite different from either connection or datagram contexts.
Stream contexts were introduced to handle the secure streams-oriented protocols
such as SSL or PCT.
In the interest of sharing the same interface, similar credential management, an
d so on, the Security Support Provider Interface has been extended to provide su
pport for stream contexts. The security protocol incorporated both the authentic
ation scheme, and the record formats. This posed a problem to the typical implem
entation, which required the blocking to be done by the caller.
To satisfy the requirements of the stream-oriented protocols, a security package
that supports stream contexts has the following characteristics:
The package sets the SECPKG_FLAG_STREAM flag to indicate that it supports stream
semantics, just as it would set a flag to indicate support for connection and d
atagram semantics.
A transport application requests stream semantics by setting the ISC_REQ_STREAM
and ASC_REQ_STREAM flags in the calls to the InitializeSecurityContext and Accep
tSecurityContext functions.
The application calls the QueryContextAttributes function with a SecPkgContext_S
treamSizes structure to query the security context for the number of buffers to
provide, and the sizes to reserve for headers or trailers.
The application provides buffer descriptors to spare during the actual processin
g of the data.
Obviously, item 4 is of the most interest. By specifying stream semantics, the c
aller is indicating a willingness to do extra work so the security provider can
handle the blocking of the messages.
In essence, for the MakeSignature and VerifySignature functions, the caller pass
es in a list of buffers. When a message is received from a channel that
is stream-oriented (such as a TCP port), the caller passes in a buffer list as f
ollows:
Buffer Length Buffer Type
1 MessageLength SECBUFFER_DATA
2 0 SECBUFFER_EMPTY
3 0 SECBUFFER_EMPTY
4 0 SECBUFFER_EMPTY
5 0 SECBUFFER_EMPTY
The security package then goes to work on the blob. If the function returns succ
essfully, the buffer list looks like this:
Buffer Length Buffer Type
1 Header Length SECBUFFER_STREAM_HEADER
2 Data Length SECBUFFER_DATA
3 Trailer Length SECBUFFER_STREAM_TRAILER
4 0 SECBUFFER_EMPTY
5 0 SECBUFFER_EMPTY
The provider could have also returned buffer #4 as follows:
Buffer Length Buffer Type
4 x SECBUFFER_EXTRA
This indicates that the data in this buffer is part of the next record, and has
not yet been processed.
Conversely, if the message function returns the SEC_E_INCOMPLETE_MESSAGE error c
ode, the returned buffer list would look like this:
Buffer Length Buffer Type
1 x SECBUFFER_MISSING
This indicates that more data was needed to process the record. Unlike most erro
rs returned from a message function, this buffer type does not indicate that the
context has been compromised, just that more data is needed. Security providers
must not update their state in this condition.
Similarly, on the send side of the communication, the caller can simply call the
MakeSignature function, in which case the security package may need to realloca
te the buffer, copy things around, and so on. Or the caller can be more efficien
t by providing a buffer list as follows:
Buffer Length Type
1 Header Length SECBUFFER_STREAM_HEADER
2 Data Length SECBUFFER_DATA
3 Trailer Length SECBUFFER_STREAM_TRAILER
This allows the caller to use the buffers more efficiently. By calling the Query
ContextAttributes function to determine the amount of space to reserve before ca
lling MakeSignature, the operation is more efficient for the application and the
security package.
24.7.6.4 Context Requirements
Context requirements are expressed as a combination of bit flags, passed to eith
er the InitializeSecurityContext or AcceptSecurityContext function. These flags
affect the context in a number of ways, and are detailed in the following table.
Not all flags apply to all contexts; some are valid only for the server, others
only for the client.
The caller uses the fContextReq parameter of the InitializeSecurityContext or Ac
ceptSecurityContext call to specify a set of flags that indicate the required ca
pabilities. When the function returns, the pfContextAttr parameter indicates the
attributes of the established context. The caller is responsible for determinin
g whether the final context attributes are acceptable. For example, if the calle
r requested mutual authentication, but the security package indicates that it wa
s not or could not be performed, the caller must decide whether to cancel the co
ntext or continue on.
The following table describes the various context requirements.
Type Description
DELEGATE Indicates that the server in the transport application should be
allowed simple delegation rights, that is, impersonation of the client on the n
ode at which the server is executing.
MUTUAL_AUTH Indicates that both parties must authenticate the identity of th
e peer.
REPLAY_DETECT Indicates that the context should be established to allow detect
ion of replayed packets later through the message support functions, MakeSignatu
re and VerifySignature. Implies INTEGRITY.
SEQUENCE_DETECT Indicates that the context should be established to allow detect
ion of out-of-order delivery of packets later through the message support functi
ons. Implies INTEGRITY.
CONFIDENTIALITY Indicates that the context should be established to protect data
while in transit. Reserved for future use.
USE_SESSION_KEY Indicates that a new session key should be negotiated.
PROMPT_FOR_CREDS Indicates that, if the client is an interactive user, th
e security package should prompt the user for the appropriate credentials to use
, if possible.
USE_SUPPLIED_CREDS Indicates that package-specific credential information i
s available in the input buffer. The security package should use these credentia
ls to authenticate the connection.
ALLOCATE_MEMORY Indicates that the security package should allocate the memory.
The caller must eventually call the FreeContextBuffer function to free memory al
located by the security package.
USE_DCE_STYLE Indicates that the caller expects a three-leg authentication tra
nsaction.
DATAGRAM Indicates that datagram semantics should be used. For more infor
mation, see Datagram Contexts.
CONNECTION Indicates that connection semantics should be used. For more inf
ormation, see Connection-Oriented Contexts.
STREAM Indicates that stream semantics should be used. For more information, se
e Stream Contexts.
Type Description
EXTENDED_ERROR Indicates that if the context fails (or failed), it will generat
e an error reply message for the peer.
INTEGRITY Buffer integrity can be verified, but no sequencing or reply det
ection is enabled.
24.8 Datatype Descriptions
BINDPTR
A union containing a pointer to a FUNCDESC, VARDESC, or an ITypeComp interface.
It is defined as follows:
typedef union tagBINDPTR {
FUNCDESC FAR* lpfuncdesc;
VARDESC FAR* lpvardesc;
ITypeComp FAR* lptcomp;
} BINDPTR;
BOOL
Boolean variable (should be TRUE or FALSE).
Quick Info
Header file: WTYPES.H
typedef long BOOL;
BSTR
A length-prefixed string used by Automation data manipulation functions.
typedef OLECHAR *BSTR;
BSTRs are wide, double-byte (Unicode) strings on 32-bit Windows platforms and na
rrow, single-byte strings on the Apple® PowerMac .
Quick Info
Header file: WTYPES.H
typedef [wire_marshal( wireBSTR )] OLECHAR * BSTR;
BYTE
BYTE is an unsigned character data type that is binary data.
Quick Info
Header file: WINDEF.H
typedef unsigned char BYTE;
CLIPFORMAT
typedef union _userCLIPFORMAT switch(long fContext) u
{
case WDT_INPROC_CALL: DWORD dwValue;
case WDT_REMOTE_CALL: [string] wchar_t * pwszName;
} userCLIPFORMAT;
typedef [unique] userCLIPFORMAT * wireCLIPFORMAT;
typedef [wire_marshal(wireCLIPFORMAT)] WORD CLIPFORMAT;
CONST
Variable that remains constant during an execution.
Quick Info
Header file: WTYPES.H
#define CONST const
CUSTDATA
Used for retrieving custom data. It is defined as follows:
typedef struct tagCUSTDATA
{
DWORD cCustData;
/* [size_is] */ LPCUSTDATAITEM prgCustData;
} CUSTDATA;
The following table describes the fields of the CUSTDATA structure.
Value Description
cCustData Number of custom data items in prgCustData
prgCustData Array of custom data items

DISPID
Used by IDispatch::Invoke to identify methods, properties, and arguments.
typedef LONG DISPID;
The following dispatch identifiers (DISPIDs) have special meaning.
DISPID Description
DISPID_VALUE The default member for the object. This property or method is in
voked when an COM client specifies the object name without a property or method.
DISPID_NEWENUM The _NewEnum property. This special, restricted property is requ
ired for collection objects. It returns an enumerator object that supports IEnum
VARIANT, and should have the restricted attribute specified in Object Definition
Language.
DISPID_EVALUATE The Evaluate method. This method is implicitly invoked when the
COM client 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
The Evaluate method has the DISPID DISPID_EVALUATE.
DISPID_PROPERTYPUT The parameter that receives the value of an assignment i
n a PROPERTYPUT.
DISPID_CONSTRUCTOR The C++ constructor function for the object.
DISPID_DESTRUCTOR The C++ destructor function for the object.
DISPID_UNKNOWN The value returned by IDispatch::GetIDsOfNames to indicate that
a member or parameter name was not found.
Note: The reserved DISPIDs are:
DISPID_Name-800
DISPID_Delete-801
DISPID_Object-802
DISPID_Parent-803
DISPPARAMS
Used by IDispatch::Invoke to contain the arguments passed to a method or propert
y.
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;

DWORD
A 32-bit unsigned integer or the address of a segment and its associated offset.
Quick Info
Header file: WTYPES.H
typedef unsigned long DWORD;
FAR *
Defined to nothing.
#define far
#define FAR far
FUNCDESC
Describes a function, and is defined as follows:
typedef struct tagFUNCDESC {
MEMBERID memid; // Function member ID.
/* [size_is] */ SCODE __RPC_FAR *lprgscode;
/* [size_is] */ ELEMDESC __RPC_FAR *lprgelemdescParam; 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 follows).
short oVft; // For FUNC_VIRTUAL, specifies the offset in
// the VTBL.
short cScodes; // Count of permitted return values.
ELEMDESC elemdescFunc; // Contains the return type of the function.
WORD wFuncFlags; // Definition of flags follows.
} FUNCDESC;
The cParams field specifies the total number of required and optional parameters
.
The cParamsOpt field specifies the form of optional parameters accepted by the f
unction, 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 ar
ray of variants. Any number of variant arguments greater than cParams 1 must be p
ackaged by the caller into a safe array and passed as the final parameter. The c
aller must free this array of optional parameters after control is returned from
the call.
Any other number indicates that the last n parameters of the function are varian
ts and do not need to be specified by the caller explicitly. The parameters left
unspecified should be filled in by the compiler or interpreter as variants of t
ype VT_ERROR with the value DISP_E_PARAMNOTFOUND.
For 16-bit systems (Macintosh), the fields cScodes and lprgscode store the count
and the set of errors that a function can return. If cScodes = 1, then the set o
f errors is unknown. If cScodes = 1, or if cScodes = 0, then lprgscodeis undefine
d.
HANDLE
A pointer to any type.
Quick Info
Header file: WTYPES.H
typedef void *HANDLE;
HBITMAP
Handle to a bitmap.
typedef HANDLE HBITMAP;
HENHMETAFILE
Handle to an enhanced metafile.
typedef HANDLE HENHMETAFILE;
HGLOBAL
Handle to a global memory block.
typedef HANDLE HGLOBAL;
HINSTANCE
Handle to an instance.
typedef HANDLE HINSTANCE;
HKEY
Handle to a registry key.
typedef HANDLE HKEY;
HMETAFILEPICT
typedef struct _remoteMETAFILEPICT
{
long mm;
long xExt;
long yExt;
userHMETAFILE * hMF;
} remoteMETAFILEPICT;
typedef union _userHMETAFILEPICT switch( long fContext ) u
{
case WDT_INPROC_CALL: long hInproc;
case WDT_REMOTE_CALL: remoteMETAFILEPICT* hRemote;
default: long hGlobal;
} userHMETAFILEPICT;

HREFTYPE
A handle that identifies a type description.
typedef unsigned long HREFTYPE;
HTASK
A handle to a task.
typedef HANDLE HTASK;
HUGEP
#ifndef HUGEP
#if defined(_WIN32) || defined(_MPPC_)
#define HUGEP
#else
#define HUGEP __huge
#endif // WIN32
#endif // HUGEP
INVOKEKIND
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 using normal property-access syn
tax.
INVOKE_PROPERTYPUT The function is invoked using 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 using property reference assignm
ent syntax.
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 can support only a value assignment, a reference assig
nment, or both. The INVOKEKIND enumeration constants are the same constants tha
t are passed to IDispatch::Invoke to specify the way in which a function is invo
ked.
IPID
Interface pointer identifier. It is identical to a GUID.
typedef GUID IPID;
LANGID
Language identifier.
The first release of Windows NT supports 35 sublanguages/locales.
The following 28 sublanguages/locales use the Latin 1 script:
Latin 1 Script
Identifier Language Sublanguage/Locale Language Code
0x0406 Danish Danish DAN
0x0413 Dutch Dutch (Standard) NLD
0x0813 Dutch Belgian (Flemish) NLB
0x0409 English American ENU
0x0809 English British ENG
0x0c09 English Australian ENA
0x1009 English Canadian ENC
0x1409 English New Zealand ENZ
0x1809 English Ireland ENI
0x040b Finnish Finnish FIN
0x040c French French (Standard) FRA
0x080c French Belgian FRB
0x0c0c French Canadian FRC
0x100c French Swiss FRS
0x0407 German German (Standard) DEU
0x0807 German Swiss DES
0x0c07 German Austrian DEA
0x040f Icelandic Icelandic ISL
0x0410 Italian Italian (Standard) ITA
0x0810 Italian Swiss ITS
0x0414 Norwegian Norwegian (Bokmal) NOR
0x0814 Norwegian Norwegian (Nynorsk) NON
0x0416 Portuguese Portuguese (Brazilian) PTB
0x0816 Portuguese Portuguese (Standard) PTG
0x041D Swedish Swedish SVE
0x040a Spanish Spanish (Standard/Traditional) ESP
0x080a Spanish Mexican ESM
0x0c0a Spanish Spanish (Modern) ESN
The following 5 sublanguages/locales use the Latin 2 script:
Latin 2 Script
Identifier Sublanguage/Locale Language Code
0x041f Turkish TRK
0x0415 Polish PLK
0x0405 Czech CSY
0x041b Slovak SKY
0x040e Hungarian HUN
The following sublanguage/locale uses the Cyrillic script:
Cyrillic Script
Identifier Sublanguage/Locale Language Code
0x0419 Russian RUS
The following sublanguage/locale uses another script:
Other Script
Identifier Sublanguage/Locale Language Code
0x0408 Greek ELL
The following special identifiers are also defined:
Special Identifiers
Identifier Sublanguage/Locale
0x0000 Language-Neutral
0x0400 Process Default Language

LCID
Identifies a locale for national language support. Locale information is used f
or international string comparisons and localized member names.
typedef unsigned long LCID;
LCTYPE
An LCTYPE constant is a constant that specifies a particular piece of locale inf
ormation.
The values in the following list correspond to the names of these values in the
configuration registry, under both the user's preferences (as values in the regi
stry key HKEY_CURRENT_USER\Control Panel\International) and the system's install
ed languages (as files pointed to by registry keys, one key per language install
ed, under HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\NLS). All values a
re null-terminated Unicode strings. If no maximum length is indicated, the string
s may vary in length.
Constant Description
LOCALE_ILANGUAGE Language identifier indicating the language. The maximum
number of characters allowed for this string is 5.
LOCALE_SLANGUAGE Full localized name of the language.
LOCALE_SENGLANGUAGE Full English name of the language from the International
Organization for Standardization (ISO) Standard 639. This is always restricted
to characters mappable into the ASCII 127-character subset.
LOCALE_SABBREVLANGNAME Abbreviated name of the language, created by taking the
2-letter language abbreviation from the ISO Standard 639 and adding a third lett
er, as appropriate, to indicate the sublanguage.
LOCALE_SNATIVELANGNAME Native name of the language.
LOCALE_SSORTNAME The full localized name of the sort for the given locale
ID.
LOCALE_ICOUNTRY Country code, based on international phone codes, also referred
to as IBM country codes. The maximum number of characters allowed for this strin
g is 6.
LOCALE_SCOUNTRY Full localized name of the country.
LOCALE_SENGCOUNTRY Full English name of the country. This is always restric
ted to characters mappable into the ASCII 127-character subset.
LOCALE_SABBREVCTRYNAME Abbreviated name of the country from the ISO Standard 31
66.
LOCALE_SNATIVECTRYNAME Native name of the country.
LOCALE_IDEFAULTLANGUAGE Language identifier for the principal language spoken in
this locale. This is provided so that partially specified locales can be comple
ted with default values. The maximum number of characters allowed for this strin
g is 5.
LOCALE_IDEFAULTCOUNTRY Country code for the principal country in this locale. T
his is provided so that partially specified locales can be completed with defaul
t values. The maximum number of characters allowed for this string is 6.
LOCALE_IDEFAULTANSICODEPAGE American National Standards Institute (ANSI) cod
e page associated with this locale. The maximum number of characters allowed for
this string is 6.
LOCALE_IDEFAULTOEMCODEPAGE Original equipment manufacturer (OEM) code page
associated with the locale. The maximum number of characters allowed for this st
ring is 6.
LOCALE_IDEFAULTCODEPAGE Original equipment manufacturer (OEM) code page associat
ed with the country. The maximum number of characters allowed for this string is
6.
LOCALE_IDEFAULTEBCDICCODEPAGE Default EBCDIC code page associated with the loc
ale. The maximum number of characters allowed for this string is 6.
LOCALE_SLIST Character(s) used to separate list items. For example, a comma i
s used in many locales.
LOCALE_IMEASURE System of measurement. This value is 0 if the metric system (Sys
téme International d'Unités, or S.I.) is used and 1 if the U.S. system is used. The
maximum number of characters allowed for this string is 2.
LOCALE_SDECIMAL Character(s) used as the decimal separator.
LOCALE_STHOUSAND Character(s) used to separate groups of digits to the le
ft of the decimal.
LOCALE_SGROUPING Sizes for each group of digits to the left of the decima
l. An explicit size is needed for each group; semicolons separate sizes. If the
last value is zero, the proceeding value is repeated. To group thousands, specif
y 3;0, for example.
LOCALE_IDIGITS Number of fractional digits. The maximum number of characters al
lowed for this string is 3.
LOCALE_ILZERO Specifier for leading zeros in decimal fields. The maximum numbe
r of characters allowed for this string is 2. The specifier can be one of the fo
llowing values:
Value Meaning
0 No leading zeros
1 Leading zeros
LOCALE_INEGNUMBER Negative number mode. The mode can be one of these value
s:
Value Meaning
0 (1.1)
1 1.1
2 1.1
3 1.1
4 1.1
LOCALE_SNATIVEDIGITS Native equivalents to ASCII 0 through 9.
LOCALE_SENGCURRNAME The full English name of the currency associated with th
e locale.
LOCALE_SNATIVECURRNAME The native name of the currency associated with the loca
le.
LOCALE_SCURRENCY String used as the local monetary symbol.
LOCALE_SINTLSYMBOL Three characters of the international monetary symbol sp
ecified in ISO 4217, "Codes for the Representation of Currencies and Funds," fol
lowed by the character separating this string from the amount.
LOCALE_SMONDECIMALSEP Character(s) used as the monetary decimal separator.
LOCALE_SMONTHOUSANDSEP Character(s) used as the monetary separator between grou
ps of digits to the left of the decimal.
LOCALE_SMONGROUPING Sizes for each group of monetary digits to the left of t
he decimal. An explicit size is needed for each group; sizes are separated by se
micolons. If the last value is zero, the preceding value is repeated. To group t
housands, specify 3;0, for example.
LOCALE_ICURRDIGITS Number of fractional digits for the local monetary forma
t. The maximum number of characters allowed for this string is 3.
LOCALE_IINTLCURRDIGITS Number of fractional digits for the international moneta
ry format. The maximum number of characters allowed for this string is 3.
LOCALE_ICURRENCY Positive currency mode. The maximum number of characters
allowed for this string is 2. The mode can be one of the following values:

Value Meaning
0 Prefix, no separation
1 Suffix, no separation
2 Prefix, 1-char. separation
3 Suffix, 1-char. separation
LOCALE_INEGCURR Negative currency mode. The maximum number of characters allowed
for this string is 3. The mode can be one of the following values:
Value Example
0 ($1.1)
1 $1.1
2 $ 1.1
3 $1.1
4 (1.1$)
5 1.1$
6 1.1 $
7 1.1$
8 1.1 $ (space before $)
9 $ 1.1 (space after $)
10 1.1 $ (space before $)
11 $ 1.1 (space after $)
12 $ 1.1 (space after $)
13 1.1 $ (space before $)
14 ($ 1.1) (space after $)
15 (1.1 $) (space before $)
LOCALE_SDATE Character(s) for the date separator.
LOCALE_STIME Character(s) for the time separator.
LOCALE_STIMEFORMAT Time formatting strings for this locale. The string can
consist of a combination of the hour, minute, and second format pictures defined
in the Error! Bookmark not defined. table in National Language Support Constant
s.
LOCALE_SYEARMONTH The Year/Month formatting string for the locale. This st
ring shows the proper format for a date string that contains only the year and t
he month.
LOCALE_SSHORTDATE Short date formatting string for this locale. The string
can consist of a combination of day, month, and year format pictures defined in
Error! Bookmark not defined. table in National Language Support Constants.
LOCALE_SLONGDATE Long date formatting string for this locale. The string
can consist of a combination of day, month, and year format pictures defined in
the Error! Bookmark not defined. table in National Language Support Constants an
d any string of characters enclosed in single quotes. Characters in single quote
s remain as given.
LOCALE_IDATE Short date format-ordering specifier. The maximum number of char
acters allowed for this string is 2. The specifier can be one of the following v
alues:
Value Meaning
0 Month-Day-Year
1 Day-Month-Year
2 Year-Month-Day
LOCALE_ILDATE Long date format-ordering specifier. The maximum number of chara
cters allowed for this string is 2. The specifier can be one of the following va
lues:
Value Meaning
0 Month-Day-Year
1 Day-Month-Year
2 Year-Month-Day
LOCALE_ITIME Time format specifier. The maximum number of characters allowed
for this string is 2. The specifier can be one of the following values:
Value Meaning
0 AM / PM 12-hour format
1 24-hour format
LOCALE_ICENTURY Specifier for full 4-digit century. The maximum number of charac
ters allowed for this string is 2. The specifier can be one of the following val
ues:
Value Meaning
0 Abbreviated 2-digit century
1 Full 4-digit century
LOCALE_ITLZERO Specifier for leading zeros in time fields. The maximum number o
f characters allowed for this string is 2. The specifier can be one of the follo
wing values:
Value Meaning
0 No leading zeros for hours
1 Leading zeros for hours
LOCALE_IDAYLZERO Specifier for leading zeros in day fields. The maximum n
umber of characters allowed for this string is 2. The specifier can be one of th
e following values:
Value Meaning
0 No leading zeros for days
1 Leading zeros for days
LOCALE_IMONLZERO Specifier for leading zeros in month fields. The maximum
number of characters allowed for this string is 2. The specifier can be one of
the following values:
Value Meaning
0 No leading zeros for months
1 Leading zeros for months
LOCALE_S1159 String for the AM designator.
LOCALE_S2359 String for the PM designator.
LOCALE_ICALENDARTYPE Current calendar type. This type can be one of these val
ues:
Value Meaning
1 Gregorian (as in United States)
2 Gregorian (English strings always)
3 Era: Year of the Emperor (Japan)
4 Era: Year of Taiwan Region
5 Tangun Era (Korea)
LOCALE_IOPTIONALCALENDAR Additional calendar types. This can be a zero-se
parated list of one or more of these calendars type values:
Value Meaning
0 No additional types valid
1 Gregorian (as in United States)
2 Gregorian (English strings always)
3 Era: Year of the Emperor (Japan)
4 Era: Year of Taiwan Region
5 Tangun Era (Korea)

LOCALE_IFIRSTDAYOFWEEK Specifier for the first day in a week. The specifier can
be one of these values:
Value Meaning
0 LOCALE_SDAYNAME1
1 LOCALE_SDAYNAME2
2 LOCALE_SDAYNAME3
3 LOCALE_SDAYNAME4
4 LOCALE_SDAYNAME5
5 LOCALE_SDAYNAME6
6 LOCALE_SDAYNAME7
LOCALE_IFIRSTWEEKOFYEAR Specifier for the first week of the year. The specifier
can be one of these values:
Value Meaning
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_SDAYNAME1 Native long name for Monday.


LOCALE_SDAYNAME2 Native long name for Tuesday.
LOCALE_SDAYNAME3 Native long name for Wednesday.
LOCALE_SDAYNAME4 Native long name for Thursday.
LOCALE_SDAYNAME5 Native long name for Friday.
LOCALE_SDAYNAME6 Native long name for Saturday.
LOCALE_SDAYNAME7 Native long name for Sunday.
LOCALE_SABBREVDAYNAME1 Native abbreviated name for Monday.
LOCALE_SABBREVDAYNAME2 Native abbreviated name for Tuesday.
LOCALE_SABBREVDAYNAME3 Native abbreviated name for Wednesday.
LOCALE_SABBREVDAYNAME4 Native abbreviated name for Thursday.
LOCALE_SABBREVDAYNAME5 Native abbreviated name for Friday.
LOCALE_SABBREVDAYNAME6 Native abbreviated name for Saturday.
LOCALE_SABBREVDAYNAME7 Native abbreviated name for Sunday.
LOCALE_SMONTHNAME1 Native long name for January.
LOCALE_SMONTHNAME2 Native long name for February.
LOCALE_SMONTHNAME3 Native long name for March.
LOCALE_SMONTHNAME4 Native long name for April.
LOCALE_SMONTHNAME5 Native long name for May.
LOCALE_SMONTHNAME6 Native long name for June.
LOCALE_SMONTHNAME7 Native long name for July.
LOCALE_SMONTHNAME8 Native long name for August.
LOCALE_SMONTHNAME9 Native long name for September.
LOCALE_SMONTHNAME10 Native long name for October.
LOCALE_SMONTHNAME11 Native long name for November.
LOCALE_SMONTHNAME12 Native long name for December.
LOCALE_SMONTHNAME13 Native name for 13th month, if exists.
LOCALE_SABBREVMONTHNAME1 Native abbreviated name for January.
LOCALE_SABBREVMONTHNAME2 Native abbreviated name for February.
LOCALE_SABBREVMONTHNAME3 Native abbreviated name for March.
LOCALE_SABBREVMONTHNAME4 Native abbreviated name for April.
LOCALE_SABBREVMONTHNAME5 Native abbreviated name for May.
LOCALE_SABBREVMONTHNAME6 Native abbreviated name for June.
LOCALE_SABBREVMONTHNAME7 Native abbreviated name for July.
LOCALE_SABBREVMONTHNAME8 Native abbreviated name for August.
LOCALE_SABBREVMONTHNAME9 Native abbreviated name for September.
LOCALE_SABBREVMONTHNAME10 Native abbreviated name for October.
LOCALE_SABBREVMONTHNAME11 Native abbreviated name for November.
LOCALE_SABBREVMONTHNAME12 Native abbreviated name for December.
LOCALE_SABBREVMONTHNAME13 Native abbreviated name for 13th month, if exist
s.
LOCALE_SPOSITIVESIGN String value for the positive sign.
LOCALE_SNEGATIVESIGN String value for the negative sign.
LOCALE_IPOSSIGNPOSN Formatting index for positive values. The maximum number
of characters allowed for this string is 2. The index can be one of the followi
ng values:
Value Meaning
0 Parentheses surround the amount and the monetary symbol.
1 The sign string precedes the amount and the monetary symbol.
2 The sign string succeeds the amount and the monetary symbol.
3 The sign string immediately precedes the monetary symbol.
4 The sign string immediately succeeds the monetary symbol.
LOCALE_INEGSIGNPOSN Formatting index for negative values. This index uses th
e same values as LOCALE_IPOSSIGNPOSN. The maximum number of characters allowed f
or this string is 2.
LOCALE_IPOSSYMPRECEDES Position of monetary symbol in a positive monetary value
. This value is 1 if the monetary symbol precedes the positive amount, 0 if it f
ollows it. The maximum number of characters allowed for this string is 2.
LOCALE_IPOSSEPBYSPACE Separation of monetary symbol in a positive monetary val
ue. This value is 1 if the monetary symbol is separated by a space from a positi
ve amount, 0 if it is not. The maximum number of characters allowed for this str
ing is 2.
LOCALE_INEGSYMPRECEDES Position of monetary symbol in a negative monetary value
. This value is 1 if the monetary symbol precedes the negative amount, 0 if it f
ollows it. The maximum number of characters allowed for this string is 2.
LOCALE_INEGSEPBYSPACE Separation of monetary symbol in a negative monetary val
ue. This value is 1 if the monetary symbol is separated by a space from the nega
tive amount, 0 if it is not. The maximum number of characters allowed for this s
tring is 2.
LOCALE_IPAPERSIZE Default paper size associated with the locale.
LOCALE_NOUSEROVERRIDE This constant may be OR'ed with any other LCTYPE constan
t in a call to the GetLocaleInfo function. This always causes the function to by
pass any user overrides, and return the system default value for the other LCTYP
E specified in the function call, based on the given LCID.

LONG
32-bit signed integer.
typedef long LONG;
LPBC
A pointer to a Bind Context.
typedef [unique] IbindCtx *LPBC;
LPBYTE
Pointer to a BYTE.
typedef unsigned char FAR *LPBYTE;
LPCLSID
A pointer to a class ID.
typedef CLSID *LPCLSID;
LPCOLESTR
A pointer to an OLECHAR array.
typedef [string] const OLECHAR *LPOLESTR;
LPCTSTR
Pointer to a constant null-terminated Unicode or Windows character string.
typedef const TCHAR FAR *LPCTSTR;
LPCWSTR
Pointer to a constant null-terminated Unicode character string.
typedef [string] const WCHAR *LPCWSTR;
LPDWORD
Pointer to a DWORD.
typedef DWORD *LPDWORD;
LPIID
A pointer to an interface identifier.
typedef IID *LPIID;
LPMALLOC
A pointer to an IMalloc interface.
typedef [unique] IMalloc *LPMALLOC;
LPMALLOCSPY
A pointer to an IMallocSpy interface.
typedef [unique] IMallocSpy *LPMALLOCSPY;
LPMARSHAL
A pointer to an IMarshal interface.
typedef [unique] IMarshal *LPMARSHAL;
LPMONIKER
A pointer to an IMoniker interface.
typedef [unique] IMoniker *LPMONIKER;
LPOLESTR
A pointer to an OLECHAR array.
typedef [string] OLECHAR *LPOLESTR;
LPRUNNINGOBJECTTABLE
A pointer to the running object table interface.
Typedef [unique] IRunningObjectTable *LPRUNNINGOBJECTTABLE;
LPSECURITY_ATTRIBUTES
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
[size_is(nLength)] LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
LPSTR
Pointer to a null-terminated Windows character string.
typedef [string] CHAR *LPSTR;
LPSTREAM
Pointer to an IStream interface.
typedef [unique] IStream *LPSTREAM;
LPUNKNOWN
A pointer to an IUnknown interface.
typedef [unique] IUnknown *LPUNKNOWN;
LPVOID
Pointer to any type.
typedef void *LPVOID;
LPWORD
Pointer to a WORD.
typedef WORD *LPWORD;
LPWSTR
Pointer to a null-terminated Unicode character string.
typedef [string] WCHAR *LPWSTR;
MEMBERID
Identifies the member in a type description. For IDispatch interfaces, this is t
he same as DISPID.
typedef DISPID MEMBERID;
This is a 32-bit integral value in the following format.
Bits Value
0 15 Offset. Any value is permissible.
16 21 The nesting level of this type information in the inheritance hierarchy.
For example:
interface mydisp : IDispatch
The nesting level of IUnknown is 0, IDispatch is 1, and MyDisp is 2.
22 25 Reserved. Must be zero.
26 28 Value of the DISPID.
29 True if this is the member ID for a FUNCDESC; otherwise False.
30 31 Must be 01.
Negative IDs are reserved for use by Automation.
OLECHAR
A wide character.
typedef WCHAR OLECHAR;
PFILETIME
typedef struct _FILETIME
{
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;
PHKEY
Pointer to a registry key.
typedef HKEY FAR *PHKEY;
PROPID
A pointer to an unsigned long.
typedef ULONG PROPID;
PROPSPEC
The PROPSPEC structure is used by many of the methods of IPropertyStorage to spe
cify a property either by its property identifier or the associated string name.
The structure and related definitions are defined as follows in the header file
s:
const ULONG PRSPEC_LPWSTR = 0
const ULONG PRSPEC_PROPID = 1
typedef ULONG PROPID
typedef struct tagPROPSPEC
{
ULONG ulKind; // PRSPEC_LPWSTR or PRSPEC_PROPID
union
{
PROPID propid;
LPOLESTR lpwstr;
}
} PROPSPEC
Members
ulKind
If ulKind is set to PRSPEC_LPWSTR, lpwstr is used and set to a string name. If u
lKind is set to PRSPEC_PROPID, propid is used and set to a property identifier v
alue.
propid
Specifies the value of the property identifier. Use either this value or the fol
lowing lpwstr, not both.
lpwstr
Specifies the string name of the property as a null-terminated Unicode string.
Remarks
String names are optional and can be assigned to a set of properties when the pr
operty is created with a call to IPropertyStorage::WriteMultiple, or later, with
a call to IPropertyStorage::WritePropertyNames.
QuickInfo
Windows NT: Use version 4.0 or later.
Windows: Use Windows 95 or later. Available as a redistributable for Windows 95.
Windows CE: Unsupported.
Header: Declared in objidl.h.
PROPVARIANT
The PROPVARIANT structure is used in most of the methods of IPropertyStorage to
define the type tag and the value of a property in a property set. There are fiv
e members. The first, the value type tag, and the last, the value of the propert
y, are significant. The middle three are reserved for future use. The PROPVARIAN
T structure is defined as follows:
Note The bool member in previous definitions of this structure has been renamed
to boolVal, since some compilers now recognize bool as a keyword.
struct PROPVARIANT{
VARTYPE vt; // value type tag
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
// none // VT_EMPTY, VT_NULL, VT_ILLEGAL
unsigned char bVal; // VT_UI1
short iVal; // VT_I2
USHORT uiVal; // VT_UI2
long lVal; // VT_I4
ULONG ulVal; // VT_UI4
LARGE_INTEGER hVal; // VT_I8
ULARGE_INTEGER uhVal; // VT_UI8
float fltVal; // VT_R4
double dblVal; // VT_R8
CY cyVal; // VT_CY
DATE date; // VT_DATE
BSTR bstrVal; // VT_BSTR
VARIANT_BOOL boolVal; // VT_BOOL
SCODE scode; // VT_ERROR
FILETIME filetime; // VT_FILETIME
LPSTR pszVal; // VT_LPSTR // string in the current system
Ansi code page
LPWSTR pwszVal; // VT_LPWSTR // string in Unicode
CLSID* puuid; // VT_CLSID
CLIPDATA* pclipdata; // VT_CF
BLOB blob; // VT_BLOB, VT_BLOBOBJECT
IStream* pStream; // VT_STREAM, VT_STREAMED_OBJECT
IStorage* pStorage; // VT_STORAGE, VT_STORED_OBJECT
CAUB caub; // VT_VECTOR | VT_UI1
CAI cai; // VT_VECTOR | VT_I2
CAUI caui; // VT_VECTOR | VT_UI2
CAL cal; // VT_VECTOR | VT_I4
CAUL caul; // VT_VECTOR | VT_UI4
CAH cah; // VT_VECTOR | VT_I8
CAUH cauh; // VT_VECTOR | VT_UI8
CAFLT caflt; // VT_VECTOR | VT_R4
CADBL cadbl; // VT_VECTOR | VT_R8
CACY cacy; // VT_VECTOR | VT_CY
CADATE cadate; // VT_VECTOR | VT_DATE
CABSTR cabstr; // VT_VECTOR | VT_BSTR
CABOOL cabool; // VT_VECTOR | VT_BOOL
CASCODE cascode; // VT_VECTOR | VT_ERROR
CALPSTR calpstr; // VT_VECTOR | VT_LPSTR
CALPWSTR calpwstr; // VT_VECTOR | VT_LPWSTR
CAFILETIME cafiletime; // VT_VECTOR | VT_FILETIME
CACLSID cauuid; // VT_VECTOR | VT_CLSID
CACLIPDATA caclipdata; // VT_VECTOR | VT_CF
CAPROPVARIANT capropvar; // VT_VECTOR | VT_VARIANT
}} PROPVARIANT
Remarks
PROPVARIANT is the fundamental data type by which property values are read and w
ritten through the IPropertyStorage interface.
The data type PROPVARIANT is related to the data type VARIANT, defined as part o
f Automation in OLE2 and defined in the Win32 SDK header file oleauto.h. Several
definitions are reused from Automation, as follows:
typedef struct tagCY {
unsigned long Lo;
long Hi;
} CY
typedef CY CURRENCY;
typedef short VARIANT_BOOL;
typedef unsigned short VARTYPE;
typedef double DATE;
typedef OLECHAR* BSTR;
typedef struct tagCLIPDATA {
ULONG cbSize; //Includes sizeof(ulClipFmt)
long ulClipFmt;
BYTE* pClipData;
} CLIPDATA
In addition, several new data types that define counted arrays of other data typ
es are required. The data types of all counted arrays begin with the letters CA
(such as CAUB) and have an ORed vt value. The counted array structure has the fo
llowing form (where name is the specific name of the counted array):
#define TYPEDEF_CA(type, name)
typedef struct tag ## name {\
ULONG cElems;\
type *pElems;\
} name

Propvariant Type Code Propvariant Member Value Representation


VT_EMPTY 0 None A property with a type indicator of VT_EMPTY has
no data associated with it; that is, the size of the value is zero.
VT_NULL 1 None This is like a pointer to NULL.
VT_UI1 17 bVal 1-byte unsigned integer
VT_I2 2 iVal Two bytes representing a 2-byte signed integer value.
VT_UI2 18 uiVal 2-byte unsigned integer
VT_I4 3 lVal 4-byte signed integer value
VT_UI4 19 ulVal 4-byte unsigned integer
VT_I8 20 hVal 8-byte signed integer
VT_UI8 21 uhVal 8-byte unsigned integer
VT_R4 4 fltVal 32-bit IEEE floating point value
VT_R8 5 dblVal 64-bit IEEE floating point value
VT_CY 6 cyVal 8-byte two's complement integer (scaled by 10,000). This
type is commonly used for currency amounts.
VT_DATE 7 date A 64-bit floating point number representing the number o
f days (not seconds) since December 31, 1899. For example, January 1, 1900 is 2.
0, January 2, 1900 is 3.0, and so on). This is stored in the same representation
as VT_R8.
VT_BSTR 8 bstrVal Pointer to a null terminated Unicode string. The string
is immediately preceded by a DWORD representing the byte count, but bstrVal poin
ts past this DWORD to the first character of the string. BSTRs must be allocated
and freed using the OLE Automation SysAllocString and SysFreeString calls.
VT_BOOL 11 boolVal (bool in earlier designs) Boolean value, a WORD co
ntaining 0 (false) or -1 (true).
VT_ERROR 10 scode A DWORD containing a status code.
VT_FILETIME 64 filetime 64-bit FILETIME structure as defined by
Win32. It is recommended that all times be stored in Universal Coordinate Time (
UTC).
VT_LPSTR 30 pszVal Pointer to a null terminated ANSI string in the
system default code page.
VT_LPWSTR 31 pwszVal Pointer to a null terminated Unicode string in t
he user's default locale.
VT_CLSID 72 puuid Pointer to a CLSID (or other GUID).
VT_CF 71 pclipdata Pointer to a CLIPDATA structure, described above
.
VT_BLOB 65 blob DWORD count of bytes, followed by that many bytes of dat
a. The byte count does not include the four bytes for the length of the count it
self; 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_BLOBOBJECT 70 blob A BLOB containing a serialized object in the sam
e representation as would appear in a VT_STREAMED_OBJECT. That is, a DWORD byte
count (where the byte count does not include the size of itself) which is in the
format of a class identifier followed by initialization data for that class.
The only significant difference between VT_BLOB_OBJECT and VT_STREAMED_OBJECT is
that the former does not have the system-level storage overhead that the latter
would have, and is therefore more suitable for scenarios involving numbers of s
mall objects.
VT_STREAM 66 pStream Pointer to an IStream interface, representing a
stream which is a sibling to the "Contents" stream.
VT_STREAMED_
OBJECT 68 pStream As in VT_STREAM, but indicates that the stream contains
a serialized object, which is a CLSID followed by initialization data for the cl
ass. The stream is a sibling to the Contents stream that contains the property s
et.
VT_STORAGE 67 pStorage Pointer to an IStorage interface, repres
enting a storage object that is a sibling to the "Contents" stream.
VT_STORED_
OBJECT 69 pStorage As in VT_STORAGE, but indicates that the designa
ted IStorage contains a loadable object.
VT_VECTOR 0x1000 ca* If the type indicator is one of the simple propv
ariant types ORed with this one, the value is one of the counted array values. T
his is a DWORD count of elements, followed by that many repetitions of the value
.
For example, a type indicator of VT_LPSTR|VT_VECTOR has a DWORD element count, a
DWORD byte count, the first string data, padding bytes for 32-bit alignment (se
e below), a DWORD byte count, the second string data, and so on.
Nonsimple types cannot be ORed with VT_VECTOR. These types are VT_STREAM, VT_STR
EAM_OBJECT, VT_STORAGE, VT_STORAGE_OBJECT. VT_BLOB and VT_BLOB_OBJECT types also
cannot be ORed with VT_VECTOR.
VT_VARIANT 12 capropvar A DWORD type indicator followed by the c
orresponding value. VT_VARIANT can be used only with VT_VECTOR.
VT_TYPEMASK 0xFFF Used as a mask for VT_VECTOR and other modifiers
to extract the raw VT value.
Clipboard format identifiers, stored with the tag VT_CF, use one of five differe
nt representations (identified in the ulClipFmt member of the CLIPDATA structure
):
ulClipFmt Value pClipData value
-1L a DWORD containing a built-in Windows clipboard format value.
-2L a DWORD containing a Macintosh clipboard format value.
-3L a GUID containing a format identifier (rarely used).
any positive value a null-terminated string containing a Windows clipboard
format name, one suitable for passing to RegisterClipboardFormat. The code page
used for characters in the string is per the code page indicator. The "positive
value" here is the length of the string, including the null byte at the end.
0L no data (rarely used)
Within a vector of values, each repetition of a value is to be aligned to 32-bit
boundaries. The exception to this rule is scalar types which are less than 32 b
its: VT_UI1, VT_12, VT_U12, and VT_BOOL. Vectors of these values are packed.
Therefore, a value with type tag VT_I2 | VT_VECTOR would be a DWORD element coun
t, followed by a sequence of packed 2-byte integers with no padding between them
.
However, a value with type tag VT_LPSTR | VT_VECTOR would be a DWORD element cou
nt, followed by a sequence of (DWORD cch, char rgch[]) strings, each of which ma
y be followed by null padding to round to a 32-bit boundary.
QuickInfo
Windows NT: Use version 4.0 or later.
Windows: Use Windows 95 or later. Available as a redistributable for Windows 95.
Windows CE: Unsupported.
Header: Declared in objidl.h.
PSECURITY_DESCRIPTOR
A pointer to a security descriptor.
typedef PVOID PSECURITY_DESCRIPTOR;
PULONG
Pointer to an unsigned long (32 bits).
typedef ULONG *PULONG;
PVALENT
typedef struct value_entA {
LPSTR ve_valuename;
DWORD ve_valuelen;
DWORD ve_valueptr;
DWORD ve_type;
}VALENTA, FAR *PVALENTA;
typedef struct value_entW {
LPWSTR ve_valuename;
DWORD ve_valuelen;
DWORD ve_valueptr;
DWORD ve_type;
}VALENTW, FAR *PVALENTW;
#ifdef UNICODE
typedef VALENTW VALENT;
typedef PVALENTW PVALENT;
#else
typedef VALENTA VALENT;
typedef PVALENTA PVALENT;
#endif // UNICODE
PVOID
Pointer to any type.
typedef void *PVOID;
PWCHAR
Pointer to a Unicode character.
typedef WCHAR *PWCHAR;
REFCLSID
A pointer to a class ID.
typedef CLSID *REFCLSID;
REFFMTID
A pointer to an FMTID.
typedef FMTID *REFFMTID;
REFGUID
A pointer to a globally unique identifier.
typedef GUID *REFGUID;
REFIID
A pointer to an interface identifier.
typedef IID *REFIID;
REGKIND
Controls how a type library is registered. It is defined as follows:
typedef enum tagREGKIND{
REGKIND_DEFAULT,
REGKIND_REGISTER,
REGKIND_NONE
} REGKIND;

Value Description
REGKIND_DEFAULT Use default register behavior
REGKIND_REGISTER Registered type
REGKIND_NONE Not a registered type

REGSAM
Data type used for specifying the security access attributes in the registry. A
REGSAM value can be one or more of the following values:
KEY_ALL_ACCESS
Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_S
UB_KEY, KEY_CREATE_LINK, and KEY_SET_VALUE access.
KEY_CREATE_LINK
Permission to create a symbolic link.
KEY_CREATE_SUB_KEY
Permission to create subkeys.
KEY_ENUMERATE_SUB_KEYS
Permission to enumerate subkeys.
KEY_EXECUTE
Permission for read access.
KEY_NOTIFY
Permission for change notification.
KEY_QUERY_VALUE
Permission to query subkey data.
KEY_READ
Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY access.
KEY_SET_VALUE
Permission to set subkey data.
KEY_WRITE
Combination of KEY_SET and KEY_CREATE_SUB_KEY access.
RPC_AUTH_IDENTITY_HANDLE
typedef void * RPC_AUTH_IDENTITY_HANDLE;
Remarks
An identity handle points to the data structure that contains the client's authe
ntication and authorization credentials specified for remote procedure calls.
RPC_AUTHZ_HANDLE
A pointer to the authorization service.
typedef void __RPC_FAR *RPC_AUTHZ_HANDLE;
RPC_FAR
Defined to nothing.
#define RPC_FAR
RPCOLEMESSAGE
typedef struct tagRPCOLEMESSAGE
{
void *reserved1;
RPCOLEDATAREP dataRepresentation;
void *Buffer;
ULONG cbBuffer;
ULONG iMethod;
void *reserved2[5];
ULONG rpcFlags;
} RPCOLEMESSAGE;
SECURITY_INFORMATION
The SECURITY_INFORMATION structure identifies the object-related security inform
ation being set or queried. This security information includes:
The owner of an object
The primary group of an object
The discretionary access-control list (ACL) of an object
The system ACL of an object
typedef DWORD SECURITY_INFORMATION;
Each item of security information is designated by a bit flag. The following val
ues specify the bits:
Value Meaning
OWNER_SECURITY_INFORMATION Indicates the owner identifier of the object is
being referenced.
GROUP_SECURITY_INFORMATION Indicates the primary group identifier of the ob
ject is being referenced.
DACL_SECURITY_INFORMATION Indicates the discretionary ACL of the object is
being referenced.
SACL_SECURITY_INFORMATION Indicates the system ACL of the object is being
referenced.
QuickInfo
Windows NT: Use version 3.1 or later.
Windows: Unsupported.
Windows CE: Unsupported.
Header: Declared in winnt.h.
SIZEL
typedef struct tagSIZEL
{
LONG cx;
LONG cy;
} SIZEL, *PSIZEL, *LPSIZEL;
SOLE_AUTHENTICATION_SERVICE
Identifies an authentication service. This structure is retrieved through a call
to CoQueryAuthenticationServices, and passed in to CoInitializeSecurity.
typedef struct tagSOLE_AUTHENTICATION_SERVICE {
DWORD dwAuthnSvc;
DWORD dwAuthzSvc;
OLECHAR* pPrincipalName;
HRESULT hr;
} SOLE_AUTHENTICATION_SERVICE;
Members
dwAuthnSvc
The authentication service. It may contain a single value taken from the list of
RPC_C_AUTHN_xxx constants defined in rpcdce.h. RPC_C_AUTHN_NONE turns off authe
ntication. On Win32, RPC_C_AUTHN_DEFAULT causes COM to use the RPC_C_AUTHN_WINNT
authentication.
dwAuthzSvc
The authorization service. It may contain a single value taken from the list of
RPC_C_AUTHZ_xxx constants defined in rpcdce.h. The validity and trustworthiness
of authorization data, like any application data, depends on the authentication
service and authentication level selected. This parameter is ignored when using
the RPC_C_AUTHN_WINNT authentication service.
pPrincipalName
Principal name to be used with the authentication service. If the principal name
is NULL, COM assumes the current user identifier. A NULL principal name is allo
wed for NT LM SSP and kerberos authentication services, but may not work for oth
er authentication services.
hr
When used in CoInitializeSecurity, set on return to indicate the status of the c
all to register the authentication services.
QuickInfo
Windows NT: Use version 4.0 or later.
Windows: Use Windows 95 or later. Available as a redistributable for Windows 95.
Windows CE: Unsupported.
Header: Declared in objidl.h.
STDAPI
Standard API calling convention.
#define STDAPI EXTERN_C HRESULT STDAPICALLTYPE
SYSKIND
Identifies the target operating system platform. It is defined as follows:
typedef enum tagSYSKIND {
SYS_WIN16,
SYS_WIN32,
SYS_MAC
} SYSKIND;

Value Description
SYS_WIN16 The target operating system for the type library is 16-bit Windo
ws systems. By default, data members are packed.
SYS_WIN32 The target operating system for the type library is 32-bit Windo
ws systems. By default, data members are naturally aligned (for example, 2-byte
integers are aligned on even-byte boundaries; 4-byte integers are aligned on qua
d-word boundaries, and so on).
SYS_MAC The target operating system for the type library is Apple Macintosh. By
default, all data members are aligned on even-byte boundaries.

SYSTEMTIME
The SYSTEMTIME structure has the following form:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME;
The SYSTEMTIME structure represents a date and time using individual members for
the month, day, year, weekday, hour, minute, second, and millisecond.
Members
wYear The current year.
wMonth The current month; January is 1.
wDayOfWeek The current day of the week; Sunday is 0, Monday is 1, and so on.
wDay The current day of the month.
wHour The current hour.
wMinute The current minute.
wSecond The current second.
wMilliseconds The current millisecond.
TLIBATTR
Contains information about a type library. Information from this structure is us
ed to identify the type library and to provide national language support for mem
ber names. It is defined as follows:
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;

TYPEATTR
Contains attributes of an ITypeInfo, and is defined as follows:
typedef struct FARSTRUCT tagTYPEATTR {
GUID guid; // The GUID of the type information.
LCID lcid; // Locale of member names and doc
// strings.
unsigned long dwReserved;
MEMBERID memidConstructor; // ID of constructor, or MEMBERID_NIL if
// none.
MEMBERID memidDestructor; // ID of destructor, or 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 information
// 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 VTBL.
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 indicate
s alignment on the 64K boundary; 1 indicates no special alignment. For other val
ues, n indicates aligned on byte n.
UINT
Unsigned integer.
typedef unsigned int UINT;
ULARGE_INTEGER
The ULARGE_INTEGER structure is used to specify a 64-bit unsigned integer value.
typedef union _ULARGE_INTEGER {
struct {
DWORD LowPart;
DWORD HighPart;
};
DWORDLONG QuadPart;
} ULARGE_INTEGER;

Members
LowPart
Specifies the low-order 32 bits.
HighPart
Specifies the high-order 32 bits.
QuadPart
Specifies a 64-bit unsigned integer.
Remarks
The ULARGE_INTEGER structure is actually a union. If your compiler has built-in
support for 64-bit integers, use the QuadPart member to store the 64-bit integer
. Otherwise, use the LowPart and HighPart members to store the 64-bit integer.
QuickInfo
Windows NT: Use version 3.1 or later.
Windows: Use Windows 95 or later.
Windows CE: Use version 1.0 or later.
Header: Declared in winnt.h.
ULONG
Unsigned long integer.
typedef DWORD ULONG;
USHORT
Unsigned short integer.
typedef unsigned short USHORT;
VARDESC
Describes a variable, constant, or data member. It 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

VARIANT and VARIANTARG


Use VARIANTARG to describe arguments passed within DISPPARAMS, and VARIANT to sp
ecify variant data that cannot be passed by reference. The VARIANT type cannot h
ave the VT_BYREF bit set. VARIANTs can be passed by value, even if VARIANTARGs c
annot.
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 {
unsigned char bVal; // VT_UI1.
short iVal; // VT_I2 .
long lVal; // VT_I4 .
float fltVal; // VT_R4 .
double dblVal; // VT_R8 .
VARIANT_BOOL boolVal; // 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|*.
unsigned char FAR* pbVal; // VT_BYREF|VT_UI1.
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* pboolVal; // 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* pparray; // VT_ARRAY|*.
VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
void FAR* byref; // Generic ByRef.
};
};
To simplify extracting values from VARIANTARGs, Automation provides a set of fun
ctions for manipulating this type. Use of these functions is strongly recommende
d to ensure that applications apply consistent coercion rules.
The vt value governs the interpretation of the union as follows:
Value Description
VT_EMPTY No value was specified. If an optional argument to an Automation
method is left blank, do not pass a VARIANT of type VT_EMPTY. Instead, pass a V
ARIANT of type VT_ERROR with a value of DISP_E_PARAMNOTFOUND.
VT_EMPTY | VT_BYREF Not valid.
VT_UI1 An unsigned 1-byte character is stored in bVal.
VT_UI1 | VT_BYREF A reference to an unsigned 1-byte character was passed.
A pointer to the value is in pbVal.
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.
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 value was passed. A p
ointer 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 value was passed. A p
ointer 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 1
5 digits to the left of the decimal point and 4 digits to the right. The value i
s 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 obtai
ned and freed by the BSTR functions.
VT_BSTR | VT_BYREF A reference to a string was passed. A BSTR* that points
to a BSTR is in pbstrVal. The referenced pointer must be obtained or freed by th
e BSTR functions.
VT_NULL A propagating null value was specified. (This should not be confused wit
h the null pointer.) The null value is used for tri-state logic, as with SQL.
VT_NULL | VT_BYREF Not valid.
VT_ERROR An SCODE was specified. The type of the error is specified in sc
odee. Generally, operations on error values should raise an exception or propaga
te the error to the return value, as appropriate.
VT_ERROR | VT_BYREF A reference to an SCODE was passed. A pointer to the val
ue is in pscode.
VT_BOOL A Boolean (True/False) value was specified. A value of 0xFFFF (all bits
1) indicates True; a value of 0 (all bits 0) indicates False. No other values ar
e valid.
VT_BOOL | VT_BYREF A reference to a Boolean value. A pointer to the Boolean
value is in pbool.
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, 19
00 is 3.0, and so on. The value is passed in date.
This is the same numbering system used by most spreadsheet programs, although so
me specify incorrectly that February 29, 1900 existed, and thus set January 1, 1
900 to 1.0. The date can be converted to and from an MS-DOS representation using
VariantTimeToDosDateTime.
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 known only 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_U
NKNOWN.
VT_DISPATCH | VT_BYREF A pointer to a pointer to an object was specified. The p
ointer to the object is stored in the location referred to by ppdispVal.

VT_VARIANT Invalid. VARIANTARGs must be passed by reference.


VT_VARIANT | VT_BYREF A pointer to another VARIANTARG is passed in pvarVal. Th
is 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 lan
guages 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 the IUnknown interface is passed in ppunkVa
l. The pointer to the interface is stored in the location referred to by ppunkVa
l.
VT_ARRAY | <anything> An array of data type <anything> was passed. (VT_EMPTY a
nd VT_NULL are invalid types to combine with VT_ARRAY.) The pointer in pbyrefVal
points to an array descriptor, which describes the dimensions, size, and in-mem
ory location of the array. The array descriptor is never accessed directly.

VARTYPE
An enumeration type used in VARIANT, TYPEDESC, OLE property sets, and safe array
s.
The enumeration constants listed in the following VARENUM section are valid in t
he vt field of a VARIANT structure.
typedef unsigned short VARTYPE;
enum VARENUM{
VT_EMPTY = 0, // Not specified.
VT_NULL = 1, // Null.
VT_I2 = 2, // 2-byte signed int.
VT_I4 = 3, // 4-byte signed int.
VT_R4 = 4, // 4-byte real.
VT_R8 = 5, // 8-byte real.
VT_CY = 6, // Currency.
VT_DATE = 7, // Date.
VT_BSTR = 8, // Binary string.
VT_DISPATCH = 9, // IDispatch
VT_ERROR = 10, // Scodes.
VT_BOOL = 11, // Boolean; True=-1, False=0.
VT_VARIANT = 12, // VARIANT FAR*.
VT_UNKNOWN = 13, // IUnknown FAR*.
VT_UI1 = 17, // Unsigned char.
// Other constants that are not valid in VARIANTs omitted here.
};
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.

VOID
Any type.
#define VOID void
WCHAR
Unicode character.
typedef wchar_t WCHAR
WINAPI
Calling convention for the Win32 API.
#define WINAPI FAR PASCAL
WINOLEAPI
Calling convention for the Windows OLE API.
#define WINOLEAPI STDAPI
WORD
16-bit unsigned integer.
typedef unsigned short WORD;

Appendix A: Header Files and Libraries


The following table lists header files, import libraries, and element types for
all elements.

Element Name Header File Import Library Element Type


DllRegisterServer olectl.h user-defined callback function
DllUnregisterServer olectl.h user-defined callback function
ACTIVATEFLAGS ocidl.h enumeration
ADVF objidl.h enumeration
BIND_FLAGS objidl.h enumeration
BINDSPEED oleidl.h enumeration
BINDSTATUS urlmon.h enumeration
BINDVERB urlmon.h enumeration
BSCF urlmon.h enumeration
BSCO_OPTION enumeration
CALLTYPE objidl.h enumeration
CHUNK_BREAKTYPE filter.h enumeration
CHUNKSTATE filter.h enumeration
CLASSPATHTYPE wtypes.h enumeration
CLSCTX wtypes.h enumeration
COINIT objbase.h enumeration
DATADIR objidl.h enumeration
DISCARDCACHE oleidl.h enumeration
DOCMISC docobj.h enumeration
DROPEFFECT oleidl.h enumeration
DVASPECT ocidl.h enumeration
DVASPECT2 ocidl.h enumeration
DVASPECTINFOFLAG ocidl.h enumeration
DVEXTENTMODE ocidl.h enumeration
EOLE_AUTHENTICATION_CAPABILITIES objidl.h enumeration
EXTCONN objidl.h enumeration
GUIDKIND ocidl.h enumeration
HITRESULT ocidl.h enumeration
IFILTER_FLAGS filter.h enumeration
IFILTER_INIT filter.h enumeration
KEYMODIFIERS controls.idl enumeration
LOCKTYPE objidl.h enumeration
MKRREDUCE objidl.h enumeration
MKSYS objidl.h enumeration
MSHCTX wtypes.h enumeration
MSHLFLAGS wtypes.h enumeration
OLECLOSE oleidl.h enumeration
OLECMDEXECOPT docobj.h enumeration
OLECMDF docobj.h enumeration
OLECMDID docobj.h enumeration
OLECMDTEXTF docobj.h enumeration
OLECONTF oleidl.h enumeration
OLEDCFLAGS ocidl.h enumeration
OLEGETMONIKER oleidl.h enumeration
OLELINKBIND oleidl.h enumeration
OLEMISC oleidl.h enumeration
OLERENDER oleidl.h enumeration
OLEUIPASTEFLAG oledlg.h enumeration
OLEUPDATE oleidl.h enumeration
OLEVERBATTRIB oleidl.h enumeration
OLEWHICHMK oleidl.h enumeration
PENDINGMSG objidl.h enumeration
PENDINGTYPE objidl.h enumeration
POINTERINACTIVE ocidl.h enumeration
PRINTFLAG docobj.h enumeration
PROPSETFLAG objidl.h enumeration
QACONTAINERFLAGS ocidl.h enumeration
REGCLS objbase.h enumeration
RPC_C_AUTHN_LEVEL_xxx rpcdce.h enumeration
RPC_C_AUTHN_xxx rpcdce.h enumeration
RPC_C_AUTHZ_xxx rpcdce.h enumeration
RPC_C_IMP_LEVEL_xxx rpcdce.h enumeration
SERVERCALL objidl.h enumeration
STATFLAG wtypes.h enumeration
STATSTATE wtypes.h enumeration
STGC wtypes.h enumeration
STGFMT oleext.h enumeration
STGM objbase.h enumeration
STGMOVE wtypes.h enumeration
STGTY objidl.h enumeration
STREAM_SEEK objidl.h enumeration
TYMED objidl.h enumeration
UASFLAGS ocidl.h enumeration
USERCLASSTYPE oleidl.h enumeration
VIEWSTATUS ocidl.h enumeration
BindMoniker objbase.h ole32.dll function
CLSIDFromProgID objbase.h ole32.dll function
CLSIDFromString objbase.h ole32.dll function
CoAddRefServerProcess objbase.h ole32.dll function
CoCopyProxy objbase.h ole32.dll function
CoCreateFreeThreadedMarshaler objbase.h ole32.dll function
CoCreateGuid objbase.h ole32.dll function
CoCreateInstance objbase.h ole32.dll function
CoCreateInstanceEx objbase.h ole32.dll function
CoDisconnectObject objbase.h ole32.dll function
CoDosDateTimeToFileTime objbase.h ole32.dll function
CoFileTimeNow objbase.h ole32.dll function
CoFileTimeToDosDateTime objbase.h ole32.dll function
CoFreeAllLibraries objbase.h ole32.dll function
CoFreeLibrary objbase.h ole32.dll function
CoFreeUnusedLibraries objbase.h ole32.dll function
CoGetCallContext objbase.h ole32.dll function
CoGetClassObject objbase.h ole32.dll function
CoGetCurrentProcess objbase.h ole32.dll function
CoGetInstanceFromFile objbase.h ole32.dll function
CoGetInstanceFromIStorage objbase.h ole32.dll function

CoGetInterfaceAndReleaseStream objbase.h ole32.dll function


CoGetMalloc objbase.h ole32.dll function
CoGetMarshalSizeMax objbase.h ole32.dll function
CoGetPSClsid objbase.h ole32.dll function
CoGetStandardMarshal objbase.h ole32.dll function
CoGetTreatAsClass objbase.h ole32.dll function
CoImpersonateClient objbase.h ole32.dll function
CoInitialize objbase.h ole32.dll function
CoInitializeEx objbase.h ole32.dll function
CoInitializeSecurity objbase.h ole32.dll function
CoIsHandlerConnected objbase.h ole32.lib function
CoLoadLibrary objbase.h ole32.dll function
CoLockObjectExternal objbase.h ole32.dll function
CoMarshalInterface objbase.h ole32.dll function
CoMarshalInterThreadInterfaceInStream objbase.h ole32.dll function
CoQueryAuthenticationServices objbase.h ole32.dll function
CoQueryClientBlanket objbase.h ole32.dll function
CoQueryProxyBlanket objbase.h ole32.dll function
CoRegisterClassObject objbase.h ole32.dll function
CoRegisterMallocSpy objbase.h ole32.dll function
CoRegisterMessageFilter objbase.h ole32.dll function
CoRegisterPSClsid objbase.h ole32.dll function
CoReleaseMarshalData objbase.h ole32.dll function
CoReleaseServerProcess objbase.h ole32.dll function
CoResumeClassObjects objbase.h ole32.dll function
CoRevertToSelf objbase.h ole32.dll function
CoRevokeClassObject objbase.h ole32.dll function
CoRevokeMallocSpy objbase.h ole32.dll function
CoSuspendClassObjects objbase.h ole32.dll function
CoTaskMemAlloc objbase.h ole32.dll function
CoTaskMemFree objbase.h ole32.dll function
CoTaskMemRealloc objbase.h ole32.dll function
CoTreatAsClass objbase.h ole32.dll function
CoUninitialize objbase.h ole32.dll function
CoUnmarshalInterface objbase.h ole32.dll function
CreateAntiMoniker objbase.h ole32.dll function
CreateBindCtx objbase.h ole32.dll function
CreateClassMoniker objbase.h ole32.dll function
CreateGenericComposite objbase.h ole32.dll function
CreateILockBytesOnHGlobal ole2.h ole32.dll function
CreateItemMoniker objbase.h ole32.dll function
CreatePointerMoniker objbase.h ole32.dll function
CreateStreamOnHGlobal ole2.h ole32.dll function
DllCanUnloadNow objbase.h oleaut32.dll function
DllGetClassObject objbase.h ole32.dll function
FreePropVariantArray objidl.h ole32.dll function
GetClassFile objbase.h ole32.dll function
GetConvertStg ole2.h ole32.dll function
GetHGlobalFromILockBytes ole2.h ole32.dll function
GetHGlobalFromStream ole2.h ole32.dll function
GetRunningObjectTable objbase.h ole32.dll function
IIDFromString objbase.h ole32.dll function
IsEqualGUID objbase.h ole32.lib function
MonikerCommonPrefixWith objbase.h ole32.dll function
MonikerRelativePathTo objbase.h ole32.dll function
ProgIDFromCLSID objbase.h ole32.dll function
PropStgNameToFmtId objidl.h ole32.dll function
PropVariantClear objidl.h ole32.dll function
PropVariantCopy objidl.h ole32.dll function
ReadClassStg ole2.h ole32.dll function
ReadClassStm ole2.h ole32.dll function
ReadFmtUserTypeStg ole2.h ole32.dll function
ReleaseStgMedium ole2.h ole32.dll function
SetConvertStg ole2.h ole32.dll function
StgCreateDocfile objbase.h ole32.dll function
StgCreateDocfileOnILockBytes objbase.h ole32.dll function
StgCreatePropSetStg objidl.h ole32.dll function
StgCreatePropStg objidl.h ole32.dll function
StgIsStorageFile objbase.h ole32.dll function
StgIsStorageILockBytes objbase.h ole32.dll function
StgOpenPropStg objidl.h ole32.dll function
StgOpenStorage objbase.h ole32.lib function
StgOpenStorageOnILockBytes objbase.h ole32.lib function
StgSetTimes objbase.h ole32.dll function
StringFromCLSID objbase.h ole32.dll function
StringFromGUID2 objbase.h ole32.dll function
StringFromIID objbase.h ole32.dll function
WriteClassStg ole2.h ole32.dll function
WriteClassStm ole2.h ole32.dll function
WriteFmtUserTypeStg ole2.h ole32.dll function
IAdviseSink objidl.h interface method
IBindCtx objidl.h interface method
ICatInformation comcat.h interface method
ICatRegister comcat.h interface method
IClassActivator objidl.h interface method
IClassFactory unknwn.h interface method
IClassFactory2 ocidl.h interface method
IClientSecurity objidl.h interface method
IConnectionPoint ocidl.h interface method
IConnectionPointContainer ocidl.h interface method
IDataAdviseHolder objidl.h interface method
IDataObject objidl.h interface method
IEnumConnectionPoints ocidl.h interface method
IEnumConnections ocidl.h interface method
IEnumFORMATETC objidl.h interface method
IEnumMoniker objidl.h interface method
IEnumSTATDATA objidl.h interface method
IEnumSTATPROPSETSTG objidl.h interface method
IEnumSTATPROPSTG objidl.h interface method
IEnumSTATSTG objidl.h interface method
IEnumString objidl.h interface method
IEnumUnknown objidl.h interface method
IErrorLog ocidl.h interface method
IExternalConnection objidl.h interface method
ILockBytes objidl.h interface method
IMalloc objidl.h interface method
IMallocSpy objidl.h interface method
IMarshal objidl.h interface method
IMessageFilter objidl.h interface method
IMoniker objidl.h interface method
IMultiQI objidl.h interface method
IOleItemContainer oleidl.h interface method
IParseDisplayName oleidl.h interface method
IPersist objidl.h interface method
IPersistFile objidl.h interface method
IPersistMemory ocidl.h interface method
IPersistMoniker urlmon.h interface method
IPersistPropertyBag ocidl.h interface method
IPersistStorage objidl.h interface method
IPersistStream objidl.h interface method
IPersistStreamInit ocidl.h interface method
IPropertyBag ocidl.h interface method
IPropertySetStorage objidl.h interface method
IPropertyStorage objidl.h interface method
IProvideClassInfo ocidl.h interface method
IProvideClassInfo2 ocidl.h interface method
IRootStorage objidl.h interface method
IROTData objidl.h interface method
IRunnableObject objidl.h interface method
IRunningObjectTable objidl.h interface method
IServerSecurity objidl.h interface method
IStdMarshalInfo objidl.h interface method
IStorage objidl.h interface method
IStream objidl.h interface method
IUnknown unknwn.h interface method
FACILITY_NT_BIT winerror.h macro
FAILED winerror.h macro
GetScode winerror.h macro
HRESULT_CODE winerror.h macro
HRESULT_FACILITY winerror.h macro
HRESULT_FROM_NT winerror.h macro
HRESULT_FROM_WIN32 winerror.h macro
HRESULT_SEVERITY winerror.h macro
IS_ERROR winerror.h macro
IsEqualCLSID winerror.h macro
IsEqualIID winerror.h macro
MAKE_HRESULT winerror.h macro
MAKE_SCODE winerror.h macro
SCODE_CODE winerror.h macro
SCODE_FACILITY winerror.h macro
SCODE_SEVERITY winerror.h macro
SUCCEEDED winerror.h macro
APPDETAIL wtypes.h structure
BIND_OPTS objidl.h structure
BIND_OPTS2 objidl.h structure
BINDINFO urlmon.h structure
CADWORD ocidl.h structure
CALPOLESTR ocidl.h structure
CAUUID ocidl.h structure
CLASSDETAIL objidl.h structure
COAUTHINFO wtypes.h structure
CONNECTDATA ocidl.h structure
CONTROLINFO ocidl.h structure
COSERVERINFO objidl.h structure
CSPLATFORM objidl.h structure
DVASPECTINFO ocidl.h structure
DVEXTENTINFO ocidl.h structure
DVTARGETDEVICE winbase.h structure
FILETIME olectl.h structure
FILTERREGION filter.h structure
FONTDESC olectl.h structure
FORMATETC objidl.h structure
FULLPROPSPEC filter.h structure
HLBWINFO hlink.h structure
HLITEM hlink.h structure
INTERFACEINFO objidl.h structure
LICINFO ocidl.h structure
MULTI_QI objidl.h structure
OBJECTDESCRIPTOR oleidl.h structure
OCPFIPARAMS olectl.h structure
OLECMD docobj.h structure
OLECMDTEXT docobj.h structure
OLEINPLACEFRAMEINFO oleidl.h structure
OLEMENUGROUPWIDTHS oleidl.h structure
OLEUIBUSY oledlg.h structure
OLEUICHANGEICON oledlg.h structure
OLEUICHANGESOURCE oledlg.h structure
OLEUICONVERT oledlg.h structure
OLEUIEDITLINKS oledlg.h structure
OLEUIGNRLPROPS oledlg.h structure
OLEUIINSERTOBJECT oledlg.h structure
OLEUILINKPROPS oledlg.h structure
OLEUIOBJECTPROPS oledlg.h structure
OLEUIPASTEENTRY oledlg.h structure
OLEUIPASTESPECIAL oledlg.h structure
OLEUIVIEWPROPS oledlg.h structure
OLEVERB oleidl.h structure
PACKAGEDETAIL wtypes.h structure
PACKAGEINFO wtypes.h structure
PACKAGEINFOLIST objidl.h structure
PAGERANGE docobj.h structure
PAGESET docobj.h structure
PICTDESC olectl.h structure
POINTF ocidl.h structure
PROPPAGEINFO ocidl.h structure
PROPSPEC objidl.h structure
PROPVARIANT objidl.h structure
QACONTAINER ocidl.h structure
QACONTROL ocidl.h structure
QUERYCONTEXT wtypes.h structure
RemSNB objidl.h structure
SNB objidl.h structure
SOLE_AUTHENTICATION_SERVICE objidl.h structure
STAT_CHUNK filter.h structure
STATDATA objidl.h structure
STATPROPSETSTG objidl.h structure
STATPROPSTG objidl.h structure
STATSTG objidl.h structure
STGMEDIUM objidl.h structure
STORAGELAYOUT objidl.h structure
uCLSSPEC wtypes.h structure
BstrFromVector oleauto.h oleaut32.lib function
CreateDispTypeInfo oleauto.h oleaut32.lib function
CreateErrorInfo oleauto.h oleaut32.lib function
CreateStdDispatch oleauto.h oleaut32.lib function
CreateTypeLib oleauto.h oleaut32.lib function
DispGetIDsOfNames oleauto.h oleaut32.lib function
DispGetParam oleauto.h oleaut32.lib function
DispInvoke oleauto.h oleaut32.lib function
DosDateTimeToVariantTime oleauto.h oleaut32.lib function
GetActiveObject oleauto.h oleaut32.lib function
GetErrorInfo oleauto.h oleaut32.lib function
LHashValOfNameSys oleauto.h oleaut32.lib function
LoadRegTypeLib oleauto.h oleaut32.lib function
LoadTypeLib oleauto.h oleaut32.lib function
QueryPathOfRegTypeLib oleauto.h oleaut32.lib function
RegisterActiveObject oleauto.h oleaut32.lib function
RegisterTypeLib oleauto.h oleaut32.lib function
RevokeActiveObject oleauto.h oleaut32.lib function
SafeArrayAccessData oleauto.h oleaut32.lib function
SafeArrayAllocData oleauto.h oleaut32.lib function
SafeArrayAllocDescriptor oleauto.h oleaut32.lib function
SafeArrayCopy oleauto.h oleaut32.lib function
SafeArrayCopyData oleauto.h oleaut32.lib function
SafeArrayCreate oleauto.h oleaut32.lib function
SafeArrayCreateVector oleauto.h oleaut32.lib function
SafeArrayDestroy oleauto.h oleaut32.lib function
SafeArrayDestroyData oleauto.h oleaut32.lib function
SafeArrayDestroyDescriptor oleauto.h oleaut32.lib function
SafeArrayGetDim oleauto.h oleaut32.lib function
SafeArrayGetElement oleauto.h oleaut32.lib function
SafeArrayGetElemsize oleauto.h oleaut32.lib function
SafeArrayGetLBound oleauto.h oleaut32.lib function
SafeArrayGetUBound oleauto.h oleaut32.lib function
SafeArrayLock oleauto.h oleaut32.lib function
SafeArrayPtrOfIndex oleauto.h oleaut32.lib function
SafeArrayPutElement oleauto.h oleaut32.lib function
SafeArrayRedim oleauto.h oleaut32.lib function
SafeArrayUnaccessData oleauto.h oleaut32.lib function
SafeArrayUnlock oleauto.h oleaut32.lib function
SetErrorInfo oleauto.h oleaut32.lib function
SysAllocString oleauto.h oleaut32.lib function
SysAllocStringByteLen oleauto.h oleaut32.lib function
SysAllocStringLen oleauto.h oleaut32.lib function
SysFreeString oleauto.h oleaut32.lib function
SysReAllocString oleauto.h oleaut32.lib function
SysReAllocStringLen oleauto.h oleaut32.lib function
SysStringByteLen oleauto.h oleaut32.lib function
SysStringLen oleauto.h oleaut32.lib function
SystemTimeToVariantTime oleauto.h oleaut32.lib function
VarDateFromUdate oleauto.h oleaut32.lib function
VariantChangeType oleauto.h oleaut32.lib function
VariantChangeTypeEx oleauto.h oleaut32.lib function
VariantClear oleauto.h oleaut32.lib function
VariantCopy oleauto.h oleaut32.lib function
VariantCopyInd oleauto.h oleaut32.lib function
VariantInit oleauto.h oleaut32.lib function
VariantTimeToDosDateTime oleauto.h oleaut32.lib function
VariantTimeToSystemTime oleauto.h oleaut32.lib function
VarNumFromParseNum oleauto.h oleaut32.lib function
VarParseNumFromStr oleauto.h oleaut32.lib function
VarUdateFromDate oleauto.h oleaut32.lib function
VectorFromBstr oleauto.h oleaut32.lib function
Appendix B: Bibliography
[CAE RPC] CAE Specification, X/Open DCE: Remote Procedure Call, X/Open Com
pany Limited, Reading, Berkshire, UK (xopen.co.uk), 1994. X/Open Document Number
C309. ISBN 1-85912-041-5.
Appendix C: Specification Revision History
06 March 1995 First major draft of the COM Specification
24 October 1995 Second major draft of the COM Specification
17 February, 1997 First major draft of the COM Core Technology Specificati
on based on the COM Specification and Win32 SDK online help
10 April 1998 Update of COM Core Technology Specification
Appendix D: Glossary
A
Activation
The process of loading an object in memory, which puts it into its running state
. See also Binding.
Absolute moniker
A moniker that specifies the absolute location of an object. An absolute moniker
is analogous to a full path. See also Moniker.
Advisory holder
A COM object that caches, manages, and sends notifications of changes to contain
er applications' advisory sinks. See also Advisory sink.
Advisory sink
A COM object that can receive notifications of changes in an object because it i
mplements the IAdviseSink or IAdviseSink2 interface. Containers that need to be
notified of changes in objects implement an advisory sink. Notifications origina
te in the server, which uses an advisory holder object to cache and manage notif
ications to containers. See also Advisory holder.
Aggregate object
A COM object that is made up of one or more other COM objects. One object in the
aggregate is designated the controlling object, which controls which interfaces
in the aggregate are exposed and which are private. This controlling object has
a special implementation of IUnknown called the controlling IUnknown. All objec
ts in the aggregate must pass calls to IUnknown methods through the controlling
IUnknown. See also Aggregation.
Aggregation
A composition technique for implementing COM objects. It allows you to build a n
ew object by reusing one or more existing objects' interface implementations. Th
e aggregate object chooses which interfaces to expose to clients, and the interf
aces are exposed as if they were implemented by the aggregate object. Clients of
the aggregate object communicate only with the aggregate object. See also Aggre
gate object. Contrast with Containment.
Anti-moniker
The inverse of a file, item, or pointer moniker. An anti-moniker is added to the
end of a file, item, or pointer moniker to nullify it. Anti-monikers are used i
n the construction of relative monikers. See also Relative Moniker.
Artificial reference counting
A technique used to safeguard an object before calling a function or method that
could prematurely destroy it. A program calls IUnknown::AddRef to increment the
object's reference count before making the call that could free the object. Aft
er the function returns, the program calls IUnknown::Release to decrement the co
unt.
Asynchronous call
A call to a function that is executed separately so that the caller can continue
processing instructions without waiting for the function to return. Contrast wi
th Synchronous call.
Automation
A way to manipulate an application's objects from outside the application. Autom
ation is typically used to create applications that expose objects to programmin
g tools and macro languages, create and manipulate one application's objects fro
m another applications, or to create tools for accessing and manipulating object
s.
B
Bind context
A COM object that implements the IBindCtx interface. Bind contexts are used in m
oniker operations to hold references to the objects activated when a moniker is
bound. The bind context contains parameters that apply to all operations during
the binding of a generic composite moniker and provides the moniker implementati
on with access to information about its environment. See also Binding, Generic c
omposite moniker, and Moniker.
Binding
Associating a name with its referent. Specifically, locating the object named by
a moniker, putting it into its running state if it isn't already, and returning
an interface pointer to it. Objects can be bound at run time (also called late
binding or dynamic binding) or at compile time (also called static binding). See
also Moniker and Running state.
C
Class
The definition of an object in code. In C++, the class of an object is defined a
s a data type, but this is not the case in other languages. Because COM can be c
oded in any language, class is used to refer to the general object definition. S
ee also Class factory.
Class factory
A COM object that implements the IClassFactory interface and that creates one or
more instances of an object identified by a given class identifier(CLSID). See
also Class identifier.
Class identifier (CLSID)
A globally unique identifier (GUID) associated with an COM class object. If a cl
ass object will be used to create more than one instance of an object, the assoc
iated server application should register its CLSID in the system registry so tha
t clients can locate and load the executable code associated with the object(s).
Every COM server or container that allows linking to its embedded objects must
register a CLSID for each supported object definition. See also Class and Class
factory.
Class object
In object-oriented programming, an object whose state is shared by all the objec
ts in a class and whose behavior acts on that classwide state data. In COM, clas
s objects are called class factories, and typically have no behavior except to c
reate new instances of the class. See also Class factory.
Client
A COM object that requests services from another object.
CLSID
See Class identifier.
Commit
To persistently save any changes made to a storage or stream object since it was
opened or changes were last saved. See also Revert.
Component
An object that encapsulates both data and code, and provides a well-specified se
t of publicly available services.
Component Object Model (COM)
The object-oriented programming model that defines how objects interact within a
single process or between processes. In COM, clients have access to an object t
hrough interfaces implemented on the object. See also Interface.
Composite moniker
A moniker that consists of two or more monikers that are treated as a unit. A co
mposite moniker can be non-generic, meaning that its component monikers have spe
cial knowledge of each other, or generic, meaning that its component monikers kn
ow nothing about each other except that they are monikers. See also Generic comp
osite moniker.
COM object
An object that conforms to the Component Object Model (COM). A COM object is an
instance of an object definition, which specifies the object's data and one or m
ore implementations of interfaces on the object. Clients interact with a COM obj
ect only through its interfaces. See also Component Object Model and Interface.
Connectable object
A COM object that implements, at a minimum, the IConnectionPointContainer interf
ace, for the management of connection point objects. Connectable objects support
communication from the server to the client. A connectable object creates and m
anages one or more connection point subobjects, which receive events from interf
aces implemented on other objects and send them on to the client. See also Conne
ction point object and Advisory sink.
Connection point object
A COM object that is managed by a connectable object and that implements the ICo
nnectionPoint interface. One or more connection point objects can be created and
managed by a connectable object. Each connection point object manages incoming
events from a specific interface on another object and sends those events on to
the client. See also Connectable object, Advisory sink.
Containment
A composition technique for implementing COM objects. It allows one object to re
use some or all of the interface implementations of one or more other objects. T
he outer object acts as a client to the other objects, delegating implementation
when it wishes to use the services of one of the contained objects. Contrast wi
th Aggregation.
Controlling object
The object within an aggregate object that controls which interfaces within the
aggregate object are exposed and which are private. The IUnknown interface of th
e controlling object is called the controlling IUnknown. Calls to IUnknown metho
ds of other objects in the aggregate must be passed to the controlling IUnknown.
See also Aggregate object.
D
Data transfer object
An object that implements the IDataObject interface and contains data to be tran
sferred from one object to another through either the Clipboard or drag-and-drop
operations.
Dependent object
A COM object that is typically initialized by another object (the host object).
Although the dependent object's lifetime may only make sense during the lifetime
of the host object, the host object does not function as the controlling IUnkno
wn for the dependent object. In contrast, an object is an aggregated object when
its lifetime (by means of its reference count) is completely controlled by the
managing object. See also Host object. Contrast with Aggregation and Containment
.
Direct access mode
One of two access modes in which a storage object can be opened. In direct mode,
all changes are immediately committed to the root storage object. See also Tran
sacted access mode.
E
.EXE server
See Out-of-process server.
F
File moniker
A moniker based on a path in the file system. File monikers can be used to ident
ify objects that are saved in their own files. A file moniker is a COM object th
at supports the system-provided implementation of the IMoniker interface for the
file moniker class. See also Item moniker, Generic composite moniker, and Monik
er.
Format identifier
A GUID that identifies a persistent property set. Also referred to as FMTID. See
also Property set.
G
Generic composite moniker
A sequenced collection of monikers, starting with a file moniker to provide the
document-level path and continuing with one or more item monikers that, taken as
a whole, uniquely identifies an object. See also Composite moniker, Item monike
r, and File moniker.
H
Helper function
A function that encapsulates calls to other functions and interface methods publ
icly available in COM. Helper functions are a convenient way to call frequently
used sequences of function and method calls that accomplish common tasks.
Host object
A COM object that forms a hierarchical relationship with one or more other COM o
bjects, known as the dependent objects. Typically, the host object instantiates
the dependent objects, and their existence only makes sense within the lifetime
of the host object. However, the host object does not act as the controlling IUn
known for the dependent objects, nor does it directly delegate to the interface
implementations of those objects. See also Dependent object.
I
In parameter
A parameter that is allocated, set, and freed by the caller of a function or int
erface method. An In parameter is not modified by the called function. See also
In/Out parameter and Out parameter.
In/Out parameter
A parameter that is initially allocated by the caller of a function or interface
method, and set, freed, and reallocated, if necessary, by the process that is c
alled. See also In parameter and Out parameter.
In-process server
A server implemented as a DLL that runs in the process space of the client. See
also Out-of-process server, Local server, and Remote server.
Instance
An object for which memory is allocated or which is persistent.
Interface
A group of semantically related functions that provide access to a COM object. E
ach COM interface defines a contract that allows objects to interact according t
o the Component Object Model (COM). While COM provides many interface implementa
tions, most interfaces can also be implemented by developers designing COM appli
cations. See also Component Object Model and COM object.
Interface identifier (IID)
A globally unique identifier (GUID) associated with an interface. Some functions
take IIDs as parameters to allow the caller to specify which interface pointer
should be returned.
Item moniker
A moniker based on a string that identifies an object in a container. Item monik
ers can identify objects smaller than a file, including embedded objects in a co
mpound document, or a pseudo-object (like a range of cells in a spreadsheet). Se
e also File moniker, Generic composite moniker, Moniker, and Pseudo-object.
L
Licensing
A feature of COM that provides control over object creation. Licensed objects ca
n be created only by clients that are authorized to use them. Licensing is imple
mented in COM through the IClassFactory2 interface and by support for a license
key that can be passed at run time.
Link source
The data that is the source of a linked object. A link source may be a file or a
portion of a file, such as a selected range of cells within a file (also called
a pseudo object). See also Linked object.
Loaded state
The state of an object after its data structures have been loaded into memory an
d are accessible to the client process. See also Active state, Passive state, an
d Running state.
Local server
An out-of-process server implemented as an .EXE application running on the same
machine as its client application. See also In-process server, Out-of-process se
rver, and Remote server.
Lock
A pointer held to¾and possibly, a reference count incremented on¾a running object. C
OM defines two types of locks that can be held on an object: strong and weak. To
implement a strong lock, a server must maintain both a pointer and a reference
count, so that the object will remain "locked" in memory at least until the serv
er calls Release. To implement a weak lock, the server maintains only a pointer
to the object, so that the object can be destroyed by another process.
M
Marshaling
Packaging and sending interface method calls across thread or process boundaries
.
Moniker
An object that implements the IMoniker interface. A moniker acts as a name that
uniquely identifies a COM object. In the same way that a path identifies a file
in the file system, a moniker identifies a COM object in the directory namespace
. See also Binding.
Moniker class
An implementation of the IMoniker interface. System-supplied moniker classes inc
lude file monikers, item monikers, generic composite monikers, anti-monikers, po
inter monikers, and URL monikers.
Moniker client
An application that uses monikers to acquire interface pointers to objects manag
ed by another application.
Moniker provider
An application that makes available monikers that identify the objects it manage
s, so that the objects are accessible to other applications.
N
O
Object
In COM, a programming structure encapsulating both data and functionality that a
re defined for which the only public access is through the programming structure
's interfaces. A COM object must support, at a minimum, the IUnknown interface,
which maintains the object's existence while it is being used and provides acces
s to the object's other interfaces. See also COM and Interface.
Object state
The relationship between a compound document object in its container and the app
lication responsible for the object's creation: active, passive, loaded, or runn
ing. Passive objects are stored on disk or in a database, and the object is not
selected or active. In the loaded state, the object's data structures have been
loaded into memory, but they are not available for operations such as editing. R
unning objects are both loaded and available for all operations. Active objects
are running objects that have a visible user interface.
Out-of-process server
A server, implemented as an .EXE application, which runs outside the process of
its client, either on the same machine or a remote machine. See also Local serve
r and Remote server.
Out parameter
A parameter that is allocated and freed by the caller, but its value is set by t
he function being called. See also In parameter and In/Out parameter.
P
Passive state
The state of a COM object when it is stored (on disk or in a database). The obje
ct is not selected or active. See also Active state, Loaded state, Object state,
and Running state.
Persistent properties
Information that can be stored persistently as part of a storage object such as
a file or directory. Persistent properties are grouped into property sets, which
can be displayed and edited.
Persistent properties are different from the run-time properties of objects crea
ted with COM Controls and Automation technologies, which can be used to affect s
ystem behavior. The PROPVARIANT structure defines all valid types of persistent
properties, whereas the VARIANT structure defines all valid types of run-time pr
operties. See also Property, and Property sets.
Persistent storage
Storage of a file or object in a medium such as a file system or database so tha
t the object and its data persist when the file is closed and then re-opened at
a later time.
Pointer moniker
A moniker that maps an interface pointer to an object in memory. Whereas most mo
nikers identify objects that can be persistently stored, pointer monikers identi
fy objects that cannot. They allow such objects to participate in a moniker bind
ing operation.
Property identifier
A four-byte signed integer that identifies a persistent property within a proper
ty set. See also Persistent property and Property set.
Property set
A logically related group of properties that is associated with a persistently s
tored object. To create, open, delete, or enumerate one or more property sets, i
mplement the IPropertySetStorage interface. If you are using compound files, you
can use COM's implementation of this interface rather than implementing your ow
n. See also Persistent properties.
Property set storage
A COM storage object that holds a property set. A property set storage is a depe
ndent object associated with and managed by a storage object. See also Dependent
object, Property set.
Proxy
An interface-specific object that packages parameters for that interface in prep
aration for a remote method call. A proxy runs in the address space of the sende
r and communicates with a corresponding stub in the receiver's address space. Se
e also Stub, Marshaling, and Unmarshaling.
Proxy manager
In standard marshaling, a proxy that manages all the interface proxies for a sin
gle object. See also Marshaling, Proxy.
Pseudo-object
A portion of a larger object, such as a range of cells in a spreadsheet, that is
represented as a distinct a COM object.
R
Reference counting
Keeping a count of each interface pointer held on an object to ensure that the o
bject is not destroyed before all references to it are released. See also Lock.
Relative moniker
A moniker that specifies the location of an object relative to the location of
another object. A relative moniker is analogous to a relative path, such as ..\b
ackup\report.old. See also Moniker.
Remote Server
A server application, implemented as an EXE, running on a different machine from
the client application using it. See also In-process server, Local server, and
Out-of-process server.
Revert
To discard any changes made to an object since the last time the changes were co
mmitted or the object's storage was opened. See also Commit and Transacted acces
s mode.
Root storage object
The outermost storage object in a document. A root storage object can contain ot
her nested storage and stream objects. For example, a compound document is saved
on disk as a series of storage and stream objects within a root storage object.
See also Storage object, and Stream object.
Running state
The state of a COM object when its server application is running and it is possi
ble to access its interfaces and receive notification of changes. See also Activ
e state, Loaded state, Passive state.
Running Object Table (ROT)
A globally accessible table on each computer that keeps track of all COM objects
in the running state that can be identified by a moniker. Moniker providers reg
ister an object in the table, which increments the object's reference count. Bef
ore the object can be destroyed, its moniker must be released from the table. Se
e also Running state.
S
Self-registration
The process by which a server can perform its own registry operations.
Sink
See Advisory sink.
State
See Active state, Loaded state, Object state, Passive state, and Running state.
Storage object
A COM object that implements the IStorage interface. A storage object contains n
ested storage objects or stream objects, resulting in the equivalent of a direct
ory/file structure within a single file. See also Root storage object and Stream
object.
Stream object
A COM object that implements the IStream interface. A stream object is analogous
to a file in a directory/file system. See also Storage object.
Strong lock
See Lock.
Structured Storage
COM's technology for storing compound files in native file systems. See also Com
pound file, Storage object, and Stream object.
Stub
When a function's or interface method's parameters are marshaled across a proces
s boundary, the stub is an interface-specific object that unpackages the marshal
ed parameters and calls the required method. The stub runs in the receiver's add
ress space and communicates with a corresponding proxy in the sender's address s
pace. See also Proxy, Marshaling, and Unmarshaling.
Stub manager
Manages all of the interface stubs for a single object.
Subobject
See Dependent object.
Synchronous call
A function call that does not allow further instructions in the calling process
to be executed until the function returns. See also Asychronous call.
System registry
A system-wide repository of information supported by Windows, which contains inf
ormation about the system and its applications, including COM clients and server
s.
T
Type information
Information about an object's class provided by a type library. To provide type
information, a COM object implements the IProvideClassInfo interface.
U
Uniform data transfer
A model for transferring data via the Clipboard, drag and drop, or Automation. O
bjects conforming to this model implement the IDataObject interface. See also Da
ta transfer object.
Unmarshaling
Unpacking parameters that have been sent to a proxy across process boundaries.
V
Virtual Table (VTBL)
An array of pointers to interface method implementations. See also Interface.
W
Weak lock
See Lock .
Appendix E: Index
A
aggregation 83, 218
artificial reference counts 84
C
CoGetMalloc 48
CoMarshalInterface 754
CoTaskMemAlloc 48
CoUnmarshalInterface 754
CTextRender 89
functions
Load 97
D
DebugORPCClientFillBuffer 749
DebugORPCClientGetBufferSize 748
direct mode 65
E
Enumerators 61, 298
exceptions 61
F
facility codes 294
FACILITY_CONTROL 294
FACILITY_DISPATCH 294
FACILITY_ITF 294, 295
FACILITY_NULL 294
FACILITY_RPC 294
FACILITY_STORAGE 294
FACILITY_WIN32 294
FACILITY_WINDOWS 294
G
GUID 82
H
handler 52, 178, 219
HRESULT 294
I
IConnectionPoint 301
GetConnectionInterface 302
GetConnectionPointContainer 302
Advise 302
Unadvise 303
EnumConnections 304
IConnectionPointContainer 304
EnumConnectionPoints 304
FindConnectionPoint 305
IEnum
functions
Clone 300
Next 299
Reset 299
Skip 299
IEnumConnectionPoints 305
Next 306
Skip 307
Reset 307
Clone 308
IEnumConnections 308
Next 309
Skip 310
Reset 310
Clone 311
in parameter 48
in-out parameter 48
interface
definition 78
IPSFactoryBuffer 217
IRpcChannelBuffer 217
IRpcProxyBuffer 181, 217
IRpcStubBuffer 217
IStdMarshalInfo 219
O
Object Handlers 178
OBJREF 754
out parameter 48
R
Remote Debugging 744
Running Object Table 483
S
S_FALSE 295
S_OK 295
StringFromCLSID 219
StringFromIID 219
T
transacted mode 66
U
Unicode 77

Вам также может понравиться