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 create and use objects from the server using the CLSID to identify the object through the processes described in Chapter 7.
In addition, COM servers themselves may be clients of other objects, usually when the server is using those other objects to help implement part of its own objects. This chapter will cover the various methods of using an object as part of another 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 support this capability that are covered at the end of this chapter.
One of the most important ways for a client to get a pointer to an object is for the client to ask that a server be launched, and that an instance of the object provided by the server be created and activated. It is the responsibility of the server to ensure that this happens properly. There are several important parts to this.
The server must implement code for a class object through an implementation
of either the
IClassFactory()
or
IClassFactory2()
interface.
The server must register its CLSID in the system registry on the machine on which it resides, and further, has the option of publishing its machine location to other systems on a network to allow clients to call it without requiring the client to know the server's location.
The server is primarily responsible for security--that is, for the most part, the server determines whether it will provide a pointer to one of its objects to a client.
In-process servers should implement and export certain functions that allow the client process to instantiate them.
When a client uses a CLSID to request the creation of an object instance,
the first step is creation of a class object, an intermediate object that
contains an implementation of the methods of the
IClassFactory()
interface.
While COM provides several instance creation functions, the first
step in the implementation of these functions is the creation of a class object.
As a result, all servers must implement the methods of the
IClassFactory()
interface.
This interface contains two methods:
CreateInstance
and
LockServer
.
CreateInstance
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 client is ready for it to do so.
To enable a server to be responsible for its own licensing, COM defines
IClassFactory2()
, which inherits its definition from
IClassFactory()
.
Thus, a server implementing
IClassFactory2()
must, by definition, implement the methods of
IClassFactory()
.
For more information on
IClassFactory2()
, see Section
Section 8.3.
COM also provides helper functions for implementing out-of-process servers. For more information, see Section Section 8.6.
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 exists on a machine to indicate that the software was installed from
a valid source, such as the vendor's installation disks.
If the machine license
does not exist, the server can disallow object creation.
Machine licensing
prevents piracy in cases where a user attempts to copy the software from one
machine to another; because the license information is not copied with the
software, and the machine that receives the copy is not licensed.
However, in a component software industry, vendors need a finer level of control over licensing. In addition to machine license control, the a vendor needs to allow some clients to create a component object while preventing other clients from the same capability. This kind of licensing requires that the client application obtain a license key from component while the client application is still under development. The client application uses the license key later at run-time to create objects on an unlicensed machine.
For example, if a vendor provides a library of controls to developers, the developer who purchases the library will have a full machine license, allowing the objects to be created on the development machine. The developer can then build a client application on the licensed machine incorporating one or more of the controls. When the resulting client application is run on another machine, the controls used in the client application must be created on the other machine even if that machine does not possess a machine license 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 factory 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 provide license keys for run-time
licensing if the
fRunTimeKeyAvail
member is
TRUE
.
The
RequestLicKey
method provides a license key for
the component.
A machine license must be available when the client calls this
method.
The
CreateInstanceLic
method creates an instance
of the licensed component if the license key parameter (BSTR
bstrKey) is valid.
In its type information, a component uses the attribute
licensed
to mark the
coclass
that supports licensing
through
IClassFactory2()
.
To incorporate a licensed component into your client application, you
use the methods in
IClassFactory2()
.
First, you need a separate development tool that is also a client of
the licensed component.
The purpose of this tool is to obtain the run-time
license key and save it in your client application.
This tool runs only on
a machine that possesses a machine license for the component.
The tool calls
the
GetLicInfo
and
RequestLicKey
methods
to obtain the run-time license key and then saves the license key in your
client application.
For example, the development tool could create a
.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 instantiate the object directly with
IClassFactory::CreateInstance()
.
If
CreateInstance
succeeds, then the second
machine is itself licensed for the component and objects can be created at
will.
If
CreateInstance
fails with the return code
CLASS_E_NOTLICENSED
, the only way to create the object is to pass
the run-time key to the
CreateInstanceLic
method.
CreateInstanceLic
verifies the key and creates the object if the
key is valid.
In this way an application built with components (such as controls), can run on a machine that has no other license--only the client application containing the run-time license is allowed to create the component objects in question.
The
IClassFactory2()
interface supports flexibility
in licensing schemes.
For example, the server implementor can encrypt license
keys in the component for added security.
Server implementers can also enable
or disable levels of functionality in their objects by providing different
license keys for different functions.
For example, one key might allow a base
level of functionality, while another would allow basic and advanced functionality,
and so on.
See
OLE Controls Inside Out
published by MS
Press for detailed consideration of these issues.
After you have defined a class in code (ensuring that it implements
IClassFactory()
or
IClassFactory2()
) and assigned
it a CLSID, you need to put information in the registry that will allow COM,
on request of a client with the CLSID, to create instances of its objects.
This information tells the system, for a given CLSID, where the DLL or EXE
code for that class is located, and how it is to be launched.
There is more
than one way of registering a class in the registry.
In addition, there are
other ways of ``registering'' a class with the system when it is running,
so the system is aware that a running object is currently in the system.
These
topics are described in the following sections.
If a class is intended to be available to clients at any time, as most
applications are, you usually register it through an installation and setup
program.
This means putting information about the application into the registry,
including how and where its objects are to be instantiated.
This information
must be registered for all CLSIDs.
Other information is optional.
Win32 tools,
such as
Regsvr32
, make it simple to write a setup program
that registers servers at installation.
If you are not relying on system defaults, there are two important keys in the registry: 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-process, out-of-process local, or out-of-process remote.
Under the new AppID key, are several named-values that define information
specific to that application.
Among these are
RemoteServerName
,
and
ActivateAtStorage
, both of which can be used to permit
a client with no built-in knowledge of the location of the server, to create
an object.
For more information on remote instantiation, see Section
Section 7.1.3
and Section
Section 7.1.4.
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 its class object is automatically passed by COM to the
DllGetClassObject()
function, which the class must implement to give
the calling object a pointer to its class object.
Classes implemented in executables can specify that COM should execute
their process and wait for the process to register their class object's
IClassFactory()
through a call to the
CoRegisterClassObject()
function.
For detailed COM registry information, see Chapter 8.
When an executable (EXE) server is launched, it should call
CoRegisterClassObject()
, which registers the CLSID for the server
in what is called the class table (this is a different table than the running
object table).
When a server is registered 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 server is not listed in the class table will the SCM check the registry
for appropriate values and launch the server associated with the given CLSID.
You pass
CoRegisterClassObject()
the CLSID for the
class and a pointer to its
IUnknown()
interface.
Clients
who subsequently call
CoGetClassObject()
with this CLSID
will retrieve a pointer to their requested interface, as long as security
does not forbid it.
There are several instance creation and activation functions
described in Section
Section 7.1.4,
The server for a class object should call
CoRevokeClassObject()
to revoke the class object (remove its registration) when all
of the following are true:
There are no existing instances of the object definition
There are no locks on the class object
The application providing services to the class object is not under user control (not visible to the user on the display).
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 clients.
When the server calls
CreateFileMoniker
to create a file moniker to be registered in the ROT, servers should pass
local file names that are drive-based, not in UNC format.
This ensures that
the moniker comparison data that is generated by the ROT register call will
match what is used while doing a ROT lookup on the part of a remote client.
This is because when the distribed COM service receives an activation request
for a file local to the server from a remote client, the file is converted
to a local-drive-based path.
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 require the user to go through a lengthy installation procedure
or setup program.
Besides the licensing issues, which are handled through
IClassFactory2()
, an installation procedure typically creates the
necessary registry entries for a component to run properly in the COM context.
Self-Registration is the standard means through which a server module
can package its own registry operations, both registration and unregistration,
into the module itself.
When used with licensing handled through
IClassFactory2()
, a server can become an entirely self-contained
module with no need for external installation programs or
.REG
files.
[Footnote 41]
Any self-registering module, DLL or EXE, should first include
a string called
OleSelfRegister
in the
StringFileInfo
section of its version information resource:
[Footnote 41]
The existence of this data allows any interested party, such as an application that 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
DllRegisterServer()
and
DllUnregisterServer()
.
Any application that wishes to instruct the server to register itself (that
is, all its CLSIDs and type library IDs) can obtain a pointer to
DllRegisterServer()
through the Win32 API function
GetProcAddress
.
Within
DllRegisterServer()
, the DLL creates
all its necessary registry entries, storing the correct path to the DLL for
all
InprocServer32
or
InprocHandler32
entries.
When an application wishes to remove the component from the system,
it should unregister that component by calling
DllUnregisterServer()
.
Within this call, the server removes exactly those entries it
previously created in
DllRegisterServer()
.
The server should
not blindly remove all entries for its classes because other software may
have stored additional entries, such as a
TreatAs
key.
If the server is packaged in an EXE module, then the application wishing
to register the server launches the EXE server with the command-line argument
/RegServer
or
-RegServer
(case-insensitive).
If the application wishes to unregister the server, it launches the EXE with
the command-line argument
/UnregServer
or
-UnregServer
.
The self-registering EXE detects these command-line arguments
and invokes the same operations as a DLL would within
DllRegisterServer()
and
DllUnregisterServer()
, respectively, registering
its module path under
LocalServer32
instead of
InprocServer32
or
InprocHandler32
.
The server must register the full path to the installation location
of the DLL or EXE module for their respective
InprocServer32
,
InprocHandler32
, and
LocalServer32
keys in the
registry.
The module path is easily obtained through the Win32 API function
GetModuleFileName
.
Four helper functions that can be called by out-of-process servers are now available to simplify the job of writing server code. COM clients and COM in-process servers typically would not call them. These functions are designed to help prevent race conditions in server activation when the servers have multiple apartments or multiple class objects. They can also, however, as easily be used for single-threaded and single class object servers. The functions are as follows:
CoAddRefServerProcess()
CoReleaseServerProcess()
CoSuspendClassObjects()
CoResumeClassObjects()
To shut down properly, a COM server must keep track of how many object
instances it has instantiated and how many times its
IClassFactory::LockServer()
method has been called.
Only when both of these counts reach zero,
can a server shut down.
In single-threaded COM servers, the decision to shut
down was coordinated with incoming activation requests by the fact that the
requests were serialized by the message queue.
The server, upon receiving
a
Release()
on its final object instance and deciding to
shut down, would revoke its class objects before any more activation requests
were dispatched.
If an activation request did come in after this point, COM
would recognize that the class objects were revoked, and would return an error
to the SCM, which would then cause a new instance of the local
server process to be run.
However, in an apartment model server, in which different class objects are registered on different apartments, and in all free-threaded servers, this decision to shut down must be co-ordinated with activation requests across multiple threads, so one thread of the server does not decide to shut down while another thread of the server is busy handing out class objects or object instances. One classical but cumbersome approach to solving this is to have the server, after it has revoked its class objects, recheck its instance count and stay alive until all instances have been released.
To make it easier for server writers to handle these types of race conditions,
COM provides two new reference counting functions.
CoAddRefServerProcess()
increments a global per-process reference count.
CoReleaseServerProcess()
decrements the global per-process reference count.
When the global
per-process reference count reaches zero, COM automatically does a
CoSuspendClassObjects()
, which prevents any new activation requests
from coming in.
The server can then deregister its various class objects from
its various threads at leisure without worry that another activation request
may come in.
All new activation requests are henceforth handled by the SCM
launching a new instance of the local server
process.
The simplest way for a local server application to make use of these
APIs is to call
CoAddRefServerProcess()
in the constructor
for each of its instance objects, and in each of its
IClassFactory::LockServer()
methods when the
fLock
parameter is
TRUE
.
The server application should also call
CoReleaseServerProcess()
in the destructor of each of its instance objects, and in each
of its
IClassFactory::LockServer()
methods when the
fLock
parameter is
FALSE
.
Finally, the server application should pay attention to the return code
from
CoReleaseServerProcess()
and if it returns 0, the
server application should initiate its cleanup, which, for a server with multiple
threads, typically means that it should signal its various threads to exit
their message loops and call
CoRevokeClassObject()
and
CoUninitialize()
.
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, COM contacts the server, marshals the
IClassFactory()
interface of the class object, returns to the client
process, unmarshals the
IClassFactory()
interface, and
returns this to the client.
At this point, clients typically call
IClassFactory::LockServer(TRUE)
to prevent the server process from
shutting down.
However, there is a window of time between when the class object
is marshaled and when the client calls
LockServer
, in which
another client could connect to the same server, get an instance and
Release()
that instance causing the server to shutdown and leaving
the first client high and dry with a disconnected
IClassFactory()
pointer.
To prevent this race condition, COM adds an implicit
IClassFactory::LockServer(TRUE)
to the class object when it marshals
the
IClassFactory()
interface, and an implicit
IClassFactory::LockServer(FALSE)
when the client releases the
IClassFactory()
interface.
Because of this change, it is no longer
necessary to remote
LockServer
calls back to the server,
so the proxy for
IClassFactory::LockServer()
simply returns
S_OK
without actually
remoting the call.
There is another activation-related race condition during initialization
of an out-of-process server process.
A COM server that registers multiple
classes
typically
calls
CoRegisterClassObject(....REGCLS_LOCAL_SERVER)
for
each CLSID it supports.
After it has done this for all classes, the server
enters its message loop.
For a single-threaded COM server, all activation
requests are blocked until the server enters the message loop.
However, for
an apartment model server that registers different class objects in different
apartments, and for all free-threaded servers, activation requests can arrive
earlier than this.
In the case of apartment model servers, activation requests
could arrive as soon as any one thread has entered its message loop.
In the
case of free-threaded servers, an activation request could arrive as soon
as the first class object is registered.
Since an activation can happen this
early, it is also possible for the final
Release()
to occur
(and 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, any server that wants to register multiple class objects with COM
should call
CoRegisterClassObject(...., REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED)
for each different CLSID the server supports.
After all classes
have been registered and the server process is ready to accept incoming activation
requests, the server should make one call to
CoResumeClassObjects()
.
This API tells COM to inform the SCM about
all the registered classes, and it begins letting
activation requests into the server process.
Using these APIs has serveral
advantages.
First, only one call is made to the SCM regardless of how many
CLSIDs are registered, thus reducing the overall registration time (and hence
startup time of the server application).
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-threaded server, no activation requests
will come in until the server calls
CoResumeClassObjects()
,
giving the server a chance to register all of its CLSIDs and get properly
set up before having to deal with activation requests, and possible shut down
requests.
As mentioned earlier this specification, object handlers from one perspective are 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 just a fancy proxy for a local or remote server that does a little more than just forward calls through RPC. The latter view is more precise architecturally: a ``handler'' is simply the piece of code that runs in the client's space on behalf of a remote 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 object, 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 handlers, leaving ``handler'' for the more general situation.)
The structure of an object handler is exactly the same as a full-in
process server: an object handler implements an object, a class factory, and
the two functions
DllGetClassObject()
and
DllCanUnloadNow()
exactly as described above.
The key difference between handlers and full DLL servers (and simple proxy objects, for that matter) is the extent to which they implement their respective objects. Whereas the full DLL server implements the complete object (using other objects internally, if desired), the handler only implements a partial object depending on a local or remote server to complete the implementation. Again, the reasons for this is that sometimes a certain interface can only be useful when implemented 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 the handler would implement the restricted in-process interface but leave all others for implementation in the local or remote server.
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 rewriting all that code yourself you would like to reuse that other object for your own 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 Section 3.5, implementation inheritance has some significant drawbacks and problems that do not make it a good object reusability mechanism for a system object model.
For that reason COM supports two notions of object reuse, containment
and aggregation, that were also described in
Chapter 3.
In that chapter we saw that containment,
the most common and simplest for of object reuse, is where the ``outer object''
simply uses other ``inner objects'' for their services.
The outer object is
nothing more than a client of the inner objects.
We also saw in
Chapter 3
the notion of aggregation, where the outer object
exposes interfaces from inner objects as if the outer object implemented those
interfaces itself.
We brought up the catch that there has to be some mechanism
through which the
IUnknown()
behavior of inner object interfaces
exposed in this manner is appropriate to the outer object.
We are now in a
position to see exactly how the solution manifests itself.
The following sections treat Containment and Aggregation in more detail
using the
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 that 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()
Let's say that when we decide to implement the
TextRender
object we find that another 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 for that text through
IDataObject::GetData
.
This ``TextImage
'' object
cannot render memory 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 current text in
IDataObject::GetData
, would delegate the rendering to the
TextImage
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
TextImage
's
IDataObject::GetData
asking for the metafile
or bitmap format.
This delegation is illustrated in
Figure 8-1.
To create this configuration, the
TextRender
object
would, during its own creation, instantiate the
TextImage
object with the following code, storing the
TextImage
'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.
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 time we find that the implementor of the
TextImage
object at the time the implementor of the
TextRender
object sat down to work (or perhaps is making a revision of his
object) that the vendor of the
TextImage
object has improved
TextImage such that it implements everything that
TextRender
would like to do through its
IDataObject()
interface.
That
is,
TextImage
still accepts text through
SetData
but has 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 implementation of
IDataObject
is exactly the implementation that
TextRender
requires.
What we, as the implementors of TextRender, would like to do now
is simply expose
TextImage
's
IDataObject()
as our own as shown in
Figure 8-2.
The only catch is that we must implement the proper behavior of the
IUnknown()
members in the inner object's (TextImage
)
IDataObject()
interface:
AddRef
and
Release()
have to affect the reference count on the outer object
(TextRender
) and not the reference count of the inner object.
Furthermore,
QueryInterface()
has to be able to 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 object.
[Footnote 42]
One other catch remains: the outer object must have a means to
control the lifetime of the inner object through
AddRef
and
Release()
as well as have a means to query 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 inner object exclusively and never delegates
to the outer object.
[Footnote 42]
This requires that the inner object separate the
IUnknown()
members of its functional interfaces from an implementation of
IUnknown()
that strictly controls the inner object itself.
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 the 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
.
If 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 object saves
pUnkOuter
for later use, but does not call
AddRef()
on it.
The reason is that the inner object's lifetime is entirely contained
within the outer object's lifetime, so there is no need for the call and to
do so would create a circular reference.
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 inner object must be sure to return its non-delegating
IUnknown()
.
If the inner object itself aggregates other objects (which
is unknown to the outer 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 object, the outer object calls
QueryInterface()
in the non-delegating
IUnknown()
to obtain the pointer
to return to the client.
The inner object must delegate to the controlling unknown,
that is,
pUnkOuter
, all
IUnknown
calls
occurring in any interface it implements other than the non-delegating
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 reference counting and
QueryInterface()
, and provides a way for the outer object to control the inner
object's lifetime separately.
The mechanism is illustrated in
Figure 8-3.
Now let's look at how this mechanism manifests itself in code.
First
off, the
TextRender
object no longer needs its own
IDataObject()
implementation and can thus remove it from its class,
but will need to add a member
m_pUnkImage
to maintain the
TextImage
's non-delegating
IUnknown
:
classCTextRender()
: publicIPersistFile()
{ private: ULONG m_cRef; //Reference Count char * m_pszText; //Pointer to allocated text ULONG m_cchText; //Number of characters in m_pszTextIUnknown()
* m_pUnkImage; //TextImageIUnknown()
//Other internal member functions here public: [Constructor, Destructor] //Outer objectIUnknown()
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
TextImage
object for containment
using
CoCreateInstance()
with the
pUnkOuter
parameter set to
NULL
.
In aggregation, this parameter will
be
TextRender
's own
IUnknown
(obtained
using a typecast).
Furthermore, TextRender must request
IUnknown()
initially from
TextImage
(storing the pointer
in
m_pUnkImage
):
//TextRender initialization
HRESULT hr;
hr=CoCreateInstance(CLSID_TextImage, CLSCTX_ SERVER, (IUnknown()
*)this, IID_IUnknown, (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 its own
IDataObject()
any longer, its implementation 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'sAddRef()
is our own. AddRef(); return NOERROR; }
Note that delegating
QueryInterface()
to the inner
object is done only for those interfaces that the outer object knows it wants
to expose.
The outer object should 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 outer object should the inner one be revised
with new functionality.
In order to avoid reference counting cycles, special action is needed if the outer 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 should invoke
Release()
on the
punkOuter
that was given to the inner object at its instantiation 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 that controlling objects delay the acquisition of cached pointers and release them when there is no possible use for them.
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 previous example is itself an aggregate object that
uses other inner objects.
In such a case
TextImage
will
be passing some controlling unknown to those other inner objects.
If
TextImage
is not being aggregated by anyone else, then the controlling
unknown is its own; otherwise it passes the
pUnkOuter
from
IClassFactory::CreateInstance
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 controlling unknown if its interface is exposed from that final outer object. Therefore performance and efficiency of multiple levels of aggregation is not an issue. At worst each delegation is a single extra function call.
The final topic related to COM Servers for this chapter is what is known as emulation: 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. This 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 indicate 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 (persistently) until canceled or changed.
In addition it supplies
CoGetTreatAsClass()
to allow a
caller to determine if a given CLSID is marked for emulation.
The
ICatRegister()
interface provides methods for
registering and unregistering component category information in the Registry.
This includes both the human-readable names of categories and the categories
implemented/required by a given component or class.
There is no need to implement this interface.
The Component Category
Manager, a system-provided COM object that can be instantiated by using
CoCreateInstance()
, implements
ICatRegister()
.
The owner of a category uses this interface to register or unregister the human-readable names. The owner of a component uses this interface to add or remove categories implemented or required by this component.
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
RegisterCategories
|
Registers one or more component categories. |
UnRegisterCategories
|
Removes the registration of one or more component categories. |
RegisterClassImplCategories
|
Registers the class as implementing one or more component categories. |
UnRegisterClassImplCategories
|
Removes one or more implemented category identifiers from a class. |
RegisterClassReqCategories
|
Registers the class as requiring one or more component categories. |
UnRegisterClassReqCategories
|
Removes one or more required category identifiers from a class. |
- Registers one or more component categories.
Each component category
consists of a
CATID and a list of locale-dependent description strings.
ICatRegister::RegisterCategories()
HRESULT RegisterCategories(
#include <comcat.h>
ULONG cCategories,
CATEGORYINFO * rgCategoryInfo
);
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.
[in] The number of component categories to register.
[in] The array of
cCategories
CATEGORYINFO
structures.
By providing the same
CATID
for multiple
CATEGORYINFO
structures, multiple locales can be registered
for the same component category.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterClassImplCategories()
,
ICatRegister::RegisterClassReqCategories()
,
ICatRegister::UnRegisterCategories()
,
ICatRegister::UnRegisterClassImplCategories()
,
ICatRegister::UnRegisterClassReqCategories()
- Registers the class as implementing one or more component categories.
ICatRegister::RegisterClassImplCategories()
HRESULT RegisterClassImplCategories(
#include <comcat.h>
REFCLSID rclsid,
ULONG cCategories,
CATID * rgcatid
);
In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.
[in] The class ID of the relevent class for which category information will be set.
[in] The number of category CATIDs to associate as category identifiers for the class.
[in] The array of
cCategories
CATID
to associate
as category identifiers for the class.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterCategories()
,
ICatRegister::RegisterClassReqCategories()
,
ICatRegister::UnRegisterCategories()
,
ICatRegister::UnRegisterClassImplCategories()
,
ICatRegister::UnRegisterClassReqCategories()
- Registers the class as requiring one or more component categories.
ICatRegister::RegisterClassReqCategories()
HRESULT RegisterClassReqCategories(
#include <comcat.h>
REFCLSID rclsid,
ULONG cCategories,
CATID * rgcatid
);
In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. This function can only be called by the owner of a class, usually as part of the installation of the component.
[in] The class ID of the relevent class for which category information will be set.
[in] The number of category CATIDs to associate as category identifiers for the class.
[in] The array of
cCategories
CATID
to associate
as category identifiers for the class.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterCategories()
,
ICatRegister::RegisterClassImplCategories()
,
ICatRegister::UnRegisterCategories()
,
ICatRegister::UnRegisterClassImplCategories()
,
ICatRegister::UnRegisterClassReqCategories()
- Removes the registration of one or more component categories.
Each component
category consists of a CATID and a list of locale-dependent description strings.
ICatRegister::UnRegisterCategories()
HRESULT UnRegisterCategories(
#include <comcat.h>
ULONG cCategories,
REFCATID rgcatid
);
This function will be successful even if one or more of the category IDs specified are not registered. This function can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.
This method does not remove the component category tags from individual classes. To do this, use the
ICatRegister::UnRegisterClassCategories()
method.
[in] The number of cCategories CATIDs to be removed.
[in] Identifies the categories to be removed.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterCategories()
,
ICatRegister::RegisterClassImplCategories()
,
ICatRegister::RegisterClassReqCategories()
,
ICatRegister::UnRegisterClassImplCategories()
,
ICatRegister::UnRegisterClassReqCategories()
- Removes one or more implemented category
identifiers from a class.
ICatRegister::UnRegisterClassImplCategories()
HRESULT UnRegisterClassImplCategories(
#include <comcat.h>
REFCLSID rclsid,
ULONG cCategories,
CATID * rgcatid
);
In case of an error, this function does not ensure that the Registry is restored to the state prior to the call. The call will be successful even if one or more of the category IDs specified are not registered for the class. This function can only be called by the owner of a class, usually as part of the de-installation of the component.
[in] The class ID of the relevant class to be manipulated.
[in] The number of category CATIDs to remove.
[in] The array of
cCategories
CATID
that
are to be removed.
Only the category IDs specified in this array are removed.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterCategories()
,
ICatRegister::RegisterClassImplCategories()
,
ICatRegister::RegisterClassReqCategories()
,
ICatRegister::UnRegisterCategories()
,
ICatRegister::UnRegisterClassReqCategories()
- Removes one or more required category identifiers
from a class.
ICatRegister::UnRegisterClassReqCategories()
HRESULT UnRegisterClassReqCategories(
#include <comcat.h>
REFCLSID rclsid,
ULONG cCategories,
CATID * rgcatid*
);
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.
[in] The class ID of the relevent class to be manipulated.
[in] The number of category CATIDs to remove.
[in] The array of
cCategories
CATID
that
are to be removed.
Only the category IDs specified in this array are removed.
S_OK
The function was successful.
E_INVALIDARG
One or more arguments are incorrect.
ICatRegister::RegisterCategories()
,
ICatRegister::RegisterClassImplCategories()
,
ICatRegister::UnRegisterCategories()
,
ICatRegister::UnRegisterClassImplCategories()
,
ICatRegister::RegisterClassReqCategories()
The
IClassFactory()
interface contains two methods
intended to
deal with an entire class of objects, and so is implemented on the class object
for
a specific class of objects (identified by a CLSID).
The first method,
CreateInstance()
,
creates an uninitialized object of a specified CLSID, and the second,
LockServer()
,
locks the object's server in memory, allowing new objects to be created more
quickly.
You must implement this interface for every class that you register in the system registry and to which you assign a CLSID, so objects of that class can be created.
After calling the
CoGetClassObject()
function to
get an
IClassFactory()
interface pointer to the class object,
call the
CreateInstance
method of this interface to create a new
uninitialized
object.
It is not, however, always necessary to go through this process to create
an
object.
To create a single uninitialized object, you can, instead, just call
CoCreateInstance()
.
COM also provides numerous helper functions
(with names of the form
OleCreate
Xxx)
to create compound document
objects.
Call the
LockServer
method to keep the object server
in memory and
enhance performance only if you intend to create more than one object of the
specified class.
IUnknown Methods
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
CreateInstance
|
Creates an uninitialized object. |
LockServer
|
Locks object application open in memory. |
CoGetClassObject()
,
CoCreateInstance()
- Creates an uninitialized object.
IClassFactory::CreateInstance()
HRESULT CreateInstance(
#include <unknwn.h>
IUnknown * pUnkOuter,
REFIID riid,
void ** ppvObject
);
The
IClassFactory()
interface is always on a class
object.
The
CreateInstance
method creates an uninitialized object
of the class
identified with the specified CLSID.
When an object is created in this way,
the
CLSID must be registered in the system registry with
CoRegisterClassObject()
.
The pUnkOuter parameter indicates whether the object is being created as part of an aggregate. Object definitions are not required to support aggregation--they must be specifically designed and implemented to support it.
The
riid
parameter specifies the IID (interface
identifier) of the
interface through which you will communicate with the new object.
If
pUnkOuter
is non-NULL
(indicating
aggregation), the value of the
riid
parameter must be
IID_IUnknown
.
If the object is not part of
an aggregate,
riid
often specifies the interface though
which the object
will be initialized.
For COM embeddings, the initialization interface is
IPersistStorage()
, but in other situations, other interfaces
are
used.
To initialize the object, there must be a subsequent call to an
appropriate method in the initializing interface.
Common initialization
functions include
IPersistStorage::InitNew()
(for new,
blank
embeddable components),
IPersistStorage::Load()
(for reloaded
embeddable components),
IPersistStream::Load()
, (for objects
stored
in a stream object) or
IPersistFile::Load()
(for objects
stored in
a file).
In general, if an application supports only one class of objects, and
the class
object is registered for single use, only one object can be created.
The
application must not create other objects, and a request to do so should return
an error from
IClassFactory::CreateInstance()
.
The same
is true for
applications that support multiple classes, each with a class object registered
for single use; a
CreateInstance
for one class followed
by a
CreateInstance
for any of the classes should return an
error.
To avoid returning an error, applications that support multiple classes
with
single-use class objects can revoke the registered class object of the first
class by calling
CoRevokeClassObject()
when a request for
instantiating a second is received.
For example, suppose there are two classes,
A and B.
When
IClassFactory::CreateInstance()
is called
for class A,
revoke the class object for B.
When B is created, revoke the class object
for
A.
This solution complicates shutdown because one of the class objects might
have already been revoked (and cannot be revoked twice).
[in] If the object is being created as part of an aggregate, pointer
to
the controlling
IUnknown()
interface of the aggregate.
Otherwise,
pUnkOuter
must be
NULL
.
[in] Reference to the identifier of the interface to be used to communicate
with the newly created object.
If
pUnkOuter
is
NULL
, this parameter is frequently the IID of the initializing
interface; if
pUnkOuter
is non-NULL
,
riid
must be
IID_IUnknown
(defined
in the
header as the IID for
IUnknown()
).
[out] Indirect pointer to the requested interface.
If the object does
not support
the interface specified in
riid,
ppvObject
must be set to
NULL
.
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.
CoRegisterClassObject()
,
CoRevokeClassObject()
,
CoCreateInstance()
,
CoGetClassObject()
- Called by the client of a class object to keep a server open in
memory,
allowing instances to be created more quickly.
IClassFactory::LockServer()
HRESULT LockServer(
#include <unknwn.h>
BOOL fLock
);
IClassFactory::LockServer()
controls whether an object's
server is kept in
memory.
Keeping the application alive in memory allows instances to be created
more quickly.
Most clients do not need to call this function. It is provided only for those clients that require special performance in creating multiple instances of their objects.
If the lock count is zero, there are no more objects in use, and the
application is not under user control, the server can be closed.
One way to
implement
IClassFactory::LockServer()
is to call
CoLockObjectExternal()
.
The process that locks the object application is responsible for unlocking
it.
Once the class object is released, there is no mechanism that guarantees the
caller connection to the same class later (as in the case where a class object
is registered as single-use).
It is important to count all calls, not just
the
last one, to
IClassFactory::LockServer()
, because calls
must be balanced
before attempting to release the pointer to the
IClassFactory()
interface
on the class object or an error results.
For every call to
LockServer
with
fLock
set to
TRUE
, there must
be a call to
LockServer
with
fLock
set to
FALSE
.
When the lock count and the class object reference
count are both zero, the class object can be freed.
[in] If
TRUE
, increments the lock count; if
FALSE
, decrements the lock count.
This method supports the standard return values
E_FAIL
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as
well
as the following:
S_OK
The specified object was either locked (
fLock
=
TRUE
) or unlocked from memory (
fLock
=
FALSE
).
The
IClassFactory2()
interface enables a class factory
object,
in any sort of object server, to control object creation through licensing.
This
interface is an extension to
IClassFactory()
.
This extension
enables a class factory executing on a licensed machine to provide a license
key
that can be used later to create an object instance on an unlicensed machine.
Such
considerations are important for objects like controls that are used to build
applications on a licensed machine.
Subsequently, the application built must
be
able to run on an unlicensed machine.
The license key gives only that one
client
application the right to instantiate objects through
IClassFactory2()
when a full machine license does not exist.
Implement this interface on a class factory object if you need to
control object creation through a license.
A class that supports licensing
should be marked in an object's type information with the
[licensed]
attribute on the object's
coclass
entry.
The
CreateInstance
method inherited from
IClassFactory()
is allowed
to return
CLASS_E_NOTLICENSED
to indicate that object creation
is controlled
through licensing.
The caller can create an instance of this object only
through
IClassFactory2::CreateInstanceLic()
if the caller
has a license
key obtained from
IClassFactory2::RequestLicKey()
.
Otherwise,
no object
creation is allowed.
Use this interface to create licensed objects or to obtain a license key that can be used in later creations.
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
CreateInstance
|
Creates an uninitialized object. |
LockServer
|
Locks object application open in memory. |
|
Description
|
GetLicInfo
|
Fills a
LICINFO
structure
with information on the licensing capabilities of this class factory.
|
RequestLicKey
|
Creates and returns a license key that the
caller can save and use later in calls to
IClassFactory2::CreateInstanceLic() .
|
CreateInstanceLic
|
Creates an instance of the licensed object
given a license key from
IClassFactory2::RequestLicKey() .
|
- Creates an instance of the object class supported by this class
factory,
given a license key previously obtained from
IClassFactory2::CreateInstanceLic()
IClassFactory2::RequestLicKey()
.
This method is the only possible means to create an object on an otherwise
unlicensed machine.
HRESULT CreateInstanceLic(
#include <ocidl.h>
IUnknown * pUnkOuter,
IUnknown * pUnkReserved,
REFIID riid,
BSTR bstrKey,
void ** ppvObject
);
If the class factory does not provide a license key (that is,
IClassFactory2::RequestLicKey()
returns
E_NOTIMPL
and the
fRuntimeKeyAvail
field in
LICINFO
is set to
FALSE
in
IClassFactory2::GetLicInfo()
), then this method can also
return
E_NOTIMPL
.
In such cases, the class factory is implementing
IClassFactory2()
simply
to specify whether or not the machine is licensed at all through the
fLicVerified
field of
LICINFO
.
[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
.
[in] Unused.
Must be
NULL
.
[in] Reference to the identifier of the interface to be used to communicate with the newly created object.
[in] Run-time license key previously obtained from
IClassFactory2::RequestLicKey()
that is required to create
an object.
[out] Indirect pointer to the interface of the type specified in
riid.
This parameter is set to
NULL
on failure.
This method supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as
well as the following:
S_OK
The license was successfully created.
E_NOTIMPL
This method is not implemented because objects can only be created on
fully
licensed machines through
IClassFactory::CreateInstance()
.
E_POINTER
The pointers passed in
bstrKey
or
ppvObject
are not valid.
For example, it may be
NULL
.
E_NOINTERFACE
The object can be created (and the license key is valid) except the object does not support the interface specified by riid.
CLASS_E_NOAGGREGATION
The
pUnkOuter
parameter is non-NULL
,
but this object class does not support aggregation.
CLASS_E_NOTLICENSED
The key provided in bstrKey is not a valid license key.
IClassFactory2::GetLicInfo()
,
IClassFactory2::RequestLicKey()
,
LICINFO
- Fills a caller-allocated
IClassFactory2::GetLicInfo()
LICINFO
structure
with information describing
the licensing capabilities of this class factory.
HRESULT GetLicInfo(
#include <ocidl.h>
LICINFO * pLicInfo
);
E_NOTIMPL
is not allowed as a return value since
this method provides
critical information for the client of a licensed class factory.
[out] Pointer to the caller-allocated
LICINFO
structure
to be filled on output.
This method supports the standard return value
E_UNEXPECTED
, as well as
the following:
S_OK
The
LICINFO
structure was successfully filled in.
E_POINTER
The address in
pLicInfo
is not valid.
For example,
it may be
NULL
.
IClassFactory2::CreateInstanceLic()
,
IClassFactory2::RequestLicKey()
,
LICINFO
- If the fRuntimeKeyAvail field in
IClassFactory2::RequestLicKey()
LICINFO
has
been returned
as TRUE from
IClassFactory2::GetLicInfo()
, then this method
creates and returns a license key.
The caller can save the license key persistently
and use it later in calls to
IClassFactory2::RequestLicKey()
.
HRESULT RequestLicKey(
#include <ocidl.h>
DWORD dwReserved ,
BSTR * pbstrKey
);
The caller can save the license key for subsequent calls to
IClassFactory2::CreateInstanceLic()
to create objects on
an
otherwise unlicensed machine.
The caller must free the
BSTR
with
SysFreeString()
when the
key is no longer needed.
The value of
fRuntimeKeyAvail
is returned
through a previous call to
IClassFactory2::GetLicInfo()
.
This method allocates the
BSTR
key with
SysAllocString()
or
SysAllocString[Len]
, and the caller becomes responsible
for this
BSTR
once this method returns successfully.
This method need not be implemented when a class factory does not support run-time license keys.
[in] Unused. Must be zero.
[out] Pointer to the caller-allocated
BSTR
variable
that
receives the callee-allocated license key on successful return from this method.
This parameter is set to
NULL
on any failure.
This method supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as
well as the following:
S_OK
The license key was successfully created.
E_NOTIMPL
This class factory does not support run-time license keys.
E_POINTER
The address in
pbstrKey
is not valid.
For example,
it may
be
NULL
.
CLASS_E_NOTLICENSED
This class factory supports run-time licensing, but the current machine itself is not licensed. Thus, a run-time key is not available on this machine.
IClassFactory2::CreateInstanceLic()
,
IClassFactory2::GetLicInfo()
,
LICINFO
The
IExternalConnection()
interface enables an embedded
object
to keep track of external 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
implement 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 time when its stub manager's count of strong external references has reached zero. This situation would arise, for example, at the end of a silent update, when the final 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 zero, 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
the stub manager to do so, it can save its data before the stub manager has
a
chance 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 object combines this count with its own tally of strong
internal references to maintain an accurate total of all locks.
All embeddable compound-document objects that support links to
themselves or portions of themselves should implement
IExternalConnection
to prevent possible data loss during shutdown.
In addition, an in-place
container should call
OleLockRunning()
to hold a strong
lock on its
embedded objects.
An object's stub manager should call
IExternalConnection()
whenever
an external connection is added or released.
IUnknown Methods
|
Description
|
QueryInterface()
|
Returns pointers to supported interfaces. |
AddRef()
|
Increments reference count. |
Release()
|
Decrements reference count. |
|
Description
|
AddConnection
|
Increments count of external locks. |
ReleaseConnection
|
Decrements count of external locks. |
- Increments an object's count of its strong external connections
(links).
IExternalConnection::AddConnection()
HRESULT AddConnection(
#include <objidl.h>
DWORD exconn,
DWORD dwreserved
);
An object created by a EXE object server relies on its stub manager
to
call
IExternalConnection::AddConnection()
whenever a link
client activates
and therefore creates an external lock on the object.
When the link client
breaks the connection, the stub manager calls
IExternalConnection::ReleaseConnection()
to release the
lock.
Since DLL object applications exist in the same process space as their
objects,
they do not use RPC (remote procedure calls) and therefore do not have stub
managers to keep track of external connections.
Therefore, DLL servers that
support external links to their objects must implement
IExternalConnection()
so link clients can directly call
the interface to
inform them when connections are added or released.
The following is a typical implementation for the
AddConnection
method:
DWORD XX::AddConnection(DWORD extconn, DWORD dwReserved) { return extconn&EXTCONN_STRONG ? ++m_cStrong : 0; }
[in] Type of external connection to the object.
The only type of external
connection currently supported by this interface is strong, which means that
the object must remain alive as long as this external connection exists.
Strong
external connections are represented by the value
EXTCONN_STRONG
=
0x0001, which is defined in the enumeration
EXTCONN
.
[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 whether a zero value is returned.
DWORD
valueThe number of reference counts on the object; used for debugging purposes only.
IExternalConnection::ReleaseConnection()
,
IRunnableObject::LockRunning()
- Decrements an object's count of its strong external connections
(references).
IExternalConnection::ReleaseConnection()
HRESULT ReleaseConnection(
#include <objidl.h>
DWORD extconn,
DWORD dwreserved,
BOOL fLastReleaseCloses
);
If
fLastReleaseCloses
equals
TRUE
,
calling
ReleaseConnection
causes the object to shut itself down.
Calling this
method is the only way in which a DLL object, running in the same process
space
as the container application, will know when to close following a silent
update.
[in] Type of external connection to the object.
The only type of external
connection currently supported by this interface is strong, which means that
the object must remain alive as long as this external connection exists.
Strong external connections are represented by the value
EXTCONN_STRONG
= 0x0001, which is defined in the enumeration
EXTCONN
.
[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 on whether a zero value is returned.
[in]
TRUE
specifies that if the connection being
released
is the last external lock on the object, the object should close.
FALSE
specifies that the object should remain open until
closed by the user or another process.
DWORD
valueThe number of connections to the object; used for debugging purposes only.
IExternalConnection::AddConnection()
- Disconnects all remote process connections being maintained on behalf of all
the interface pointers that point to a specified object.
Only the process
that
actually manages the object should call
CoDisconnectObject()
CoDisconnectObject()
.
STDAPI CoDisconnectObject((
#include <objbase.h>
IUnknown * pUnk,
DWORD dwReserved
);
The
CoDisconnectObject()
function enables a server
to correctly
disconnect all external clients to the object specified by
pUnk.
The
CoDisconnectObject()
function performs the following
tasks:
Checks to see if the object to be disconnected implements
the
IMarshal()
interface.
If so, it gets the pointer to that
interface; if
not, it gets a pointer to the standard marshaler's (i.e.,
COM's)
IMarshal()
implementation.
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 server calls
CoDisconnectObject()
to
forcibly disconnect an object's clients, usually in response to a user closing
the server application.
Similarly, a COM container that supports external links to its embedded
objects can call
CoDisconnectObject()
to destroy those
links.
Again, this
call is normally made in response to a user closing the application.
CoDisconnectObject()
does not necessarily disconnect out-of-process
clients immediately.
If any marshaled calls are pending on the server object,
CoDisconnectObject()
disconnects the object only when those calls have returned.
In the meantime,
CoDisconnectObject()
sets a flag that causes any new marshaled
calls to return
CO_E_OBJECTNOTCONNECTED
.
[in] Pointer to any IUnknown-derived interface on the object to be disconnected.
[in] Reserved for future use; must be zero.
S_OK
All connections to remote processes were successfully deleted.
- Called either to lock an object to ensure that it stays in memory, or to
release such a lock.
Call
CoLockObjectExternal()
CoLockObjectExternal()
to place
a strong lock on an object to ensure that it stays in memory.
STDAPI CoLockObjectExternal((
#include <objbase.h>
IUnknown * pUnk,
BOOL fLock,
BOOL fLastUnlockReleases
);
The
CoLockObjectExternal
function prevents the reference
count of
an object from going to zero, thereby ``locking'' it into existence until
the
lock is released.
The same function (with different parameters) releases the
lock.
The lock is implemented by having the system call
IUnknown::AddRef()
on the object.
The system then waits
to call
IUnknown::Release()
on the object until a later call to
CoLockObjectExternal()
with
fLock
set to
FALSE
.
This function can
be used to maintain a reference count on the object on behalf of the end user,
because it acts outside of the object, as does the user.
The
CoLockObjectExternal
function
must
be called in the process
in which the object actually resides (the EXE process, not the process in
which
handlers may be loaded).
Calling
CoLockObjectExternal()
sets a strong lock
on an object.
A strong
lock keeps an object in memory, while a weak lock does not.
Strong locks are
required, for example, during a silent update to an OLE embedding.
The embedded
object's container must remain in memory until the update process is complete.
There must also be a strong lock on an application object to ensure that the
application stays alive until it has finished providing services to its
clients.
All external references place a strong reference lock on an object.
The
CoLockObjectExternal
function is typically called
in the following
situations:
Object servers should call
CoLockObjectExternal()
with both
fLock
and
fLastLockReleases
set
to
TRUE
when they become visible.
This call creates a strong lock on behalf of the user.
When the application
is
closing, free the lock with a call to
CoLockObjectExternal()
,
setting
fLock
to
FALSE
and
fLastLockReleases
to
TRUE
.
A call to
CoLockObjectExternal()
on the
server can also be used in
the implementation of
IOleContainer::LockContainer
.
There are several things to be aware of when you use
CoLockObjectExternal()
in the implementation of
IOleContainer::LockContainer
.
An embedded object would
call
IOleContainer::LockContainer
on its container to keep it
running (to
lock it) in the absence of other reasons to keep it running.
When the embedded
object becomes visible, the container must weaken its connection to the
embedded object with a call to the
OleSetContainedObject
function, so other connections can affect the object.
Unless an application manages all aspects of its application and document
shutdown completely with calls to
CoLockObjectExternal()
,
the container
must keep a private lock count in
IOleContainer::LockContainer
so that
it exits when the lock count reaches zero and the container is invisible.
Maintaining all aspects of shutdown, and thereby avoiding keeping a private
lock count, means that
CoLockObjectExternal()
should be
called whenever
one of the following conditions occur:
A document is created and destroyed or made visible or invisible.
An application is started and shut down by the user.
A pseudo-object is created and destroyed.
For debugging purposes, it may be useful to keep a count of the number of external locks (and unlocks) set on the application.
The end user has explicit control over the lifetime of an application, even if there are external locks on it. That is, if a user decides to close the application (File, Exit), it must shut down. In the presence of external locks (such as the lock set by
CoLockObjectExternal()
), the application can call theCoDisconnectObject()
function to force these connections to close prior to shutdown.
[in] Pointer to the
IUnknown()
interface on the object
to be locked or unlocked.
[in] Whether the object is to be locked or released.
Specifying
TRUE
holds a reference to the object (keeping it in memory),
locking it independently of external or internal
AddRef/Release
operations, registrations, or revocations.
If
fLock
is
TRUE,
fLastLockReleases
is ignored.
FALSE
releases a lock previously set with a call to this function.
[in] Whether a given lock is the last reference that is supposed to
keep an object
alive.
If it is,
TRUE
releases all pointers to the object
(there
may be other references that are not supposed to keep it alive).
This function supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as
well as the following:
S_OK
The object was locked successfully.
- Informs COM that a class object, previously registered with the
CoRevokeClassObject()
CoRegisterClassObject()
function, is no longer available
for use.
HRESULT CoRevokeClassObject((
#include <objbase.h>
DWORD dwRegister
);
A successful call to
CoRevokeClassObject()
means
that the class
object has been removed from the global class object table (although it does
not release the class object).
If other clients still have pointers to the
class object and have caused the reference count to be incremented by calls
to
IUnknown::AddRef()
, the reference count will not be zero.
When this
occurs, applications may benefit if subsequent calls (with the obvious
exceptions of
IUnknown::AddRef()
and
IUnknown::Release()
) to the
class object fail.
An object application
must
call
CoRevokeClassObject()
to revoke
registered class objects before exiting the program.
Class object implementers
should call
CoRevokeClassObject()
as part of the release
sequence.
You
must specifically revoke the class object even when you have specified the
flags
value
REGCLS_SINGLEUSE
in a
call to
CoRegisterClassObject()
,
indicating that only one application can connect to the class object.
[in] Token previously returned from the
CoRegisterClassObject()
function.
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.
CoGetClassObject()
,
CoRegisterClassObject()
- Registers an EXE class object with COM so other applications can
connect to
it.
EXE object applications should call
CoRegisterClassObject()
CoRegisterClassObject()
on startup.
It can also be used to register internal objects for use by the
same
EXE or other code (such as DLLs) that the EXE uses.
STDAPI CoRegisterClassObject((
#include <objbase.h>
REFCLSID rclsid,
IUnknown * pUnk,
DWORD dwClsContext,
DWORD flags,
LPDWORD * lpdwRegister
);
Only EXE object applications call
CoRegisterClassObject()
.
Object
handlers or DLL object applications do not call this function--instead,
they
must implement and export the
DllGetClassObject()
function.
At startup, a multiple-use EXE object application must create a class
object
(with the
IClassFactory()
interface on it), and call
CoRegisterClassObject()
to register the class object.
Object
applications
that support several different classes (such as multiple types of embeddable
objects) must allocate and register a different class object for each.
Multiple registrations of the same class object are independent and do not produce an error. Each subsequent registration yields a unique key in lpdwRegister.
Multiple document interface (MDI) applications must register their class
objects.
Single document interface (SDI) applications must register their
class
objects only if they can be started by means of the
/Embedding
switch.
The server for a class object should call
CoRevokeClassObject()
to
revoke the class object (remove its registration) when all of the following
are
true:
There are no existing instances of the object definition.
There are no locks on the class object.
The application providing services to the class object is not under user control (not visible to the user on the display).
After the class object is revoked, when its reference count reaches zero, the class object can be released, allowing the application to exit.
For information on the
flags
parameter, refer to
the
REGCLS
enumeration.
[in] CLSID to be registered.
[in] Pointer to the
IUnknown()
interface on the class
object whose availability is being published.
[in] Context in which the executable code is to be run.
For information
on
these context values, see the
CLSCTX
enumeration.
[in] How connections are made to the class object.
For information on
these
flags, see the
REGCLS
enumeration.
[out] Pointer to a value that identifies the class object registered;
later
used by the
CoRevokeClassObject()
function to revoke the
registration.
This function supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
, and
E_UNEXPECTED
, as
well as the following:
S_OK
The class object was registered successfully.
CO_E_OBJISREG
Already registered in the class object table.
CoGetClassObject()
,
CoRevokeClassObject()
,
DllGetClassObject()
,
REGCLS
,
CLSCTX
- Determines whether the DLL that implements this function is in use.
If not,
the caller can safely unload the DLL from memory.
DllCanUnloadNow()
STDAPI DllCanUnloadNow((
);
#include <objbase.h>
A call to
DllCanUnloadNow()
determines whether the
DLL from which
it is exported is still in use.
A DLL is no longer in use when it is not
managing any existing objects (the reference count on all of its objects is
0).
COM does not provide this function. DLLs that support the Component Object Model (COM) should implement and export
DllCanUnloadNow()
.
You should not have to call
DllCanUnloadNow()
directly.
COM calls
it only through a call to the
CoFreeUnusedLibraries()
function.
When it returns
S_OK
,
CoFreeUnusedLibraries()
safely frees the DLL.
You need to implement
DllCanUnloadNow()
in, and export
it from,
DLLs that are to be dynamically loaded through a call to the
CoGetClassObject()
function.
(You also need to implement
and export
the
DllGetClassObject()
function in the same DLL).
If a DLL loaded through a call to
CoGetClassObject()
fails to export
DllCanUnloadNow()
, the DLL will not be unloaded until the
application
calls the
CoUninitialize()
function to release the COM
libraries.
If the DLL links to another DLL, returning
S_OK
from
DllCanUnloadNow()
will also cause the second, dependent DLL to be unloaded.
To eliminate the
possibility of a crash, the primary DLL should call the
CoLoadLibrary()
function, specifying the path to the second
DLL as
the first parameter, and setting the auto free parameter to
TRUE
.
This forces
the COM library to reload the second DLL and set it up for a call to
CoFreeUnusedLibraries()
to free it separately when appropriate.
DllCanUnloadNow()
should return
S_FALSE
if there are any existing
references to objects that the DLL manages.
S_OK
The DLL can be unloaded.
S_FALSE
The DLL cannot be unloaded now.
- Retrieves the class object from a DLL object handler or object application.
DllGetClassObject()
DllGetClassObject()
is called from within the
CoGetClassObject()
function when the class context is a
DLL.
STDAPI DllGetClassObject((
#include <objbase.h>
REFCLSID rclsid,
REFIID riid,
LPVOID * ppv
);
If a call to the
CoGetClassObject()
function finds
the class
object that is to be loaded in a DLL,
CoGetClassObject()
uses the DLL's
exported
DllGetClassObject()
function.
COM does not provide this function. DLLs that support the COM Component Object Model (COM) must implement
DllGetClassObject()
in COM object handlers or DLL applications.
You should not call
DllGetClassObject()
directly.
When an object is
defined in a DLL,
CoGetClassObject()
calls the
CoLoadLibrary()
function to load the DLL, which, in turn,
calls
DllGetClassObject()
.
You need to implement
DllGetClassObject()
in (and
export it from)
DLLs that support the Component Object Model.
[in] CLSID that will associate the correct data and code.
[in] Reference to the identifier of the interface that the caller is
to use to
communicate with the class object.
Usually, this is
IID_IClassFactory
(defined in the COM headers as the interface identifier for
IClassFactory()
).
[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
.
This function supports the standard return values
E_INVALIDARG
,
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well
as the following:
S_OK
The object was retrieved successfully.
CLASS_E_CLASSNOTAVAILABLE
The DLL does not support the class (object definition).
Following is an example (in C++) of an implementation of
DllGetClassObject()
.
In this example,
DllGetClassObject()
creates a
class object and calls its
QueryInterface()
method to retrieve
a pointer
to the interface requested in
riid.
The implementation
safely releases
the reference it holds to the
IClassFactory()
interface
because it
returns a reference-counted pointer to
IClassFactory()
to the caller.
HRESULT_export PASCAL DllGetClassObject()
(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj)
{
HRESULT hres = E_OUTOFMEMORY;
*ppvObj = NULL;
CClassFactory *pClassFactory = new CClassFactory(rclsid);
if (pClassFactory != NULL) {
hRes = pClassFactory->QueryInterface(riid, ppvObj);
pClassFactory->Release();
}
return hRes;
}
CoGetClassObject()
,
DllCanUnloadNow
- Instructs an in-process server to create its registry entries for all classes
supported in this server module.
If this function fails, the state of the
registry for all its classes is indeterminate.
DllRegisterServer()
STDAPI DllRegisterServer((
#include <olectl.h>
void
);
E_NOTIMPL
is not a valid return code.
This function supports the standard return values
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well as the following:
S_OK
The registry entries were created successfully.
SELFREG_E_TYPELIB
The server was unable to complete the registration of all the type libraries used by its classes.
SELFREG_E_CLASS
The server was unable to complete the registration of all the object classes.
- Instructs an in-process server to remove only those entries created
through
DllUnregisterServer()
DllRegisterServer()
.
STDAPI DllUnregisterServer((
#include <olectl.h>
void
);
The server must not disturb any entries that it did not create which
currently exist for its object classes.
For example, between registration
and
unregistration, the user may have specified a
TreatAs
relationship
between this class and another.
In that case, unregistration can remove all
entries except the
TreatAs
key and any others that were
not explicitly
created in
DllRegisterServer()
.
The Win32 registry functions
specifically
disallow the deletion of an entire populated tree in the registry.
The server
can attempt, as the last step, to remove the CLSID key, but if other
entries still exist, the key will remain.
This function supports the standard return values
E_OUTOFMEMORY
and
E_UNEXPECTED
, as well as the following:
S_OK
The registry entries were created successfully.
S_FALSE
Unregistration of this server's known entries was successful, but other entries still exist for this server's classes.
SELFREG_E_TYPELIB
The server was unable to remove the entries of all the type libraries used by its classes.
SELFREG_E_CLASS
The server was unable to remove the entries of all the object classes.
- Increments a global per-process reference count.
CoAddRefServerProcess()
ULONG CoAddRefServerProcess((
#include <objbase.h>
void
);
Servers can call
CoAddRefServerProcess()
to increment
a global
per-process reference count.
This function is particularly helpful to servers
that are implemented with multiple threads, either multi-apartmented or
free-threaded.
Servers of these types must coordinate the decision to shut
down
with activation requests across multiple threads.
Calling
CoAddRefServerProcess()
increments a global per-process
reference count,
and calling
CoReleaseServerProcess()
decrements that count.
When that count reaches zero, COM automatically calls
CoSuspendClassObjects()
, which prevents new activation
requests
from coming in.
This permits the server to deregister its class objects from
its various threads without worry that another activation request may come
in.
New activation requests result in launching a new instance of the local server
process.
The simplest way for a local server application to make use of these
API
functions is to call
CoAddRefServerProcess()
in the constructor
for each
of its instance objects, and in each of its
IClassFactory::LockServer()
methods when the
fLock
parameter
is
TRUE
.
The server application should also call
CoReleaseServerProcess()
in the destruction of each of its instance objects, and in each of its
IClassFactory::LockServer()
methods when the
fLock
parameter is
FALSE
.
Finally, the server application should pay attention
to the return code
from
CoReleaseServerProcess()
and if it returns 0, the
server application
should initiate its cleanup, which, for a server with multiple threads,
typically means that it should signal its various threads to exit their message
loops and call
CoRevokeClassObject()
and
CoUninitialize()
.
If these APIs are used at all, they must be called in both the object
instances
and the
LockServer
method, otherwise the server application
may be shut
down prematurely.
In-process servers typically should not call
CoAddRefServerProcess()
or
CoReleaseServerProcess()
.
S_OK
The CLSID was retrieved successfully.
CoReleaseServerProcess()
,
IClassFactory::LockServer()
,
Out-of-process Server Implementation Helpers
- Decrements the global per-process reference count.
CoReleaseServerProcess()
ULONG CoReleaseServerProcess((
#include <objbase.h>
void
);
Servers can call
CoReleaseServerProcess()
to decrement
a global
per-process reference count incremented through a call to
CoAddRefServerProcess()
When that count reaches zero, COM automatically calls
CoSuspendClassObjects()
, which prevents new activation
requests
from coming in.
This permits the server to deregister its class objects from
its various threads without worry that another activation request may come
in.
New activation requests result in launching a new instance of the local server
process.
The simplest way for a local server application to make use of these
API
functions is to call
CoAddRefServerProcess()
in the constructor
for each
of its instance objects, and in each of its
IClassFactory::LockServer()
methods when the
fLock
parameter is
TRUE
.
The server application should
also call
CoReleaseServerProcess()
in the destruction of
each of its
instance objects, and in each of its
IClassFactory::LockServer()
methods
when the
fLock
parameter is
FALSE
.
Finally, the server application must
check the return code from
CoReleaseServerProcess()
; if
it returns 0, the
server application should initiate its cleanup.
This typically means that
a
server with multiple threads should signal its various threads to exit their
message loops and call
CoRevokeClassObject()
and
CoUninitialize()
.
If these APIs are used at all, they must be called in both the object
instances
and the
LockServer
method, otherwise the server application
may be
shutdown prematurely.
In-process Servers typically should not call
CoAddRefServerProcess()
or
CoReleaseServerProcess()
.
0
The server application shouild initiate its cleanup.
Other Values
Server application should not yet initiate its cleanup.
CoSuspendClassObjects()
,
CoReleaseServerProcess()
,
IClassFactory::LockServer()
,
Out-of-process Server
Implementation Helpers
- Called by a server that can register multiple class objects to inform the
COM SCM about all registered classes, and permits activation requests for
those class objects.
CoResumeClassObjects()
WINOLEAPI CoResumeClassObjects((
#include <objbase.h>
void
);
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
activation requests into the server process.
This reduces the overall registration time, and thus the server
application startup time, by making a single call to the SCM, no matter how
many CLSIDs are registered for the server.
Another advantage is that if the
server has multiple apartments with different CLSIDs registered in different
apartments, or is a free-threaded server, no activation requests will come
in
until the server calls
CoResumeClassObjects()
.
This gives
the server a
chance to register all of its CLSIDs and get properly set up before having
to
deal with activation requests, and possibly shutdown requests.
S_OK
The CLSID was retrieved successfully.
CoRegisterClassObject()
,
CoSuspendClassObjects()
,
Out-of-process Server Implementation Helpers
- Prevents any new activation requests from the SCM on all class
objects
registered within the process.
CoSuspendClassObjects()
WINOLEAPI CoSuspendClassObjects((
#include <objbase.h>
void
);
CoSuspendClassObjects()
prevents any new activation
requests from the SCM
on all class objects registered within the process.
Even though a process
may
call this API, the process still must call
CoRevokeClassObject()
for each CLSID it has registered, in the apartment it registered in.
Applications typically do not need to call this API, which is generally only
called internally by COM when used in conjunction with
CoReleaseServerProcess()
.
S_OK
The CLSID was retrieved successfully.
CoRevokeClassObject()
,
CoReleaseServerProcess()
,
Out-of-process Server Implementation Helpers
- The
LICINFO()
LICINFO
structure contains parameters that describe the licensing
behavior of a class factory that supports licensing.
The structure is
filled during the
IClassFactory2::GetLicInfo()
method.
(
);
#include <ocidl.h>
typedef struct tagLICINFO { ULONG cbLicInfo; BOOL fRuntimeKeyAvail; BOOL fLicVerified; } LICINFO;
Size of the
LICINFO
structure.
Whether this class factory allows the creation of its objects on a unlicensed
machine through the use of a license key.
If
TRUE
,
IClassFactory2::RequestLicKey()
can be called to obtain
the
key.
If
FALSE
, objects can be created only on a fully licensed
machine.
Whether a full machine license exists so that calls to
IClassFactory::CreateInstance()
and
IClassFactory2::RequestLicKey()
will succeed.
If
TRUE
, the full machine license exists.
Thus, objects
can be created freely.
and a license key is available if
fRuntimeKeyAvail
is also TRUE.
If
FALSE
,
this class factory cannot create any instances of objects on this machine
unless
the proper license key is passed to
IClassFactory2::CreateInstanceLic()
.
IClassFactory::CreateInstance()
,
IClassFactory2::CreateInstanceLic()
,
IClassFactory2::GetLicInfo()
,
IClassFactory2::RequestLicKey()
The
REGCLS
enumeration defines values used in
CoRegisterClassObject()
to control the type of
connections to a class object.
It is defined as follows:
typedef enum tagREGCLS { REGCLS_SINGLEUSE = 0, REGCLS_MULTIPLEUSE = 1, REGCLS_MULTI_SEPARATE = 2, REGCLS_SUSPENDED = 4, REGCLS_SURROGATE = 8, } REGCLS;
REGCLS_SINGLEUSE
Once an application is connected to a class object with
CoGetClassObject()
, the class object is removed from
public view so that no other applications can connect to it.
This value
is commonly used for single document interface (SDI) applications.
Specifying
this value does not affect the responsibility of the object application to
call
CoRevokeClassObject()
; it must always call
CoRevokeClassObject()
when it is finished with an object
class.
REGCLS_MULTIPLEUSE
Multiple applications can connect to the class object through calls
to
CoGetClassObject()
.
If both the
REGCLS_MULTIPLEUSE
and
CLSCTX_LOCAL_SERVER
are set in a call to
CoRegisterClassObject()
, the class object is also
automatically registered as an in-process server, whether or not
CLSCTX_INPROC_SERVER
is explicitly set.
REGCLS_MULTI_SEPARATE
Multiple applications can connect to the class object through calls
to
CoGetClassObject()
.
If
REGCLS_MULTI_SEPARATE
is set, each execution context must be set separately:
CoRegisterClassObject()
does not automatically register
an out-of-process server (for which
CLSCTX_LOCAL_SERVER
is set)
as an in-process server.
When a class object is registered with only the
REGCLS_MULTI_SEPARATE
and
CLSCTX_LOCAL_SERVER
flags set and the server tries to bind to an object with its own CLSID, another
instance of the server is started.
REGCLS_SUSPENDED
Suspends registration and activation requests for the specified CLSID
until there
is a call to
CoResumeClassObjects()
.
This is used typically
to
register the CLSIDs for servers that can register multiple class objects to
reduce
the overall registration time, and thus the server application startup time,
by
making a single call to the SCM, no matter how many CLSIDs are registered
for the server.
REGCLS_SURROGATE
The class object is a surrogate process used to run DLL servers. The class factory registered by the surrogate process is not the actual class factory implemented by the DLL server, but a generic class factory implemented by the surrogate. This generic class factory delegates instance creation and marshaling to the class factory of the DLL server running in the surrogate.
In
CoRegisterClassObject()
, members of both the
REGCLS
and
the
CLSCTX
enumerations, taken together, determine how
the class
object is registered.
An EXE surrogate (in which DLL servers are run) calls
CoRegisterClassObject()
to register a class factory using
a new
REGCLS
value,
REGCLS_SURROGATE
.
The following table summarizes the allowable
REGCLS
value combinations and the
object registrations affected by the combinations.
REGCLS_ SINGLEUSE
|
REGCLS_ MULTIPLEUSE
|
REGCLS_ MULTI_ SEPARATE
|
Other
|
|
CLSCTX_INPROC_SERVER
|
Error | In-process | In-process | Error |
CLSCTX_LOCAL_SERVER
|
Local | In-process/ local | Local | Error |
Both of the above
|
Error | In-process/ local | In-process/ local | Error |
Other
|
Error | Error | Error | Error |
All class factories for dll surrogates should be registered with
REGCLS_SURROGATE
set.
Do not set
REGCLS_SINGLUSE
or
REGCLS_MULTIPLEUSE
when
you register a surrogate for DLL servers.
CoGetClassObject()
,
CoRegisterClassObject()
,
CoRevokeClassObject()
,
DllGetClassObject()