Microsoft KB Archive/190985

= How to get IDispatch of an Excel or Word document from an OCX =

Article ID: 190985

Article Last Modified on 3/30/2007

-

APPLIES TO


 * Microsoft Visual C++ 5.0 Enterprise Edition
 * Microsoft Visual C++ 6.0 Enterprise Edition
 * Microsoft Visual C++ 5.0 Professional Edition
 * Microsoft Visual C++ 6.0 Professional Edition
 * Microsoft Visual C++ 6.0 Standard Edition
 * Microsoft Office Word 2007
 * Microsoft Office Word 2003
 * Microsoft Word 2002 Standard Edition
 * Microsoft Word 2000 Standard Edition
 * Microsoft Word 97 Standard Edition
 * Microsoft Office Excel 2007
 * Microsoft Office Excel 2003
 * Microsoft Excel 2002 Standard Edition
 * Microsoft Excel 2000 Standard Edition
 * Microsoft Excel 97 Standard Edition

-



This article was previously published under Q190985



SUMMARY
It is common for an OLE control to need the IDispatch of its container. You can often get the IDispatch by using QueryInterface from immediately accessible interfaces on the server, such as IOleClientSite. However, for some servers, such as Microsoft Excel, this approach fails.

Another way to get the IDispatch is by using the GetActiveObject API to get the server's IDispatch from the ROT. However, this method requires that you must be able to obtain the CLSID or ProgID of the server. Furthermore, ambiguous situations can occur where you can't distinguish between multiple instances of the server.

This article uses another approach to obtain the IDispatch, which works for both Microsoft Excel and Microsoft Word, even when multiple instances are running.

The steps listed below allow you to build a control that can obtain the IDispatch of the container's Document object.



Step By Step Example
 Create a new MFC ActiveX ControlWizard application named OffCntrDisp.  Add the following member variables to your COleControl-derived class: char m_szDocName[512]; IDispatch *m_pDocDisp; NOTE: m_szDocName holds the name of the document containing our control, and m_pDocDisp is the IDispatch interface for that document.

  Add the following to the end of your COleControl-derived class definition: // Interface Maps. protected: // IoleObject. BEGIN_INTERFACE_PART(MyOleObject, IOleObject) INIT_INTERFACE_PART(COffCtlDispCtrl, MyOleObject) 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)(IEnumOLEVERB**); STDMETHOD(Update); STDMETHOD(IsUpToDate); STDMETHOD(GetUserClassID)(CLSID*); 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(MyOleObject)

DECLARE_INTERFACE_MAP; This is added to override COleControl's default implementation of IOleObject with your custom MyOleObject.

  In your COleControl-derived class's constructor, add the following: m_pDocDisp = NULL;   Just after the following line in OffCntrDispCtl.cpp: IMPLEMENT_OLECTLTYPE(COffCntrDispCtrl, IDS_OFFCNTRDISP,

_dwOffCntrDispOleMisc) add the following code:      BEGIN_INTERFACE_MAP(COffCntrDispCtrl, COleControl)          INTERFACE_PART(COffCntrDispCtrl, IID_IOleObject, MyOleObject)      END_INTERFACE_MAP This, along with the modifications in step 3, override COleControl's IOleObject.

  Just below the code added in step 5, add the following: static char buf[8192]; static void DoMsg(char *msg) { ::MessageBox(NULL, msg, "Message", MB_SETFOREGROUND); }

static void DoErr(char *msg, long err) { static char errBuf[8192]; sprintf(errBuf, "%s, Error: %ld (%08lx)", msg, err,err); ::MessageBox(NULL, errBuf, "Error", MB_SETFOREGROUND); } These are helpful routines used later for displaying messages.

  Paste all of the following code at the end of your OffCntrDispCtl.cpp file: STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetHostNames(LPCOLESTR     pwApp, LPCOLESTR pwObj) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis);

// Convert OLESTR into ASCII string.

WideCharToMultiByte(CP_ACP, 0, pwObj, -1, pThis->m_szDocName,         512, NULL, NULL);

