The EX28B Example—An Embedding Container

Now we can move on to the working program. It's a good time to open the \vcpp32\ex28b\ex28b.dsw workspace and build the EX28B project. If you choose Insert Object from the Edit menu and select Ex28a Document, the EX28A component will start. If you change the component's data, the container and the component will look like this.

Click to view at full size.

The CEx28bView Class

You can best understand the program by first concentrating on the view class. Look at the code in Figure 28-6, but ignore all IOleClientSite pointers. The container program will actually work if you pass NULL in every IOleClientSite pointer parameter. It just won't get notifications when the metafile or the native data changes. Also, components will appear displaying their stand-alone menus instead of the special embedded menus.

EX28BVIEW.H

#if !defined(AFX_EX28BVIEW_H__1EAAB6E1_6011_11D0_848F_00400526305B__INCLUDED_)
#define AFX_EX28BVIEW_H__1EAAB6E1_6011_11D0_848F_00400526305B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define CF_OBJECTDESCRIPTOR "Object Descriptor"
#define CF_EMBEDDEDOBJECT "Embedded Object"
#define SETFORMATETC(fe, cf, asp, td, med, li)   \
    ((fe).cfFormat=cf, \
     (fe).dwAspect=asp, \
     (fe).ptd=td, \
     (fe).tymed=med, \
     (fe).lindex=li)
////////////////////////////////////////////////////////////////////
class CEx28bView : public CScrollView
{
public:
    CLIPFORMAT m_cfObjDesc;
    CLIPFORMAT m_cfEmbedded;
    CSize m_sizeTotal;  // document size
    CRectTracker m_tracker;
    CRect m_rectTracker; // logical coords
protected: // create from serialization only
    CEx28bView();
    DECLARE_DYNCREATE(CEx28bView)

// Attributes
public:
    CEx28bDoc* GetDocument();

private:
    void GetSize();
    void SetNames();
    void SetViewAdvise();
    BOOL MakeMetafilePict(COleDataSource* pSource);
    COleDataSource* SaveObject();
    BOOL DoPasteObject(COleDataObject* pDataObject);
    BOOL DoPasteObjectDescriptor(COleDataObject* pDataObject);

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CEx28bView)
    public:
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual void OnInitialUpdate();
    protected:
    virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CEx28bView();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
    //{{AFX_MSG(CEx28bView)
    afx_msg void OnEditCopy();
    afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI);
    afx_msg void OnEditCopyto();
    afx_msg void OnEditCut();
    afx_msg void OnEditPaste();
    afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI);
    afx_msg void OnEditPastefrom();
    afx_msg void OnEditInsertobject();
    afx_msg void OnUpdateEditInsertobject(CCmdUI* pCmdUI);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
    afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, 
                             UINT message);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in ex28bView.cpp
inline CEx28bDoc* CEx28bView::GetDocument()
    { return (CEx28bDoc*)
m_pDocument; }
#endif

////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations 
//  immediately before the previous line.

#endif 
//!defined(AFX_EX28BVIEW_H__1EAAB6E1_6011_11D0_848F_00400526305B__INCLUDED_)

EX28BVIEW.CPP

#include "stdafx.h"
#include "ex28b.h"

#include "ex28bDoc.h"
#include "ex28bView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = _FILE_;
#endif

////////////////////////////////////////////////////////////////////
// CEx28bView

IMPLEMENT_DYNCREATE(CEx28bView, CScrollView)

BEGIN_MESSAGE_MAP(CEx28bView, CScrollView)
    //{{AFX_MSG_MAP(CEx28bView)
    ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
    ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
    ON_COMMAND(ID_EDIT_COPYTO, OnEditCopyto)
    ON_UPDATE_COMMAND_UI(ID_EDIT_COPYTO, OnUpdateEditCopy)
    ON_COMMAND(ID_EDIT_CUT, OnEditCut)
    ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCopy)
    ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
    ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
    ON_COMMAND(ID_EDIT_PASTEFROM, OnEditPastefrom)
    ON_COMMAND(ID_EDIT_INSERTOBJECT, OnEditInsertobject)
    ON_UPDATE_COMMAND_UI(ID_EDIT_INSERTOBJECT, 
                         OnUpdateEditInsertobject)
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_SETCURSOR()
    //}}AFX_MSG_MAP
    // Standard printing commands
    ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW,
               CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP()

////////////////////////////////////////////////////////////////////
// CEx28bView construction/destruction

