Contents Index Topic Contents | ||
Previous Topic: Active Documents Next Topic: Programming an Active Document Container |
Programming an Active Document Server
An Active Document server is an OLE local server, similar to an OLE in-place server, that provides all the functionality of the document application. The document is contained inside an Active Document container, but is allowed to merge its menus with the container's menus and has complete use of the container's view area. The server provides the majority of the user interface, including menus, toolbars, status bars, and scroll bars. This allows the server to be hosted by any number of different Active Document containers while still maintaining a user interface that is familiar to the user.
Registering an Active Document Server
The IOleDocumentView Interface
Palette Management for Active Document Servers
The Basics
To create an Active Document server, you must create a file-based OLE local server that implements the following interfaces. Some of these interfaces are not required for a minimal implementation of a server. This table specifies which interfaces are required.
Interface Required Description IOleDocument Yes The container uses this interface to cause an Active Document to create views of itself, enumerate the views the object supports, and provide miscellaneous information about the object's capabilities. IOleDocumentView Yes The container uses this interface to activate, deactivate, close, and communicate with a document view object. IPersistStorage Yes The container uses this interface to initialize the object and put it in the loaded or running state, and to instruct the object to perform various save operations or to release its storage. IOleObject Yes The container uses this interface to execute verbs, communicate site information, and close the view. IDataObject Yes The container uses this interface to get and set data in the object. IOleInPlaceObject Yes The container uses this interface to activate and deactivate the view object. IOleInPlaceActiveObject Yes The container uses this interface to control the view object while it is active. IEnumFORMATETC No The container uses this interface to determine what data formats the object supports. IEnumOleDocumentViews No The container uses this interface to enumerate the views that an Active Document supports. IOleCommandTarget No The container uses this interface to dispatch commands and obtain command status information from the Active Document. IPrint No The container uses this interface to print an Active Document. Registering an Active Document Server
The following are the minimum registry entries necessary to register an Active Document server. Any information in "<>" brackets is implementation-specific.
HKEY_CLASSES_ROOT\<file extension> = <prog ID> HKEY_CLASSES_ROOT\<prog ID> = <document name> HKEY_CLASSES_ROOT\<prog ID>\CLSID = <clsid> HKEY_CLASSES_ROOT\CLSID\<clsid>\LocalServer32 = <server path and file> HKEY_CLASSES_ROOT\CLSID\<clsid>\InprocHandler32 = "ole32.dll" HKEY_CLASSES_ROOT\CLSID\<clsid>\Insertable = "" HKEY_CLASSES_ROOT\CLSID\<clsid>\DocObject = ""The Insertable and DocObject registry keys noted in this example are not required if the CATID_Insertable and CATID_DocObject component categories are registered for the server. Registering the component categories causes these keys to be added automatically. For more information, see the ICatRegister::RegisterClassImplCategories method.
The following registry entry is optional and causes an icon to be displayed for the file in Windows® Explorer.
HKEY_CLASSES_ROOT\CLSID\<clsid>\DefaultIcon = <server path and file>,<icon index>The IDataObject Interface
The IDataObject interface is not specific to Active Documents, but one point must be clarified. Some Active Document containers will store the document's information in a format specific to the container. Therefore, these containers will make a copy of the data before actually activating the document. This is done by requesting the data using the object's IDataObject::GetDataHere method for the "Embed Source" registered clipboard format, DVASPECT_CONTENT and TYMED_ISTORAGE. In response to this call, your server must store the document's current data in the IStorage interface provided in the STGMEDIUM structure. This can easily be accomplished by using the OleSave API.
The IOleDocument Interface
The IOleDocument interface is the basis for your Active Document server. The container uses this interface to create views, obtain a view enumerator (IEnumOleDocumentViews), and obtain document status.
Creating the Document View
The container will request that your server create a document view to be displayed. It will do so in the following manner:
- The container calls your server's IOleObject::DoVerb method with the iVerb parameter equal to OLEIVERB_SHOW, OLEIVERB_PRIMARY, or OLEIVERB_UIACTIVATE.
- Your server obtains the container's IOleDocumentSite pointer by calling QueryInterface on the IOleClientSite pointer that is passed to IOleObject::DoVerb.
- Your server then calls the container's IOleDocumentSite::ActivateMe method, passing either a valid IOleDocumentView pointer or NULL. If you pass a valid pointer, the document view will be activated. If you pass NULL, the container will call your server's IOleDocument::CreateView method to create the view.
The following is a very simple example of an IOleObject::DoVerb implementation.
STDMETHODIMP COleObject::DoVerb( LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect) { HRESULT hr = E_FAIL; switch (iVerb) { case OLEIVERB_SHOW: case OLEIVERB_PRIMARY: case OLEIVERB_UIACTIVATE: { //try to get the IOleDocumentSite pointer LPOLEDOCUMENTSITE pOleDocSite; hr = pActiveSite->QueryInterface(IID_IOleDocumentSite, (LPVOID*)&pOleDocSite); if(SUCCEEDED(hr)) { //passing NULL to this will cause the site to call our CreateView method hr = pOleDocSite->ActivateMe(NULL); } } break; default: break; } return hr; }The following is a simple example of an IOleDocument::CreateView implementation.
STDMETHODIMP COleDocument::CreateView( IOleInPlaceSite *pInPlaceSite, IStream *pStream, DWORD dwReserved, IOleDocumentView **ppOleDocumentView) { HRESULT hr = E_FAIL; //NULL the view pointer *ppOleDocumentView = NULL; //this implementation only supports one view, so fail if the view already exists if(!m_pOleDocView) { m_pOleDocView = new COleDocumentView(); if(m_pOleDocView) { //AddRef since the pointer is being given away m_pOleDocView->AddRef(); //if a site has been given, set this as the site for the view just created if(pInPlaceSite) { m_pOleDocView->SetInPlaceSite(pInPlaceSite); } //if given a stream to initialize from, initialize the view state if(pStream) { m_pOleDocView->ApplyViewState(pStream); } *ppOleDocumentView = m_pOleDocView; hr = S_OK; } } return hr; }The IOleDocumentView Interface
The IOleDocumentView interface is used by the container to manage the display of your Active Document. The container uses this interface to set the view's site and to activate and deactivate the view, among other things.
Getting the Document View's Site
When your server's IOleDocumentView::SetInPlaceSite method is called, the server should release any saved IOleInPlaceSite pointer that it is maintaining, and then call AddRef and store the new IOleInPlaceSite pointer. This pointer will be used to inform the site of in-place activation and deactivation and to obtain the handle of the view's parent window. The parent window handle is obtained by calling this interface's IOleWindow::GetWindow. The following is a simple example of this method's implementation.
STDMETHODIMP COleDocumentView::SetInPlaceSite(IOleInPlaceSite *pNewSite) { //clean up previous site if it exists if(m_pInPlaceSite) { if(m_fUIActive) { //call the view's private method that handles UI deactivation DeactivateUI(); } m_pInPlaceSite->Release(); m_pInPlaceSite = NULL; } m_pInPlaceSite = pNewSite; if(m_pInPlaceSite) { m_pInPlaceSite->AddRef(); } return S_OK; }Activating the Document View
The document view activation occurs in the following manner:
- The server's IOleDocumentView::UIActivate method is called with a nonzero fActivate parameter.
- The server ensures that the object is in-place active. The object is made in-place active by calling the site's IOleInPlaceSite::OnInPlaceActivate method and showing the view window.
- The server performs whatever actions are necessary to become UI-active. This usually involves adding toolbars, merging menu items, and adding any other UI objects.
- The server notifies the site that the view is getting UI-activated by calling the site's IOleInPlaceSite::OnUIActivate method.
Some containers can contain more than one Active Document. These containers notify the object that the Active Document window is getting activated by calling the object's IOleInPlaceActiveObject::OnDocWindowActivate method with a nonzero argument. This does not normally occur until the object has already become UI-active. When this occurs, the object must display its UI objects, such as menus and toolbars. It is not necessary, however, to completely UI-deactivate the object.
Deactivating the Document View
The document view deactivation occurs in the following manner:
- The server's IOleDocumentView::UIActivate method is called with a zero fActivate parameter.
- The server removes any menu items, toolbars, or other UI objects.
- The server notifies the site that the view is getting UI-deactivated by calling the site's IOleInPlaceSite::OnUIDeactivate method.
Some containers can contain more than one Active Document. These containers notify the object that the Active Document window is getting deactivated by calling the object's IOleInPlaceActiveObject::OnDocWindowActivate method with a zero argument. When this occurs, the object must hide its UI objects, such as toolbars. It is not necessary to remove the object's menus at this point because the container will do this on its own. It is also not necessary to completely UI-deactivate the object.
Palette Management for Active Document Servers
In general, the palette management scheme for Active Documents is the same as the scheme used for ActiveX Controls, except that Active Documents do not receive ambient properties from their client. The Active Document server has these responsibilities for managing palettes:
- The server notifies the container as to which palette the Active Document needs to use by implementing the IViewObject::GetColorSet method. This is the same method that OLE controls use to manage their palettes.
- The server must realize its palette in the background except when handling the WM_QUERYNEWPALETTE message (forwarded from the container). Again, this is exactly the way OLE controls handle this.
The only difference between the responsibilities of Active Documents and OLE controls is that Active Documents do not handle the DISPID_AMBIENT_PALETTE property.
Top of Page
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.