// Get IDispatch. pThis->GetDocDispatch;

// Test it out by getting the document name. pThis->TestDispatch;

return S_OK; }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetClientSite(LPOLECLIENTSITE     pClientSite) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.SetClientSite(pClientSite); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetColorScheme(LPLOGPALETTE plp) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.SetColorScheme(plp); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetMiscStatus(DWORD     dwAspect, DWORD* pdwStatus) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetMiscStatus(dwAspect, pdwStatus); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::EnumAdvise(LPENUMSTATDATA*     ppenumAdvise) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.EnumAdvise(ppenumAdvise); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Unadvise(DWORD     dwConnection) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.Unadvise(dwConnection); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Advise(LPADVISESINK     pAdvSink, DWORD* pdwConnection) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.Advise(pAdvSink, pdwConnection); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetExtent(DWORD     dwDrawAspect, LPSIZEL lpsizel) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetExtent(dwDrawAspect, lpsizel); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetExtent(DWORD     dwDrawAspect, LPSIZEL lpsizel) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.SetExtent(dwDrawAspect, lpsizel); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetUserType(DWORD     dwFormOfType, LPOLESTR* ppszUserType) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis);

return pThis->m_xOleObject.GetUserType(dwFormOfType,         ppszUserType); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetUserClassID(CLSID*     pClsid) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetUserClassID(pClsid); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::IsUpToDate {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis);

return pThis->m_xOleObject.IsUpToDate; }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Update {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.Update; }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::EnumVerbs(LPENUMOLEVERB*     ppenumOleVerb) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis);

return pThis->m_xOleObject.EnumVerbs(ppenumOleVerb); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::DoVerb(LONG iVerb, LPMSG     lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex, HWND hwndParent,      LPCRECT lprcPosRect) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.DoVerb(iVerb, lpmsg, pActiveSite,         lindex, hwndParent, lprcPosRect); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetClipboardData(DWORD     dwReserved, LPDATAOBJECT *ppDataObject) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetClipboardData(dwReserved,         ppDataObject); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::InitFromData(LPDATAOBJECT     pDataObject, BOOL fCreation, DWORD dwReserved) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.InitFromData(pDataObject, fCreation,         dwReserved); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetMoniker(DWORD     dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppmk) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetMoniker(dwAssign, dwWhichMoniker,         ppmk); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetMoniker(DWORD     dwWhichMoniker, LPMONIKER pmk) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.SetMoniker(dwWhichMoniker, pmk); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Close(DWORD     dwSaveOption) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.Close(dwSaveOption); }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetClientSite(LPOLECLIENTSITE*     ppClientSite) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.GetClientSite(ppClientSite);

}

STDMETHODIMP_(ULONG) COffCntrDispCtrl::XMyOleObject::Release {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.Release; }

STDMETHODIMP_(ULONG) COffCntrDispCtrl::XMyOleObject::AddRef {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.AddRef; }

STDMETHODIMP COffCntrDispCtrl::XMyOleObject::QueryInterface(REFIID     iid, LPVOID* ppvObj) {         METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject) ASSERT_VALID(pThis); return pThis->m_xOleObject.QueryInterface(iid, ppvObj); } This is your implementation of IOleObject, which mostly delegates all of its calls to the default COleControl's IOleObject implementation, except for SetHostNames. You trap SetHostNames and store the document name where the control is inserted.