CEx28bView::CEx28bView() : m_sizeTotal(20000, 25000),
    // 20 x 25 cm when printed
    m_rectTracker(0, 0, 0, 0) 
{
    m_cfObjDesc = ::RegisterClipboardFormat(CF_OBJECTDESCRIPTOR);
    m_cfEmbedded = ::RegisterClipboardFormat(CF_EMBEDDEDOBJECT);
}

CEx28bView::~CEx28bView()
{
}

BOOL CEx28bView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
    //  the CREATESTRUCT cs

    return CScrollView::PreCreateWindow(cs);
}

////////////////////////////////////////////////////////////////////
// CEx28bView drawing

void CEx28bView::OnDraw(CDC* pDC)
{
    CEx28bDoc* pDoc = GetDocument();

    if(pDoc->m_lpOleObj != NULL) {
        VERIFY(::OleDraw(pDoc->
m_lpOleObj, DVASPECT_CONTENT,
               pDC->GetSafeHdc(), 
m_rectTracker) == S_OK);
    }

    m_tracker.m_rect = 
m_rectTracker;
    pDC->LPtoDP(m_tracker.m_rect);   // device
    if(pDoc->m_bHatch) {
        m_tracker.
m_nStyle |= CRectTracker::hatchInside;
    }
    else {
        m_tracker.
m_nStyle &= ~CRectTracker::hatchInside;
    }
    m_tracker.Draw(pDC);
}

////////////////////////////////////////////////////////////////////
// CEx28bView printing

BOOL CEx28bView::OnPreparePrinting(CPrintInfo* pInfo)
{
    pInfo->SetMaxPage(1);
    return DoPreparePrinting(pInfo);
}

void CEx28bView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: add extra initialization before printing
}

void CEx28bView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: add cleanup after printing
}

////////////////////////////////////////////////////////////////////
// CEx28bView diagnostics

#ifdef _DEBUG
void CEx28bView::AssertValid() const
{
    CScrollView::AssertValid();
}

void CEx28bView::Dump(CDumpContext& dc) const
{
    CScrollView::Dump(dc);
}

CEx28bDoc* CEx28bView::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEx28bDoc)));
    return (CEx28bDoc*)m_pDocument;
}
#endif //_DEBUG

////////////////////////////////////////////////////////////////////
// CEx28bView message handlers

void CEx28bView::OnInitialUpdate() 
{
    TRACE("CEx28bView::OnInitialUpdate\n");
    m_rectTracker = CRect(1000, -1000, 5000, -5000);
    m_tracker.m_nStyle = CRectTracker::solidLine |
        CRectTracker::resizeOutside;
    SetScrollSizes(MM_HIMETRIC, 
m_sizeTotal);
    CScrollView::OnInitialUpdate();
}

void CEx28bView::OnEditCopy() 
{
    COleDataSource* pSource = SaveObject();
    if(pSource) {
        pSource->SetClipboard(); // OLE deletes data source 
    }
}

void CEx28bView::OnUpdateEditCopy(CCmdUI* pCmdUI) 
{
    // serves Copy, Cut, and Copy To
    pCmdUI->Enable(GetDocument()->
m_lpOleObj != NULL);
}

void CEx28bView::OnEditCopyto() 
{
    // Copy text to an STG file (nothing special about STG ext)
    CFileDialog dlg(FALSE, "stg", "*.stg");
    if (dlg.DoModal() != IDOK) {
        return;
    }
    CEx28bDoc* pDoc = GetDocument();
    // Create a structured storage home for the object ( pStgSub).
    //  Create a root storage file, then a substorage named "sub".
    LPSTORAGE pStgRoot;
    VERIFY(::StgCreateDocfile(dlg.GetPathName().AllocSysString(),
           STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
           0, &pStgRoot) == S_OK);
    ASSERT(pStgRoot != NULL);

    LPSTORAGE pStgSub;
    VERIFY(pStgRoot->CreateStorage(CEx28bDoc::s_szSub,
           STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
           0, 0, &pStgSub) == S_OK); 
    ASSERT(pStgSub != NULL);

    // Get the IPersistStorage* for the object
    LPPERSISTSTORAGE pPS = NULL;
    VERIFY(pDoc->m_lpOleObj->QueryInterface(IID_IPersistStorage,
          (void**) &pPS) == S_OK);

    // Finally, save the object in its new home in the user's file
    VERIFY(::OleSave(pPS, pStgSub, FALSE) == S_OK); 
    // FALSE means different stg
    pPS->SaveCompleted(NULL);  // What does this do?
    pPS->Release();

    pStgSub->Release();
    pStgRoot->Release();
}

