/************************************************************************* * @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 SETVERSIONSTAMP(MVSV); #include #ifdef _DEBUG static char s_aszModule[] = __FILE__; /* For error report */ #endif #include #ifdef IA64 #include #endif #include #include #include #include <_mvutil.h> #include #include #include #include #include #include #include #include #include #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 * * @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 * * @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 * @xref * * @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 * @xref * * @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 * @xref * @xref * * ********************************************************************/ 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 * * ********************************************************************/ 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 */