Create and Manage Monikers

Patron maintains three monikers within its various objects: a File moniker naming the document, a File!Item moniker naming the page, and a File!Item!Item moniker naming each tenant. All of this starts in CPatronDoc::Rename, in which we first learn of the document name itself:


void CPatronDoc::Rename(LPTSTR pszFile)
{
LPMONIKER pmk;

CDocument::Rename(pszFile);

//Unregister old moniker (m_dwRegROT set to 0).
INOLE_RevokeAsRunning(&m_dwRegROT);

if (NULL==pszFile)
return;

CreateFileMoniker(pszFile, &pmk);

if (NULL!=pmk)
{
LPMONIKER pmkAll;

INOLE_RegisterAsRunning(this, pmk, 0, &m_dwRegROT);

//Give a moniker to linked objects in tenants.
m_pPG->NotifyTenantsOfRename(pszFile, pmk);

//Register a File!"\" wildcard moniker as well.
CreateItemMoniker(TEXT("!"), TEXT("\\"), &pmkAll);

if (NULL!=pmkAll)
{
LPMONIKER pmkWild;

INOLE_RevokeAsRunning(&m_dwRegROTWild);
pmk->ComposeWith(pmkAll, FALSE, &pmkWild);

if (NULL!=pmkWild)
{
INOLE_RegisterAsRunning(this, pmk, 0
, &m_dwRegROTWild);
pmkWild->Release();
}

pmkAll->Release();
}

//There's no need for us to hold on to this.
pmk->Release();
}

return;
}

First we create a file moniker and register that moniker as running. Then we pass this moniker down to the current page through CPages::NotifyTenantOfRename, which calls CPage::NotifyTenantsOfRename. After this, we create a File!"\" wildcard moniker and register it as running too. Thus, all pages in this document are marked as running. An external client can retrieve the document object's pointer, query for IOleItemContainer, and call IOleItemContainer::GetObject to look up any page in the document.

Patron's document object does not hold on to its file moniker because we never have occasion to use it outside this renaming sequence. It does hold the registration value that comes back from IRunningObjectTable::Register. CPatronDoc::FDirtySet uses this value to call IRunningObjectTable::NoteChangeTime as needed.

This is not to say that the file moniker is not needed elsewhere. The page and tenants hold on to a copy of this moniker for their own needs, such as creating CFSTR_LINKSOURCE data, passing the moniker to IOleObject::SetMoniker, and implementing IOleClientSite::GetMoniker. This means that we have to pass down this document moniker to the page and tenants, as described earlier. This lands us in CPage::NotifyTenantsOfRename:


void CPage::NotifyTenantsOfRename(LPTSTR pszFile, LPMONIKER pmk)
{
PCTenant pTenant;
UINT i;
LPMONIKER pmkPage;
LPMONIKER pmkAll;
OLECHAR szTemp[32];

//Save file moniker.
if (NULL!=m_pmkFile)
m_pmkFile->Release();

m_pmkFile=pmk;
m_pmkFile->AddRef();

//Create page moniker to send to tenants.
GetStorageName(szTemp);
CreateItemMoniker(TEXT("!"), szTemp, &pmkPage);

for (i=0; i < m_cTenants; i++)
{
if (TenantGet(i, &pTenant, FALSE))
pTenant->NotifyOfRename(pszFile, pmk, pmkPage);
}

/*
* Register a File!Page!"\" wildcard moniker as well.
* Notice that page is already marked as running
* with document's wildcard moniker.
*/
CreateItemMoniker(TEXT("!"), TEXT("\\"), &pmkAll);

if (NULL!=pmkAll)
{
LPMONIKER pmkWild=NULL;
LPMONIKER pmkTemp=NULL;

INOLE_RevokeAsRunning(&m_dwRegROTWild);
pmk->ComposeWith(pmkPage, FALSE, &pmkTemp);

if (NULL!=pmkTemp)
{
pmkTemp->ComposeWith(pmkAll, FALSE, &pmkWild);
pmkTemp->Release();
}

if (NULL!=pmkWild)
{
INOLE_RegisterAsRunning(this, pmk, 0
, &m_dwRegROTWild);
pmkWild->Release();
}

pmkAll->Release();
}

//If anything held on to this, it called AddRef.
pmkPage->Release();
return;
}

