Two final requirements make a linking container complete. First, you should call IOleLink::BindIfRunning whenever you load a linked object. This is done automatically, for some odd reason, if you pass your IOleClientSite pointer to OleLoad. Patron does not do this, so we make the call explicitly in CTenant::ObjectInitialize:
BOOL CTenant::ObjectInitialize(LPUNKNOWN pObj, LPFORMATETC pFE
, DWORD dwData)
{
HRESULT hr;
LPOLELINK pIOleLink=NULL;
§
if (SUCCEEDED(pObj->QueryInterface(IID_IOleLink
, (PPVOID)&pIOleLink)))
{
LPMONIKER pmk;
hr=pIOleLink->GetSourceMoniker(&pmk);
if (FAILED(hr) œœ NULL==pmk)
m_tType=TENANTTYPE_STATIC;
else
{
m_tType=TENANTTYPE_LINKEDOBJECT;
pmk->Release();
//Connect to the object if source is running.
pIOleLink->BindIfRunning();
}
pIOleLink->Release();
}
§
}
The second requirement is to update any automatic links when you load them from storage. This ensures that objects appear as current as possible in the document. So when opening a document (or in Patron's case, a page), we must check to see whether the document contains any automatic links. If it does have any, we call OleUIUpdateLinks. This function displays a dialog box with a progress indicator, as shown in Figure 20-6.
Figure 20-6.
The Update Links dialog box displayed through OleUIUpdateLinks.
OleUIUpdateLinks is a bit different from other OLE UI functions in that it doesn't take a structure pointer. The reason for this is basically that the function does not provide for customization. What OleUIUpdateLinks does take, however, is a pointer to your IOleUILinkContainer interface, an HWND of the owner window, a caption for the title bar, and a count of how many links it has to update. As shown beginning on the following page, Patron invokes this dialog inside CPage::Open after it has loaded all tenants.
§
UINT cLinks;
LPOLELINK pIOleLink;
LPUNKNOWN pIUnknown;
UINT uRet;
OLEUIEDITLINKS el;
PCIOleUILinkContainer pIUILinks;
HWND hWndDoc;
§
//First, count the number of automatic links.
cLinks=0;
for (i=0; i < m_cTenants; i++)
{
if (TenantGet(i, &pTenant, FALSE))
{
DWORD dw;
pTenant->ObjectGet(&pIUnknown);
hr=pIUnknown->QueryInterface(IID_IOleLink
, (PPVOID)&pIOleLink);
pIUnknown->Release();
if (FAILED(hr))
continue;
pIOleLink->GetUpdateOptions(&dw);
pIOleLink->Release();
if (OLEUPDATE_ALWAYS==dw)
cLinks++;
}
}
//If we have any automatic links, invoke the update dialog.
if (0==cLinks)
return TRUE;
//Create an IOleUILinkContainer instantiation.
if (!m_pPG->GetUILinkContainer(&pIUILinks))
return TRUE; //Guess we can't update; oh well.
hWndDoc=GetParent(m_hWnd);
LoadString(m_pPG->m_hInst, IDS_CAPTION, szCap, sizeof(szCap));
if (!OleUIUpdateLinks(pIUILinks, hWndDoc, szCap, cLinks))
{
/*
* If updating failed, ask to show Links dialog. NOTE:
* OleUIPromptUser has variable wsprintf argument list
* after hWnd parameter! Use appropriate typecasting!
*/
uRet=OleUIPromptUser(IDD_CANNOTUPDATELINK, hWndDoc, szCap);
if (IDC_PU_LINKS==uRet)
{
//Display Links dialog.
memset(&el, 0, sizeof(el));
el.cbStruct=sizeof(el);
el.hWndOwner=hWndDoc;
el.lpOleUILinkContainer=(LPOLEUILINKCONTAINER)pIUILinks;
OleUIEditLinks((LPOLEUIEDITLINKS)&el);
}
}
m_pPG->m_fDirty=pIUILinks->m_fDirty;
pIUILinks->Release();
§
Our first task is to count how many automatic links are on this page (which is the document for Patron's purposes) by checking the types of all tenants on the page and incrementing the variable cLinks for each. If cLinks is 0 at the end, there are no links and there's nothing we need to do. Otherwise, we instantiate the IOleUILinkContainer interface, using exactly the same implementation we created for the Links dialog box. This was completely intentional, but note that OleUIUpdateLinks will use only the GetNextLink and UpdateLink members of that interface. We also grab the window handle of the document and a caption string and finally call OleUIUpdateLinks.
The progress dialog box will update its indicator bar and the displayed percentage every time it finishes updating a link. If it fails on any one of them, it will finish updating the remaining links, but it will then return FALSE instead of TRUE. A FALSE return value means the application should display the message shown in Figure 20-7 on the following page. This gives the user the option of going directly to the Links dialog box to correct any problems. The OLE UI Library even has a function named OleUIPromptUser to create this prompt (because MessageBox can't provide a Links button), which returns IDC_PU_LINKS if the user chooses the Links button. In response, the application invokes the Links dialog box exactly as before, passing our IOleUILinkContainer object.
Figure 20-7.
The message displayed from OleUIPromptUser (IDD_CANNOTUPDATELINK).
One last item, and then we're finished. Be sure to update your document's dirty flag after calling OleUIUpdateLinks because things might have changed that do require a save.
So now you can link to the world, no matter how complicated the moniker. If links are broken, you can repair them with the Links dialog box. Your container stays current with all automatic links by updating them as you load those objects. You want to be sure now that this last feature works and that it can properly invoke the Links dialog box when necessary.