Managing Substorages

As mentioned earlier, Patron manages a substorage for each page in the overall document. This, of course, requires somewhat more complex code when pages are created or destroyed. Whenever Patron creates a new page in the document, it calls IStorage::CreateStorage using the name Page<ID>, where <ID> is the page identifier in ASCII. When Patron opens an existing page, it calls IStorage::OpenStorage with the appropriate name as well. Both operations occur in CPage::Open. The member CPage::GetStorageName simply generates the text name to use in both calls:


BOOL CPage::Open(LPSTORAGE pIStorage)
{
BOOL fNULL=FALSE;
HRESULT hr=NOERROR;
DWORD dwMode=STGM_TRANSACTED | STGM_READWRITE
| STGM_SHARE_EXCLUSIVE;
OLECHAR szTemp[32];

if (NULL==m_pIStorage)
{
fNULL=TRUE;

if (NULL==pIStorage)
return FALSE;

GetStorageName(szTemp);

hr=pIStorage->OpenStorage(szTemp, NULL, dwMode, NULL, 0
, &m_pIStorage);

if (FAILED(hr))
{
hr=pIStorage->CreateStorage(szTemp, dwMode, 0, 0
, &m_pIStorage);
}
}

§
}


UINT CPage::GetStorageName(LPOLESTR pszName)
{
return wsprintf(pszName, TEXT("Page %lu"), m_dwID);
}

The CPages class assigns the page ID through the CPage constructor, which saves the value in m_dwID. The CPages class, once again, keeps a persistent DWORD counter for page IDs in the Page List stream. IDs are not recycled when a page is destroyed, but the DWORD counter would overflow only if you sat down and created (for example) one page every second until the year 2129. I desperately hope this software is obsolete by then!

Speaking of destroying a page, this operation requires a call to IStorage::DestroyElement to counter the IStorage::CreateStorage in the preceding code. The function CPage::Destroy takes care of this in Patron:


//pIStorage is document's root storage.
void CPage::Destroy(LPSTORAGE pIStorage)
{
if (NULL!=pIStorage)
{
OLECHAR szTemp[32];

Close(FALSE);
GetStorageName(szTemp)
pIStorage->DestroyElement(szTemp);
}

return;
}

void CPage::Close(BOOL fCommit)
{
if (NULL==m_pIStorage)
return;

if (fCommit)
Update();

if (0L==m_pIStorage->Release())
m_pIStorage=NULL;

return;
}

BOOL CPage::Update(void)
{
if (NULL!=m_pIStorage)
m_pIStorage->Commit(STGC_DEFAULT);

return TRUE;
}