COM Tutorial Samples |
Tutorial Home |
Previous Lesson |
Lesson List |
Next Lesson |
The APTCLIEN sample shows a client of several single-threaded apartments in the APTSERVE server. APTCLIEN's main purpose is to exercise and reveal for study the multiple apartments in the APTSERVE local out-of-process server. The REMCLIEN sample (later in this series), run in conjunction with APTCLIEN, shows the effects of multiple clients on the APTSERVE multithreaded server, with one client controlling from a remote machine.
The COM objects that APTCLIEN manipulates are the car-related ones from previous samples in this tutorial series, with the following interfaces: ICar, IUtility, and ICruise. APTCLIEN works in conjunction with the separate APTSERVE.EXE, which provides the COCar, COUtilityCar, and COCruiseCar COM objects.
APTCLIEN.EXE creates its own COUtilityCruiseCar COM object by reusing the COCruiseCar COM object by containment and augmenting it with a native implementation of the IUtility interface. Like LOCCLIEN, APTCLIEN's COUtilityCruiseCar composite object reuses COCruiseCar by containment. However, because COCruiseCar and COCar are in different apartments, COCruiseCar reuses COCar by containment rather than the aggregation shown in previous lessons.
The composition of COUtilityCruiseCar is also interesting because the COUtilityCruiseCar object's containment of the COCruiseCar object crosses the process boundary between APTCLIEN and the out-of-process local server APTSERVE.EXE. In addition, COCruiseCar's containment of COCar crosses thread boundaries within APTSERVE. APTCLIEN uses standard marshaling support for the custom interfaces it uses on the COCruiseCar and COCar objects. This marshaling support is provided by the previous MARSHAL code sample, so you must build the MARSHAL code sample prior to building and running APTCLIEN and APTSERVE.
For functional descriptions and a tutorial code tour of APTCLIEN, see the Code Tour section in APTCLIEN.HTM. For details on the external user operation of APTCLIEN, see both the Usage and Operation sections in APTCLIEN.HTM. To read APTCLIEN.HTM, run TUTORIAL.EXE in the main tutorial directory and click the APTCLIEN lesson in the table of lessons. You can also achieve the same thing by clicking the APTCLIEN.HTM file after locating the main tutorial directory in the Windows Explorer. See also APTSERVE.HTM in the main directory for more details on how APTSERVE works and exposes its services to APTCLIEN. You must build APTSERVE.EXE before building APTCLIEN. The makefile for APTSERVE automatically registers that server in the registry, so you must build APTSERVE before attempting to run APTCLIEN.
For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.
For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the APTCLIEN sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:
MSDEV APTCLIEN.DSP
You can also simply double-click the APTCLIEN.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.
APTCLIEN is an application that you can execute directly from Windows in the normal manner or from the Command Prompt window. No command line parameters are recognized by APTCLIEN.
The client sample and other related samples must be compiled before you can run the client. For more details on building the samples, see Building the Code Samples.
If you have already built the appropriate samples, APTCLIEN.EXE is the client executable to run for this sample.
The APTCLIEN.EXE application provides the user interface for this lesson. It exercises the associated, but independent, APTSERVE.EXE out-of-process local server. Here is a summary of operation from the standpoint of APTCLIEN.EXE as a COM client of the APTSERVE.EXE COM server.
The APTCLIEN and APTSERVE samples are directly analogous to the LOCCLIEN and LOCSERVE samples. The same components are used, and the same menu system exercises those objects.
As in the LOCCLIEN code sample, support is provided for a trace logging facility that will display in the APTCLIEN client application an integrated trace log of activity in both client and server.
Although APTCLIEN functions much like the prior LOCCLIEN sample, here is a short review for those readers who are visiting this code sample out of sequence. The COM objects that are used in the APTCLIEN and APTSERVE code samples represent sport utility vehicles. We invent some basic feature sets for modeling such car objects. These feature sets are implemented as interfaces to COM objects. The ICar interface provides some basic car behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides off-road utility systems: Offroad and Winch. The ICruise interface provides automatic cruise control facilities: Engage and Adjust.
APTCLIEN.EXE provides menus for creating, releasing, and invoking methods for four COM objects: COCar, COUtilityCar, COCruiseCar, and COUtilityCruiseCar. These objects have combinations of the ICar, IUtility, and ICruise interfaces. COCar objects expose the ICar interface. COUtilityCar objects expose the ICar and IUtility interfaces. COCruiseCar objects expose the ICar and ICruise interfaces. COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility interfaces. As a result, COCar objects have only the basic car behavior (ICar). COUtilityCar objects have basic car behavior (ICar) with sport utility systems (IUtility). COCruiseCar objects have basic car behavior (ICar) with an automatic cruise control system (ICruise). COUtilityCruiseCar objects have basic car behavior (ICar), a cruise control system (ICruise), and a sport utility system (IUtility).
COCar is constructed as an aggregatable COM object with a native implementation of the ICar interface. COUtilityCar is constructed using containment and is implemented in APTSERVE.EXE. For details, see the APTSERVE lesson. COCruiseCar is also constructed using containment and is also implemented in APTSERVE.EXE. COUtilityCruiseCar is constructed using containment and is implemented in APTCLIEN.EXE. In this sample, COUtilityCruiseCar reuses COCruiseCar by containment, which in turn reuses COCar by containment to illustrate three levels of nested object reuse by containment.
APTCLIEN.EXE presents a menu for each of these four main COM objects. Each menu has commands that call the methods of the various available interfaces. The code samples (both APTCLIEN and APTSERVE) issue trace message log statements throughout. When you exercise the objects from APTCLIEN.EXE, the main APTCLIEN window will display a log of internal activity in these COM objects.
Here is a description of the menu operation of APTCLIEN.
Menu Selection: File/Exit
Exits APTCLIEN.
Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates
that there is already an instance of the object.
Menu Selection: Car/Release
Releases the COCar COM object.
Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.
Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.
Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.
Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.
Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item
indicates that there is already an instance of the object.
Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.
Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.
Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.
Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.
Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.
Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.
Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.
Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item
indicates that there is already an instance of the object.
Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.
Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.
Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.
Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.
Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.
Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.
Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.
Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu
item indicates that there is already an instance of the object.
Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.
Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.
Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.
Menu Selection: Log/Clear
Clears the trace message log display.
Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside
the menu item indicates that logging is on. Logging can be engaged but
simply turned on or off. Unchecking this command turns the trace message
logging facility off but does not disengage the logging mechanisms.
Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows
Clipboard.
Menu Selection: Help/APTCLIEN Tutorial
Opens the APTCLIEN.HTM tutorial file in the Web browser.
Menu Selection: Help/APTSERVE Tutorial
Opens the APTSERVE.HTM tutorial file in the Web browser.
Menu Selection: Help/MARSHAL Tutorial
Opens the MARSHAL.HTM tutorial file in the Web browser.
Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from
this lesson or another one in the Windows Notepad.
Menu Selection: Help/About APTCLIEN
Displays the About dialog box for this application.
Menu Selection: Help/About APTSERVE
Displays the About dialog box for APTSERVE.EXE, which is used by this
application.
Files Description APTCLIEN.TXT Short sample description. MAKEFILE The generic makefile for building the code sample application of this tutorial lesson. APTCLIEN.H The include file for the APTCLIEN application. Contains class declarations, function prototypes, and resource identifiers. APTCLIEN.CPP The main implementation file for APTCLIEN.EXE. Has WinMain and CMainWindow implementation, as well as the main menu dispatching. APTCLIEN.RC The application resource definition file. APTCLIEN.ICO The application icon resource. UTCRUCAR.H The class declaration for the COUtilityCruiseCar COM object. UTCRUCAR.CPP Implementation file for the COUtilityCruiseCar COM object. Also has the definition of the CreateUtilityCruiseCar function. APTCLIEN.DSP Microsoft Visual Studio Project file.
Like all code samples in the series, APTCLIEN uses many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the APPUTIL library source code in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.
In addition to covering the coding techniques used, this lesson examines the internal behavior of the APTCLIEN and APTSERVE client/server pair. As a client, APTCLIEN offers nothing beyond what was shown in LOCCLIEN. The significant difference is simply the use of different CLSIDs (CLSID_AptCar, CLSID_AptUtilityCar, and CLSID_AptCruiseCar) for the creation of the APTSERVE objects to be controlled.
We will tour internal behavior in both APTCLIEN and APTSERVE by looking at some representative trace logs. Trace lines that begin with "C:" mark behavior reported in the APTCLIEN.EXE client. Lines that begin with "L:" mark behavior reported in the APTSERVE.EXE local out-of-process server. The internal APTSERVE trace lines will also show the thread ID of the executing thread in angle brackets after the "L". Since the client is single-threaded, its thread ID is not shown. These trace logs were obtained under the Windows 95 operating system. Because a multithreaded program is being traced, your results may be slightly different if you replicate this logged behavior on you own machine. For example, the server's AptThreadProc function will execute at different times depending on the operating system's task load and scheduling priorities.
After running APTCLIEN, choose the Create command from the Car menu. Here is the resulting trace log.
C: === Car Menu: Create. L: APTSERVE now logging to client. L<FFFE7005>: CmdLine Switches= -Embedding L<FFFE7005>: CServer::OpenFactories. Begin. L: CServer::OwnThis. Thread <FFFE7005> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFE7005>. L<FFFE7005>: CFCar::CImpIClassFactory Constructor. Non-Aggregating. L<FFFE7005>: CFCar Constructor. m_pUnkOuter=0x0. L<FFFE7005>: CFUtilityCar::CImpIClassFactory Constructor. Non-Aggregating. L<FFFE7005>: CFUtilityCar Constructor. m_pUnkOuter=0x0. L<FFFE7005>: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating. L<FFFE7005>: CFCruiseCar Constructor. m_pUnkOuter=0x0. L<FFFE7005>: CServer::OpenFactories. AptCar. L<FFFE7005>: CFCar::AddRef. New cRefs=1. L<FFFE7005>: CFCar::AddRef. New cRefs=2. L<FFFE7005>: CServer::OpenFactories. AptUtilityCar. L<FFFE7005>: CFUtilityCar::AddRef. New cRefs=1. L<FFFE7005>: CFUtilityCar::AddRef. New cRefs=2. L<FFFE7005>: CServer::OpenFactories. AptCruiseCar. L<FFFE7005>: CFCruiseCar::AddRef. New cRefs=1. L: AptThreadProc. Starting Apartment Thread <FFFEE289>. L: AptThreadProc. Starting Apartment Thread <FFFEE4B1>. L<FFFE7005>: CFCruiseCar::AddRef. New cRefs=2. L: AptThreadProc. Registering class factory of apartment <FFFEE289>. L: AptThreadProc. Registering class factory of apartment <FFFEE4B1>. L<FFFEE289>: CFCar::AddRef. New cRefs=3. L<FFFEE4B1>: CFUtilityCar::AddRef. New cRefs=3. L: CServer::UnOwnThis. Ownership relinquished by <FFFE7005>. L<FFFE7005>: CServer::OpenFactories. End. L: AptThreadProc. Starting Apartment Thread <FFFEE811>. L: AptThreadProc. Registering class factory of apartment <FFFEE811>. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=3. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1. L<FFFEE289>: CFCar::AddRef. New cRefs=4. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2. L<FFFEE289>: CFCar::AddRef. New cRefs=5. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: CFCar::AddRef. New cRefs=6. L<FFFEE289>: CFCar::AddRef. New cRefs=7. L<FFFEE289>: CFCar::Release. New cRefs=6. L<FFFEE289>: CFCar::AddRef. New cRefs=7. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE289>: CFCar::AddRef. New cRefs=8. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE289>: CFCar::Release. New cRefs=7. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE289>: CFCar::AddRef. New cRefs=8. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE289>: CFCar::Release. New cRefs=7. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=1. L<FFFEE289>: CFCar::Release. New cRefs=6. L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0. L<FFFEE289>: COCar::CImpICar Constructor. Non-Aggregating. L<FFFEE289>: COCar Constructor. m_pUnkOuter=0x0. L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>. L<FFFEE289>: CServer::ObjectsUp. New cObjects=1. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>. L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: COCar::AddRef. New cRefs=1. L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870658. L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: COCar::AddRef. New cRefs=2. L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: COCar::AddRef. New cRefs=3. L<FFFEE289>: COCar::AddRef. New cRefs=4. L<FFFEE289>: COCar::Release. New cRefs=3. L<FFFEE289>: COCar::AddRef. New cRefs=4. L<FFFEE289>: COCar::Release. New cRefs=3. L<FFFEE289>: COCar::Release. New cRefs=2. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0. L<FFFEE289>: CFCar::Release. New cRefs=5. L<FFFEE289>: CFCar::Release. New cRefs=4. L<FFFEE289>: CFCar::Release. New cRefs=3.
This log shows that the following threads are started.
<FFFE7005> - Main thread for the APTSERVE local server application. <FFFEE289> - AptCar component apartment thread. <FFFEE4B1> - AptUtilityCar component apartment thread. <FFFEE811> - AptCruiseCar component apartment thread.
We will abbreviate these to <05>, <89>, <B1>, and <11>. The main thread, <05>, is also called the main STA thread and could be exploited more fully as an apartment in the strict sense. But it is not exploited as such in this code sample. All four of these threads are separate single-threaded apartments.
The main thread, <05>, executes the OpenFactories method. We see the use of the CThreaded ownership mechanism, with the main thread requesting and getting ownership of CServer. On this main STA thread we see the constructors for each class factory. The class factories create the separate STA threads for the AptCar, AptUtilityCar, and AptCruiseCar objects. At some variable length of time after the thread creation calls, the operating system calls each thread's AptThreadProc function to start the thread. We see these calls reported as the threads for AptCar (<89>), AptUtilityCar (<B1>), and AptCruiseCar (<11>) are started. Within AptThreadProc, each thread also registers the STA apartment's new class factory with COM. The main STA thread, <05>, relinquishes ownership of CServer, and the log then reports that OpenFactories has ended.
We then see the CFCar class factory used to create the requested new COCar object. CFCar and the constructor of the new COCar object both execute on the AptCar thread, <89>. When the class factory is successful in creating the COCar object, we see the server's object count incremented to 1 inside the protection of the OwnThis ownership by thread <89>. We then see the delegated release of the IClassFactory held by the client. COM still holds three outstanding references to the registered CFCar class factory and two outstanding references to the COCar object.
Next choose the ICar::Shift command from the Car menu to see the client call the interface methods of the new COCar object.
C: === Car Menu: ICar::Shift C: --Obtaining Interface Pointer. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1. L<FFFEE289>: COCar::AddRef. New cRefs=3. L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=0. L<FFFEE289>: COCar::Release. New cRefs=2. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1. L<FFFEE289>: COCar::AddRef. New cRefs=3. C: Interface obtained. *ppv=0x520BC4 C: --Calling pICar->Shift L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=1. C: --Releasing pICar
In the process of obtaining the interface pointer, ICar's own AddRef and Release functions are called. Before this Shift call, COCar had two outstanding references. Afterwards, it ended with three references prior to the actual call to pICar->Shift. The Shift method call indicates this is the first call to this object instance's ICar interface.
We see COM efficiency at work if we choose the ICar::Shift command from the Car menu again.
C: === Car Menu: ICar::Shift C: --Obtaining Interface Pointer. C: Interface obtained. *ppv=0x520BC4 C: --Calling pICar->Shift L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=2. C: --Releasing pICar
COM had cached the interface information during the previous ICar::Shift call, so that on this second call the interface pointer was returned directly without QueryInterface calls to the COCar object. The actual Shift call is executed on thread <89>. This call was marshaled from the client process to this server's AptCar thread, <89>. The ICar interface call counter indicates 2 for this second call to ICar::Shift.
To show the functioning of a composite multithreaded object in the server, choose the Create command from the UtilityCruiseCar menu.
C: === UtilityCruiseCar Menu: Create. C: CreateUtilityCruiseCar. pUnkOuter=0x0. C: COUtilityCruiseCar::CImpICar Constructor. Non-Aggregating C: COUtilityCruiseCar::CImpICruise Constructor. Non-Aggregating C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating. C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0. C: COUtilityCruiseCar::Init. pUnkOuter=0x870138 C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory. L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=4. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=2. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=5. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE811>: CFCruiseCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=6. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=7. L<FFFEE811>: CFCruiseCar::Release. New cRefs=6. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=7. L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=8. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE811>: CFCruiseCar::Release. New cRefs=7. L<FFFEE811>: CFCruiseCar::QueryInterface. pIClassFactory returned. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=8. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE811>: CFCruiseCar::Release. New cRefs=7. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=1. L<FFFEE811>: CFCruiseCar::Release. New cRefs=6. C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained. C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0. L<FFFEE811>: COCruiseCar::CImpICar Constructor. Non-Aggregating L<FFFEE811>: COCruiseCar::CImpICruise Constructor. Non-Aggregating. L<FFFEE811>: COCruiseCar Constructor. m_pUnkOuter=0x0. L: CServer::OwnThis. Thread <FFFEE811> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE811>. L<FFFEE811>: CServer::ObjectsUp. New cObjects=2. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE811>. L<FFFEE811>: COCruiseCar::Init. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1. L<FFFEE289>: CFCar::AddRef. New cRefs=4. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2. L<FFFEE289>: CFCar::AddRef. New cRefs=5. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::CImpIClassFactory::QueryInterface. Delegating. L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: CFCar::AddRef. New cRefs=6. L<FFFEE289>: CFCar::AddRef. New cRefs=7. L<FFFEE289>: CFCar::Release. New cRefs=6. L<FFFEE289>: CFCar::AddRef. New cRefs=7. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE289>: CFCar::AddRef. New cRefs=8. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE289>: CFCar::Release. New cRefs=7. L<FFFEE289>: CFCar::QueryInterface. pIClassFactory returned. L<FFFEE289>: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3. L<FFFEE289>: CFCar::AddRef. New cRefs=8. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=2. L<FFFEE289>: CFCar::Release. New cRefs=7. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=1. L<FFFEE289>: CFCar::Release. New cRefs=6. L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0. L<FFFEE289>: COCar::CImpICar Constructor. Non-Aggregating. L<FFFEE289>: COCar Constructor. m_pUnkOuter=0x0. L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>. L<FFFEE289>: CServer::ObjectsUp. New cObjects=3. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=1. L<FFFEE289>: COCar::AddRef. New cRefs=1. L<FFFEE289>: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x8706BC. L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating. L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=2. L<FFFEE289>: COCar::AddRef. New cRefs=2. L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating. L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating. L<FFFEE289>: COCar::CImpICar::QueryInterface. Delegating. L<FFFEE289>: COCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: COCar::AddRef. New cRefs=3. L<FFFEE289>: COCar::AddRef. New cRefs=4. L<FFFEE289>: COCar::Release. New cRefs=3. L<FFFEE289>: COCar::AddRef. New cRefs=4. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=3. L<FFFEE289>: COCar::AddRef. New cRefs=5. L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=2. L<FFFEE289>: COCar::Release. New cRefs=4. L<FFFEE289>: COCar::QueryInterface. pICar returned. L<FFFEE289>: COCar::CImpICar::Addref. Delegating. New cI=3. L<FFFEE289>: COCar::AddRef. New cRefs=5. L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=2. L<FFFEE289>: COCar::Release. New cRefs=4. L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=1. L<FFFEE289>: COCar::Release. New cRefs=3. L<FFFEE289>: CFCar::CImpIClassFactory::Release. Delegating. New cI=0. L<FFFEE289>: CFCar::Release. New cRefs=5. L<FFFEE289>: CFCar::Release. New cRefs=4. L<FFFEE289>: CFCar::Release. New cRefs=3. L<FFFEE811>: COCruiseCar::Init (New Containment of COCar) Succeeded. L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned. L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=1. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=1. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870698. L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating. L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating. L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned. L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=2. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=2. L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating. L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating. L<FFFEE811>: COCruiseCar::CImpICruise::QueryInterface. Delegating. L<FFFEE811>: COCruiseCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=3. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4. L<FFFEE811>: COCruiseCar::Release. New cRefs=3. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4. L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned. L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=5. L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=2. L<FFFEE811>: COCruiseCar::Release. New cRefs=4. L<FFFEE811>: COCruiseCar::QueryInterface. pICruise returned. L<FFFEE811>: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=5. L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=2. L<FFFEE811>: COCruiseCar::Release. New cRefs=4. L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=1. L<FFFEE811>: COCruiseCar::Release. New cRefs=3. C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory. L<FFFEE811>: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0. L<FFFEE811>: CFCruiseCar::Release. New cRefs=5. L<FFFEE811>: CFCruiseCar::Release. New cRefs=4. L<FFFEE811>: CFCruiseCar::Release. New cRefs=3. L<FFFEE811>: COCruiseCar::QueryInterface. pICar returned L<FFFEE811>: COCruiseCar::CImpICar::Addref. Delegating. New cI=1. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4. L<FFFEE811>: COCruiseCar::CImpICar::Release. Delegating. New cI=0. L<FFFEE811>: COCruiseCar::Release. New cRefs=3. L<FFFEE811>: COCruiseCar::QueryInterface. pICar returned L<FFFEE811>: COCruiseCar::CImpICar::Addref. Delegating. New cI=1. L<FFFEE811>: COCruiseCar::AddRef. New cRefs=4. C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded. C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned. C: COUtilityCruiseCar::AddRef. New cRefs=1. C: CreateUtilityCruiseCar Succeeded. *ppv=0x870138.
COUtilityCruiseCar is a composite COM object created in the client. It reuses the COCruiseCar COM object provided by the server. COCruiseCar in turn reuses the COCar COM object provided by the server. In the server, we see the CruiseCar class factory executing on the AptCruiseCar thread, <11>, to create a new COCruiseCar whose constructor also runs on this thread. The server object count is incremented to 2 by this thread when it obtains ownership of CServer. The object count is now 2, one for the original COCar created at the start of this lesson and one for the new COCruiseCar.
To complete the creation of COCruiseCar its Init method is called on the AptCruiseCar thread, <11>. We see the resulting CFCar class factory executed on the AptCar thread, <89>. The creation of this new COCar object is completed on this thread, and it increments the server's object count to 3. Through the remainder of the log, the behavior winds back out of the object creations with COM, causing various QueryInterface, AddRef, and Release calls. Upon completion, there are three outstanding references on CFCar, three on the new COCar object, three on CFCruiseCar, and four on the new COCruiseCar object. Most of these outstanding references to our custom interfaces are held within COM.
Now choose the ICar::Shift command from the UtilityCruiseCar menu.
C: === UtilityCruiseCar Menu: ICar::Shift C: --Obtaining Interface Pointer. C: COUtilityCruiseCar::QueryInterface. pICar returned C: COUtilityCruiseCar::CImpICar::Addref. Delegating. New cI=1. C: COUtilityCruiseCar::AddRef. New cRefs=2. C: Interface obtained. *ppv=0x870144 C: --Calling pICar->Shift C: COUtilityCruiseCar::CImpICar::Shift. Delegating. nGear=1. L<FFFEE811>: COCruiseCar::CImpICar::Shift. Delegating. nGear=1. L<FFFEE289>: COCar::CImpICar::Shift. Called. ICar calls=1. C: --Releasing pICar C: COUtilityCruiseCar::CImpICar::Release. Delegating. New cI=0. C: COUtilityCruiseCar::Release. New cRefs=1.
The interface obtained is on the composite COUtilityCruiseCar object that is created within the client. Using this interface, the Shift call is delegated to the contained COCruiseCar object. At this point COM has to marshal the call from the APTCLIEN client process to thread <11>, where the COCruiseCar object resides. COCruiseCar is a composite object and delegates the ICar::Shift call to its contained COCar object. COM now has to marshal the call again within the server from the COCruiseCar apartment thread, <11>, to the COCar thread, <89>. On this AptCar thread, <89>, we see the Shift action finally performed.
For this call to ICar::Shift on the COUtilityCruiseCar object, COM enforces the STA apartment model and ensures that, regardless of the calling thread, the call performed in the particular object is executed on the STA thread of that object. The ICar interface call counter shows 1, indicating a first call to this interface on the new contained COCar object. This count confirms that this is a new instance of COCar. If the COCar originally created at the start of this lesson had been somehow mistakenly used, we would see an interface call count of 3 because of the two Shift calls to that COCar object earlier in this lesson.
To watch the shutdown of the COUtilityCruiseCar object, choose the Release command from the UtilityCruiseCar menu.
C: === UtilityCruiseCar Menu: Release. C: COUtilityCruiseCar::Release. New cRefs=0. C: COUtilityCruiseCar::Destructor. L<FFFEE811>: COCruiseCar::CImpICruise::Release. Delegating. New cI=0. L<FFFEE811>: COCruiseCar::Release. New cRefs=3. L<FFFEE811>: COCruiseCar::CImpICar::Release. Delegating. New cI=0. L<FFFEE811>: COCruiseCar::Release. New cRefs=2. L<FFFEE811>: COCruiseCar::Release. New cRefs=1. L<FFFEE811>: COCruiseCar::Release. New cRefs=0. L: CServer::OwnThis. Thread <FFFEE811> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE811>. L<FFFEE811>: CServer::ObjectsDown. New cObjects=2. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE811>. L<FFFEE811>: COCruiseCar::Destructor. L<FFFEE289>: COCar::CImpICar::Release. Delegating. New cI=0. L<FFFEE289>: COCar::Release. New cRefs=2. L<FFFEE289>: COCar::Release. New cRefs=1. L<FFFEE289>: COCar::Release. New cRefs=0. L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>. L<FFFEE289>: CServer::ObjectsDown. New cObjects=1. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>. L<FFFEE289>: COCar::Destructor. L<FFFEE289>: COCar::CImpICar Destructor. L<FFFEE811>: COCruiseCar::CImpICruise Destructor. L<FFFEE811>: COCruiseCar::CImpICar Destructor. C: COUtilityCruiseCar::CImpIUtility Destructor. C: COUtilityCruiseCar::CImpICruise Destructor. C: COUtilityCruiseCar::CImpICar Destructor.
This event causes the COUtilityCruiseCar destructor to run. It releases the IUnknown of the contained COCruiseCar object. In the server, we see the resulting releases of interfaces on COCruiseCar held by the client and by COM. These releases run on the AptCruiseCar thread, <11>, and decrement the COCruiseCar reference count to 0, forcing the destruction of the COCruiseCar object. Thread <11> gains ownership of CServer and decrements the server's object count to 2. The COCruiseCar destruction causes this object to release its hold on its contained COCar. We see the resulting releases of the interfaces on COCar held by COCruiseCar and by COM. These run on the AptCar thread, <89>, and decrement of the COCar reference count to 0, which forces the destruction of the COCar object. The AptCar thread, <89>, gains ownership of CServer and decrements the server's object count to 1. Finally, we see the destructors for all the various interface implementation C++ objects.
The resulting object count of 1 reflects the remaining existence of the original COCar object created at the start of this lesson. We will now release this only remaining object supplied by APTSERVE and watch the server shutdown. From the Car menu, choose Release.
C: === Car Menu: Release. L<FFFEE289>: COCar::Release. New cRefs=2. L<FFFEE289>: COCar::Release. New cRefs=1. L<FFFEE289>: COCar::Release. New cRefs=0. L: CServer::OwnThis. Thread <FFFEE289> waiting to own CServer. L: CServer::OwnThis. CServer now owned by Thread <FFFEE289>. L<FFFEE289>: CServer::ObjectsDown. New cObjects=0. L<FFFEE289>: CServer::ObjectsDown. Closing down APTSERVE server. L: CServer::UnOwnThis. Ownership relinquished by <FFFEE289>. L<FFFE7005>: CServer::CloseFactories. Begin. L<FFFEE289>: COCar::Destructor. L: CServer::OwnThis. Thread <FFFE7005> waiting to own CServer. L<FFFEE289>: COCar::CImpICar Destructor. L: CServer::OwnThis. CServer now owned by Thread <FFFE7005>. L<FFFE7005>: CServer::CloseFactories. Terminate AptCar Apartment. L<FFFE7005>: CServer::CloseFactories. Terminate AptUtilityCar Apartment. L: AptThreadProc. Revoking class factory of apartment <FFFEE289>. L<FFFE7005>: CServer::CloseFactories. Terminate AptCruiseCar Apartment. L: AptThreadProc. Revoking class factory of apartment <FFFEE4B1>. L<FFFEE289>: CFCar::QueryInterface. 'this' pIUnknown returned. L<FFFE7005>: CServer::CloseFactories. Releasing all Classfactory interfaces. L<FFFEE289>: CFCar::AddRef. New cRefs=4. L<FFFE7005>: CFCar::Release. New cRefs=3. L<FFFE7005>: CFCar::Release. New cRefs=2. L: AptThreadProc. Revoking class factory of apartment <FFFEE811>. L<FFFEE4B1>: CFUtilityCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE289>: CFCar::Release. New cRefs=1. L<FFFE7005>: CFUtilityCar::Release. New cRefs=2. L<FFFEE811>: CFCruiseCar::QueryInterface. 'this' pIUnknown returned. L<FFFEE4B1>: CFUtilityCar::AddRef. New cRefs=3. L<FFFEE289>: CFCar::Release. New cRefs=0. L<FFFE7005>: CFUtilityCar::Release. New cRefs=2. L<FFFEE811>: CFCruiseCar::AddRef. New cRefs=4. L<FFFEE4B1>: CFUtilityCar::Release. New cRefs=1. L<FFFEE289>: CFCar::Destructor. L<FFFEE811>: CFCruiseCar::Release. New cRefs=3. L<FFFEE4B1>: CFUtilityCar::Release. New cRefs=0. L<FFFEE289>: CFCar::CImpIClassFactory Destructor. L<FFFE7005>: CFUtilityCar::Destructor. L<FFFEE811>: CFCruiseCar::Release. New cRefs=2. L: AptThreadProc. Apartment Thread <FFFEE4B1> Terminated. L<FFFE7005>: CFUtilityCar::CImpIClassFactory Destructor. L: AptThreadProc. Apartment Thread <FFFEE811> Terminated. L: AptThreadProc. Apartment Thread <FFFEE289> Terminated. L<FFFE7005>: CFCruiseCar::Release. New cRefs=1. L<FFFE7005>: CFCruiseCar::Release. New cRefs=0. L<FFFE7005>: CFCruiseCar::Destructor. L<FFFE7005>: CFCruiseCar::CImpIClassFactory Destructor. L: CServer::UnOwnThis. Ownership relinquished by <FFFE7005>. L<FFFE7005>: CServer::CloseFactories. End. L<FFFE7005>: Exiting APTSERVE local server application.
The client releases the IUnknown of the COCar object. In the server, we see a resulting cascade of releases of COCar interfaces held by the client and by COM. These are executed on the AptCar thread, <89>, and decrement the COCar reference count to 0, which forces the destruction of the COCar object. Thread <89> gains ownership of CServer and decrements the server's object count to 0, triggering a shutdown of the entire server. This happens in the ObjectsDown method, where a WM_CLOSE message is sent to the main thread, <05>, of the server process. This message causes an exit of the server application's message loop. The server's CServer::CloseFactories method is then called.
Within CloseFactories, we see the unregistering of the class factories with COM and the termination of the AptCar, AptUtilityCar, and AptCruiseCar threads. The apartment thread message loops are exited after WM_QUIT messages are sent to each apartment thread from CloseFactories, which is running on the main thread, <05>. Before CloseFactories returns, we see the releases of the interfaces held on the class factories. These releases decrement each factory's reference count to 0 and cause the destructors to run for the CFCar, CFUtilityCar, and CFCruiseCar objects. After CloseFactories, execution exits the main application, and the operating system unloads APTSERVE.