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.
The Coding Part
Add the following code to ex32c.h.
friend class CEx32cDoc;

Listing 1.
Add the following code to the InitInstance() of ex32c.cpp.
TRACE("CEx32cApp::InitInstance\n");

Listing 2.
Edit the following code portion.
// When a server application is launched stand-alone, it is a good idea
// to update the system registry in case it has been damaged.
m_server.UpdateRegistry(OAT_SERVER);
AfxMessageBox("Server can't be run stand-alone, use container -- Registry updated");
return FALSE;
// // Dispatch commands specified on the command line
// if (!ProcessShellCommand(cmdInfo))
// return FALSE;
// // The one and only window has been initialized, so show and update it.
// m_pMainWnd->ShowWindow(SW_SHOW);
// m_pMainWnd->UpdateWindow();
// return TRUE;

Listing 3.
And the following code to the ExitInstance().
int CEx32cApp::ExitInstance()
{
// TODO: Add your specialized code here and/or call the base class
TRACE("CEx32cApp::ExitInstance\n");
return CWinApp::ExitInstance();
}

Listing 4.
Edit the OnDraw() code of ex32cView.cpp.
void CEx32cView::OnDraw(CDC* pDC)
{
CEx32cDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->Rectangle(CRect(500, -1000, 1500, -2000));
pDC->TextOut(5, 5, pDoc->m_strText);
}

Listing 5.
Edit the OnPrepareDC() as shown below.
void CEx32cView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
// TODO: Add your specialized code here and/or call the base class
pDC->SetMapMode(MM_HIMETRIC);
}

Listing 6.
Add the following codes to ex32cDoc.h just after the preprocessor directives
extern const CLSID clsid; // defined in ex32c.cpp
void ITrace(REFIID iid, const char* str);
#define SETFORMATETC(fe, cf, asp, td, med, li) \
((fe).cfFormat=cf, \
(fe).dwAspect=asp, \
(fe).ptd=td, \
(fe).tymed=med, \
(fe).lindex=li)

Listing 7.
Add the following friend class, then adds those member variables and member function.
friend class CEx32cView;
private:
CString m_strText;
LPOLECLIENTSITE m_lpClientSite;
LPOLEADVISEHOLDER m_lpOleAdviseHolder;
LPDATAADVISEHOLDER m_lpDataAdviseHolder;
CString m_strContainerApp;
CString m_strContainerObj;
HGLOBAL MakeMetaFile();

Listing 8.
Manually add the following interface for IOleObject, IDataObject and IPersistStorage just after the previous codes.
BEGIN_INTERFACE_PART(OleObject, IOleObject)
STDMETHOD(SetClientSite)(LPOLECLIENTSITE);
STDMETHOD(GetClientSite)(LPOLECLIENTSITE*);
STDMETHOD(SetHostNames)(LPCOLESTR, LPCOLESTR);
STDMETHOD(Close)(DWORD);
STDMETHOD(SetMoniker)(DWORD, LPMONIKER);
STDMETHOD(GetMoniker)(DWORD, DWORD, LPMONIKER*);
STDMETHOD(InitFromData)(LPDATAOBJECT, BOOL, DWORD);
STDMETHOD(GetClipboardData)(DWORD, LPDATAOBJECT*);
STDMETHOD(DoVerb)(LONG, LPMSG, LPOLECLIENTSITE, LONG,
HWND, LPCRECT);
STDMETHOD(EnumVerbs)(LPENUMOLEVERB*);
STDMETHOD(Update)();
STDMETHOD(IsUpToDate)();
STDMETHOD(GetUserClassID)(LPCLSID);
STDMETHOD(GetUserType)(DWORD, LPOLESTR*);
STDMETHOD(SetExtent)(DWORD, LPSIZEL);
STDMETHOD(GetExtent)(DWORD, LPSIZEL);
STDMETHOD(Advise)(LPADVISESINK, LPDWORD);
STDMETHOD(Unadvise)(DWORD);
STDMETHOD(EnumAdvise)(LPENUMSTATDATA*);
STDMETHOD(GetMiscStatus)(DWORD, LPDWORD);
STDMETHOD(SetColorScheme)(LPLOGPALETTE);
END_INTERFACE_PART(OleObject)
BEGIN_INTERFACE_PART(DataObject, IDataObject)
STDMETHOD(GetData)(LPFORMATETC, LPSTGMEDIUM);
STDMETHOD(GetDataHere)(LPFORMATETC, LPSTGMEDIUM);
STDMETHOD(QueryGetData)(LPFORMATETC);
STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC, LPFORMATETC);
STDMETHOD(SetData)(LPFORMATETC, LPSTGMEDIUM, BOOL);
STDMETHOD(EnumFormatEtc)(DWORD, LPENUMFORMATETC*);
STDMETHOD(DAdvise)(LPFORMATETC, DWORD, LPADVISESINK, LPDWORD);
STDMETHOD(DUnadvise)(DWORD);
STDMETHOD(EnumDAdvise)(LPENUMSTATDATA*);
END_INTERFACE_PART(DataObject)
BEGIN_INTERFACE_PART(PersistStorage, IPersistStorage)
STDMETHOD(GetClassID)(LPCLSID);
STDMETHOD(IsDirty)();
STDMETHOD(InitNew)(LPSTORAGE);
STDMETHOD(Load)(LPSTORAGE);
STDMETHOD(Save)(LPSTORAGE, BOOL);
STDMETHOD(SaveCompleted)(LPSTORAGE);
STDMETHOD(HandsOffStorage)();
END_INTERFACE_PART(PersistStorage)
DECLARE_INTERFACE_MAP()