void CEx28bView::OnEditCut() 
{
    OnEditCopy();
    GetDocument()->OnEditClearAll();
}
void CEx28bView::OnEditPaste() 
{
    CEx28bDoc* pDoc = GetDocument();
    COleDataObject dataObject;
    VERIFY(dataObject.AttachClipboard());
    pDoc->DeleteContents();
    DoPasteObjectDescriptor(&dataObject);
    DoPasteObject(&dataObject);
    SetViewAdvise();
    GetSize();
    pDoc->SetModifiedFlag();
    pDoc->UpdateAllViews(NULL);
}

void CEx28bView::OnUpdateEditPaste(CCmdUI* pCmdUI) 
{
    // Make sure that object data is available
    COleDataObject dataObject;
    if (dataObject.AttachClipboard() &&
        dataObject.IsDataAvailable(
m_cfEmbedded)) {
        pCmdUI->Enable(TRUE);
    } else {
        pCmdUI->Enable(FALSE);
    }
}

void CEx28bView::OnEditPastefrom() 
{
    CEx28bDoc* pDoc = GetDocument();
    // Paste from an STG file
    CFileDialog dlg(TRUE, "stg", "*.stg");
    if (dlg.DoModal() != IDOK) {
        return;
    }
    // Open the storage and substorage
    LPSTORAGE pStgRoot;
    VERIFY(::StgOpenStorage(dlg.GetPathName().AllocSysString(),
           NULL, STGM_READ|STGM_SHARE_EXCLUSIVE,
           NULL, 0, &pStgRoot) == S_OK);
    ASSERT(pStgRoot != NULL);

    LPSTORAGE pStgSub;
    VERIFY(pStgRoot->OpenStorage(CEx28bDoc::s_szSub, NULL,
           STGM_READ|STGM_SHARE_EXCLUSIVE,
           NULL, 0, &pStgSub) == S_OK);
    ASSERT(pStgSub != NULL);

    // Copy the object data from the user storage to the temporary
    //  storage
    VERIFY(pStgSub->CopyTo(NULL, NULL, NULL, 
           pDoc->
m_pTempStgSub) == S_OK);
    // Finally, load the object -- pClientSite not necessary
    LPOLECLIENTSITE pClientSite =
        (LPOLECLIENTSITE) pDoc->GetInterface(&IID_IOleClientSite);
    ASSERT(pClientSite != NULL);
    pDoc->DeleteContents();
    VERIFY(::OleLoad(pDoc->m
_pTempStgSub, IID_IOleObject,
           pClientSite, (void**) &pDoc->
m_lpOleObj) == S_OK);
    SetViewAdvise();
    pStgSub->Release();
    pStgRoot->Release();
    GetSize();
    pDoc->SetModifiedFlag();
    pDoc->UpdateAllViews(NULL);
}

void CEx28bView::OnEditInsertobject() 
{
    CEx28bDoc* pDoc = GetDocument();
    COleInsertDialog dlg;
    if(dlg.DoModal() == IDCANCEL) return;
    // no addrefs done for GetInterface
    LPOLECLIENTSITE pClientSite =
        (LPOLECLIENTSITE) pDoc->GetInterface(&IID_IOleClientSite);
    ASSERT(pClientSite != NULL);
    pDoc->DeleteContents();
    VERIFY(::OleCreate(dlg.GetClassID(), IID_IOleObject,
           OLERENDER_DRAW, NULL, pClientSite, pDoc->m_pTempStgSub,
           (void**) &pDoc->
m_lpOleObj) == S_OK);
    SetViewAdvise();

    pDoc->m_lpOleObj->DoVerb(OLEIVERB_SHOW, NULL, pClientSite, 0, 
        NULL, NULL); // OleRun doesn't show it
    SetNames();
    GetDocument()->SetModifiedFlag();
    GetSize();
    pDoc->UpdateAllViews(NULL);
}

void CEx28bView::OnUpdateEditInsertobject(CCmdUI* pCmdUI) 
{
    pCmdUI->Enable(GetDocument()->m_lpOleObj == NULL);
}

