(Optional) MDI Servers, User Interface, and Shutdown

If you want to support multiple objects in one instance of your application through an MDI interface, you have to consider a few additional issues and follow different procedures from those we've covered in this chapter. These are especially important for applications that can run only a single instance.

Only full servers can use MDI because miniservers are always single-object servers.

Register your class factory with REGCLS_MULTIPLEUSE, and remove any code in IClassFactory::CreateInstance that prevents creation of multiple objects.

When modifying your user interface on IOleObject::SetHostNames, save the container's application and document string with your document's variables (for step 4). Do not remove File New and File Open, but remove File Close and File Save and modify the File Save As and File Exit items as described earlier. File New and File Open simply create new file-based documents as they always have, which does not interfere with the document holding the embedded object.

If you have multiple documents open, you need to switch your user interface between the embedded and nonembedded states as you change document windows. This means that switching to an embedded object document installs the embedded object UI, while switching to a normal document reinstates your normal UI. Because you will be switching the UI, you should hold on to the strings from IOleObject::SetHostNames because this function will not be called again.

Do not shut down when the last embedded object document is closed if the user has at any time in the life of this application used File New or File Open. Invoking either function passes control of the application to the user. Generally, this means you should set your user control flag to TRUE on any File New or File Open operation. When you test for shutdown in a function such as ObjectDestroyed, don't close if this flag is TRUE. You should also be sure that you close only when the last object is closed—that is, your object count is truly 0.

Hiding an object through IOleObject::DoVerb(OLEIVERB_HIDE) should hide only its document window unless that's the only object. If it is, you should also hide the frame window. If another object is created before the existing one is shown again, you must show the frame window and the new document but not the existing document. In addition, whenever the visible document is closed and there is still a hidden document, hide the frame window again so that the whole server is in the state expected by the container that sent OLEIVERB_HIDE.

You will find that I did not implement these MDI features in Cosmo, even though Cosmo can compile into an MDI application. My reason is that Microsoft is slowly moving away from encouraging the MDI interface, although support for MDI applications will remain in Windows for a long time to come. Microsoft is doing this because the document-centric user interface possible with in-place activation can eliminate all document management from applications, making it the sole responsibility of the system shell. At that point, individual applications don't need MDI. We'll see why in Chapter 25.