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.
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.
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
);
}
Compile!
Follow these next steps to test your control in Microsoft Excel 97:
Start Microsoft Excel 97.
Bring up the Control Toolbox toolbar (on the View menu, click Toolbars).
Click the far-right hammer & wrench icon in the Control Toolbox toolbar,
and select your new control; it should be called OffCntrDisp.
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:
Start Excel 2007.
Click the Developer tab.
If the Developer tab is not visible on the Ribbon, follow these steps to enable the tab:
Click the Microsoft Office Button, and then click Excel Options.
Click the Popular tab, and then click to select the Show Developer Tab in the Ribbon check box.
Click OK.
In the Controls group on the Developer tab, click Insert.
Under ActiveX controls, click More Controls.
In the More Controls dialog box, click OffCntrDisp.
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.