void CEx28bView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    TRACE("**Entering CEx28bView::OnLButtonDown -- point = "
          "(%d, %d)\n", point.x, point.y);
    if(m_tracker.Track(this, point, FALSE, NULL)) {
        CClientDC dc(this);
        OnPrepareDC(&dc);
        m_rectTracker = 
m_tracker.m_rect;
        dc.DPtoLP(
m_rectTracker); // Update logical coords
        GetDocument()->UpdateAllViews(NULL);
    }
    TRACE("**Leaving CEx28bView::OnLButtonDown\n");
}

void CEx28bView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
    if(m_tracker.HitTest(point) == CRectTracker::hitNothing) return;
    // Activate the object
    CEx28bDoc* pDoc = GetDocument();
    if(pDoc->m_lpOleObj != NULL) {
        LPOLECLIENTSITE pClientSite =
            (LPOLECLIENTSITE) 
             pDoc->GetInterface(&IID_IOleClientSite);
        ASSERT(pClientSite != NULL);
        VERIFY(pDoc->
m_lpOleObj->DoVerb(OLEIVERB_OPEN, NULL,
               pClientSite, 0,GetSafeHwnd(), CRect(0, 0, 0, 0))
               == S_OK);
        SetNames();
        GetDocument()->SetModifiedFlag();
    }
}

BOOL CEx28bView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if(m_tracker.SetCursor(pWnd, nHitTest)) {
        return TRUE;
    }
    else {
        return CScrollView::OnSetCursor(pWnd, nHitTest, message);
    }
}

////////////////////////////////////////////////////////////////////

void CEx28bView::SetViewAdvise() 
{
    CEx28bDoc* pDoc = GetDocument();
    if(pDoc->m_lpOleObj != NULL) {
        LPVIEWOBJECT2 pViewObj;
        pDoc->
m_lpOleObj->QueryInterface(IID_IViewObject2, 
            (void**) &pViewObj);
        LPADVISESINK pAdviseSink = 
            (LPADVISESINK) pDoc->GetInterface(&IID_IAdviseSink);
        VERIFY(pViewObj->SetAdvise(DVASPECT_CONTENT, 0, pAdviseSink)
            == S_OK);
        pViewObj->Release();
    }
}

void CEx28bView::SetNames() // sets host names
{
    CEx28bDoc* pDoc = GetDocument();
    CString strApp = AfxGetApp()->
m_pszAppName;
    if(pDoc->m_lpOleObj != NULL) {
        pDoc->
m_lpOleObj->SetHostNames(strApp.AllocSysString(),
        NULL);
    }
}

void CEx28bView::GetSize()
{
    CEx28bDoc* pDoc = GetDocument();
    if(pDoc->m_lpOleObj != NULL) {
        SIZEL size;      // Ask the component for its size
        pDoc->
m_lpOleObj->GetExtent(DVASPECT_CONTENT, &size);
        m_rectTracker.right = m_rectTracker.left + size.cx;
        m_rectTracker.bottom = m_rectTracker.top - size.cy;
    }
}

BOOL CEx28bView::DoPasteObject(COleDataObject* pDataObject)
{
    TRACE("Entering CEx28bView::DoPasteObject\n");
    // Update command UI should keep us out of here if not 
    //  CF_EMBEDDEDOBJECT
    if (!pDataObject->IsDataAvailable(m_cfEmbedded)) {
        TRACE("CF_EMBEDDEDOBJECT format is unavailable\n");
        return FALSE;
    }
    CEx28bDoc* pDoc = GetDocument();
    // Now create the object from the IDataObject*.
    //  OleCreateFromData will use CF_EMBEDDEDOBJECT format if
    //  available.
    LPOLECLIENTSITE pClientSite = 
        (LPOLECLIENTSITE) pDoc->GetInterface(&IID_IOleClientSite);
    ASSERT(pClientSite != NULL);
    VERIFY(::OleCreateFromData(pDataObject->m_lpDataObject,
           IID_IOleObject, OLERENDER_DRAW, NULL, pClientSite,
           pDoc->
m_pTempStgSub, (void**) &pDoc->m_lpOleObj) == S_OK);
    return TRUE;
}

BOOL CEx28bView::DoPasteObjectDescriptor(COleDataObject* pDataObject)
{
    TRACE("Entering CEx28bView::DoPasteObjectDescriptor\n");
    STGMEDIUM stg;

    FORMATETC fmt;
    CEx28bDoc* pDoc = GetDocument();
    if (!pDataObject->IsDataAvailable(m_cfObjDesc)) {
        TRACE("OBJECTDESCRIPTOR format is unavailable\n");
        return FALSE;
    }
    SETFORMATETC(fmt, m_cfObjDesc, DVASPECT_CONTENT, NULL, 
        TYMED_HGLOBAL, -1);
    VERIFY(pDataObject->GetDatam_cfObjDesc, &stg, &fmt));

    return TRUE;
}

