1105 lines
33 KiB
C++
1105 lines
33 KiB
C++
/*************************************************************************
|
|
* @doc SHROOM EXTERNAL API *
|
|
* *
|
|
* SVMGR.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1995-1997 *
|
|
* All Rights reserved. *
|
|
* *
|
|
* The Service Manager is responsible for reading the object description *
|
|
* from the command interpreter, getting object properties and data, and *
|
|
* building the objects. *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* Written By : John Rush *
|
|
* Current Owner: johnrush *
|
|
* *
|
|
**************************************************************************/
|
|
|
|
#include <verstamp.h>
|
|
SETVERSIONSTAMP(MVSV);
|
|
#include <mvopsys.h>
|
|
|
|
#ifdef _DEBUG
|
|
static char s_aszModule[] = __FILE__; /* For error report */
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
|
|
#ifdef IA64
|
|
#include <itdfguid.h>
|
|
#endif
|
|
|
|
#include <iterror.h>
|
|
#include <orkin.h>
|
|
#include <wrapstor.h>
|
|
#include <_mvutil.h>
|
|
#include <mvsearch.h>
|
|
#include <dynarray.h>
|
|
#include <groups.h>
|
|
#include <itwbrk.h>
|
|
#include <itwbrkid.h>
|
|
#include <ccfiles.h>
|
|
#include <itdb.h>
|
|
#include <itsortid.h>
|
|
#include <itgroup.h>
|
|
|
|
#include "itsvmgr.h"
|
|
#include "svutil.h"
|
|
#include "svdoc.h"
|
|
#include "iterr.h"
|
|
|
|
extern HINSTANCE _hInstITCC;
|
|
|
|
CITSvMgr::CITSvMgr(void)
|
|
{
|
|
m_fInitialized = FALSE;
|
|
m_piistmLog = NULL;
|
|
}
|
|
|
|
CITSvMgr::~CITSvMgr(void)
|
|
{
|
|
if (m_fInitialized)
|
|
Dispose();
|
|
}
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | Initiate |
|
|
* Creates and initiates a structure containing
|
|
* data necessary for future service requests.
|
|
*
|
|
* @parm IStorage * | pStorage |
|
|
* Pointer to destination IStorage. In most cases, this
|
|
* is the ITSS IStorage file.
|
|
*
|
|
* @parm IStream * | piistmLog |
|
|
* Optional IStream to log error messages.
|
|
*
|
|
* @rvalue S_OK | The service manager was initialized successfully
|
|
* @rvalue E_INVALIDARG | One of the required arguments was NULL or otherwise not valid
|
|
* @rvalue E_OUTOFMEMORY | Some resources needed by the service manager could not be allocated
|
|
*
|
|
* @xref <om.LoadFromStream>
|
|
*
|
|
* @comm This should be the first method called for the service manager.
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::Initiate
|
|
(IStorage *piistgStorage, IStream *piistmLog)
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
|
|
if (m_fInitialized)
|
|
return SetErrReturn(E_ALREADYINIT);
|
|
|
|
if (NULL == piistgStorage)
|
|
return SetErrReturn(E_INVALIDARG);
|
|
|
|
if(m_piistmLog = piistmLog)
|
|
m_piistmLog->AddRef();
|
|
|
|
m_pPLDocunent = NULL;
|
|
m_pCatHeader = NULL;
|
|
m_dwMaxPropSize = 0;
|
|
*m_szCatFile = '\0';
|
|
m_piitdb = NULL;
|
|
m_pipstgDatabase = NULL;
|
|
m_dwMaxUID = 0;
|
|
|
|
hResult = (m_piistgRoot = piistgStorage)->AddRef();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
ZeroMemory (&m_dlCLSID, sizeof (DL));
|
|
ZeroMemory (&m_dlObjList, sizeof (DL));
|
|
|
|
// Create database
|
|
if (SUCCEEDED(hResult = CoCreateInstance(CLSID_IITDatabaseLocal, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IITDatabase, (void **)&m_piitdb))
|
|
&&
|
|
SUCCEEDED(hResult = m_piitdb->QueryInterface
|
|
(IID_IPersistStorage, (void **)&m_pipstgDatabase)))
|
|
{
|
|
if (FAILED(hResult = m_pipstgDatabase->InitNew(piistgStorage)))
|
|
LogMessage(SVERR_InitNew, "IITDatabase", hResult);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hResult))
|
|
{
|
|
|
|
if (m_piistgRoot)
|
|
m_piistgRoot->Release();
|
|
piistgStorage->Release();
|
|
|
|
if (m_piitdb)
|
|
{
|
|
m_piitdb->Release();
|
|
m_piitdb = NULL;
|
|
}
|
|
if (m_pipstgDatabase)
|
|
{
|
|
m_pipstgDatabase->Release();
|
|
m_pipstgDatabase = NULL;
|
|
}
|
|
} else
|
|
m_fInitialized = TRUE;
|
|
|
|
return (hResult);
|
|
} /* SVInitiate */
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | Dispose |
|
|
*
|
|
* Signal that all services are no longer needed, causing any resources
|
|
* allocated during use of the service manager to be freed.
|
|
*
|
|
* @rvalue S_OK | All resources freed successfully
|
|
*
|
|
* @xref <om.Initiate>
|
|
*
|
|
* @comm This should be the last method called for the service manager.
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::Dispose ()
|
|
{
|
|
if (FALSE == m_fInitialized)
|
|
return SetErrReturn(E_NOTINIT);
|
|
|
|
if (m_pPLDocunent)
|
|
{
|
|
m_pPLDocunent->Release();
|
|
m_pPLDocunent = NULL;
|
|
}
|
|
|
|
// Destroy catalog
|
|
if (*m_szCatFile)
|
|
{
|
|
CloseHandle (m_hCatFile);
|
|
DeleteFile (m_szCatFile);
|
|
}
|
|
|
|
if (m_pCatHeader)
|
|
{
|
|
_GLOBALFREE ((HANDLE)m_pCatHeader);
|
|
m_pCatHeader = NULL;
|
|
*m_szCatFile = '\0';
|
|
}
|
|
|
|
m_piitdb->Release();
|
|
m_pipstgDatabase->Release();
|
|
|
|
m_piistgRoot->Release();
|
|
if(m_piistmLog)
|
|
m_piistmLog->Release();
|
|
|
|
if (DynArrayValid (&m_dlCLSID))
|
|
{
|
|
PCLSIDENTRY pEntry;
|
|
for (pEntry = (PCLSIDENTRY)DynArrayGetFirstElt (&m_dlCLSID);
|
|
pEntry; pEntry = (PCLSIDENTRY)DynArrayNextElt (&m_dlCLSID))
|
|
{
|
|
pEntry->pCf->Release();
|
|
}
|
|
DynArrayFree (&m_dlCLSID);
|
|
}
|
|
|
|
if (DynArrayValid (&m_dlObjList))
|
|
{
|
|
POBJENTRY pEntry;
|
|
for (pEntry = (POBJENTRY)DynArrayGetFirstElt (&m_dlObjList);
|
|
pEntry; pEntry = (POBJENTRY)DynArrayNextElt (&m_dlObjList))
|
|
{
|
|
if (pEntry->piitbc)
|
|
{
|
|
pEntry->piitbc->Release();
|
|
pEntry->piitbc = NULL;
|
|
|
|
if (pEntry->piistg)
|
|
{
|
|
pEntry->piistg->Commit(STGC_DEFAULT);
|
|
pEntry->piistg->Release();
|
|
} else if (pEntry->piistm)
|
|
pEntry->piistm->Release();
|
|
#ifdef _DEBUG
|
|
pEntry->piistg = NULL;
|
|
pEntry->piistm = NULL;
|
|
#endif
|
|
}
|
|
delete pEntry->wszStorage;
|
|
}
|
|
DynArrayFree (&m_dlObjList);
|
|
}
|
|
|
|
m_fInitialized = FALSE;
|
|
|
|
return S_OK;
|
|
} /* Dispose */
|
|
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | AddDocument |
|
|
* Adds the authored properties and indexable content that were added to the
|
|
* given document template into the service manager's build process. Once
|
|
* AddDocument is called, the data will included in the pending
|
|
* Build() operation.
|
|
*
|
|
* @parm CSvDoc *| pDoc | Pointer to document template containing the
|
|
* content and properties for the current document.
|
|
*
|
|
* @rvalue S_OK | The properties and content were added successfully
|
|
* @rvalue E_MISSINGPROP | The document did not have one of the mandatory properties.
|
|
* Currently, the only required property is STDPROP_UID.
|
|
*
|
|
* @rvalue E_INVALIDARG | One of the required arguments was NULL or otherwise not valid
|
|
* @rvalue E_OUTOFMEMORY | Some resources needed by the service manager could not be allocated
|
|
*
|
|
* @xref <om.AddObjectEntry>
|
|
* @xref <om.Build>
|
|
*
|
|
* @comm
|
|
********************************************************************/
|
|
|
|
HRESULT WINAPI CITSvMgr::AddDocument (CSvDoc *lpDoc)
|
|
{
|
|
WCHAR *lpch;
|
|
HRESULT hr;
|
|
DWORD cbData;
|
|
CSvDocInternal *pDoc = (CSvDocInternal *)lpDoc;
|
|
|
|
if (FALSE == m_fInitialized)
|
|
return SetErrReturn(E_NOTINIT);
|
|
|
|
if (NULL == pDoc)
|
|
return SetErrReturn(E_INVALIDARG);
|
|
|
|
// Suck in the document properties from the batch buffer
|
|
if (!pDoc->m_lpbfDoc)
|
|
return SetErrReturn(E_MISSINGPROP);
|
|
|
|
// Create property list
|
|
if (m_pPLDocunent == NULL)
|
|
{
|
|
hr = CoCreateInstance (CLSID_IITPropList, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IITPropList, (LPVOID *)&m_pPLDocunent);
|
|
if (FAILED (hr))
|
|
{
|
|
LogMessage(SVERR_CoCreateInstance, "IID_IITPropList", hr);
|
|
return SetErrReturn(hr);
|
|
}
|
|
}
|
|
else
|
|
m_pPLDocunent->Clear();
|
|
|
|
// Get the property list for the document
|
|
cbData = *(LPDWORD)DynBufferPtr (pDoc->m_lpbfDoc);
|
|
m_pPLDocunent->LoadFromMem (DynBufferPtr (pDoc->m_lpbfDoc) + sizeof (DWORD), cbData);
|
|
|
|
// Get the UID for the document
|
|
CProperty UidProp;
|
|
if (FAILED(hr = m_pPLDocunent->Get(STDPROP_UID, UidProp))
|
|
|| TYPE_STRING == UidProp.dwType)
|
|
{
|
|
LogMessage(SVERR_NoUID);
|
|
return SetErrReturn(E_MISSINGPROP);
|
|
}
|
|
// This could be a pointer to a UID or a DWORD UID
|
|
if (TYPE_VALUE == UidProp.dwType)
|
|
pDoc->m_dwUID = UidProp.dwValue;
|
|
else if (TYPE_POINTER == UidProp.dwType)
|
|
pDoc->m_dwUID = *((LPDWORD&)UidProp.lpvData);
|
|
|
|
if (m_dwMaxUID < pDoc->m_dwUID)
|
|
m_dwMaxUID = pDoc->m_dwUID;
|
|
|
|
if (cbData)
|
|
CatalogSetEntry (m_pPLDocunent, 0);
|
|
|
|
// Now scan through the batch entry buffer. For each object, farm out the
|
|
// persisted property values to that object.
|
|
if (!pDoc->m_lpbfEntry || !DynBufferLen (pDoc->m_lpbfEntry))
|
|
return S_OK;
|
|
|
|
// Ensure the buffer is terminated with zero marker
|
|
cbData = 0;
|
|
if (!DynBufferAppend (pDoc->m_lpbfEntry, (LPBYTE)&cbData, sizeof (DWORD)))
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
|
|
// Work through all the property lists
|
|
for (lpch = (WCHAR *) DynBufferPtr(pDoc->m_lpbfEntry); lpch;)
|
|
{
|
|
WCHAR szObject[MAX_OBJECT_NAME];
|
|
WCHAR szPropDest[MAX_OBJECT_NAME];
|
|
|
|
m_pPLDocunent->Clear();
|
|
|
|
// found zero marker instead of name
|
|
if (*(LPDWORD)lpch == 0L)
|
|
break;
|
|
|
|
// read the object name
|
|
WSTRCPY(szObject, lpch);
|
|
lpch += WSTRLEN(szObject) + 1;
|
|
|
|
// read the property destination
|
|
WSTRCPY(szPropDest, lpch);
|
|
lpch += WSTRLEN(szPropDest) + 1;
|
|
|
|
cbData = *(LPDWORD)lpch;
|
|
((LPBYTE&)lpch) += sizeof(DWORD);
|
|
|
|
m_pPLDocunent->LoadFromMem (lpch, cbData);
|
|
((LPBYTE&)lpch) += cbData;
|
|
|
|
// we now have a property list, examine the object type and pass to the
|
|
// lower level build routines.
|
|
POBJENTRY pEntry;
|
|
for (pEntry = (POBJENTRY)DynArrayGetFirstElt (&m_dlObjList);
|
|
pEntry; pEntry = (POBJENTRY)DynArrayNextElt (&m_dlObjList))
|
|
{
|
|
if (!WSTRICMP (szObject, pEntry->wszObjName) && pEntry->piitbc)
|
|
{
|
|
hr = pEntry->piitbc->SetEntry
|
|
(*szPropDest ? szPropDest : NULL, m_pPLDocunent);
|
|
if (FAILED (hr))
|
|
{
|
|
LogMessage(SVERR_SetEntry, pEntry->wszObjName, hr);
|
|
|
|
pEntry->piitbc->Close();
|
|
pEntry->piitbc->Release();
|
|
pEntry->piitbc = NULL;
|
|
|
|
if (pEntry->piistg)
|
|
pEntry->piistg->Release();
|
|
else if (pEntry->piistm)
|
|
pEntry->piistm->Release();
|
|
#ifdef _DEBUG
|
|
pEntry->piistg = NULL;
|
|
pEntry->piistm = NULL;
|
|
#endif
|
|
m_piistgRoot->DestroyElement (pEntry->wszStorage);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// UNDONE: document catalog build (titles and other custom doc properties)
|
|
}
|
|
return S_OK;
|
|
} /* AddDocument */
|
|
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | Build |
|
|
* Takes the data accumulated during AddDocument calls and completes the
|
|
* creation of the objects that were requested in the original command
|
|
* interpreter description.
|
|
*
|
|
* @rvalue S_OK | All requested objects, such as wordwheels, and full-text
|
|
* index, were built successfully and added to the output storage.
|
|
*
|
|
* @rvalue E_OUTOFMEMORY | Some resources needed by the service manager could not be allocated
|
|
*
|
|
* @xref <om.AddObjectEntry>
|
|
* @xref <om.AddDocument>
|
|
*
|
|
* @comm
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::Build ()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ITBuildObjectControlInfo itboci = {sizeof (itboci), m_dwMaxUID};
|
|
|
|
if (FALSE == m_fInitialized)
|
|
return SetErrReturn(E_NOTINIT);
|
|
|
|
// Handle Document Catalog
|
|
hr = CatalogCompleteUpdate();
|
|
|
|
// Save all breaker and sort instance data to the storage
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (FAILED(hr = m_pipstgDatabase->Save(m_piistgRoot, TRUE)))
|
|
LogMessage(SVERR_DatabaseSave, hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && DynArrayValid (&m_dlObjList))
|
|
{
|
|
// Build objects
|
|
for (POBJENTRY pObj = (POBJENTRY)DynArrayGetFirstElt (&m_dlObjList);
|
|
pObj; pObj = (POBJENTRY)DynArrayNextElt (&m_dlObjList))
|
|
{
|
|
if (NULL == pObj->piitbc)
|
|
continue;
|
|
|
|
hr = pObj->piitbc->SetBuildStats(itboci);
|
|
if (FAILED(hr) && hr != E_NOTIMPL)
|
|
{
|
|
;// LogMessage();
|
|
continue;
|
|
}
|
|
|
|
if (pObj->piistg)
|
|
{
|
|
// Must support IPersistStorage
|
|
IPersistStorage *pPersistStorage;
|
|
if (FAILED(hr = pObj->piitbc->QueryInterface
|
|
(IID_IPersistStorage, (void **)&pPersistStorage)))
|
|
{
|
|
ITASSERT(0); // We shoulnd't ever hit this condition!
|
|
continue;
|
|
}
|
|
|
|
if(FAILED(hr = pPersistStorage->Save(pObj->piistg, TRUE)))
|
|
LogMessage(SVERR_IPSTGSave, pObj->wszObjName, hr);
|
|
|
|
pPersistStorage->Release();
|
|
|
|
pObj->piistg->Commit(STGC_DEFAULT);
|
|
pObj->piistg->Release();
|
|
}
|
|
else if (pObj->piistm)
|
|
{
|
|
// Must support IPersistStream then
|
|
IPersistStreamInit *pPersistStream;
|
|
if (FAILED(hr = pObj->piitbc->QueryInterface
|
|
(IID_IPersistStreamInit, (void **)&pPersistStream)))
|
|
{
|
|
ITASSERT(0); // We shoulnd't ever hit this condition!
|
|
continue;
|
|
}
|
|
if(FAILED(hr = pPersistStream->Save(pObj->piistm, TRUE)))
|
|
LogMessage(SVERR_IPSTMSave, pObj->wszObjName, hr);
|
|
|
|
pPersistStream->Release();
|
|
pObj->piistm->Release();
|
|
#ifdef _DEBUG
|
|
pObj->piistm = NULL;
|
|
#endif
|
|
}
|
|
// What's the deal??? We checked for these earlier!
|
|
else ITASSERT(0);
|
|
|
|
if (FAILED (hr))
|
|
m_piistgRoot->DestroyElement (pObj->wszStorage);
|
|
|
|
pObj->piitbc->Release();
|
|
pObj->piitbc = NULL;
|
|
}
|
|
hr = S_OK; // We don't return componenet build errors
|
|
}
|
|
|
|
LogMessage(SV_BuildComplete);
|
|
|
|
return hr;
|
|
} /* Build */
|
|
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | CreateDocTemplate |
|
|
* Returns a popinter to a CSVDoc class which can be sent to AddDocument.
|
|
* Pass this pointer to FreeDocTemplate when you no longer need it.
|
|
*
|
|
* @rvalue S_OK | The CSvDoc object was succesfully created.
|
|
*
|
|
* @rvalue E_OUTOFMEMORY | Some resources needed by the service manager could not be allocated
|
|
*
|
|
* @xref <om.AddObjectEntry>
|
|
* @xref <om.AddDocument>
|
|
* @xref <om.FreeDocTemplate>
|
|
*
|
|
*
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::CreateDocTemplate (CSvDoc **pDoc)
|
|
{
|
|
*pDoc = new CSvDocInternal;
|
|
if (*pDoc)
|
|
return S_OK;
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
} /* CreateDocTemplate */
|
|
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | FreeDocTemplate |
|
|
* Frees a CSvDoc class returned from CreateDocTemplate.
|
|
*
|
|
* @rvalue E_INVALIDARG | Thie CSvDoc pointer is NULL.
|
|
* @rvalue S_OK | The CSvDoc object was freed.
|
|
*
|
|
* @xref <om.CreateDocTemplate>
|
|
*
|
|
*
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::FreeDocTemplate (CSvDoc *pDoc)
|
|
{
|
|
if (NULL == pDoc)
|
|
return SetErrReturn(E_INVALIDARG);
|
|
delete (CSvDocInternal *)pDoc;
|
|
return S_OK;
|
|
} /* FreeDocTemplate */
|
|
|
|
|
|
/********************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | CreateBuildObject|
|
|
* Creates a build object.
|
|
*
|
|
* @parm LPCWSTR | szObjectName | Name of object to create.
|
|
* @parm REFCLSID | clsid |Class ID of object.
|
|
*
|
|
* @rvalue S_OK | The operation completed successfully.
|
|
* @rvalue E_INVALIDARG | The argument was not valid
|
|
*
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::CreateBuildObject(LPCWSTR szObjectName, REFCLSID clsid)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (FALSE == m_fInitialized)
|
|
return SetErrReturn(E_NOTINIT);
|
|
|
|
if (NULL == szObjectName)
|
|
return SetErrReturn(E_INVALIDARG);
|
|
|
|
if (!DynArrayValid (&m_dlCLSID))
|
|
// This allocates a dynamic array that can hold a
|
|
// MAXIMUM of 500 unique classes
|
|
if (FALSE == DynArrayInit (&m_dlCLSID,
|
|
sizeof (CLSIDENTRY) * 5, 100, sizeof (CLSIDENTRY), 0))
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
|
|
// Is this entry already in the list?
|
|
PCLSIDENTRY pclsidEntry;
|
|
IClassFactory *pCF;
|
|
for (pclsidEntry = (PCLSIDENTRY)DynArrayGetFirstElt (&m_dlCLSID);
|
|
pclsidEntry; pclsidEntry = (PCLSIDENTRY)DynArrayNextElt (&m_dlCLSID))
|
|
{
|
|
if (clsid == pclsidEntry->clsid)
|
|
{
|
|
pCF = pclsidEntry->pCf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create new class factory
|
|
if (NULL == pclsidEntry)
|
|
{
|
|
hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL,
|
|
IID_IClassFactory, (VOID **)&pCF);
|
|
if (FAILED (hr))
|
|
{
|
|
LogMessage(SVERR_ClassFactory, szObjectName, hr);
|
|
return hr;
|
|
}
|
|
|
|
pclsidEntry = (PCLSIDENTRY)DynArrayAppendElt (&m_dlCLSID);
|
|
if (NULL == pclsidEntry)
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
pclsidEntry->clsid = clsid;
|
|
pclsidEntry->pCf = pCF;
|
|
}
|
|
|
|
// Create new class object
|
|
IITBuildCollect *pInterface;
|
|
if (FAILED (hr = pCF->CreateInstance
|
|
(NULL, IID_IITBuildCollect, (VOID **)&pInterface)))
|
|
{
|
|
LogMessage(SVERR_CreateInstance, szObjectName, hr);
|
|
return hr;
|
|
}
|
|
|
|
// Construct Storage/Stream name
|
|
WCHAR szStorage [CCH_MAX_OBJ_NAME + CCH_MAX_OBJ_STORAGE + 1];
|
|
pInterface->GetTypeString(szStorage, NULL);
|
|
WSTRCAT(szStorage, szObjectName);
|
|
|
|
// Check for IPersistStorage support
|
|
IStorage *pSubStorage = NULL;
|
|
IStream *pStream = NULL;
|
|
IPersistStorage *pPersistStorage;
|
|
if (SUCCEEDED(hr = pInterface->QueryInterface
|
|
(IID_IPersistStorage, (void **)&pPersistStorage)))
|
|
{
|
|
// Create sub-storage for object persistance
|
|
if (FAILED (hr = m_piistgRoot->CreateStorage
|
|
(szStorage, STGM_READWRITE, 0, 0, &pSubStorage)))
|
|
{
|
|
pPersistStorage->Release();
|
|
return hr;
|
|
}
|
|
|
|
hr = pPersistStorage->InitNew(pSubStorage);
|
|
|
|
pPersistStorage->Release(); // Don't need to hold on to this
|
|
if (FAILED(hr))
|
|
{
|
|
LogMessage(SVERR_CreateInstance, szObjectName, hr);
|
|
|
|
pSubStorage->Release();
|
|
m_piistgRoot->DestroyElement(szStorage);
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IPersistStreamInit *pPersistStream;
|
|
if (FAILED(hr = pInterface->QueryInterface
|
|
(IID_IPersistStreamInit, (void **)&pPersistStream)))
|
|
// No IPersistX interfaces supported!
|
|
return hr;
|
|
|
|
if (FAILED (hr = m_piistgRoot->CreateStream
|
|
(szStorage, STGM_READWRITE, 0, 0, &pStream)))
|
|
{
|
|
pPersistStream->Release();
|
|
return hr;
|
|
}
|
|
|
|
hr = pPersistStream->InitNew();
|
|
pPersistStream->Release();
|
|
if (FAILED(hr))
|
|
{
|
|
pStream->Release();
|
|
m_piistgRoot->DestroyElement(szStorage);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (!DynArrayValid (&m_dlObjList))
|
|
// This allocates a dynamic array that can hold a
|
|
// MAXIMUM of 1000 objects
|
|
if (FALSE == DynArrayInit (&m_dlObjList,
|
|
sizeof (OBJENTRY) * 20, 50, sizeof (OBJENTRY), 0))
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
|
|
// Create object node
|
|
// TODO: Check for duplicate entries!
|
|
POBJENTRY pObj;
|
|
pObj = (POBJENTRY)DynArrayAppendElt (&m_dlObjList);
|
|
WSTRCPY (pObj->wszObjName, szObjectName);
|
|
pObj->piitbc = pInterface;
|
|
pObj->piistg = pSubStorage;
|
|
pObj->piistm = pStream;
|
|
pObj->wszStorage = new WCHAR [WSTRLEN(szStorage) + 1];
|
|
WSTRCPY(pObj->wszStorage, szStorage);
|
|
|
|
return S_OK;
|
|
} /* CreateBuildObject */
|
|
|
|
|
|
/***************************************************************
|
|
* @method HRESULT WINAPI | IITSvMgr | GetBuildObject |
|
|
* Retrieves an object built with CreateBuildObject
|
|
*
|
|
* @parm LPCWSTR | pwstrObjectName | Name of object
|
|
* @parm REFIID | refiid | Identifier for object
|
|
* @parm void | **ppInterface | Indirect interface pointer
|
|
*
|
|
*
|
|
***************************************************************/
|
|
HRESULT WINAPI CITSvMgr::GetBuildObject
|
|
(LPCWSTR pwstrObjectName, REFIID refiid, void **ppInterface)
|
|
{
|
|
if (NULL == pwstrObjectName || NULL == ppInterface)
|
|
return E_INVALIDARG;
|
|
|
|
if (!*pwstrObjectName && refiid == IID_IITDatabase)
|
|
{ // Return the database pointer
|
|
(*((IITDatabase **)ppInterface) = m_piitdb)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT hr = E_NOTEXIST;
|
|
POBJENTRY pEntry;
|
|
for (pEntry = (POBJENTRY)DynArrayGetFirstElt (&m_dlObjList);
|
|
pEntry; pEntry = (POBJENTRY)DynArrayNextElt (&m_dlObjList))
|
|
{
|
|
if (!WSTRCMP(pEntry->wszObjName, pwstrObjectName))
|
|
{
|
|
hr = pEntry->piitbc->QueryInterface(refiid, ppInterface);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} /* GetBuildObjectInterface */
|
|
|
|
|
|
HRESULT WINAPI CITSvMgr::SetPropDest
|
|
(LPCWSTR szObjectName, LPCWSTR szDestination, IITPropList *pPropList)
|
|
{
|
|
if (FALSE == m_fInitialized)
|
|
return SetErrReturn(E_NOTINIT);
|
|
|
|
// Remove this once we support prop dest for arbitrary objects
|
|
if (szObjectName != NULL)
|
|
return SetErrReturn(E_NOTIMPL);
|
|
|
|
// Handle catalog
|
|
if (szObjectName == NULL)
|
|
{
|
|
DWORD dwSize;
|
|
|
|
if (szDestination != NULL)
|
|
return SetErrReturn(E_INVALIDARG);
|
|
|
|
if (NULL != m_pCatHeader)
|
|
return SetErrReturn(E_ALREADYINIT);
|
|
|
|
pPropList->SetPersist(STDPROP_UID, FALSE);
|
|
pPropList->GetHeaderSize (dwSize);
|
|
if (!dwSize)
|
|
// There are no document properties
|
|
return S_OK;
|
|
|
|
m_pCatHeader =
|
|
(LPBYTE)_GLOBALALLOC (GMEM_FIXED, sizeof (DWORD) + dwSize);
|
|
if (NULL == m_pCatHeader)
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
*((LPDWORD&)m_pCatHeader) = dwSize;
|
|
pPropList->SaveHeader (m_pCatHeader + sizeof (DWORD), dwSize);
|
|
|
|
// Don't permenantly change the persist state
|
|
pPropList->SetPersist(STDPROP_UID, TRUE);
|
|
}
|
|
return S_OK;
|
|
} /* SetPropDest */
|
|
|
|
|
|
HRESULT WINAPI CITSvMgr::CatalogCompleteUpdate (void)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwSize, dwUID, dwOffset, dwLastUID;
|
|
LPSTR pInput = NULL, pCur, pEnd;
|
|
BTREE_PARAMS bp;
|
|
HBT hbt = NULL;
|
|
IStream *pDataStream = NULL;
|
|
IStorage *pStorage = NULL;
|
|
LPSTR pBin = NULL;
|
|
|
|
if (NULL == m_pCatHeader || !*m_szCatFile)
|
|
return S_OK;
|
|
|
|
CloseHandle (m_hCatFile);
|
|
m_hCatFile = NULL;
|
|
|
|
// Create sub-storage
|
|
if (FAILED (hr = m_piistgRoot->CreateStorage
|
|
(SZ_CATALOG_STORAGE, STGM_READWRITE, 0, 0, &pStorage)))
|
|
{
|
|
SetErrCode(&hr, E_FAIL);
|
|
exit0:
|
|
// Release everything
|
|
if (pBin)
|
|
delete pBin;
|
|
if (pStorage)
|
|
pStorage->Release();
|
|
if (pDataStream)
|
|
pDataStream->Release();
|
|
if (pInput)
|
|
UnmapViewOfFile (pInput);
|
|
if (hbt)
|
|
RcAbandonHbt(hbt);
|
|
DeleteFile (m_szCatFile);
|
|
_GLOBALFREE ((HANDLE)m_pCatHeader);
|
|
m_pCatHeader = NULL;
|
|
*m_szCatFile = '\0';
|
|
return hr;
|
|
}
|
|
|
|
// Create Data Stream
|
|
hr = pStorage->CreateStream
|
|
(SZ_BTREE_DATA, STGM_WRITE, 0, 0, &pDataStream);
|
|
if (FAILED(hr))
|
|
goto exit0;
|
|
|
|
if (S_OK !=(hr = FileSort
|
|
(NULL, (LPB)m_szCatFile, NULL, NULL, 0, NULL, NULL, NULL, NULL)))
|
|
{
|
|
SetErrCode(&hr, E_FAIL);
|
|
goto exit0;
|
|
}
|
|
|
|
bp.hfs = pStorage;
|
|
bp.cbBlock = 2048;
|
|
bp.bFlags = fFSReadWrite;
|
|
|
|
bp.rgchFormat[0] = KT_LONG; // UID
|
|
bp.rgchFormat[1] = '4'; // OFFSET
|
|
bp.rgchFormat[2] = '\0';
|
|
|
|
// Create BTREE
|
|
hbt = HbtInitFill(SZ_BTREE_BTREE_A, &bp, &hr);
|
|
if (hbt == hNil)
|
|
goto exit0;
|
|
|
|
pBin = new char[m_dwMaxPropSize];
|
|
|
|
// Map the temp file to memory
|
|
pInput = MapSequentialReadFile(m_szCatFile, &dwSize);
|
|
if (NULL == pInput)
|
|
{
|
|
SetErrCode(&hr, E_FAIL);
|
|
goto exit0;
|
|
}
|
|
pCur = pInput;
|
|
pEnd = pInput + dwSize;
|
|
dwOffset = 0;
|
|
dwLastUID = 0xFFFFFFFF;
|
|
|
|
while (pEnd != pCur)
|
|
{
|
|
char chTemp[9], *pchend;
|
|
chTemp[8] = '\0';
|
|
|
|
// Read in the record
|
|
MEMCPY (chTemp, pCur, 8);
|
|
dwUID = strtol(chTemp, &pchend, 16);
|
|
pCur += 8;
|
|
|
|
pCur = StringToLong (pCur, &dwSize) + 1;
|
|
BinFromHex (pCur, pBin, dwSize);
|
|
pCur += dwSize + 2;
|
|
dwSize /= 2;
|
|
|
|
if (dwUID != dwLastUID)
|
|
{
|
|
if (FAILED (hr = RcFillHbt(hbt,(KEY)&dwUID,(QV)&dwOffset)))
|
|
{
|
|
delete pBin;
|
|
goto exit0;
|
|
}
|
|
|
|
hr = pDataStream->Write (&dwSize, sizeof (DWORD), NULL);
|
|
if (FAILED(hr))
|
|
goto exit0;
|
|
if (FAILED (hr = pDataStream->Write (pBin, dwSize, NULL)))
|
|
goto exit0;
|
|
dwLastUID = dwUID;
|
|
}
|
|
else
|
|
{ // What to do with duplicates?
|
|
// For now, we skip them
|
|
}
|
|
|
|
dwOffset += dwSize + sizeof (DWORD);
|
|
}
|
|
|
|
hr = RcFiniFillHbt(hbt);
|
|
if (FAILED(hr))
|
|
goto exit0;
|
|
if (S_OK !=(hr = RcCloseBtreeHbt(hbt)))
|
|
goto exit0;
|
|
hbt = NULL;
|
|
|
|
// Create and write header
|
|
IStream *pStream;
|
|
hr = pStorage->CreateStream (SZ_BTREE_HEADER, STGM_WRITE, 0, 0, &pStream);
|
|
if (FAILED(hr))
|
|
goto exit0;
|
|
|
|
hr = pStream->Write (m_pCatHeader, sizeof (DWORD), NULL);
|
|
if (FAILED (hr))
|
|
{
|
|
pStream->Release();
|
|
goto exit0;
|
|
}
|
|
|
|
hr = pStream->Write (m_pCatHeader + sizeof (DWORD),
|
|
*((LPDWORD&)m_pCatHeader), NULL);
|
|
pStream->Release();
|
|
if (FAILED (hr))
|
|
goto exit0;
|
|
|
|
hr = S_OK;
|
|
goto exit0;
|
|
} /* CatalogCompleteUpdate */
|
|
|
|
|
|
HRESULT WINAPI CITSvMgr::CatalogSetEntry (IITPropList *pPropList, DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwUID, dwTemp, dwDataSize;
|
|
CProperty CProp;
|
|
char szTemp[1025];
|
|
|
|
if (FAILED(hr = pPropList->Get(STDPROP_UID, CProp)))
|
|
return SetErrReturn(E_MISSINGPROP);
|
|
|
|
// This could be a pointer to a UID or a DWORD UID
|
|
if (TYPE_VALUE == CProp.dwType)
|
|
dwUID = CProp.dwValue;
|
|
else if (TYPE_POINTER == CProp.dwType)
|
|
dwUID = *((LPDWORD&)CProp.lpvData);
|
|
|
|
// Allocate memory if we need to
|
|
if (NULL == m_pCatHeader)
|
|
{
|
|
pPropList->SetPersist(STDPROP_UID, FALSE);
|
|
pPropList->GetHeaderSize (dwTemp);
|
|
if (!dwTemp)
|
|
// There are no document properties
|
|
return S_OK;
|
|
|
|
m_pCatHeader =
|
|
(LPBYTE)_GLOBALALLOC (GMEM_FIXED, sizeof (DWORD) + dwTemp);
|
|
if (NULL == m_pCatHeader)
|
|
return SetErrReturn(E_OUTOFMEMORY);
|
|
*((LPDWORD&)m_pCatHeader) = dwTemp;
|
|
pPropList->SaveHeader (m_pCatHeader + sizeof (DWORD), dwTemp);
|
|
|
|
// Don't permenantly change the persist state
|
|
pPropList->SetPersist(STDPROP_UID, TRUE);
|
|
}
|
|
|
|
hr = pPropList->GetDataSize (m_pCatHeader + sizeof (DWORD),
|
|
*((LPDWORD&)m_pCatHeader), dwDataSize);
|
|
// Returns S_FALSE if no records to write (still writes empty bit flags)
|
|
if (S_FALSE == hr || !dwDataSize)
|
|
return S_OK;
|
|
|
|
if ('\0' == *m_szCatFile)
|
|
{
|
|
// Create the temp file
|
|
char szTempPath[_MAX_PATH + 1];
|
|
|
|
if (0 == GetTempPath(_MAX_PATH, szTempPath))
|
|
return SetErrReturn(E_FILECREATE);
|
|
if (0 == GetTempFileName(szTempPath, "CAT", 0, m_szCatFile))
|
|
return SetErrReturn(E_FILECREATE);
|
|
m_hCatFile = CreateFile
|
|
(m_szCatFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
if (INVALID_HANDLE_VALUE == m_hCatFile)
|
|
return SetErrReturn(E_FILECREATE);
|
|
}
|
|
|
|
LPBYTE pData;
|
|
LPSTR pHex;
|
|
pPropList->GetDataSize (m_pCatHeader + sizeof (DWORD),
|
|
*((LPDWORD&)m_pCatHeader), dwDataSize);
|
|
pData = new BYTE[dwDataSize];
|
|
pHex = new char[dwDataSize * 2];
|
|
if (dwDataSize > m_dwMaxPropSize)
|
|
m_dwMaxPropSize = dwDataSize;
|
|
|
|
pPropList->SaveData (m_pCatHeader + sizeof (DWORD),
|
|
*((LPDWORD&)m_pCatHeader), pData, dwDataSize);
|
|
HexFromBin (pHex, pData, dwDataSize);
|
|
|
|
dwDataSize *= 2;
|
|
wsprintf (szTemp, "%08X%u:", dwUID, dwDataSize);
|
|
WriteFile (m_hCatFile, szTemp, (DWORD) STRLEN (szTemp), &dwTemp, NULL);
|
|
WriteFile (m_hCatFile, pHex, dwDataSize, &dwTemp, NULL);
|
|
WriteFile (m_hCatFile, "\r\n", (DWORD) STRLEN ("\r\n"), &dwTemp, NULL);
|
|
|
|
delete pData;
|
|
delete pHex;
|
|
|
|
return S_OK;
|
|
} /* CatalogSetEntry */
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* @method HRESULT WINAPI | IITSvMgr | HashString |
|
|
* Returns a hashed DWORD value for an input string.
|
|
* This method is an optional advanced feature, and is
|
|
* not necessary to build any object.
|
|
*
|
|
* @parm LPCWSTR | szKey | String to convert
|
|
* @parm DWORD | *pdwHash | Hashed value of string
|
|
*
|
|
* @rvalue S_OK | The operation completed successfully.
|
|
*
|
|
* @comm
|
|
* This method takes a string, and returns a hashed DWORD value.
|
|
* For example, this method allows you to use hashstring values
|
|
* as UIDs. It provides a unique value based on a title, for example.
|
|
*
|
|
* @comm Using the hash value as a UID in groups
|
|
* can cause inefficiencies due to memory considerations
|
|
* (non-sequential UIDs are more difficult to store).
|
|
*
|
|
*
|
|
********************************************************************/
|
|
HRESULT WINAPI CITSvMgr::HashString (LPCWSTR szKey, DWORD *pdwHash)
|
|
{
|
|
int ich, cch;
|
|
DWORD hash = 0L;
|
|
const int MAX_CHARS = 43;
|
|
|
|
*pdwHash = 0L;
|
|
cch = (int) WSTRLEN (szKey);
|
|
|
|
// The following is used to generate a hash value for structured
|
|
// "ascii hex" context strings. If a title's context strings use
|
|
// the format "0xHHHHHHHH", where H is a valid hex digit, then
|
|
// this algorithm replaces the standard hash algorithm. This is so
|
|
// title's can determined a context string from a given hash value.
|
|
|
|
if ( szKey[0] == L'0' && szKey[1] == L'x' && (cch == 10) )
|
|
{
|
|
WCHAR c;
|
|
|
|
for( ich = 0; ich < cch; ++ich )
|
|
{
|
|
c = szKey[ich];
|
|
hash <<= 4;
|
|
hash += (c & 0x10 ? c : (c + 9)) & 0x0f;
|
|
}
|
|
|
|
*pdwHash = hash;
|
|
return S_OK;
|
|
}
|
|
|
|
for ( ich = 0; ich < cch; ++ich )
|
|
{
|
|
if ( szKey[ich] == L'!' )
|
|
hash = (hash * MAX_CHARS) + 11;
|
|
else if ( szKey[ich] == L'.' )
|
|
hash = (hash * MAX_CHARS) + 12;
|
|
else if ( szKey[ich] == L'_' )
|
|
hash = (hash * MAX_CHARS) + 13;
|
|
else if ( szKey[ich] == L'0' )
|
|
hash = (hash * MAX_CHARS) + 10;
|
|
else if ( szKey[ich] <= L'Z' )
|
|
hash = (hash * MAX_CHARS) + ( szKey[ich] - L'0' );
|
|
else
|
|
hash = (hash * MAX_CHARS) + ( szKey[ich] - 'L0' - (L'a' - L'A') );
|
|
}
|
|
|
|
/* Since the value hashNil is reserved as a nil value, if any context
|
|
* string actually hashes to this value, we just move it.
|
|
*/
|
|
*pdwHash = (hash == hashNil ? hashNil + 1 : hash);
|
|
return S_OK;
|
|
} /* CITSvMgr::HashString */
|
|
|
|
|
|
HRESULT WINAPI CITSvMgr::LogMessage(DWORD dwResourceId, ...)
|
|
{
|
|
if (!m_fInitialized || !m_piistmLog)
|
|
return S_FALSE;
|
|
|
|
char rgchLocalBuf[1024];
|
|
char rgchFormatBuf[1024];
|
|
if (LoadString (_hInstITCC, dwResourceId,
|
|
rgchFormatBuf, 1024 * sizeof (char)))
|
|
{
|
|
va_list vl;
|
|
|
|
va_start(vl, dwResourceId);
|
|
int arg1 = va_arg(vl, int);
|
|
int arg2 = va_arg(vl, int);
|
|
int arg3 = va_arg(vl, int);
|
|
va_end(vl);
|
|
|
|
wsprintf (rgchLocalBuf, rgchFormatBuf, arg1, arg2, arg3);
|
|
}
|
|
else
|
|
STRCPY(rgchLocalBuf, "Error string could not be loaded from resource file.");
|
|
|
|
m_piistmLog->Write(rgchLocalBuf, (DWORD) STRLEN (rgchLocalBuf), NULL);
|
|
m_piistmLog->Write (".\r\n", (DWORD) STRLEN (".\r\n"), NULL);
|
|
|
|
return S_OK;
|
|
} /* LogMessage */
|