Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2. The Excel version is Excel 2003/Office 11. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary notes for this tutorial are IOleObject and OLE.
|
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 CEx32bDoc 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 Listing 38. 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 CEx32bView 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.
|
EX32BDOC.H
// ex32bDoc.h : interface of the CEx32bDoc class // /////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_EX32BDOC_H__5C6DE978_9660_4229_887E_9A63ECF07A05__INCLUDED_) #define AFX_EX32BDOC_H__5C6DE978_9660_4229_887E_9A63ECF07A05__INCLUDED_
#if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000
void ITrace(REFIID iid, const char* str);
class CEx32bDoc : public CDocument { protected: // create from serialization only CEx32bDoc(); DECLARE_DYNCREATE(CEx32bDoc)
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 CEx32bView;
private: LPOLEOBJECT m_lpOleObj; LPSTORAGE m_pTempStgRoot; LPSTORAGE m_pTempStgSub; BOOL m_bHatch; static const OLECHAR* s_szSub;
// Attributes public:
// Operations public:
// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CEx32bDoc) public: virtual BOOL OnNewDocument(); virtual void Serialize(CArchive& ar); virtual void DeleteContents(); virtual void OnCloseDocument(); protected: virtual BOOL SaveModified(); //}}AFX_VIRTUAL
// Implementation public: virtual ~CEx32bDoc(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif
protected:
// Generated message map functions protected: //{{AFX_MSG(CEx32bDoc) 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_EX32BDOC_H__5C6DE978_9660_4229_887E_9A63ECF07A05__INCLUDED_)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EX32BDOC.CPP
// ex32bDoc.cpp : implementation of the CEx32bDoc class //
#include "stdafx.h" #include "ex32b.h"
#include "ex32bDoc.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[ ] = __FILE__; #endif
const OLECHAR* CEx32bDoc::s_szSub = L"sub"; // static
///////////////////////////////////////////////////////////////////////////// // CEx32bDoc
IMPLEMENT_DYNCREATE(CEx32bDoc, CDocument)
BEGIN_MESSAGE_MAP(CEx32bDoc, CDocument) //{{AFX_MSG_MAP(CEx32bDoc) ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll) //}}AFX_MSG_MAP END_MESSAGE_MAP()
BEGIN_INTERFACE_MAP(CEx32bDoc, CDocument) INTERFACE_PART(CEx32bDoc, IID_IOleClientSite, OleClientSite) INTERFACE_PART(CEx32bDoc, IID_IAdviseSink, AdviseSink) END_INTERFACE_MAP()
///////////////////////////////////////////////////////////////////////////// // Implementation of IOleClientSite
STDMETHODIMP_(ULONG) CEx32bDoc::XOleClientSite::AddRef() { TRACE("CEx32bDoc::XOleClientSite::AddRef\n"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) return pThis->InternalAddRef(); }
STDMETHODIMP_(ULONG) CEx32bDoc::XOleClientSite::Release() { TRACE("CEx32bDoc::XOleClientSite::Release\n"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) return pThis->InternalRelease(); }
STDMETHODIMP CEx32bDoc::XOleClientSite::QueryInterface(REFIID iid, LPVOID* ppvObj) { ITrace(iid, "CEx32bDoc::XOleClientSite::QueryInterface"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) return pThis->InternalQueryInterface(&iid, ppvObj); }
STDMETHODIMP CEx32bDoc::XOleClientSite::SaveObject() { TRACE("CEx32bDoc::XOleClientSite::SaveObject\n"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) ASSERT_VALID(pThis);
LPPERSISTSTORAGE lpPersistStorage; pThis->m_lpOleObj->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 CEx32bDoc::XOleClientSite::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMoniker) { TRACE("CEx32bDoc::XOleClientSite::GetMoniker\n"); return E_NOTIMPL; }
STDMETHODIMP CEx32bDoc::XOleClientSite::GetContainer(LPOLECONTAINER* ppContainer) { TRACE("CEx32bDoc::XOleClientSite::GetContainer\n"); return E_NOTIMPL; }
STDMETHODIMP CEx32bDoc::XOleClientSite::ShowObject() { TRACE("CEx32bDoc::XOleClientSite::ShowObject\n"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) ASSERT_VALID(pThis); pThis->UpdateAllViews(NULL); return NOERROR; }
STDMETHODIMP CEx32bDoc::XOleClientSite::OnShowWindow(BOOL fShow) { TRACE("CEx32bDoc::XOleClientSite::OnShowWindow\n"); METHOD_PROLOGUE(CEx32bDoc, OleClientSite) ASSERT_VALID(pThis); pThis->m_bHatch = fShow; pThis->UpdateAllViews(NULL); return NOERROR; }
STDMETHODIMP CEx32bDoc::XOleClientSite::RequestNewObjectLayout() { TRACE("CEx32bDoc::XOleClientSite::RequestNewObjectLayout\n"); return E_NOTIMPL; }
///////////////////////////////////////////////////////////////////////////// // Implementation of IAdviseSink
STDMETHODIMP_(ULONG) CEx32bDoc::XAdviseSink::AddRef() { TRACE("CEx32bDoc::XAdviseSink::AddRef\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) return pThis->InternalAddRef(); }
STDMETHODIMP_(ULONG) CEx32bDoc::XAdviseSink::Release() { TRACE("CEx32bDoc::XAdviseSink::Release\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) return pThis->InternalRelease(); }
STDMETHODIMP CEx32bDoc::XAdviseSink::QueryInterface(REFIID iid, LPVOID* ppvObj) { ITrace(iid, "CEx32bDoc::XAdviseSink::QueryInterface"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) return pThis->InternalQueryInterface(&iid, ppvObj); }
STDMETHODIMP_(void) CEx32bDoc::XAdviseSink::OnDataChange(LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium) { TRACE("CEx32bDoc::XAdviseSink::OnDataChange\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) ASSERT_VALID(pThis); // Interesting only for advanced containers. Forward it such that // containers do not have to implement the entire interface. }
STDMETHODIMP_(void) CEx32bDoc::XAdviseSink::OnViewChange(DWORD aspects, LONG /*lindex*/) { TRACE("CEx32bDoc::XAdviseSink::OnViewChange\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) ASSERT_VALID(pThis); pThis->UpdateAllViews(NULL); // the really important one }
STDMETHODIMP_(void) CEx32bDoc::XAdviseSink::OnRename(LPMONIKER /*lpMoniker*/) { TRACE("CEx32bDoc::XAdviseSink::OnRename\n"); // Interesting only to the OLE link object. Containers ignore this. }
STDMETHODIMP_(void) CEx32bDoc::XAdviseSink::OnSave() { TRACE("CEx32bDoc::XAdviseSink::OnSave\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) ASSERT_VALID(pThis);
pThis->UpdateAllViews(NULL); }
STDMETHODIMP_(void) CEx32bDoc::XAdviseSink::OnClose() { TRACE("CEx32bDoc::XAdviseSink::OnClose\n"); METHOD_PROLOGUE(CEx32bDoc, AdviseSink) ASSERT_VALID(pThis);
pThis->UpdateAllViews(NULL); }
///////////////////////////////////////////////////////////////////////////// // CEx32bDoc construction/destruction
CEx32bDoc::CEx32bDoc() { // TODO: add one-time construction code here m_lpOleObj = NULL; m_pTempStgRoot = NULL; m_pTempStgSub = NULL; m_bHatch = FALSE; }
CEx32bDoc::~CEx32bDoc() { }
BOOL CEx32bDoc::OnNewDocument() { TRACE("Entering CEx32bDoc::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(); }
///////////////////////////////////////////////////////////////////////////// // CEx32bDoc serialization
void CEx32bDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } }
///////////////////////////////////////////////////////////////////////////// // CEx32bDoc diagnostics
#ifdef _DEBUG void CEx32bDoc::AssertValid() const { CDocument::AssertValid(); }
void CEx32bDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG
///////////////////////////////////////////////////////////////////////////// // CEx32bDoc commands
void CEx32bDoc::DeleteContents() { // TODO: Add your specialized code here and/or call the base class 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 CEx32bDoc::OnCloseDocument() { // TODO: Add your specialized code here and/or call the base class m_pTempStgSub->Release(); // must release BEFORE calling base class m_pTempStgRoot->Release(); CDocument::OnCloseDocument(); }
BOOL CEx32bDoc::SaveModified() { // TODO: Add your specialized code here and/or call the base class // Eliminate "save to file" message return TRUE; }
void CEx32bDoc::OnEditClearAll() { // TODO: Add your command handler code here DeleteContents(); UpdateAllViews(NULL); SetModifiedFlag(); m_bHatch = FALSE; }
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); }
|
Listing 38: The container’s CEx32bDoc class listing.
Continue on next module...
--------------------End part 2------------------
Further reading and digging:
Win32 process, thread and synchronization story can be found starting from Module R.
MSDN What's New (MFC Feature Pack) - feature pack.
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multi-byte character set: Story and program examples.