Because we are familiar with class factories, IPersistFile, and IOleItemContainer already, we don't need to examine all of Patron's code related to these matters. Instead, we'll just take a peek at those parts that are unique to a container.
The first of these is that the Patron's page implementation of IOleItemContainer::GetObject returns not a pointer to the tenant named by the item in question but an interface pointer for the embedded object in that tenant. The following code is taken from CImpIOleItemContainer::GetObject in IOLECONT.CPP:
if (TenantFromName(pszItem, &pTenant))
{
pTenant->ObjectGet(&pObj);
/*
* If we're asked for immediate or moderate, work only
* if object is already running.
*/
hr=IsRunning(pszItem); //This is the function below.
if ((BINDSPEED_IMMEDIATE==dwSpeed
śś BINDSPEED_MODERATE==dwSpeed) && NOERROR!=hr)
hr=ResultFromScode(MK_E_EXCEEDEDDEADLINE);
else
{
//IMPORTANT: Be sure that this object is running first.
OleRun(pObj);
hr=pObj->QueryInterface(riid, ppv);
}
pObj->Release();
}
else
hr=ResultFromScode(MK_E_NOOBJECT);
You can see that Patron first checks whether the embedded object is already running when BINDSPEED_IMMEDIATE and BINDSPEED_MODERATE are specified in the bind context. If not, we return MK_E_EXCEEDEDDEADLINE. Otherwise, if the object is already running or if we have as much time as we want, we run the object and query for whatever interface the external container linking to this embedding has asked for. The call to OleRun (which doesn't affect an already running object) is vital, especially for objects from in-process servers. This call ensures that the object will be completely registered in the running object table and that any necessary remoting stubs and proxies will be created for it when we return an interface pointer to the remote container.
It is also important for a container to implement IOleItemContainer::LockContainer, which Patron does in the same manner as IClassFactory::LockServer:
STDMETHODIMP CImpIOleItemContainer::LockContainer(BOOL fLock)
{
if (fLock)
g_cLock++;
else
{
g_cLock--;
g_cObj++;
ObjectDestroyed();
}
return NOERROR;
}
When an embedded object is activated as a link through our container, it will call LockContainer(TRUE) to ensure that the container—and thus, the embedded object itself—remains in memory as long as the other linking container requires it. When that linking container has finished with the object, it will release that object's interface pointers. The object then has to tell the embedding container (which is Patron here) that it no longer needs to stay in memory if all it's doing is servicing the link to the embedding. This is the same behavior that we implemented in a simple embedding server in Chapter 18: when the user closes the embedded object, the server terminates itself. Here Patron is acting as a server in the same manner, only for some other server's embedded objects. So when the server closes itself, it will call LockContainer(FALSE) to tell the container that it too can terminate. Patron increments its global object count and then fakes an object destruction by calling ObjectDestroyed (in PATRON.CPP), which checks for the necessary shutdown conditions as usual:
void ObjectDestroyed(void)
{
g_cObj--;
//No more objects, no locks, no user control; shut down application.
if (0==g_cObj && 0==g_cLock && IsWindow(g_hWnd) && !g_fUser)
PostMessage(g_hWnd, WM_CLOSE, 0, 0L);
return;
}