// helper function used for clipboard and drag-drop 
COleDataSource* CEx28bView::SaveObject()
{
    TRACE("Entering CEx28bView::SaveObject\n");
    CEx28bDoc* pDoc = GetDocument();
    if (pDoc->m_lpOleObj != NULL) {
        COleDataSource* pSource = new COleDataSource();

        // CODE FOR OBJECT DATA
        FORMATETC fmte;
        SETFORMATETC(fmte, m_cfEmbedded, DVASPECT_CONTENT, NULL,
            TYMED_ISTORAGE, -1);
        STGMEDIUM stgm;
        stgm.tymed = TYMED_ISTORAGE;
        stgm.pstg = pDoc->m_pTempStgSub;
        stgm.pUnkForRelease = NULL;
        pDoc->m_pTempStgSub->AddRef();   // must do both!
        pDoc->m_pTempStgRoot->AddRef();
        pSource->CacheData(m_cfEmbedded, &stgm, &fmte);

        // metafile needed too
        MakeMetafilePict(pSource);

        // CODE FOR OBJECT DESCRIPTION DATA
        HGLOBAL hObjDesc = ::GlobalAlloc(GMEM_SHARE,
            sizeof(OBJECTDESCRIPTOR));
        LPOBJECTDESCRIPTOR pObjDesc =
            (LPOBJECTDESCRIPTOR) ::GlobalLock(hObjDesc);
        pObjDesc->cbSize = sizeof(OBJECTDESCRIPTOR);
        pObjDesc->clsid = CLSID_NULL;
        pObjDesc->dwDrawAspect = 0;
        pObjDesc->dwStatus = 0;
        pObjDesc->dwFullUserTypeName = 0;
        pObjDesc->dwSrcOfCopy = 0;
        pObjDesc->sizel.cx = 0;
        pObjDesc->sizel.cy = 0;
        pObjDesc->pointl.x = 0;
        pObjDesc->pointl.y = 0;
        ::GlobalUnlock(hObjDesc);
        pSource->CacheGlobalData(
m_cfObjDesc, hObjDesc);
        return pSource;
    }
    return NULL;
}

BOOL CEx28bView::MakeMetafilePict(COleDataSource* pSource)
{
    CEx28bDoc* pDoc = GetDocument();
    COleDataObject dataObject;
    LPDATAOBJECT pDataObj; // OLE object's IDataObject interface
    VERIFY(pDoc->m_lpOleObj->QueryInterface(IID_IDataObject,
          (void**) &pDataObj) == S_OK);
    dataObject.Attach(pDataObj);
    FORMATETC fmtem;
    SETFORMATETC(fmtem, CF_METAFILEPICT, DVASPECT_CONTENT, NULL,
        TYMED_MFPICT, -1);
    if (!dataObject.IsDataAvailable(CF_METAFILEPICT, &fmtem)) {
        TRACE("CF_METAFILEPICT format is unavailable\n");
        return FALSE;
    }
    // Just copy the metafile handle from the OLE object
    //  to the clipboard data object
    STGMEDIUM stgmm;
    VERIFY(dataObject.GetData(CF_METAFILEPICT, &stgmm, &fmtem));
    pSource->CacheData(CF_METAFILEPICT, &stgmm, &fmtem);
    return TRUE;
}

Figure 28-6. The container's CEx28bView class listing.

Study the message map and the associated command handlers. They're all relatively short, and they mostly call the OLE functions described earlier. A few private helper functions need some explanation, however.

You'll see many calls to a GetInterface function. This is a member of class CCmdTarget and returns the specified OLE interface pointer for a class in your project. It's used mostly to get the IOleClientSite interface pointer for your document. It's more efficient than calling ExternalQueryInterface, but it doesn't increment the object's reference count.

GetSize

This function calls IOleObject::GetSize to get the embedded object's extents, which it converts to a rectangle for storage in the tracker.

SetNames

The SetNames function calls IOleObject::SetHostNames to send the container application's name to the component.

SetViewAdvise

This function calls the embedded object's IViewObject2::SetAdvise function to set up the advisory connection from the component object to the container document.

MakeMetafilePict

