From the look of the preceding pages, you can probably guess that there's a lot we can talk about as far as implementation is concerned: concurrency management, custom interfaces, and custom marshaling. To ease us into those topics, however, let's first look at the simple implementation of the IExternalConnection interface, which we can use to demonstrate when COM adds and removes strong locks on objects, especially the effect of calling CoLockObjectExternal.
The basis for our discussion, as well as for the section about concurrency management, is the EKoala3 sample (CHAP06\EKOALA3). This is basically the EKoala1 sample from Chapter 5 with the following changes (marked with //CHAPTER6MOD and //End CHAPTER6MOD comments in the sources):
For our discussion of IExternalConnection, only the first two changes are important. We cover the rest in the section "Implementing Concurrency Management and the Busy Dialog Box" later in this chapter.
To see how IExternalConnection works, be sure that EKoala3's registry entries are current (using its REG file—EKoala3 is not self-registering), and run the ObjectUser sample from Chapter 5. In ObjectUser, choose Use EXE Server followed by either of the Create commands. This executes the following sequence in both the client and server processes, where a number of message boxes will appear from within EKoala3:
ObjectUser calls CoGetClassObject. (COM launches server.)
EKoala3 calls CoRegisterClassObject, which registers the class factory in the object table with a strong lock. COM calls the class factory's IExternalConnection::AddConnection(EXTCONN_STRONG).
EKoala3 displays a message for AddConnection, where the strong count is 1.
EKoala3 calls CoLockObjectExternal(class factory, TRUE, TRUE), which generates another call to AddConnection(EXTCONN_STRONG).
EKoala3 displays a message for AddConnection, where the strong count is 2. EKoala3 is blocked until you close the message box; however, the message box is sitting in a message loop, so CoGetClassObject will return the IClassFactory pointer to ObjectUser.
ObjectUser calls IClassFactory::CreateInstance, which creates the object in EKoala3. COM's IClassFactory stublet creates a new stub for the new object using CoMarshalInterface.
The new stub registers the object in the object table, which generates a call to the Koala's IExternalConnection::AddConnection(EXTCONN_STRONG).
EKoala3 displays an AddConnection from within the Koala object, where its strong count is now 1. Close the message box to continue.
EKoala3 returns from its first CoLockObjectExternal call (step 4) and calls CoLockObjectExternal(class factory, TRUE, FALSE), which generates a call to ReleaseConnection(EXTCONN_STRONG).
EKoala3 displays a message for ReleaseConnection, where the strong count is 1. Close the message box to continue.
ObjectUser calls IClassFactory::Release, which releases the external connection to the class factory, resulting in a call to the class factory's ReleaseConnection(EXTCONN_STRONG).
EKoala3 displays a message for ReleaseConnection, where the strong count is 0. Close the message box to continue.
When you now choose Release in ObjectUser to free the object, COM will call the Koala object's ReleaseConnection, which will display a message that its count is 0. After you close the message box, the EKoala3 server will terminate, removing its window from the screen.
This whole demonstration is somewhat contrived because neither the class factory nor the Koala object really have a reason to implement IExternalConnection other than to demonstrate when calls are made to its member functions. The class factory's implementation of AddConnection is fairly representative of ReleaseConnection as well as of the same functions in the Koala object (which have the same implementation but a different title on the message box):
STDMETHODIMP_(DWORD) CImpIExternalConnection::AddConnection
(DWORD dwConn, DWORD dwReserved)
{
DWORD dwRet;
TCHAR szTemp[80];
if (EXTCONN_STRONG & dwConn)
{
dwRet=++m_cStrong;
wsprintf(szTemp
, TEXT("AddConnection cStrong=%lu"), m_cStrong);
}
if (EXTCONN_WEAK & dwConn)
{
dwRet=++m_cWeak;
wsprintf(szTemp
, TEXT("ReleaseConnection cWeak=%lu"), m_cWeak);
}
MessageBox(NULL, szTemp
, TEXT("EKoala3: CKoalaClassFactory::IExternalConnection")
, MB_OK);
return dwRet;
}
The implementation picks up both strong and weak locks, counting both and displaying a message for both. However, you won't see any calls with EXTCONN_WEAK when running ObjectUser and EKoala3—the code is included to illustrate the different flags in the dwConn argument.
Later in this book, we'll see some cases for which an in-process object really must implement IExternalConnection so it can lock its client in memory as well as tell the client when to shut down. There are, of course, other uses for this interface anytime you need to control an object's lifetime according to external connections, as described earlier in this chapter.