HOWTO: Get IDispatch of an Excel or Word Document from an OCXID: Q190985
|
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.
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.
// 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.
m_pDocDisp = NULL;
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.
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.
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.
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
);
}
© Microsoft Corporation 1998, All Rights Reserved.
Contributions by Joe Crump, Microsoft Corporation
Additional query words: getobject kbvc600 kbvc500 kbAutomation kbExcel
Keywords : kbAutomation kbExcel kbVC500 kbVC600
Version : WINDOWS:97; winnt:5.0,6.0
Platform : WINDOWS winnt
Issue type : kbhowto
Last Reviewed: August 8, 1999