/************************************************************************* * @doc SHROOM EXTERNAL API * * * * WWIMP.CPP * * * * Copyright (C) Microsoft Corporation 1997 * * All Rights reserved. * * * * This file contains CITWordWheelLocal, the local implementation * * of IITWordWheel * * * ************************************************************************** * * * Written By : Erin Foxford * * Current Owner: erinfox * * * * * TODO TODO TODO: Replace the blind use of critical sections with a * better way of ensuring thread-safeness while preserving performance. * But for now, in the interest of coding time, we just make sure the * code is thread-safe. * **************************************************************************/ #include #ifdef _DEBUG static char s_aszModule[] = __FILE__; /* For error report */ #endif #include // includes for ATL. // MediaView (InfoTech) includes #include #include #include "itcat.h" // catalog #include "itcc.h" // needed for STDPROP_SORTKEY def. #include "ITPropl.h" // property list #include "itrs.h" // result set #include "itquery.h" // query and index #include #include "ITDB.h" #include "DBImp.h" #include "itww.h" #include "WWImp.h" #include "itgroup.h" /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Open | * Opens a word wheel * @parm IITDatabase* | lpITDB | Pointer to database object * @parm LPCWSTR | lpszMoniker | Name of word wheel * @parm DWORD | dwFlags | One or more of the following values: * @flag ITWW_OPEN_CONNECT | If the wordwheel resides on a remote machine, * connect to the machine during this call to retrieve initialization data. Otherwise * the connection is delayed until the first API call which requires this data. * * @rvalue E_INVALIDARG | IITDatabase* or lpszMoniker was NULL * @rvalue E_ALREADYOPEN | Word wheel is already open * @rvalue E_GETLASTERROR | An I/O or transport operation failed. Call the Win32 * GetLastError function to retrieve the error code. * @rvalue STG_E_* | Any of the IStorage errors that could while opening a storage * @rvalue S_OK | The word wheel was successfully opened * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Open(IITDatabase* lpITDB, LPCWSTR lpszMoniker, DWORD dwFlags) { HRESULT hr; LPWSTR szStorageName; if (NULL == lpITDB || NULL == lpszMoniker) return E_POINTER; // TODO: Report error or close word wheel if it's already open ? if (m_hWheel || m_pSubStorage) return E_ALREADYOPEN; m_cs.Lock(); // Open substorage and pass to word wheel szStorageName = new WCHAR [CCH_MAX_OBJ_NAME + CCH_MAX_OBJ_STORAGE + 1]; WSTRCPY (szStorageName, SZ_WW_STORAGE); if (WSTRLEN (lpszMoniker) <= CCH_MAX_OBJ_NAME) WSTRCAT (szStorageName, lpszMoniker); else { MEMCPY (szStorageName, lpszMoniker, CCH_MAX_OBJ_NAME * sizeof (WCHAR)); szStorageName [CCH_MAX_OBJ_NAME + CCH_MAX_OBJ_STORAGE] = (WCHAR)'\0'; } if (SUCCEEDED(hr = lpITDB->GetObjectPersistence(szStorageName, IITDB_OBJINST_NULL, (LPVOID *) &m_pSubStorage, FALSE))) { // Open word wheel m_hWheel = WordWheelOpen(lpITDB, m_pSubStorage, &hr); } delete szStorageName; if (m_hWheel == NULL || FAILED(hr)) { exit0: if (m_pSubStorage != NULL) { m_pSubStorage->Release(); m_pSubStorage = NULL; } m_cs.Unlock(); return hr; } // NOTE: // If the client wants to load a group into the word wheel for filtering, // s/he must first call the "Open" method of ITWordWheel, then create // a group (these two steps may be interchanged), then call the "SetGroup" // method of ITWordWheel. // Store count m_cEntries = m_cMaxEntries = WordWheelLength(m_hWheel, &hr); // Open catalog object - we only need one instance hr = CoCreateInstance(CLSID_IITCatalogLocal, NULL, CLSCTX_INPROC_SERVER, IID_IITCatalog, (VOID **) &m_pCatalog); if (FAILED(hr)) goto exit0; // If we can't open the catalog hat's OK because we can run // without it. if (FAILED(m_pCatalog->Open(lpITDB))) { m_pCatalog->Release(); m_pCatalog = NULL; } m_cs.Unlock(); return S_OK; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Close | * Closes a word wheel * * @rvalue S_OK | The word wheel was successfully closed * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Close(void) { m_cs.Lock(); if (m_hWheel) { WordWheelClose(m_hWheel); m_hWheel = NULL; } else return E_NOTINIT; if (m_pSubStorage) { m_pSubStorage->Release(); m_pSubStorage = NULL; } if (m_pCatalog) { m_pCatalog->Close(); m_pCatalog->Release(); m_pCatalog = NULL; } if (m_hScratchBuffer) { _GLOBALFREE(m_hScratchBuffer); m_hScratchBuffer = NULL; } m_cbScratchBuffer = 0; if (m_pIITGroup != NULL) { m_pIITGroup->Release(); m_pIITGroup = NULL; } m_cs.Unlock(); return S_OK; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetLocaleInfo | * Gets locale info that the word wheel was built with. * @parm DWORD* | pdwCodePageID | On exit, pointer to code page ID. * @parm LCID* | plcid | On exit, pointer to locale ID. * * @rvalue S_OK | The locale info was successfully retrieved. * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetLocaleInfo(DWORD *pdwCodePageID, LCID *plcid) { HRESULT hr = S_OK; PWHEEL pwheel = NULL; if (pdwCodePageID == NULL || plcid == NULL) return (SetErrReturn(E_POINTER)); if (m_hWheel == NULL) return (E_NOTOPEN); m_cs.Lock(); // validate the parameters and lock down the structure if ((pwheel = (PWHEEL)_GLOBALLOCK(m_hWheel)) != NULL && PWHEEL_OK(pwheel)) { BTREE_PARAMS btp; GetBtreeParams(pwheel->pInfo->hbt, &btp); *pdwCodePageID = btp.dwCodePageID; *plcid = btp.lcid; } else hr = E_UNEXPECTED; if (pwheel != NULL) _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return (hr); } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetSorterInstance | * Get the ID of the sorter instance being used by the * word wheel * * @parm DWORD* | pdwObjInstance | Pointer to sorter instance ID * * @rvalue E_NOTOPEN | The word wheel has not been opened * @rvalue E_POINTER | pdwObjInstance was an invalid pointer * @rvalue S_OK | The sorter instance ID was successfully obtained * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetSorterInstance(DWORD *pdwObjInstance) { HRESULT hr = S_OK; PWHEEL pwheel = NULL; if (m_hWheel == NULL) return (E_NOTOPEN); if (pdwObjInstance == NULL) return (E_POINTER); m_cs.Lock(); // validate the parameters and lock down the structure if ((pwheel = (PWHEEL)_GLOBALLOCK(m_hWheel)) != NULL && PWHEEL_OK(pwheel)) { BTREE_PARAMS btp; GetBtreeParams(pwheel->pInfo->hbt, &btp); *pdwObjInstance = (btp.rgchFormat[0] == KT_EXTSORT ? btp.dwExtSortInstID : IITDB_OBJINST_NULL); } else hr = E_UNEXPECTED; if (pwheel != NULL) _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return (hr); } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Count | * Returns number of entries in word wheel through pcEntries * * @parm LONG* | pcEntries | Number of entries in word wheel * * @rvalue E_NOTOPEN | The word wheel has not been opened * @rvalue E_POINTER | pcEntries was an invalid pointer * @rvalue S_OK | The count was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Count(LONG *pcEntries) { if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == pcEntries) return E_POINTER; *pcEntries = m_cEntries; return S_OK; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Lookup | * Looks up an entry and returns contents in a buffer * * @parm LONG | lEntry | Entry to look up * @parm LPVOID | lpvKeyBuf | Buffer to return entry * @parm DWORD | cbKeyBuf | Buffer size in number of bytes * * @rvalue E_OUTOFRANGE | Entry number is out of range * @rvalue S_OK | The word wheel entry was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Lookup(LONG lEntry, LPVOID lpvKeyBuf, DWORD cbKeyBuf) { if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == lpvKeyBuf) return E_POINTER; m_cs.Lock(); HRESULT hr = WordWheelLookup(m_hWheel, lEntry, lpvKeyBuf, cbKeyBuf); m_cs.Unlock(); return hr; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Lookup | * Looks up an entry and returns contents as a result set * * @parm LONG | lEntry | Entry to look up * @parm IITResultList* | lpITResult | Pointer to result set to fill * @parm LONG | cEntries | Number of entries to fill result set * * @rvalue E_INVALIDARG | Invalid argument was passed (cEntries = 0 or lpITResult * is NULL) * @rvalue E_OUTOFRANGE | The entry does not exist in the word wheel * @rvalue S_OK | The word wheel entry was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Lookup(LONG lEntry, IITResultSet* lpITResult, LONG cEntries) { PWHEEL pWheel = NULL; // pointer to locked-down structure PWHEELINFO pInfo = NULL; BYTE Key[ITWW_CBKEY_MAX]; BYTE DataBuffer[8]; DWORD dwOffset; // Offset into data file DWORD cbRead; LPBYTE pPropBuffer = NULL; LARGE_INTEGER dlibMove; // For seeking ULARGE_INTEGER libNewPos; IITSortKey *pITSortKey = NULL; LONG iRow; LONG iColumn; LONG iEntry; // Loop index LONG lMaxEntry; HRESULT hr; if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == lpITResult) return E_POINTER; m_cs.Lock(); // validate the parameters and lock down the structure if ((pWheel = (PWHEEL)_GLOBALLOCK(m_hWheel))==NULL || !PWHEEL_OK(pWheel)) { hr = E_INVALIDSTATE; warning_abort; } pInfo = pWheel->pInfo; // if result set is empty, add columns based on header lpITResult->GetColumnCount(iColumn); if (0 == iColumn) { lpITResult->Add(STDPROP_SORTKEY, (LPWSTR) NULL, PRIORITY_NORMAL); if (pInfo->pKeyHdr) lpITResult->Add(pInfo->pKeyHdr); } // Check for a btree key type that the word wheel supports and set // pITSortKey. if (FAILED(hr = CheckWordWheelKeyType(pInfo->hbt, &pITSortKey))) assert_abort; lMaxEntry = lEntry + cEntries; for (iEntry = lEntry; iEntry < lMaxEntry ; iEntry++) { DWORD cbKeyData = 0L; // Amount of data in key property // exceeded entries in word wheel if (iEntry >= m_cEntries) break; // If there's a filter, let's get the proper entry in the WW. if (!m_pIITGroup) lEntry = iEntry; else { hr = m_pIITGroup->FindTopicNum((DWORD)iEntry, (DWORD*)(&lEntry)); if (FAILED(hr)) goto cleanup; else if (S_FALSE == hr) // not found break; } // lookup the entry in the map file hr = RcKeyFromIndexHbt(pInfo->hbt, pInfo->hmapbt, (KEY)Key, ITWW_CBKEY_MAX, lEntry); if (FAILED(hr)) goto cleanup; // lookup data associated w/ key hr = RcLookupByKey(pInfo->hbt, (KEY)Key, NULL, DataBuffer); if (FAILED(hr)) goto cleanup; dwOffset = *(LPDWORD) &DataBuffer[4]; // get data from data file // seek to get offset into data file where key // properties reside dlibMove.QuadPart = dwOffset; hr = (pInfo->hf)->Seek(dlibMove, STREAM_SEEK_SET, &libNewPos); if (FAILED(hr)) goto cleanup; // read in amount of key data hr = (pInfo->hf)->Read(&cbKeyData, sizeof(DWORD), &cbRead); if (FAILED(hr)) goto cleanup; // get current last row so we can set the data there lpITResult->GetRowCount(iRow); // Set key. Note that GetColumnFromPropID might return error // if caller didn't specify STDPROP_SORTKEY. if (SUCCEEDED(hr = lpITResult->GetColumnFromPropID(STDPROP_SORTKEY, iColumn))) lpITResult->Set(iRow, iColumn, (LPVOID) Key, CbKeyWordWheel((LPVOID) Key, pITSortKey)); if (cbKeyData) { // read the data hr = ReallocBufferHmem (&m_hScratchBuffer, &m_cbScratchBuffer, cbKeyData); if (FAILED(hr)) goto cleanup; if(NULL == (pPropBuffer = (BYTE *)_GLOBALLOCK(m_hScratchBuffer))) { hr = E_OUTOFMEMORY; goto cleanup; } hr = (pInfo->hf)->Read(pPropBuffer, cbKeyData, &cbRead); // Even if Set(iRow...) fails, set other properties, if there are any // TODO: This function may need optimization lpITResult->Set(iRow, pInfo->pKeyHdr, pPropBuffer); _GLOBALUNLOCK(m_hScratchBuffer); } } cleanup: if (NULL != pWheel) _GLOBALUNLOCK(m_hWheel); if (pITSortKey != NULL) pITSortKey->Release(); m_cs.Unlock(); return hr; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | Lookup | * Returns word wheel entry closest to given prefix * * @parm LPCVOID | lpcvPrefix | Prefix to look up * @parm BOOL | fExactMatch | TRUE if prefix must have exact match; FALSE otherwise * @parm LONG* | plEntry | Entry into word wheel with closest match * * @rvalue E_NOTEXIST | The prefix doesn't exist; returned when fExactMatch is set * to TRUE and there is no match. * @rvalue E_POINTER | Either lpcvPrefix or pcEntries was an invalid pointer * @rvalue S_OK | The entry was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::Lookup(LPCVOID lpcvPrefix, BOOL fExactMatch, LONG *plEntry) { HRESULT hr; if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == lpcvPrefix || NULL == plEntry) return E_POINTER; m_cs.Lock(); *plEntry = WordWheelPrefix(m_hWheel, lpcvPrefix, fExactMatch, &hr); m_cs.Unlock(); return hr; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetDataCount | * Returns number of occurrences associated with given word wheel entry * * @parm LONG | lEntry | Entry into word wheel * @parm DWORD* | pdwCount | Number of occurrences * * @rvalue E_NOTEXIST | Entry number is not in the valid range * @rvalue S_OK | The entry was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetDataCount(LONG lEntry, DWORD *pdwCount) { HRESULT hr = S_OK; BYTE Key[ITWW_CBKEY_MAX]; BYTE DataBuffer[8]; if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == pdwCount) return E_POINTER; m_cs.Lock(); PWHEEL pWheel = (PWHEEL)_GLOBALLOCK(m_hWheel); PWHEELINFO pInfo = pWheel->pInfo; if (pWheel->pIITGroup) { hr = (pWheel->pIITGroup)->FindTopicNum((DWORD)lEntry, (DWORD*)(&lEntry)); if (FAILED(hr)) goto cleanup; } // lookup the entry in the map file hr = RcKeyFromIndexHbt(pInfo->hbt, pInfo->hmapbt, (KEY)Key, ITWW_CBKEY_MAX, lEntry); if (FAILED(hr)) goto cleanup; // lookup some data (count) associated w/ key hr = RcLookupByKey(pInfo->hbt, (KEY)Key, NULL, DataBuffer); if (FAILED(hr)) goto cleanup; *pdwCount = *(LPDWORD) DataBuffer; cleanup: if (NULL != pWheel) _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return hr; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetData | * Fills given result set with rows corresponding to user data * * @parm LONG | lEntry | Entry * @parm IITResultSet* | lpITResult | Pointer to result set object to fill * * @rvalue E_INVALIDARG | Result set pointer cannot be NULL * @rvalue E_NOTEXIST | Entry number is not in the valid range * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The entry was successfully returned * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetData(LONG lEntry, IITResultSet* lpITResult) { BYTE Key[ITWW_CBKEY_MAX]; BYTE DataBuffer[8]; DWORD dwCount; // Number of occurrences DWORD dwOffset; // Offset into data file LARGE_INTEGER dlibMove; // For seeking ULARGE_INTEGER libNewPos; DWORD cbPropSize; LPBYTE pPropBuffer = NULL; DWORD cbRead; LONG iColumn; DWORD iRow; // Loop index HRESULT hr = S_OK; if (NULL == m_hWheel) return E_NOTOPEN; if (NULL == lpITResult) return E_POINTER; m_cs.Lock(); PWHEEL pWheel = (PWHEEL)_GLOBALLOCK(m_hWheel); PWHEELINFO pInfo = pWheel->pInfo; // if result set is empty, add columns based on header // also add columns passed on catalog header lpITResult->GetColumnCount(iColumn); if (0 == iColumn) { lpITResult->Add(pInfo->pOccHdr); if (m_pCatalog) m_pCatalog->GetColumns(lpITResult); } if (pWheel->pIITGroup) { hr = (pWheel->pIITGroup)->FindTopicNum((DWORD)lEntry, (DWORD*)(&lEntry)); if (FAILED(hr)) goto cleanup; } // lookup the entry in the map file hr = RcKeyFromIndexHbt(pInfo->hbt, pInfo->hmapbt, (KEY)Key, ITWW_CBKEY_MAX, lEntry); if (FAILED(hr)) goto cleanup; // lookup some data (count and offset into data file) associated w/ key hr = RcLookupByKey(pInfo->hbt, (KEY)Key, NULL, DataBuffer); if (FAILED(hr)) goto cleanup; dwCount = *(LPDWORD) DataBuffer; dwOffset = *(LPDWORD) &DataBuffer[4]; // get data from data file // seek to get offset into data file where key // properties reside and find out how much to skip dlibMove.QuadPart = dwOffset; hr = (pInfo->hf)->Seek(dlibMove, STREAM_SEEK_SET, &libNewPos); if (FAILED(hr)) goto cleanup; DWORD dwDataOffset; hr = (pInfo->hf)->Read(&dwDataOffset, sizeof(DWORD), &cbRead); if (FAILED(hr)) goto cleanup; // Seek to where data properties start dlibMove.QuadPart = dwDataOffset; hr = (pInfo->hf)->Seek(dlibMove, STREAM_SEEK_CUR, &libNewPos); if (FAILED(hr)) goto cleanup; // Loop over all properties - get size, then read in property info into // memory block for (iRow = 0; iRow < dwCount; iRow++) { hr = (pInfo->hf)->Read(&cbPropSize, sizeof(DWORD), &cbRead); if (FAILED(hr)) goto cleanup; hr = ReallocBufferHmem (&m_hScratchBuffer, &m_cbScratchBuffer, cbPropSize); if (FAILED(hr)) goto cleanup; if(NULL == (pPropBuffer = (BYTE *)_GLOBALLOCK(m_hScratchBuffer))) { hr = E_OUTOFMEMORY; goto cleanup; } hr = (pInfo->hf)->Read(pPropBuffer, cbPropSize, &cbRead); if(SUCCEEDED(hr)) // pass header and data to append to result set // TODO: This function may need optimization hr = lpITResult->Append(pInfo->pOccHdr, pPropBuffer); _GLOBALUNLOCK(m_hScratchBuffer); if (FAILED(hr)) goto cleanup; } // Then fill result set w/ any properties that might be in catalog if (m_pCatalog) hr = m_pCatalog->Lookup(lpITResult); // if (S_FALSE == hr) hr = S_OK; // don't report S_FALSE cleanup: if (NULL != pWheel) _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return hr; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetDataColumns | * Adds columns to given result set for all data properties * * @parm IITResultSet* | pRS | Pointer to result set * * @rvalue S_OK | The columns were successfully added * ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetDataColumns(IITResultSet* pRS) { PWHEEL pWheel = (PWHEEL)_GLOBALLOCK(m_hWheel); PWHEELINFO pInfo = pWheel->pInfo; if (pRS == NULL) return E_POINTER; pRS->Add(pInfo->pOccHdr); if (NULL != pWheel) _GLOBALUNLOCK(m_hWheel); return S_OK; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | SetGroup | * Associates a group with a word wheel, used for filtering. * * @parm IITGroup* | pIITGroup | pointer to a pre-loaded group interface * * @rvalue S_OK | interface pointer not NULL--successfully assigned * @rvalue E_INVALIDARG | received a NULL pointer for the interface * @rvalue E_NOTOPEN | the word wheel was not found to be open * @rvalue E_OUTOFMEMORY | unable to lock the wordwheel's memory * @rvalue E_BADFILTERSIZE | the filter group and wordwheel have different sizes * * @comm This method does not verify that the group behind the * interface has been loaded, or that the group is not empty. * If the client assigns the group interface pointer any random * positive value other than zero, without properly instantiating * the group object, this method returns S_OK. ********************************************************************/ STDMETHODIMP CITWordWheelLocal::SetGroup(IITGroup* pIITGroup) { if (NULL == m_hWheel) return E_NOTOPEN; m_cs.Lock(); PWHEEL pWheel; if (NULL == (pWheel = (PWHEEL)_GLOBALLOCK(m_hWheel))) { m_cs.Unlock(); return E_OUTOFMEMORY; } // NULL is a valid argument--client is attempting to free the group if (pIITGroup) { // We need to access the group's maxItemAllGroup and lcItem fields. // We can make two function calls, or locally store the output of a single call. _LPGROUP localImage = (_LPGROUP)(pIITGroup->GetLocalImageOfGroup()); if ((DWORD)(m_cMaxEntries) != localImage->maxItemAllGroup) { _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return E_BADFILTERSIZE; } pWheel->lNumEntries = m_cEntries = (LONG)(localImage->lcItem); } else { pWheel->lNumEntries = m_cEntries = m_cMaxEntries; } if (pIITGroup) pIITGroup->AddRef(); if (m_pIITGroup != NULL) m_pIITGroup->Release(); pWheel->pIITGroup = m_pIITGroup = pIITGroup; _GLOBALUNLOCK(m_hWheel); m_cs.Unlock(); return S_OK; } /******************************************************************** * @method STDMETHODIMP | IITWordWheel | GetGroup | * Retrieves the group associated with the word wheel. * * @parm IITGroup** | ppIITGroup | (out) Pointer to the group interface. * * @rvalue S_OK | private member variable m_pIITGroup != NULL * @rvalue E_NOTINIT | private member variable m_pIITGroup == NULL * @rvalue E_BADPARAM | ppIITGroup was NULL ********************************************************************/ STDMETHODIMP CITWordWheelLocal::GetGroup(IITGroup** ppiitGroup) { if (NULL == ppiitGroup) return E_POINTER; if (m_pIITGroup == NULL) return E_NOTINIT; (*ppiitGroup = m_pIITGroup)->AddRef(); return S_OK; }