The MakeMetafilePict function calls the embedded object's IDataObject::GetData function to get a metafile picture to copy to the clipboard data object. (A metafile picture, by the way, is a Windows METAFILEPICT structure instance, which contains a pointer to the metafile plus extent information.)

SaveObject

This function acts like the SaveDib function in the EX25A example. It creates a COleDataSource object with three formats: embedded object, metafile, and object descriptor.

DoPasteObjectDescriptor

The DoPasteObjectDescriptor function pastes an object descriptor from the clipboard but doesn't do anything with it. This function must be called prior to calling DoPasteObject.

DoPasteObject

This function calls OleCreateFromData to create an embedded object from an embedded object format on the clipboard.

The CEx28bDoc Class

This class implements the IOleClientSite and IAdviseSink interfaces. Because of our one-embedded-item-per-document simplification, we don't need to track separate site objects. The document is the site. We're using the standard MFC interface macros, and, as always, we must provide at least a skeleton function for all interface members.

Look carefully at the functions XOleClientSite::SaveObject, XOleClientSite::OnShowWindow, and XAdviseSink::OnViewChange in Figure 28-7. They're the important ones. The other ones are less important, but they contain TRACE statements as well, so you can watch the functions as they're called by the handler. Look also at the OnNewDocument, OnCloseDocument, and DeleteContents functions of the CEx28bView class. Notice how the document is managing a temporary storage. The document's m_pTempStgSub data member holds the storage pointer for the embedded object, and the m_lpOleObj data member holds the embedded object's IOleObject pointer.

EX28BDOC.H

#if !defined(AFX_EX28BDOC_H__1EAAB6DF_6011_11D0_848F_00400526305B__INCLUDED_)
#define AFX_EX28BDOC_H__1EAAB6DF_6011_11D0_848F_00400526305B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif //_MSC_VER > 1000

void ITrace(REFIID iid, const char* str);

class CEx28bDoc : public CDocument
{
protected: // create from serialization only
    CEx28bDoc();
    DECLARE_DYNCREATE(CEx28bDoc)
    BEGIN_INTERFACE_PART(OleClientSite, IOleClientSite)
        STDMETHOD(SaveObject)();
        STDMETHOD(GetMoniker)(DWORD, DWORD, LPMONIKER*);
        STDMETHOD(GetContainer)(LPOLECONTAINER*);
        STDMETHOD(ShowObject)();
        STDMETHOD(OnShowWindow)(BOOL);
        STDMETHOD(RequestNewObjectLayout)();
    END_INTERFACE_PART(OleClientSite)

