- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
The ACTIVEDOC Program
Let's look at some code to understand how ATL can be used to create an Active document. You will be looking at a sample program called ACTIVEDOC located on the MSDN CD-ROM included with Visual C++ 6. To install the ACTIVEDOC sample on your hard drive, search for the ACTIVEDOC sample and follow the instructions on the sample page. We will focus on specific areas of this sample code to see how an ATL application is built.
This example builds an in-process Active document around the RichEdit control. The majority of the code is actually in the RichEdit control. The ACTIVEDOC program wraps an Active document layer around the control and provides a unique opportunity to focus on ATL issues without being distracted by all the other issues an application normally has to worry about. In particular, you will look closely at the declaration of the CActiveDoc class and will notice how COM support easily is added through the mixin concept. You also will look at how to add support for the new IOleDocument and IOleDocumentView COM interfaces. After this example is built, it can be run inside Microsoft Binder or Microsoft Internet Explorer. Figure 25.4 illustrates the ACTIVEDOC program inside Microsoft Binder.
Figure 25.4 The ACTIVEDOC program inside Microsoft Binder.
Nineteen files are located in the ACTIVEDOC directory. Table 25.11 briefly describes these files.
Table 25.11. Files Located in the ACTIVEDOC Directory
| Filename | Description |
| toolbar.bmp | Bitmap used for the (guess what?) toolbar. |
| activedoc.mak | The Visual C++ makefile. |
| activectl.cpp | Some implementation code for CActiveDoc. |
| activedoc.cpp | Contains all the DLL entry points. |
| stdafx.cpp | Contains the precompiled headers. |
| activedoc.def | DEF table for the DLL exports. |
| activectl.h | Defines the CActiveDoc interfaces and most of the implementation. |
| menu.h | Defines the CMenu class and its implementation. This class is used to negotiate menus with the container. |
| oledocument.h | Defines two new template classes: IOleDocumentImpl and IOleDocumentViewImpl. |
| resource.h | Standard Visual C++ resource defines. |
| stdafx.h | Precompiled header file. |
| toolbar.h | Defines the CToolbar class and its implementation. This class is used to negotiate toolbars with the container. |
| activedoc.idl | IDL source for the ActiveDoc class. |
| activedoc.htm | Web page that demonstrates this Active document used with Internet Explorer. |
| activedoc.dsp | A Visual C++ 6.0 project file. |
| activedoc.dsw | A Visual C++ 6.0 workspace file. |
| activedoc.rc | Definition of the resources. |
| activedoc.rgs | Registry script file for ACTIVEDOC. |
| activedoc.txt | Description of the project. |
We will focus on two key files in this project: activectl.h and oledocument.h. These files contain the majority of the code that we will be interested in. We also will review some other files as we encounter them.
activectl.h
This file contains the definition of the CActiveDoc class and most of the implementation code. CActiveDoc is the class that implements all the support for Active documents. As we step through this header, we will discuss the important features of the CActiveDoc class.
The following code is from the beginning of activectl.h and shows the include files required by CActiveDoc. The first file, resource.h, is the standard header file that is generated by Visual C++ when dialog boxes or other resources are added to the project. It contains all the defines necessary for these resources. OleDocument.h is the header file that defines and implements two new classes: IOleDocumentImpl and IOleDocumentViewImpl. You will look more closely at this file later. Menu.h and toolbar.h provide definitions for CMenu and CToolbar. RichEdit.h is the standard header file that describes the RichEdit control.
// ActiveCtl.h : Declaration of the CActiveDoc class #include "resource.h" // main symbols #include "OleDocument.h" #include "Menu.h" #include "ToolBar.h" #include <RichEdit.h>
Next is the definition of the CActiveDoc class interface. Let's look at the inheritance that the class uses. Notice, as mentioned earlier, that CActiveDoc is defined through multiple inheritance, or mixins. If you want to add or remove functionality from CActiveDoc, you add or remove a class from which it inherits. Most of the classes that CActiveDoc inherits from are in fact OLE interfaces. Other required classes that CActiveDoc inherits from are CComObjectRoot and CComCoClass. Both these classes are required. Finally, CActiveDoc inherits from two new classes: IOleDocumentImpl and IOleDocumentViewImpl. These new classes are not part of the ATL library but are defined in the oledocument.h file.
For your information, CLSID_CActiveDoc is the unique COM identifier for CActiveDoc. It is defined in activedoc.h. IID_IActiveDoc is the unique COM interface identifier for ActiveDoc. It also is defined in activedoc.h. Most of the implementation templates—those that have Impl at the end of their name—use CActiveDoc as the parameter to the template. This provides a connection between the template classes and the Active document class that you are building. This declaration of CActiveDoc indicates that CActiveDoc supports all the COM interfaces in the inheritance list.
/////////////////////////////////////////////////////////////////////////////
// CActiveDoc
class CActiveDoc :
public CComObjectRoot,
public CComCoClass<CActiveDoc, &CLSID_CActiveDoc>,
public CComControl<CActiveDoc>,
public IDispatchImpl<IActiveDoc, &IID_IActiveDoc, &LIBID_ACTIVEDOCLib>,
public IProvideClassInfo2Impl<&CLSID_CActiveDoc, NULL,
&LIBID_ACTIVEDOCLib>,
public IPersistStreamInitImpl<CActiveDoc>,
public IPersistStorageImpl<CActiveDoc>,
public IQuickActivateImpl<CActiveDoc>,
public IOleControlImpl<CActiveDoc>,
public IOleObjectImpl<CActiveDoc>,
public IOleInPlaceActiveObjectImpl<CActiveDoc>,
public IViewObjectExImpl<CActiveDoc>,
public IOleInPlaceObjectWindowlessImpl<CActiveDoc>,
public IDataObjectImpl<CActiveDoc>,
public ISupportErrorInfo,
public IOleDocumentImpl<CActiveDoc>,
public IOleDocumentViewImpl<CActiveDoc>,
public CMenu<CActiveDoc>,
public CToolbar<CActiveDoc>
Take a look at the next section of the CActiveDoc declaration and implementation:
{
public:
CActiveDoc() : m_wndRTF(_T("RichEdit"), this, 1)
{
m_wndRTF.m_hWnd = NULL;
m_bWindowOnly = TRUE;
}
The class declaration begins with the constructor CActiveDoc(), a part of which is an initialization of m_wndRTF. If you look at the very end of the class declaration, you will notice that m_wndRTF is declared as CContainedWindow. CContainedWindow allows you to either superclass or subclass an existing control. In addition, it connects the existing control to the class that contains it. In our example, CActiveDoc is being declared as a superclass of a RichEdit control. m_wndRTF will provide the connection between the RichEdit control and CActiveDoc. All the message handling for the control will be routed through the CActiveDoc class message maps.
In the next section, you encounter the DECLARE_REGISTRY_RESOURCEID macro:
DECLARE_REGISTRY_RESOURCEID(IDR_ActiveDoc)
This macro is defined in atlcom.h:
#define DECLARE_REGISTRY_RESOURCEID(x)static HRESULT WINAPI UpdateRegistry(BOOL bRegister){return _Module.UpdateRegistryFromResource(x, bRegister);}
This macro declares a static method called UpdateRegistry. The purpose of this method is to add or remove the required Registry entries for the Active document. The ATL Object Wizard generates a Registry script (RGS) file. The RGS file is a specially encoded file that describes, in Backus-Nauer form, the required Registry entries to operate the Active document. The RGS file for this project follows:
HKCR
{
ActiveDoc.ActiveDoc.1 = s 'ActiveDoc Class'
{
CLSID = s '{ 93901785-436B-11D0-B965-000000000000} '
}
ActiveDoc.ActiveDoc = s 'ActiveDoc Class'
{
CurVer = s 'ActiveDoc.ActiveDoc.1'
}
NoRemove CLSID
{
ForceRemove { 93901785-436B-11D0-B965-000000000000} = s 'ActiveDoc Class'
{
ProgID = s 'ActiveDoc.ActiveDoc.1'
VersionIndependentProgID = s 'ActiveDoc.ActiveDoc'
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
ForceRemove 'Control'
'DocObject'= s '8'
ForceRemove 'Programmable'
ForceRemove 'Insertable'
ForceRemove 'ToolboxBitmap32'= s '%MODULE%, 1'
'MiscStatus'= s '0'
{
'1'= s '131473'
}
'TypeLib'= s '{ 93901783-436B-11D0-B965-000000000000} '
'Version'= s '1.0'
}
}
}
We won't discuss the syntax of the RGS file in this chapter, but you might recognize some familiar text that is part of this file. The name of the Active document is ActiveDoc Class, for example. You can see what its CLSID is. Notice the familiar Registry keywords, such as ProgID, InprocServer32, and Insertable. Fortunately, you don't have to write any code to read this file. A special routine is included as part of the ATL that knows how to read this file and make the appropriate Registry entries. This special routine is invoked when UpdateRegistry is called.
Following the DECLARE_REGISTRY_RESOURCEID macro are several macros enclosed by BEGIN_COM_MAP and END_COM_MAP.
BEGIN_COM_MAP(CActiveDoc)
COM_INTERFACE_ENTRY(IActiveDoc)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject,
IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow,
IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL(IDataObject)
COM_INTERFACE_ENTRY_IMPL(IOleDocument)
COM_INTERFACE_ENTRY_IMPL(IOleDocumentView)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
These macros create a COM interface map similar to the message maps used in MFC. The macros create a way for the QueryInterface call to determine whether this COM object supports a specific COM interface, and they provide a mapping to the classes that implement the specified interface. The macros used in the COM interface map are located in atlcom.h and are defined as the following:
#define COM_INTERFACE_ENTRY(x) { &IID_##x, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY} ,
#define COM_INTERFACE_ENTRY_IID(iid, x) { &iid, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY} ,
#define COM_INTERFACE_ENTRY_IMPL(x) COM_INTERFACE_ENTRY_IID(IID_##x, x##Impl<_ComMapClass>)
#define COM_INTERFACE_ENTRY_IMPL_IID(iid, x) COM_INTERFACE_ENTRY_IID(iid, x##Impl<_ComMapClass>)
These macros provide two ways of mapping an interface ID (IID) to a class method. COM_INTERFACE_ENTRY generates the IID for you by concatenating the string IID_ with the parameter that you supply. COM_INTERFACE_ENTRY_IID allows you to specify the IID yourself. COM_INTERFACE_ENTRY_IMPL and COM_INTERFACE_ENTRY_IMPL_IID are similar but map to templatized versions of interfaces.
The next section of activectl.h contains the macros BEGIN_PROPERTY_MAP and END_PROPERTY_MAP. These macros are used to define properties for Active controls.
BEGIN_PROPERTY_MAP(CActiveDoc)
// PROP_ENTRY("Description", dispid, clsid)
END_PROPERTY_MAP()
Next is a section that begins with BEGIN_MSG_MAP and END_MSG_MAP:
BEGIN_MSG_MAP(CActiveDoc)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackgnd)
COMMAND_RANGE_HANDLER(ID_BLACK, ID_BLUE, OnColorChange)
COMMAND_ID_HANDLER(ID_HELP_ABOUT, OnHelpAbout)
NOTIFY_CODE_HANDLER(TTN_NEEDTEXT, OnToolbarNeedText)
ALT_MSG_MAP(1)
// MESSAGE_HANDLER(WM_CHAR, OnChar)
// END_MSG_MAP()
END_MSG_MAP()
These macros are very similar to the message maps in MFC. They create a mapping between a Windows message and a method that supports the message.
Next are some macros that define the toolbar that is part of this Active document:
BEGIN_TOOLBAR_MAP(CActiveDoc)
TOOLBAR_BUTTON(ID_BLACK)
TOOLBAR_BUTTON(ID_RED)
TOOLBAR_BUTTON(ID_GREEN)
TOOLBAR_BUTTON(ID_BLUE)
TOOLBAR_SEPARATOR()
TOOLBAR_BUTTON(ID_HELP_ABOUT)
END_TOOLBAR_MAP()
As official-looking as these macros are, they are not part of ATL. The definition of these macros is located in toolbar.h:
#define BEGIN_TOOLBAR_MAP(x) public: }
const static int* _GetToolbarEntries(int& nButtons) { }
static const int _entries[] = {
#define TOOLBAR_BUTTON(x) x,
#define TOOLBAR_SEPARATOR() ID_SEP,
#define END_TOOLBAR_MAP() }; nButtons = sizeof(_entries)/sizeof(int); return _entries; }
These macros create an array of toolbar IDs and a method called GetToolbarEntries, which returns the number of buttons and a pointer to the entry array.
The rest of activectl.h deals with the implementation of specific COM methods. As you examine this code, you will notice that each COM method uses the STDMETHOD macro. This macro is used to describe the standard calling conventions that all COM interfaces must follow. STDMETHOD is defined as the following, where STDMETHODCALLTYPE is defined as _stdcall:
#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method
The rest of activectl.h provides an inline implementation of the code. Only five inherited methods are overridden: IOleInPlaceActiveObjectImpl::OnDocWindowActive, IPersistStorageImpl::IsDirty, IPersistStreamInitImp::Save, IPersistStreamInitImp::Load, and IOleInPlaceObjectWindowlessImpl:: SetObjectRects. We will not discuss the details of these methods. However, note that because only five methods have been overridden, the rest of the COM support is inherited as-is from the ATL base classes.
To summarize, several pieces of code in activectl.h provide the framework for Active document support. First, the CActiveDoc class inherits from a number of required COM interface classes. Second, the Registry must be configured with correct entries so that Active document containers will know how to load and run the Active document. Third, the Active document interfaces have to be exposed through the QueryInterface method. Much of this is done by using the various COM INTERFACE macros to map an interface with an implementation of the interface. Again, many of these interfaces are inherited from base classes. Fourth, methods that require changing or enhancing have to be overridden.
oledocument.h
This file contains a templatized form of the definitions and implementations of the IOleDocument and IOleDocumentView COM interfaces. Support for these interfaces is not provided as part of the ATL. You could use this header file in your own application to provide support for the IOleDocument and IOleDocumentView interfaces. The implementation of these interfaces supports only one view object, however. If your project requires more than one view, you will need to enhance these classes.
IOleDocumentImpl implements the IOleDocument methods CreateView, GetDocMiscStatus, and EnumViews.
Example 25.1. IOleDocumentImpl
#include <docobj.h>
//////////////////////////////////////////////////////////////////////////////
// IOleDocumentImpl
template <class T>
class ATL_NO_VTABLE IOleDocumentImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleControlImpl)
// IOleDocument methods
//
STDMETHOD(CreateView)(IOleInPlaceSite *pIPSite, IStream *pstm, DWORD /* dwReserved */,
IOleDocumentView **ppView)
{
ATLTRACE(_T("IOleDocument::CreateView\n"));
T* pT = static_cast<T*>(this);
if (ppView == NULL)
return E_POINTER;
// If we've already created a view then we can't create another as we
// currently only support the ability to create one view
if (pT->m_spInPlaceSite != NULL)
return E_FAIL;
IOleDocumentView* pView;
pT->_InternalQueryInterface(IID_IOleDocumentView, (void**)&pView);
// If we support IOleDocument we should support IOleDocumentView
_ASSERTE(pView != NULL);
// If they've given us a site then use it
if (pIPSite != NULL)
pView->SetInPlaceSite(pIPSite);
// If they have given us an IStream pointer then use it to initialize
the view
if (pstm != NULL)
{
pView->ApplyViewState(pstm);
}
// Return the view
*ppView = pView;
return S_OK;
}
STDMETHOD(GetDocMiscStatus)(DWORD *pdwStatus)
{
ATLTRACE(_T("IOleDocument::GetDocMiscStatus\n"));
*pdwStatus = DOCMISC_NOFILESUPPORT;
return S_OK;
}
STDMETHOD(EnumViews)(IEnumOleDocumentViews** /*ppEnum*/, IOleDocumentView ppView)
{
ATLTRACE(_T("IOleDocument::EnumViews\n"));
T* pT = static_cast<T*>(this);
if (ppView == NULL)
return E_POINTER;
// We only support one view
pT->_InternalQueryInterface(IID_IOleDocumentView, (void**)ppView);
return S_OK;
}
};
Notice that the preceding implementation of EnumViews has only one pointer to a view interface: ppView. As a result, this implementation of IOleDocument supports only one instance of a view. It certainly is possible to support more than one, but to do this, you have to extend these template classes.
Besides doing some error checking, CreateView does two basic things: It accepts an IOleInPlaceSite pointer if one is provided, and it returns a pointer to its only view. To obtain the view interface pointer, it calls its own InternalQueryInterface routine.
GetDocMiscStatus is called by the container to determine what kind of support is provided by the object. This implementation of IOleDocument returns DOCMISC_ NOFILESUPPORT. This tells the container that this object does not support reading and writing to files. Table 25.12 lists other possible status values.
Table 25.12. DOCMISC Status Values
| Name | This Object… |
| DOCMISC_CANCREATEMULTIPLEVIEWS | Can support more than one view. |
| DOCMISC_SUPPORTCOMPLEXRECTANGLES | Can support complex rectangles and requires the object to support IOleDocumentView::SetRectComplex. |
| DOCMISC_CANTOPENEDIT | Supports activation in a separate window. |
| DOCMISC_NOFILESUPPORT | Does not support reading and writing to a file. |
Because this object supports only one view, EnumViews returns a pointer to its IOleDocumentView interface.
IOleDocumentViewImpl implements the IOleDocumentView methods: SetInPlaceSite, GetInPlaceSite, GetDocument, SetRect, GetRect, SetRectComplex, Show, UIActivate, Open, CloseView, SaveViewState, ApplyViewState, and Clone.
SetRectComplex, Open, SaveViewState, ApplyViewState, and Clone are not implemented by this version of the IOleDocumentView interface. The rest of the methods are fairly straightforward, with the exception of ActiveXDocActive, which is a helper method of this class that does most of the work of activating the Active document. The tasks that ActiveXDocActive performs are the standard sequence of events that any Active document must follow in order to activate itself inside an Active container. Let's look more closely at this method.
The first item this method takes care of is to make sure that the Active document is in-place active:
if (!pT->m_bInPlaceActive)
{
BOOL bNoRedraw = FALSE;
hr = pT->m_spInPlaceSite->CanInPlaceActivate();
if (FAILED(hr))
return hr;
pT->m_spInPlaceSite->OnInPlaceActivate();
}
pT->m_bInPlaceActive = TRUE;
Next, this method obtains the location of the in-place active window inside the container. It ensures that the Active document window is visible—creating itself, if necessary—and remembers the rectangles by calling the SetObjectRects method.
if (pT->m_spInPlaceSite->GetWindow(&hwndParent) == S_OK)
{
pT->m_spInPlaceSite->GetWindowContext(&spInPlaceFrame,
&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
if (!pT->m_bWndLess)
{
if (pT->m_hWnd)
{
::ShowWindow(pT->m_hWnd, SW_SHOW);
pT->SetFocus();
}
else
pT->m_hWnd = pT->Create(hwndParent, rcPos);
}
pT->SetObjectRects(&rcPos, &rcClip);
}
After making itself visible, the method goes on to make itself UIActive by calling the IOleInPlaceSite's OnUIActivate() method. After that, it synchronizes the IOleInPlaceFrame and IOleInPlaceUIWindow interfaces by calling their SetActiveObject() and SetBorderSpace() methods.
CComPtr<IOleInPlaceActiveObject> spActiveObject;
QueryInterface(IID_IOleInPlaceActiveObject, (void**)&spActiveObject);
// Gone active by now, take care of UIACTIVATE
if (pT->DoesVerbUIActivate(iVerb))
{
if (!pT->m_bUIActive)
{
pT->m_bUIActive = TRUE;
hr = pT->m_spInPlaceSite->OnUIActivate();
if (FAILED(hr))
return hr;
pT->SetControlFocus(TRUE);
// set ourselves up in the host.
//
if (spActiveObject)
{
if (spInPlaceFrame)
spInPlaceFrame->SetActiveObject(spActiveObject, NULL);
if (spInPlaceUIWindow)
spInPlaceUIWindow->SetActiveObject(spActiveObject, NULL);
}
if (spInPlaceFrame)
spInPlaceFrame->SetBorderSpace(NULL);
if (spInPlaceUIWindow)
spInPlaceUIWindow->SetBorderSpace(NULL);
}
}
Finally, the method merges its own menus with the container's menus and tells the container to position the Active document so that it is viewable to the user by calling the ShowObject() method of the IOleClientSite interface.
// Merge the menus pT->InPlaceMenuCreate(spInPlaceFrame); pT->m_spClientSite->ShowObject(); return S_OK;
In summary, activectl.h and oledocument.h provide most of the support for Active documents. The CActiveDoc class inherits most of its behavior, whereas oledocument.h was written specifically for the ACTIVEDOC sample from scratch and provides support for IOleDocument and IOleDocumentView. When you write your own Active document using the ATL, you need to implement code that is very similar to the CActiveDoc class. You also might want to borrow and enhance the IOleDocument and IOleDocumentView support found in oledocument.h.
activedoc.htm
The final file this chapter discusses is activedoc.htm. This file deserves an honorary mention, even though it is very straightforward. This file is an HTML file that (with some minor editing) enables users to view an Active document inside Internet Explorer. If you have Internet Explorer installed on your machine and you double-click this file, you will see the window shown in Figure 25.5.
Figure 25.5 The sample ACTIVEDOC program inside Internet Explorer.
This HTML file demonstrates how you can implement a very lightweight Active document object and include it in your Web pages. The key to activating the Active document object is the OBJECT HTML keyword. Using the CLSID supplied as part of this keyword, Internet Explorer can look up the object in the Registry. Having found the object in the Registry, Internet Explorer can discover that the object supports the Active document interface and interact with it as an Active container. Edit the activedoc.htm file so that it contains the source code shown in Listing 25.2.
Example 25.2. A Test Page for the ACTIVEDOC Project
<HTML> <HEAD> <TITLE>ATL 2.0 test page for object ActiveDoc</TITLE> </HEAD> <BODY> <OBJECT ID="ActiveDoc" < CLASSID="CLSID:93901785-436B-11D0-B965-000000000000"> > </OBJECT> </BODY> </HTML>
Summary | Next Section

Account Sign In
View your cart