Listing 9.
Delete (or comment out) the AppWizard generated DECLARE_DISPATCH_MAP and DECLARE_INTERFACE_MAP.
// // Generated OLE dispatch map functions
// //{{AFX_DISPATCH(CEx32cDoc)
// // NOTE - the ClassWizard will add and remove member functions here.
// // DO NOT EDIT what you see in these blocks of generated code !
// //}}AFX_DISPATCH
// DECLARE_DISPATCH_MAP()
// DECLARE_INTERFACE_MAP()

Listing 10.
Add the following #include directive to ex32cDoc.cpp.
#include "TextDialog.h"

Listing 11.
Manually add the following interface.
BEGIN_INTERFACE_MAP(CEx32cDoc, CDocument)
INTERFACE_PART(CEx32cDoc, IID_IOleObject, OleObject)
INTERFACE_PART(CEx32cDoc, IID_IDataObject, DataObject)
INTERFACE_PART(CEx32cDoc, IID_IPersistStorage, PersistStorage)
END_INTERFACE_MAP()

Listing 12.
Delete (or comment out) the auto AppWizard generated DISPATCH_MAP and INTERFACE_MAP.
// BEGIN_DISPATCH_MAP(CEx32cDoc, CDocument)
// //{{AFX_DISPATCH_MAP(CEx32cDoc)
// // NOTE - the ClassWizard will add and remove mapping macros here.
// // DO NOT EDIT what you see in these blocks of generated code!
// //}}AFX_DISPATCH_MAP
// END_DISPATCH_MAP()
// // Note: we add support for IID_IEx32c to support typesafe binding
// // from VBA. This IID must match the GUID that is attached to the
// // dispinterface in the .ODL file.
// // {100A83A5-C1FB-4EA7-8AFC-689433F842E8}
// static const IID IID_IEx32c =
// { 0x100a83a5, 0xc1fb, 0x4ea7, { 0x8a, 0xfc, 0x68, 0x94, 0x33, 0xf8, 0x42, 0xe8 } };
// BEGIN_INTERFACE_MAP(CEx32cDoc, CDocument)
// INTERFACE_PART(CEx32cDoc, IID_IEx32c, Dispatch)
// END_INTERFACE_MAP()