Here we do much the same thing as we did in CPatronDoc::Rename—we create a moniker naming the page, hand that moniker and the file moniker to each tenant in the page, and register a wildcard moniker for all the tenants in the page. This wildcard moniker has the form File!Item!"\", of course. Note that the page has no need to register itself as running because the wildcard moniker we registered in the document has already accounted for the page. This applies to the tenants as well: because the page registers a wildcard moniker for all its tenants, all those tenants are already marked as running.

We still need to tell embedded objects of the document's file moniker, however, by calling IOleObject::SetMoniker. This occurs in CTenant::NotifyOfRename:


void CTenant::NotifyOfRename(LPTSTR pszFile, LPMONIKER pmkFile
, LPMONIKER pmkPage)
{
[Code to call IOleObject::SetHostNames omitted]

§

if (NULL!=pmkFile)
{
if (NULL!=m_pmkFile)
m_pmkFile->Release();

m_pmkFile=pmkFile;
m_pmkFile->AddRef();

m_pIOleObject->SetMoniker(OLEWHICHMK_CONTAINER, pmkFile);
}

if (NULL!=pmkFile && NULL!=pmkPage)
{
LPMONIKER pmkTenant=NULL;
LPMONIKER pmkRel=NULL;
HRESULT hr;

//Create moniker for this tenant.
GetStorageName(szObj);
hr=CreateItemMoniker(TEXT("!"), szObj, &pmkTenant);

if (SUCCEEDED(hr))
{
//Create relative moniker--no pathname.
pmkPage->ComposeWith(pmkTenant, FALSE, &pmkRel);
pmkTenant->Release();

if (SUCCEEDED(hr))
m_pIOleObject->SetMoniker(OLEWHICHMK_OBJREL, pmkRel);

//Hold on to relative moniker.
ReleaseInterface(m_pmk);
m_pmk=pmkRel;
}
}

return;
}

We call IOleObject::SetMoniker twice: once with OLEWHICHMK_CONTAINER (the document moniker) and once with OLEWHICHMK_OBJREL (the Page!Tenant moniker). This gives a linked object all the information it needs to maintain both its absolute and its relative monikers, as described earlier.

You'll notice that the tenant holds on to the document-relative moniker in the variable m_pmk. This is used later for implementing IOleClientSite::GetMoniker, which must now support all the various OLEWHICHMK_* options:


STDMETHODIMP CImpIOleClientSite::GetMoniker(DWORD dwAssign
, DWORD dwWhich, LPMONIKER *ppmk)
{
*ppmk=NULL;

switch (dwWhich)
{
case OLEWHICHMK_CONTAINER:
//This is just the file we're living in.
if (NULL!=m_pTen->m_pmkFile)
*ppmk=m_pTen->m_pmkFile;

break;

case OLEWHICHMK_OBJREL:
//This is everything but the filename.
if (NULL!=m_pTen->m_pmk)
*ppmk=m_pTen->m_pmk;

break;

case OLEWHICHMK_OBJFULL:
//Concatenate file and relative monikers for this one.
if (NULL!=m_pTen->m_pmkFile && NULL!=m_pTen->m_pmk)
{
return m_pTen->m_pmkFile->ComposeWith
(m_pTen->m_pmk, FALSE, ppmk);
}

break;
}

if (NULL==*ppmk)
return ResultFromScode(E_FAIL);

(*ppmk)->AddRef();
return NOERROR;
}

Most of the preceding code applies equally well to any link-source server that wants to provide linking to pseudo-objects or to other portions of a file, a database, or other material. Managing the monikers that name items and supplying those monikers through IOleObject and IOleClientSite are requirements of all types of link sources in OLE Documents.