For example, DirectX, a set of specialized components for
graphics programming in Microsoft Windows, does not use any COM+ component services because of its critical performance requirements.
Chapter One
With the release of Windows NT 4.0 in 1996, COM gained the functionality necessary to invoke components that ran on remote
computers connected via a network. This technology, at first called Distributed COM, is now an integral part of COM+ itself.
Chapter Two
This calling convention specifies that parameters are pushed onto
the stack from left to right and that the called function pops its own
arguments from the stack before returning.
IDL plays an important role in the development of COM+ objects,
but it is not part of COM+. IDL is simply a tool used to help
programmers define interfaces.
The sun is expected to last only another 4.5 billion years, after
which COM+ might lose some of its universal appeal.
These calls are CoTaskMemAlloc, CoTaskMemFree,
CoTaskMemRealloc, and the IMalloc methods of the task
allocator object returned by CoGetMalloc.
In early versions of OLE, applications could replace the default
memory allocator by passing the address of a custom allocator object to
CoInitializeEx. COM+ does not currently support applications
replacing this allocator, and if the parameter is anything but
NULL, CoInitializeEx returns an E_INVALIDARG
error.
The registry is a system-wide data store that contains
information about the current user, machine configuration, application
settings, and COM+ component information. You can view and edit the
data in the registry with the registry editor utility, regedit.exe.
This code will run without complaint in this case because our
implementation of ISum runs in-process with the client. However,
changing the object's v-table slightly or running the component
outside of the client process where it would depend on the marshaling
infrastructure of COM+ would make this code deadly.
Actually, the compiler first converts this older C-style cast to
a static_cast, but because that would cause an error in this
case, the compiler silently demotes the cast to use the
reinterpret_cast operator. The reinterpret_cast operator is
a type-blind casting operator, which effectively is equivalent to the
older C-style casts.
Only the IUnknown interface pointer must have the same address returned by every call to QueryInterface, other interfaces are not subject to this same restriction. You can exploit this little-known loophole to create tear-off interfaces that come and go dynamically.
Due to the potential for race conditions in multithreaded
in-process components, COM+ might not immediately unload the DLL even
if S_OK is returned by DllCanUnloadNow.
We are not suggesting that you actually write code like this, but it
can be helpful to see that in the simple case of an in-process
component, COM+ is not doing anything incredibly complex in
CoCreateInstance.
The COM+ SCM is often confused with the Windows 2000 Service Control
Manager used to manage Win32 services.
These functions are part of the Win32 API, not COM+, and because
their names are relatively descriptive, they will not be described here
in detail. See the Win32 reference documentation for more
information.
Incidentally, this is also how the Active Template Library (ATL)
handles component registration needs.
Specifically, the functions GetFileVersionInfoSize,
GetFileVersionInfo, and VerQueryValue are used for this
purpose. For sample code that uses these functions to check whether a
component has the OLESelfRegister flag in its version
information resource, see the CheckReg.dsw work-space file in the
Samples\The IUnknown Interface\CheckReg folder on the companion CD.
It is generally best to choose the leftmost base class when
casting because some compilers, including Visual C++, place the
leftmost base class at the top of the object layout and then do not
require an adjustor thunk to manipulate the this pointer. This
is why IMultiply is preferable to ISum for casting of the
IUnknown interface pointer.
The limitation is actually even more stringent because you cannot
use aggregation across apartment boundaries. For details about the
apartment models, see Chapter 4.
Chapter Three
Visual Basic and Visual J++ automatically generate type libraries
for components built in those languages.
Not all IDL attributes are supported by the current
implementation of type libraries in COM+. For details, see Chapter
16.
Both Visual C++ and Visual J++ offer a similar feature.
The LoadTypeLib function followed by
RegisterTypeLib achieves the same result.
In Java, source files are saved with the .java extension; files
compiled to binary Java bytecode are saved with the .class
extension.
Actually, Visual J++ invokes a separate helper utility named
jactivex.exe to read the type library and generate the Java to COM+
wrappers.
The Microsoft Java VM's implementation of IUnknown,
IExternalConnection, and ISupportErrorInfo cannot be
overridden. However, Microsoft allows Java components to implement the
IExternalConnectionSink interface, which notifies you when
external connections are added and released.
Chapter Four
The first version of COM to be multithreading was released in
Microsoft Windows NT 3.51.
Only executable STA components require a message loop; in-process
components do not have message loops because the client application is
responsible for processing window messages.
CoGetCurrentProcess is recommended because it always
returns a unique value identifying the current thread. The thread
identifiers returned by the GetCurrentThreadId function can be
reused as threads are created and destroyed.
Some documentation incorrectly states that only the initial thread
must call CoInitializeEx with the COINIT_MULTITHREADED
flag, and that all other threads implicitly join the MTA. This
technique might work in some cases, but it is not considered legal COM+
code.
Actually, the MTA uses threads from the thread pool created and
maintained by the RPC run-time libraries.
CoMarshalInterface is discussed further in Chapter 15.
IMarshal is the custom marshaling interface defined by COM+;
it is covered in detail in Chapter 15.
Contexts are a construct used primarily to distinguish between
configured COM+ components that require different runtime services.
They are covered later in this chapter.
In Windows NT 4.0, the stubs of objects that aggregate the FTM are
created in the apartment of the thread that marshaled the object
out-of-process. This can cause an identity problem when the object is
represented by multiple stubs in different apartments. In Windows 2000,
the stubs of objects that aggregate the FTM are always created in the
NA, which solves the identity problem. Since objects that aggregate the
FTM are apartment-neutral, it doesn't matter which thread the call
arrives on.
Although the FTM is typically used by objects marked ThreadingModel = Both, this is in no way a requirement. The ThreadingModel setting basically determines only from which
apartment your DllGetClassObject function is called.
In-process components cannot set the Instancing property
to SingleUse because multiple copies of a DLL cannot be loaded
in a client's address space.
Chapter Five
Automation has supplanted Dynamic Data Exchange (DDE), an earlier
message-based protocol sometimes used for the same purpose.
IDispatch is called a standard interface because Microsoft
defined it as part of COM+. The proxy-stub code needed to marshal the
IDispatch interface is contained in the oleaut32.dll file; this
is important because it means that applications implementing the
IDispatch interface don't have to provide their own marshaling
code.
When compiling the IDL file, MIDL will insert the flag
TYPEFLAG_FOLEAUTOMATION in the type library file for each interface
that has the oleautomation attribute. This flag is recognized by
the RegisterTypeLib and LoadTypeLibEx functions.
The correct VT_ prefixed constant is listed in the comment
to the right of the types in the VARIANT structure.
For ease of implementation, the interface designed for scripting
clients is usually built as a dual interface.
The sample is in the Samples\Automation\Collections folder on the
companion CD.
This parameter is described in the documentation as reserved; only
the IID_NULL value is permitted.
A more realistic implementation of IDispatchEx would
assign DISPIDs sequentially and keep track of those that had been
assigned.
In the system header files, OLECHAR is defined as
wchar_t.
VARIANTARG is just another name for a VARIANT. The VARIANTARG type
is used to pass parameters, while the VARIANT type carries the return
value from a property or method call.
CreateObject also offers the possibility for supplying an
optional machine name parameter to instantiate remote components, in
which case the CoCreateInstanceEx function is called.
This is an implementation of the ECMA 262 language specification
with some proprietary enhancements.
HKCR is used as an abbreviation of the HKEY_CLASSES_ROOT key in
the registry.
This element is optional; if you leave it out, a looser syntax can
be used when defining the scriptlet.
Chapter Seven
Actually, if you need to retrieve many interfaces implemented by an object, you should use the QueryMultipleInterfaces method of the IMultiQI interface; for details, see Chapter 13.
Chapter Eight
Actually, the first outgoing and incoming interfaces not marked
with the restricted IDL attribute are assumed to be the
defaults. The restricted attribute hides the specified interface
from use in languages that rely on type libraries, such as Visual Basic
and Java.
A quacking sound can be heard whenever the B key is pressed. Readers
of Kraig Brockschmidt's Inside OLE (Microsoft Press, 1995)
will understand.
Or perhaps it simply proves that Visual Basic is a most forgiving
client.
The type information is obtained via the IProvideClassInfo
interface; see Chapter 9.
It might help to refer back to Figure 8-3 you examine
these steps. Note that step 1 in the list correlates to call number 2
in Figure 8-3 because here we are assuming that a client has already
instantiated the object.
Type libraries do not accurately represent the IDL interface
definition in certain cases; see Chapter 16 for details.
A Visual Basic or Java program can read the information stored in
an existing type library by setting a reference to the TypeLib
Information (tlbinf32.dll) component, as described later in this
chapter.
The obsolete CreateTypeLib function creates a type library
in the older format and returns a pointer to the ICreateTypeLib
interface.
The CreateTypeLib2 function creates type libraries using
the memory-mapped file capabilities of Win32.
Of course, we recommend that you consider using MIDL if your future
component development plans call for a type library.
Chapter Ten
Stream objects that require only simple sequential access
implement the ISequentialStream interface. Typically, the
ISequentialStream interface is not implemented by itself. Instead,
a more sophisticated stream object implements the
ISequentialStream::Read and ISequentialStream::Write methods
as part of an IStream interface implementation. For example, the
stream object implemented as part of the structured storage service
fails when QueryInterface is called for the
ISequentialStream interface; calling QueryInterface for
IStream succeeds.
The GetHGlobalFromStream function returns a pointer to the
memory block allocated by the CreateStreamOnHGlobal
function.
The GetSizeMax method is not applicable to property bag
objects; the IsDirty method was added to the
IPersistPropertyBag2 interface—although Visual Basic does not
currently implement this interface.
The enhanced version of this interface, IPropertyBag2, has
several new methods.
This indicates that the object always thinks it's dirty.
Chapter Eleven
When instantiating an object on a remote machine, the system provides an optimization of CoCreateInstance. Instead of making two round-trips (one for CoGetClassObject and another for IClassFactory::CreateInstance), the Service Control Manager (SCM) on the remote machine executes these steps and returns the desired pointer to the caller.
It is customary, but not required, to name custom activation
interfaces in the form of IThingFactory, where Thing is the name of the coclass being instantiated by the class object.
Executable components that implement custom activation interfaces in place of IClassFactory have special lifetime issues that you must address. For more information, see the section titled "Managing the Lifetime of an Executable Component" in Chapter 13.
Currently the class moniker does not honor the BIND_OPTS2.pServerInfo member.
Although it was undocumented until recently, the CoGetObject function is now a legitimate member of the COM+ run-time library.
The GetObject function has an additional code path
executed for file monikers and objects registered in the Running Object Table, which is not discussed here.
Visual Basic can access any custom activation interface as long as it uses Automation-compatible types. Interestingly, this restriction means that IClassFactory is not directly accessible to Visual Basic programmers.
Although no implementation of the IClassActivator
interface currently exists in a system-supplied moniker, Microsoft might provide one in the future.
ProgIDs are not case sensitive.
The class moniker could simply have used the COSERVERINFO
information defined in the BIND_OPTS2 structure to achieve the same result. However, the class moniker does not currently use this information when binding to a class object.
Chapter Twelve
COM+ also provides an alternative version of the standard
surrogate, dllhst3g.exe, that supports a 3-GB user-mode address
space.
Modern components, which were designed for use in a distributed
environment, normally offer their own configuration options and thus
have no use for the Distributed COM Configuration utility.
These data types are listed in the section titled "Automation Types" in Chapter 5.
Chapter Thirteen
The COM+ run-time environment supports only in-process
components.
Actually, they run in a separate window station. See Chapter 18
for details.
Alternatively, you can use the RegisterServerEx function
(also provided in registry.cpp on the companion CD) to configure the
registry information based on a tabular data structure. See Chapter 2
for details.
By instance objects, we mean regular COM+ objects that are
not class objects.
For more information about the stub manager, see Chapter 15.
The CoDisconnectObject function terminates all client
connections. See Chapter 14.
Note that singletons are not supported by the COM+ run-time
environment.
Custom class objects are discussed in detail in Chapter 11.
Chapter Fourteen
See the appendix for details.
For more on NDR, see the sidebar titled "Network Data Representation"
The system also provides a built-in implementation of custom
marshaling called standard marshaling, which is described in Chapter
15.
For information on how to embed proxy and stub code in an
application, see Chapter 13.
We'll describe how to use custom marshaling to implement
marshal-by-value semantics later in this chapter.
CoMarshalInterface first calls QueryInterface for
your IUnknown interface pointer several times as part of its
standard stub creation and identity testing.
If standard marshaling is used, the CLSID in the stream is CLSID_StdMarshal, or {00000017-0000-0000-C000-000000000046}.
This optimization is discussed further in Chapter 19.
For information about object persistence, see Chapter 10.
Chapter Fifteen
Figure 15-1 shows the stub manager for conceptual purposes; the
stub manager does not actually exist as a separate entity.
Standard aggregation is done via the IClassFactory
interface; proxy/stub components use the IPSFactoryBuffer
interface. Both types of aggregation follow the same principles as
described in Chapter 2.
See the original COM specification for more information about the
recommended implementation of these functions.
Chapter Sixteen
See Chapter 14 for information on the NDR transfer syntax.
Java hedges by passing primitive types by value but user-defined
types by reference.
Chapter Seventeen
The CoDisableCallCancellation function disables
cancellation of synchronous method calls made on the calling
thread.
Chapter Eighteen
You can use programmatic security—discussed later in this
chapter in the section titled "Programmatic Security"—to
override both default and component security settings in the
registry.
Chapter 19 includes more information about secure reference
counting.
An administrator can give the system account network privileges
by setting the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\RestrictNullSessionAccess registry value to 0, but
this technique is not generally recommended because it significantly
compromises system security.
All components written in Microsoft Visual Basic do this.
Chapter 12 covers the AppID registry key in more detail.
Note that you cannot use the launching user identity setting when
security is turned off in this way.
The format of the security information produced by the
DCOMAccessControl object is not the same as that produced by the
Win32 API security functions. While COM+ itself supports both formats,
the Distributed COM Configuration utility on Windows NT and Windows
2000 is unable to read security information created by the
DCOMAccessControl object.
For more information about how to configure these settings, see
the section titled "Declarative Security: The Registry"
earlier in this chapter.
The IClientSecurity interface is described later in this chapter.
The IAccessControl::SetOwner method is currently not
implemented by the DCOMAccessControl object.
This field is covered in the section titled "Remote Instantiation" in Chapter 13.
Note that during an asynchronous call, the server cannot impersonate the client after the server's call to
ISynchronize::Signal completes, even if the Begin_ method
has not yet completed. For example, if a client calls the Begin_
method and the server calls ISynchronize::Signal to indicate
that it is finished processing, even if work remains to be done in the
Begin_ method, the server cannot impersonate the client after
the call to ISynchronize::Signal. If the server impersonates the
client before it calls ISynchronize::Signal, the impersonation
token is not removed from the thread until the server calls
IServerSecurity::RevertToSelf or until the Begin_ method
returns, whichever comes first. See Chapter 17 for more information on
asynchronous calls.
Chapter Nineteen
We'll leave it as an exercise for you to develop a Network
Monitor parser DLL that understands the ORPC protocol.
In other versions of Windows, the SCM is named rpcss.exe.
Remote activation works only in Windows NT and Windows 2000.
Windows 95 and Windows 98 lack the necessary security
infrastructure.
RPC string bindings are discussed in greater detail in the Appendix.
In Windows 95 and Windows 98, this functionality is available in
a separate utility named ciscnfg.exe.
Also, tunneling TCP/IP works only if the proxy servers and
firewalls permit TCP/IP traffic over a port opened to HTTP.
You can disable this option to block any potential Internet
access to COM+ components.
You can use channel hooks to pass extra data in the COM+ channel;
see the section titled "Channel Hooks" later in this
chapter.
This optimization technique is covered later in this chapter in
the section titled "The IRemUnknown Interface."
The details of this translation are covered later in this
chapter in the section titled "The OXID Resolver."
The NCA prefix for each tower identifier is short for
Network Computing Architecture. CN stands for a
connection-oriented protocol, and DG stands for a
connectionless, datagram-based protocol.
Appendix
In RPC the term stub is used on both the client and server
sides. A client stub in RPC is conceptually equivalent to a proxy in
COM.