Listing 13.
Add the implementation for the interfaces. Firstly add the implementation for OleObject just after the previous commented codes.
///////////////////////////////////////////////////////////////////////
// CEx32cDoc OLE interface functions
STDMETHODIMP_(ULONG) CEx32cDoc::XOleObject::AddRef()
{
TRACE("CEx32cDoc::XOleObject::AddRef\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
return pThis->InternalAddRef();
}
STDMETHODIMP_(ULONG) CEx32cDoc::XOleObject::Release()
{
TRACE("CEx32cDoc::XOleObject::Release\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
return pThis->InternalRelease();
}
STDMETHODIMP CEx32cDoc::XOleObject::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
ITrace(iid, "CEx32cDoc::XOleObject::QueryInterface");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
return pThis->InternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CEx32cDoc::XOleObject::SetClientSite(
LPOLECLIENTSITE pClientSite)
{
TRACE("CEx32cDoc::XOleObject::SetClientSite\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
// linked objects do not support SetClientSite
if (pClientSite != NULL)
pClientSite->AddRef();
if(pThis->m_lpClientSite != NULL) pThis->m_lpClientSite->Release();
pThis->m_lpClientSite = pClientSite;
return NOERROR;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetClientSite(
LPOLECLIENTSITE* ppClientSite)
{
TRACE("CEx32cDoc::XOleObject::GetClientSite\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
*ppClientSite = pThis->m_lpClientSite;
if (pThis->m_lpClientSite != NULL)
pThis->m_lpClientSite->AddRef(); // IMPORTANT
return NOERROR;
}
STDMETHODIMP CEx32cDoc::XOleObject::SetHostNames(
LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)
{
TRACE("CEx32cDoc::XOleObject::SetHostNames\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
CString strTitle = "EX28C object embedded in ";
if(szContainerApp != NULL) {
strTitle += CString(szContainerApp);
}
CWnd* pWnd = AfxGetMainWnd();
pWnd->SetWindowText(strTitle);
return NOERROR;
}
STDMETHODIMP CEx32cDoc::XOleObject::Close(DWORD /*dwSaveOption*/)
{
TRACE("CEx32cDoc::XOleObject::Close\n");
// linked objects do not support close
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::SetMoniker(
DWORD /*dwWhichMoniker*/, LPMONIKER /*pmk*/)
{
TRACE("CEx32cDoc::XOleObject::SetMoniker\n");
// linked objects do not support SetMoniker
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetMoniker(
DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMoniker)
{
TRACE("CEx32cDoc::XOleObject::GetMoniker\n");
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::InitFromData(
LPDATAOBJECT /*pDataObject*/, BOOL /*fCreation*/, DWORD /*dwReserved*/)
{
TRACE("CEx32cDoc::XOleObject::InitFromData\n");
// linked objects do not support InitFromData
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetClipboardData(
DWORD /*dwReserved*/, LPDATAOBJECT* ppDataObject)
{
TRACE("CEx32cDoc::XOleObject::GetClipboardData\n");
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::DoVerb(
LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex,
HWND hwndParent, LPCRECT lpPosRect)
{
TRACE("CEx32cDoc::XOleObject::DoVerb - %d\n", iVerb);
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
pThis->InternalAddRef(); // protect this object
CWnd* pWnd = AfxGetMainWnd();
switch (iVerb)
{
// open - maps to OnOpen
case OLEIVERB_OPEN:
case -OLEIVERB_OPEN-1: // allows positive OLEIVERB_OPEN-1 in registry
case OLEIVERB_PRIMARY: // OLEIVERB_PRIMARY is 0 and "Edit" in registry
case OLEIVERB_SHOW:
pWnd->ShowWindow(SW_SHOW);
pWnd->SetActiveWindow();
pWnd->SetForegroundWindow();
break;
// hide maps to OnHide
case OLEIVERB_HIDE:
case -OLEIVERB_HIDE-1: // allows positive OLEIVERB_HIDE-1 in registry
return E_NOTIMPL;
default:
// negative verbs not understood should return E_NOTIMPL
if (iVerb < 0)
return E_NOTIMPL;
AfxThrowOleException(OLEOBJ_S_INVALIDVERB);
}
pThis->InternalRelease(); // may 'delete this'
pThis->m_lpClientSite->OnShowWindow(TRUE); // hatch
return NOERROR;
}
STDMETHODIMP CEx32cDoc::XOleObject::EnumVerbs(
IEnumOLEVERB** ppenumOleVerb)
{
TRACE("CEx32cDoc::XOleObject::EnumVerbs\n");
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::Update()
{
TRACE("CEx32cDoc::XOleObject::Update\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::IsUpToDate()
{
TRACE("CEx32cDoc::XOleObject::IsUpToDate\n");
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetUserClassID(CLSID* pClsid)
{
TRACE("CEx32cDoc::XOleObject::GetUserClassID\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
return pThis->m_xPersistStorage.GetClassID(pClsid);
}
STDMETHODIMP CEx32cDoc::XOleObject::GetUserType(
DWORD dwFormOfType, LPOLESTR* ppszUserType)
{
TRACE("CEx32cDoc::XOleObject::GetUserType\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
*ppszUserType = NULL;
CLSID clsid;
pThis->m_xOleObject.GetUserClassID(&clsid);
return OleRegGetUserType(clsid, dwFormOfType, ppszUserType);
}
STDMETHODIMP CEx32cDoc::XOleObject::SetExtent(
DWORD /*dwDrawAspect*/, LPSIZEL /*lpsizel*/)
{
TRACE("CEx32cDoc::XOleObject::SetExtent\n");
return E_FAIL;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetExtent(
DWORD dwDrawAspect, LPSIZEL lpsizel)
{
TRACE("CEx32cDoc::XOleObject::GetExtent\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
// handler returns extent in metafilepict
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::Advise(
IAdviseSink* pAdvSink, DWORD* pdwConnection)
{
TRACE("CEx32cDoc::XOleObject::Advise\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
*pdwConnection = 0;
if (pThis->m_lpOleAdviseHolder == NULL &&
::CreateOleAdviseHolder(&pThis->m_lpOleAdviseHolder)
!= NOERROR) {
return E_OUTOFMEMORY;
}
ASSERT(pThis->m_lpOleAdviseHolder != NULL);
return pThis->m_lpOleAdviseHolder->Advise(pAdvSink, pdwConnection);
}
STDMETHODIMP CEx32cDoc::XOleObject::Unadvise(DWORD dwConnection)
{
TRACE("CEx32cDoc::XOleObject::Unadvise\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::EnumAdvise(
LPENUMSTATDATA* ppenumAdvise)
{
TRACE("CEx32cDoc::XOleObject::EnumAdvise\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
return E_NOTIMPL;
}
STDMETHODIMP CEx32cDoc::XOleObject::GetMiscStatus(
DWORD dwAspect, DWORD* pdwStatus)
{
TRACE("CEx32cDoc::XOleObject::GetMiscStatus\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
*pdwStatus = 0;
CLSID clsid;
pThis->m_xOleObject.GetUserClassID(&clsid);
return OleRegGetMiscStatus(clsid, dwAspect, pdwStatus);
}
STDMETHODIMP CEx32cDoc::XOleObject::SetColorScheme(LPLOGPALETTE lpLogpal)
{
TRACE("CEx32cDoc::XOleObject::SetColorScheme\n");
METHOD_PROLOGUE(CEx32cDoc, OleObject)
ASSERT_VALID(pThis);
return E_NOTIMPL;
}
Next, add the implementation codes for DataObject.
// CEx32cDoc::XDataObject
// delegate many calls to embedded COleDataSource object, which manages formats
STDMETHODIMP_(ULONG) CEx32cDoc::XDataObject::AddRef()
{
TRACE("CEx32cDoc::XDataObject::AddRef\n");
METHOD_PROLOGUE(CEx32cDoc, DataObject)
return pThis->InternalAddRef();
}
STDMETHODIMP_(ULONG) CEx32cDoc::XDataObject::Release()
{
TRACE("CEx32cDoc::XDataObject::Release\n");
METHOD_PROLOGUE(CEx32cDoc, DataObject)
return pThis->InternalRelease();
}
STDMETHODIMP CEx32cDoc::XDataObject::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
ITrace(iid, "CEx32cDoc::XDataObject::QueryInterface");
METHOD_PROLOGUE(CEx32cDoc, DataObject)
return pThis->InternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CEx32cDoc::XDataObject::GetData(
LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
TRACE("CEx32cDoc::XDataObject::GetData -- %d\n",
lpFormatEtc->cfFormat);
METHOD_PROLOGUE(CEx32cDoc, DataObject)
ASSERT_VALID(pThis);
if(lpFormatEtc->cfFormat != CF_METAFILEPICT) {
return S_FALSE;
}
HGLOBAL hPict = pThis->MakeMetaFile();
lpStgMedium->tymed = TYMED_MFPICT;
lpStgMedium->hMetaFilePict = hPict;
lpStgMedium->pUnkForRelease = NULL;