    BEGIN_INTERFACE_PART(AdviseSink, IAdviseSink)
        STDMETHOD_(void,OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
        STDMETHOD_(void,OnViewChange)(DWORD, LONG);
        STDMETHOD_(void,OnRename)(LPMONIKER);
        STDMETHOD_(void,OnSave)();
        STDMETHOD_(void,OnClose)();
    END_INTERFACE_PART(AdviseSink)

    DECLARE_INTERFACE_MAP()

friend class CEx28bView;
private:
    LPOLEOBJECT m_lpOleObj;
    LPSTORAGE m_pTempStgRoot;
    LPSTORAGE m_pTempStgSub;
    BOOL m_bHatch;
    static const OLECHAR* s_szSub;
// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CEx28bDoc)
    public:
    virtual BOOL OnNewDocument();
    virtual void Serialize(CArchive& ar);
    virtual void OnCloseDocument();
    virtual void DeleteContents();
    protected:
    virtual BOOL SaveModified();
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CEx28bDoc();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
    //{{AFX_MSG(CEx28bDoc)
    afx_msg void OnEditClearAll();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
//  immediately before the previous line.

#endif 
// !defined(AFX_EX28BDOC_H__1EAAB6DF_6011_11D0_848F_00400526305B__INCLUDED_)
EX28BDOC.CPP
#include "stdafx.h"
#include "ex28b.h"

#include "ex28bDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE
__;
#endif
const OLECHAR* CEx28bDoc::s_szSub = L"sub";   // static

////////////////////////////////////////////////////////////////////
// CEx28bDoc

IMPLEMENT_DYNCREATE(CEx28bDoc, CDocument)

BEGIN_MESSAGE_MAP(CEx28bDoc, CDocument)
    //{{AFX_MSG_MAP(CEx28bDoc)
    ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_INTERFACE_MAP(CEx28bDoc, CDocument)
    INTERFACE_PART(CEx28bDoc, IID_IOleClientSite, OleClientSite)
    INTERFACE_PART(CEx28bDoc, IID_IAdviseSink, AdviseSink)
END_INTERFACE_MAP()

////////////////////////////////////////////////////////////////////
// implementation of IOleClientSite

STDMETHODIMP_(ULONG) CEx28bDoc::XOleClientSite::AddRef()
{
    TRACE("CEx28bDoc::XOleClientSite::AddRef\n");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    return pThis->InternalAddRef();
}

STDMETHODIMP_(ULONG) CEx28bDoc::XOleClientSite::Release()
{
    TRACE("CEx28bDoc::XOleClientSite::Release\n");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    return pThis->InternalRelease();
}

STDMETHODIMP CEx28bDoc::XOleClientSite::QueryInterface(
    REFIID iid, LPVOID* ppvObj)
{
    ITrace(iid, "CEx28bDoc::XOleClientSite::QueryInterface");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    return pThis->InternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CEx28bDoc::XOleClientSite::SaveObject()
{
    TRACE("CEx28bDoc::XOleClientSite::SaveObject\n");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    ASSERT_VALID(pThis);

    LPPERSISTSTORAGE lpPersistStorage;
    pThis->m_pOleObj->QueryInterface(IID_IPersistStorage,
        (void**) &lpPersistStorage);
    ASSERT(lpPersistStorage != NULL);
    HRESULT hr = NOERROR;
    if (lpPersistStorage->IsDirty() == NOERROR)
    {
        // NOERROR == S_OK != S_FALSE, therefore object is dirty!
        hr = ::OleSave(lpPersistStorage, pThis->
m_pTempStgSub,
                       TRUE);
        if (hr != NOERROR)
            hr = lpPersistStorage->SaveCompleted(NULL);

        // Mark the document as dirty if save successful
        pThis->SetModifiedFlag();
    }
    lpPersistStorage->Release();
    pThis->UpdateAllViews(NULL);
    return hr;
}

STDMETHODIMP CEx28bDoc::XOleClientSite::GetMoniker(
    DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMoniker)
{
    TRACE("CEx28bDoc::XOleClientSite::GetMoniker\n");
    return E_NOTIMPL;
}

STDMETHODIMP CEx28bDoc::XOleClientSite::GetContainer(
    LPOLECONTAINER* ppContainer)
{
    TRACE("CEx28bDoc::XOleClientSite::GetContainer\n");
    return E_NOTIMPL;
}

STDMETHODIMP CEx28bDoc::XOleClientSite::ShowObject()
{
    TRACE("CEx28bDoc::XOleClientSite::ShowObject\n");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    ASSERT_VALID(pThis);
    pThis->UpdateAllViews(NULL);
    return NOERROR;
}

STDMETHODIMP CEx28bDoc::XOleClientSite::OnShowWindow(BOOL fShow)
{
    TRACE("CEx28bDoc::XOleClientSite::OnShowWindow\n");
    METHOD_PROLOGUE(CEx28bDoc, OleClientSite)
    ASSERT_VALID(pThis);
    pThis->m_bHatch = fShow;
    pThis->UpdateAllViews(NULL);
    return NOERROR;
}
STDMETHODIMP CEx28bDoc::XOleClientSite::RequestNewObjectLayout()
{
    TRACE("CEx28bDoc::XOleClientSite::RequestNewObjectLayout\n");
    return E_NOTIMPL;
}

////////////////////////////////////////////////////////////////////
// implementation of IAdviseSink

STDMETHODIMP_(ULONG) CEx28bDoc::XAdviseSink::AddRef()
{
    TRACE("CEx28bDoc::XAdviseSink::AddRef\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    return pThis->InternalAddRef();
}

STDMETHODIMP_(ULONG) CEx28bDoc::XAdviseSink::Release()
{
    TRACE("CEx28bDoc::XAdviseSink::Release\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    return pThis->InternalRelease();
}
STDMETHODIMP CEx28bDoc::XAdviseSink::QueryInterface(
    REFIID iid, LPVOID* ppvObj)

{
    ITrace(iid, "CEx28bDoc::XAdviseSink::QueryInterface");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    return pThis->InternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP_(void) CEx28bDoc::XAdviseSink::OnDataChange(
    LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
    TRACE("CEx28bDoc::XAdviseSink::OnDataChange\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    ASSERT_VALID(pThis);

    // Interesting only for advanced containers. Forward it such
    //  that containers do not have to implement the entire
    //  interface.
}

STDMETHODIMP_(void) CEx28bDoc::XAdviseSink::OnViewChange(
    DWORD aspects, LONG /*lindex*/)
{
    TRACE("CEx28bDoc::XAdviseSink::OnViewChange\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    ASSERT_VALID(pThis);

    pThis->UpdateAllViews(NULL);        // the really important one
}

STDMETHODIMP_(void) CEx28bDoc::XAdviseSink::OnRename(
    LPMONIKER /*lpMoniker*/)
{
    TRACE("CEx28bDoc::XAdviseSink::OnRename\n");
    // Interesting only to the OLE link object. Containers ignore
    //  this.
}

STDMETHODIMP_(void) CEx28bDoc::XAdviseSink::OnSave()
{
    TRACE("CEx28bDoc::XAdviseSink::OnSave\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    ASSERT_VALID(pThis);

    pThis->UpdateAllViews(NULL);
}
STDMETHODIMP_(void) CEx28bDoc::XAdviseSink::OnClose()
{
    TRACE("CEx28bDoc::XAdviseSink::OnClose\n");
    METHOD_PROLOGUE(CEx28bDoc, AdviseSink)
    ASSERT_VALID(pThis);

    pThis->UpdateAllViews(NULL);
}

////////////////////////////////////////////////////////////////////
// CEx28bDoc construction/destruction

CEx28bDoc::CEx28bDoc()
{
    m_lpOleObj = NULL;
    m_pTempStgRoot = NULL;
    m_pTempStgSub = NULL;
    m_bHatch = FALSE;
}

CEx28bDoc::~CEx28bDoc()
{
}

BOOL CEx28bDoc::OnNewDocument()
{
    TRACE("Entering CEx28bDoc::OnNewDocument\n");
    // Create a structured storage home for the object
    //  (m_pTempStgSub). This is a temporary file -- random name
    //  supplied by OLE.
    VERIFY(::StgCreateDocfile(NULL,
           STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|
           STGM_DELETEONRELEASE,
           0, &m_pTempStgRoot) == S_OK);
    ASSERT(m_pTempStgRoot != NULL);

    VERIFY(m_pTempStgRoot->CreateStorage(OLESTR("sub"),
           STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
           0, 0, &m_pTempStgSub) == S_OK);
    ASSERT(m_pTempStgSub != NULL);
    return CDocument::OnNewDocument();
}

////////////////////////////////////////////////////////////////////
// CEx28bDoc serialization

void CEx28bDoc::Serialize(CArchive& ar)
{
    // no hookup to MFC serialization
    if (ar.IsStoring())
    {
        // TODO: add storing code here
    }
    else
    {
        // TODO: add loading code here
    }
}

////////////////////////////////////////////////////////////////////
// CEx28bDoc diagnostics

#ifdef _DEBUG
void CEx28bDoc::AssertValid() const
{
    CDocument::AssertValid();
}

void CEx28bDoc::Dump(CDumpContext& dc) const
{
    CDocument::Dump(dc);
}
#endif //_DEBUG

////////////////////////////////////////////////////////////////////
// CEx28bDoc commands

void CEx28bDoc::OnCloseDocument() 
{
    m_pTempStgSub->Release(); // must release BEFORE calling
                              //  base class
    m_pTempStgRoot->Release();
    CDocument::OnCloseDocument();
}

void CEx28bDoc::DeleteContents() 
{
    if(m_lpOleObj != NULL) {
        // If object is running, close it, which releases our
        //  IOleClientSite
        m_lpOleObj->Close(OLECLOSE_NOSAVE);
        m_lpOleObj->Release(); // should be final release
                               //  (or else...)
        m_lpOleObj = NULL;
    }
}

void CEx28bDoc::OnEditClearAll() 
{
    DeleteContents();
    UpdateAllViews(NULL);
    SetModifiedFlag();
    m_bHatch = FALSE;
}

BOOL CEx28bDoc::SaveModified() 
{
    // Eliminate "save to file" message
    return TRUE;
}

void ITrace(REFIID iid, const char* str)
{
    OLECHAR* lpszIID;
    ::StringFromIID(iid, &lpszIID);
    CString strIID = lpszIID;
    TRACE("%s - %s\n", (const char*) strIID, (const char*) str);
    AfxFreeTaskMem(lpszIID);
}

Figure 28-7. The container's CEx28bDoc class listing.