Unfortunately, Microsoft PowerPoint doesn't call this method, so this example won't work with that product. However, Microsoft Powerpoint is a single-instance server, so you could use GetActiveObject to uniquely get the IDispatch pointer. </li>  Add the following member functions to your COleControl-derived class: void COffCntrDispCtrl::GetDocDispatch {         // No need, if we already have it. if(m_pDocDisp != NULL) return;

// Get a BindCtx. IBindCtx *pbc; HRESULT hr = CreateBindCtx(0, &pbc); if(FAILED(hr)) { DoErr("CreateBindCtx", hr); return; }

// Get running-object table. IRunningObjectTable *prot; hr = pbc->GetRunningObjectTable(&prot); if(FAILED(hr)) { DoErr("GetRunningObjectTable", hr); pbc->Release; return; }

// Get enumeration interface. IEnumMoniker *pem; hr = prot->EnumRunning(&pem); if(FAILED(hr)) { DoErr("EnumRunning", hr); prot->Release; pbc->Release; return; }

// Start at the beginning. pem->Reset;

// Churn through enumeration. ULONG fetched; IMoniker *pmon; int n = 0; while(pem->Next(1, &pmon, &fetched) == S_OK) {

// Get DisplayName. LPOLESTR pName; pmon->GetDisplayName(pbc, NULL, &pName); // Convert it to ASCII. char szName[512]; WideCharToMultiByte(CP_ACP, 0, pName, -1, szName, 512, NULL,             NULL);

// Compare it against the name we got in SetHostNames. if(!strcmp(szName, m_szDocName)) {

DoMsg("Found document in ROT!");

// Bind to this ROT entry. IDispatch *pDisp; hr = pmon->BindToObject(pbc, NULL, IID_IDispatch, (void **)&pDisp); if(!FAILED(hr)) { // Remember IDispatch. m_pDocDisp = pDisp;

// Notice... sprintf(buf, "Document IDispatch = %08lx",                     m_pDocDisp); DoMsg(buf); }                 else { DoErr("BindToObject", hr); }             }

// Release interfaces. pmon->Release;

// Break out if we obtained the IDispatch successfully. if(m_pDocDisp != NULL) break; }

// Release interfaces. pem->Release; prot->Release; pbc->Release; }

void COffCntrDispCtrl::TestDispatch {         ASSERT(m_pDocDisp);

COleDispatchDriver doc(m_pDocDisp); DISPID dispID = 0; unsigned short *ucPtr = L"Name";

// Get DISPID for Name. HRESULT hr = m_pDocDisp->GetIDsOfNames(IID_NULL, &ucPtr, 1,         LOCALE_USER_DEFAULT, &dispID); ASSERT(!FAILED(hr));

// Get Name property. CString name; doc.GetProperty(dispID, VT_BSTR, &name);

AfxMessageBox(             CString("Document name is ") + name,              MB_SETFOREGROUND          ); }                   </li> Compile!</li></ol>

Follow these next steps to test your control in Microsoft Excel 97:
 * 1) Start Microsoft Excel 97.
 * 2) Bring up the Control Toolbox toolbar (on the View menu, click Toolbars).
 * 3) Click the far-right hammer & wrench icon in the Control Toolbox toolbar, and select your new control; it should be called OffCntrDisp.
 * 4) Draw a rectangle in the sheet to insert the control. RESULTS: You should see the control appear, and shortly afterwards, a message box with "Found document in ROT." Next, you should see another message box displaying something similar to "Document IDispatch = 0043bf8c." Finally, you should see a message box informing you of the name of the document where it was inserted.

Follow these next steps to test your control in Microsoft Office Excel 2007: <ol> Start Excel 2007.</li> Click the Developer tab. If the Developer tab is not visible on the Ribbon, follow these steps to enable the tab: <ol style="list-style-type: lower-alpha;"> Click the Microsoft Office Button, and then click Excel Options.</li> Click the Popular tab, and then click to select the Show Developer Tab in the Ribbon check box.</li> Click OK.</li></ol> </li> In the Controls group on the Developer tab, click Insert.</li> Under ActiveX controls, click More Controls.</li> In the More Controls dialog box, click OffCntrDisp.</li> Draw a rectangle in the sheet to insert the control. RESULTS The control will appear. Shortly afterward, a message box that contains "Found document in ROT" appears. Next, you should see another message box that resembles "Document IDispatch = 0043bf8c." Finally, you should see a message box that informs you of the name of the document where it was inserted.</li></ol>

Additional query words: getobject XL2007

Keywords: kbautomation kbhowto KB190985

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.