|
|
#include <mvopsys.h>
#ifdef _DEBUG
static char s_aszModule[] = __FILE__; /* For error report */ #endif
#include <comdef.h>
#include <wwheel.h>
#include <groups.h>
#include <iterror.h>
#include <ccfiles.h>
#include <itww.h>
#include <itdb.h>
#include <itsort.h>
#include <itsortid.h>
BOOL static IsInFilter(_LPGROUP qFilter, LONG lOffset);
/*****************************************************************************
* * WordWheelOpen * * @doc EXTERNAL * * @api HWHEEL | WordWheelOpen | This function opens a word wheel. * * * @parm LPCSTR | lpszName | Specifies the name of the word wheel. This name * should match the name used on the left side of the word-wheel entries * specified in the [WWHEEL] section of the project file. * * @parm PHRESULT | phr | Error return value if failed. * * @rdesc Returns a handle to the specified word wheel, or NULL if the * function fails. * * * Keyword indexes are stored as word wheels in the file system. Word wheels * for keyword indexes are named using the convention "\|c", where "c" is * the identification character specified in the "key=" entry in the * KEYINDEX section of the project file. For example, the default keyword * index is named "\|0". * * @xref WordWheelClose * */
HWHEEL FAR PASCAL EXPORT_API WordWheelOpen(IITDatabase* lpITDB, IStorage* pWWStorage, PHRESULT phr) { HWHEEL hWheel = NULL; // handle to the new structure
PWHEEL pWheel = NULL; // pointer to locked-down structure
BOOL fSuccess= FALSE; char szBtreeFormat[wMaxFormat + 1]; PWHEELINFO pInfo = NULL; WORD t; WORD wCount = 1; DWORD cbSize; DWORD cbRead; LARGE_INTEGER dlibMove; ULARGE_INTEGER libNewPos; HF pHdr = NULL; BTREE_PARAMS btp;
BOOL fReadOnly = TRUE;
ERRB errb;
*phr = S_OK; // Assume success
// allocate the structure. must be 0 initialized
if ((hWheel = _GLOBALALLOC(GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof(WHEEL)+sizeof(WHEELINFO)*(wCount-1)))==NULL) { *phr = E_OUTOFMEMORY; return NULL; } pWheel = (PWHEEL)_GLOBALLOCK(hWheel);
pWheel->magic = WHEEL_MAGIC; pWheel->pIITGroup = NULL;
for (t = 0; t < wCount; t++) { pInfo = pWheel->pInfo + pWheel->wNumWheels;
// just make sure these are NULL
pInfo->pKeyHdr = NULL; pInfo->pOccHdr = NULL;
pWheel->wNumWheels++; pInfo->magic = WHEEL_INFO_MAGIC; // open the subfiles
if ((pInfo->hmapbt=HmapbtOpenHfs(pWWStorage, SZ_WORDWHEEL_MAP, &errb))==NULL) { SetErrCode(phr, errb); goto ignore_wheel; }
if ((pInfo->hbt=HbtOpenBtreeSz(SZ_BTREE_BTREE, pWWStorage, fFSOpenReadOnly, &errb))==NULL) { SetErrCode(phr, errb); goto ignore_wheel; }
GetBtreeParams(pInfo->hbt, &btp); if (btp.rgchFormat[0] == KT_EXTSORT) { IITSortKey *pITSortKey;
ITASSERT(btp.dwExtSortInstID != IITDB_OBJINST_NULL); if (FAILED(*phr = lpITDB->GetObject(btp.dwExtSortInstID, IID_IITSortKey, (LPVOID *) &pITSortKey))) { goto ignore_wheel; }
BtreeSetExtSort(pInfo->hbt, pITSortKey);
// We release pITSortKey because BtreeSetExtSort
// should've AddRef'ed it.
pITSortKey->Release(); } // Open header and save out key/occurrence
if ((pHdr = HfOpenHfs(pWWStorage, SZ_BTREE_HEADER, fFSOpenReadOnly, phr))==hfNil) goto ignore_wheel; // read global size and seek ahead to key header
*phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup;
dlibMove.QuadPart = cbSize; *phr = pHdr->Seek(dlibMove, STREAM_SEEK_CUR, &libNewPos); if (FAILED(*phr)) goto cleanup;
// read key header size and read in key header info
*phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup; // ALLOCATE memory
if (cbSize) { if (NULL == (pInfo->pKeyHdr = new BYTE[cbSize])) goto cleanup;
*phr = pHdr->Read(pInfo->pKeyHdr, cbSize, &cbRead); if (FAILED(*phr)) goto cleanup; }
// read key def. data size and seek ahead to occurrence hdr. size
*phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup;
dlibMove.QuadPart = cbSize; *phr = pHdr->Seek(dlibMove, STREAM_SEEK_CUR, &libNewPos); if (FAILED(*phr)) goto cleanup;
// read occ. header size and read in key header info
*phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup;
// ALLOCATE memory
if (cbSize) { if (NULL == (pInfo->pOccHdr = new BYTE[cbSize])) goto cleanup;
*phr = pHdr->Read(pInfo->pOccHdr, cbSize, &cbRead); if (FAILED(*phr)) goto cleanup; }
// close header
RcCloseHf(pHdr);
// Open data file
if ((pInfo->hf = HfOpenHfs(pWWStorage, SZ_BTREE_DATA, fFSOpenReadOnly,phr))==hfNil) goto ignore_wheel;
// erinfox: comment out full-text index support temporarily
// REVIEW(billa): If this is re-enabled, then the correct way to open an index
// will be through IITIndex obtained from CoCreateInstance on CLSID_IITIndexLocal.
// Any stop word information will get loaded automatically as part of the breaker
// instance that was associated with the index at build time.
#if 0
// if the index isn't there, that is okay.
pInfo->lpIndex = MVIndexOpen(pTitle->hfpbSysFile, pstrI, phr); if (pInfo->lpIndex) { // if the stop file isn't there, that is okay.
pWheel->lpsipb = MVStopListInitiate (0, phr); // If lpsipb is NULL MVStopListIndexLoad will
// simply return ERR_BADARG, so we don't need to check it
if (S_OK != MVStopListIndexLoad (pTitle->hfpbSysFile, pWheel->lpsipb, pstrS)) { MVStopListDispose (pWheel->lpsipb); pWheel->lpsipb = NULL; } } #endif
// cache the number of keywords
RcGetBtreeInfo(pInfo->hbt, (unsigned char*)szBtreeFormat, &pInfo->lNumKws, NULL);
continue;
ignore_wheel:
// close the subfiles
if (pHdr != NULL) RcCloseHf(pHdr); if (pInfo->hmapbt!=NULL) RcCloseHmapbt(pInfo->hmapbt); if (pInfo->hbt!=NULL) RcCloseBtreeHbt(pInfo->hbt); if (pInfo->hf != hfNil) RcCloseHf (pInfo->hf); #if 0
if (pInfo->lpIndex) MVIndexClose(pInfo->lpIndex); #endif
pInfo->magic=0; pWheel->wNumWheels--; }
if (!pWheel->wNumWheels) { // This can happen when we are looking for a-non existing word wheel
goto cleanup; }
if (pWheel->wNumWheels > 2) { *phr = E_TOOMANYTITLES; warning_abort; } if (pWheel->wNumWheels == 2) {
*phr = E_NOMERGEDDATA; warning_abort;
} else if (pWheel->wNumWheels==1) { // May STILL need to load from merge file!!!!
pWheel->lNumRealEntries = pWheel->lNumEntries = pWheel->pInfo->lNumKws; }
fSuccess = TRUE;
cleanup:
if (pWheel != NULL) _GLOBALUNLOCK(hWheel);
// if we failed, then cleanup.
if (!fSuccess && hWheel != NULL) { WordWheelClose(hWheel); hWheel = NULL; }
return (hWheel); }
/*****************************************************************************
* * WordWheelClose * * * @doc EXTERNAL * * @api void | WordWheelClose | This function closes a word wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @comm After a word wheel is closed, the <p hWheel> handle is invalid and * should not be used. * * @xref WordWheelOpen * * *****************************************************************************/
PUBLIC VOID FAR PASCAL EXPORT_API WordWheelClose(HWHEEL hWheel) {
PWHEEL pWheel = NULL; // pointer to locked-down structure
WORD t;
if (hWheel == NULL) return; // validate the parameters and lock down the structure
if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) warning_abort; if (!PWHEEL_OK(pWheel)) warning_abort;
for (t = 0; t < pWheel->wNumWheels; t++) { if (pWheel->pInfo[t].magic == WHEEL_INFO_MAGIC) { if (NULL != pWheel->pInfo[t].pOccHdr) delete pWheel->pInfo[t].pOccHdr; if (NULL != pWheel->pInfo[t].pKeyHdr) delete pWheel->pInfo[t].pKeyHdr;
// close the subfiles and the file system
if (pWheel->pInfo[t].hmapbt!=NULL) RcCloseHmapbt(pWheel->pInfo[t].hmapbt); if (pWheel->pInfo[t].hbt!=NULL) RcCloseBtreeHbt(pWheel->pInfo[t].hbt); if (pWheel->pInfo[t].hf != hfNil) RcCloseHf (pWheel->pInfo[t].hf); #if 0
// REVIEW(billa): If this is re-enabled, then the correct way
// to close an index will be through IITIndex obtained from
// CoCreateInstance on CLSID_IITIndexLocal at open time.
// Any stop word information will get discarded automatically
// when the index object releases its associated breaker.
if (pWheel->pInfo[t].lpIndex) MVIndexClose(pWheel->pInfo[t].lpIndex); #endif
pWheel->pInfo[t].magic=0; } } if (pWheel->hCacheData) _GLOBALFREE(pWheel->hCacheData);
if (pWheel->lpszCacheData) DisposeMemory(pWheel->lpszCacheData);
if (pWheel->hMergeData) _GLOBALFREE(pWheel->hMergeData); pWheel->magic = 0; #if 0
if (pWheel->lpsipb) MVStopListDispose (pWheel->lpsipb); #endif
_GLOBALUNLOCK(hWheel);
// release the memory used for the structure.
_GLOBALFREE(hWheel);
cleanup:
return; }
/*****************************************************************************
* * WordWheelLength * * * @doc EXTERNAL * * @api long | WordWheelLength | This function returns the number of entries * in the word wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm PHRESULT | lpErrb | Error return value if failed. * * @rdesc Returns the number of entries in the word wheel, or -1 if an * error occurs. * * @xref WordWheelOpen * * *****************************************************************************/
PUBLIC long FAR PASCAL EXPORT_API WordWheelLength(HWHEEL hWheel, PHRESULT phr) { PWHEEL pWheel = NULL; // pointer to locked-down structure.
long lRval = -1L; // -1 is the error condition.
PWHEELINFO pInfo = NULL; // validate the parameters and lock down the structure
if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) { *phr = E_HANDLE; warning_abort; } if (!PWHEEL_OK(pWheel)) { *phr = E_INVALIDARG; warning_abort; }
lRval = pWheel->lNumEntries; //DPF3("...WordWheelLength returns, %ld\n", lRval);
*phr = S_OK;
cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel);
return lRval; }
/*****************************************************************************
* * WordWheelLookup * * * @doc EXTERNAL * * @api HRESULT | WordWheelLookup | This function gets a string from a word * wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm long | lIndex | Specifies an index to the word-wheel entry. * * @parm LPVOID | lpvKeyBuf | Specifies a far pointer to the buffer to * receive the text of the word-wheel entry. * * @parm DWORD | cbKeyBuf | Specifies the length of the buffer in bytes. * * @rdesc Returns S_OK if successful; otherwise returns error code * * @comm Word-wheel entries are numbered starting at zero. * * @xref WordWheelLength WordWheelOpen * * *****************************************************************************/
PUBLIC HRESULT FAR PASCAL EXPORT_API WordWheelLookup(HWHEEL hWheel, long lIndex, LPVOID lpvKeyBuf, DWORD cbKeyBuf) { PWHEEL pWheel = NULL; // pointer to locked-down structure
HRESULT hr = E_INVALIDARG; // assume failure
BYTE rgbKeyBuf[ITWW_CBKEY_MAX];
// validate the parameters and lock down the structure
if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) warning_abort; if (!PWHEEL_OK(pWheel)) warning_abort;
if (lIndex >= 0 && lIndex < pWheel->lNumEntries) { PWHEELINFO pInfo = NULL; LONG lWheelNum = 0; IITSortKey *pITSortKey = NULL; #ifdef MERGE_UPDATE
VirtualToReal(lIndex,pWheel,&lIndex,&lWheelNum); #endif
pInfo = pWheel->pInfo + lWheelNum;
// If there's a filter, let's get the proper entry in the WW.
if (pWheel->pIITGroup) { hr = (pWheel->pIITGroup)->FindTopicNum((DWORD)lIndex, (DWORD*)(&lIndex)); if (FAILED(hr)) goto cleanup; }
// lookup the entry in the map file once we know _which_ map file to look in
if (SUCCEEDED(hr = RcKeyFromIndexHbt(pInfo->hbt, pInfo->hmapbt, (KEY) &rgbKeyBuf[0], ITWW_CBKEY_MAX, lIndex)) && SUCCEEDED(hr = CheckWordWheelKeyType(pInfo->hbt, &pITSortKey))) { DWORD cbKey;
// Check to see if the key we got back will fit in the caller's buffer.
// If it will fit, copy it, otherwise return an error.
if ((cbKey = CbKeyWordWheel((LPVOID) &rgbKeyBuf[0], pITSortKey)) <= cbKeyBuf) MEMCPY(lpvKeyBuf, (LPVOID) &rgbKeyBuf[0], cbKey); else hr = E_INVALIDARG; }
if (pITSortKey != NULL) pITSortKey->Release();
#ifdef _MAC
StringMapWinToMac (pInfo->hbt, lpBuf); #endif
} else hr = E_OUTOFRANGE;
cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel);
return hr; }
/*****************************************************************************
* * WordWheelPrefix * * * @doc EXTERNAL * * @api long | WordWheelPrefix | This function locates a word-wheel entry * whose text starts with the specified prefix. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm LPCSTR | lpszPrefix | Specifies a far pointer to a string * containing the text of the prefix. * * @parm PHRESULT | lpErrb | Error return value if failed. * * @rdesc Returns the index of the first entry containing the specified * prefix. If no entry contains the prefix, the function returns the index * of the entry immediately prior to the point where a word with the * specified prefix would be found. The lowest index returned is zero. * * If an error occurs, the function returns -1. * * @comm Word-wheel entries are numbered starting at zero. * * @xref WordWheelLength WordWheelLookup WordWheelOpen * * *****************************************************************************/ PUBLIC long FAR PASCAL EXPORT_API WordWheelPrefix(HWHEEL hWheel, LPCVOID lpcvPrefix, BOOL fExactMatch, PHRESULT phr) { PWHEEL pWheel = NULL; // pointer to locked-down structure
BTPOS btpos; // position in BTREE for prefix
BYTE rgbKeyTemp[ITWW_CBKEY_MAX +1]; // holds key nearest prefix for single or Update
long lRval = -1; // assume failure
RC rc; // to catch return codes
PWHEELINFO pInfo = NULL; BOOL ifOver1 = FALSE; #ifdef MERGE_UPDATE
BYTE rgbKeyTemp2[ITWW_CBKEY_MAX +1]; // holds key nearest prefix for Main
long lRval2 = -1; BOOL ifOver2 = FALSE; #endif
#ifdef _MAC
BYTE szSearchKey[ITWW_CBKEY_MAX+1]; LPBYTE lpb; LPCMAP lpCMap; LPCHARTAB FAR * lplpCharTab; #endif
// validate the parameters and lock down the structure
if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) { *phr = E_HANDLE; warning_abort; }
if (!PWHEEL_OK(pWheel)) { *phr = E_INVALIDARG; warning_abort; } pInfo = pWheel->pInfo;
#ifdef _MAC
if (lplpCharTab = (LPCHARTAB FAR *)BtreeGetCMap (pInfo->hbt)) { lpCMap = (LPCMAP)lplpCharTab[0]->lpCMapTab;
// Map Mac string back to Windows string
for (lpb = szSearchKey; ; lpb++, lpstrPrefix++ ) { if ((*lpb = *lpstrPrefix) == EMBEDFONT_BYTE_TAG) { /* Load new table */ lpstrPrefix++; lpb++; lpCMap = lplpCharTab[*lpb = *lpstrPrefix]->lpCMapTab; lpstrPrefix++; lpb++; } *lpb = lpCMap[*lpstrPrefix].MacToWin; if (*lpstrPrefix == 0) break; } // lookup the entry in the btree file.
rc=RcLookupByKey(pInfo->hbt, (KEY)szSearchKey, &btpos, NULL); } else #endif
// look up the prefix in the btree.
rc=RcLookupByKey(pInfo->hbt, (KEY)lpcvPrefix, &btpos, NULL);
// if caller asked for exact match and it didn't happen,
// return w/ an error
if (fExactMatch) { if (rc != S_OK) { SetErrCode(phr, rc); warning_abort; }
}
if (rc != S_OK && rc != E_NOTEXIST) { SetErrCode(phr, rc); warning_abort; } // If we ran off the end, then position ourselves at the
// last key in the btree.
if (!FValidPos(&btpos)) { // maybe this was here for merge update; at any rate,
// setting lRval to btpos.cKey is wrong
#if 0
if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos))!= S_OK) { SetErrCode(phr, rc); warning_abort; } lRval = btpos.cKey; //ericjut: Putting the good value
#endif
if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos)) != S_OK) warning_abort;
lRval = pInfo->lNumKws - 1;
ifOver1 = TRUE; } else {
// We are somewhere in the btree. We have either typed in
// a string which is a prefix to a keyword in the btree or not.
// See where we landed in the btree and compare the keyword in
// the dialog with where we are.
rc = RcLookupByPos(pInfo->hbt,&btpos, (KEY)rgbKeyTemp, ITWW_CBKEY_MAX ,NULL); if (rc == S_OK) rc = RcIndexFromKeyHbt(pInfo->hbt, pInfo->hmapbt, (LPLONG)&lRval, (KEY)rgbKeyTemp); if (rc != S_OK) { lRval = -1; SetErrCode(phr, rc); warning_abort; }
// If the keyword we looked for is not a prefix of the
// string at btpos, then we are positioned at the keyword
// that would follow this keyword if it were in fact in the
// btree. Back up one keyword to let him see the previous
// one to give enough context so he sees his is not present.
// If already at the first keyword, don't back up any farther.
if (!FIsPrefix(pInfo->hbt, (KEY)lpcvPrefix, (KEY)rgbKeyTemp)) if (lRval > 0) lRval--; }
#ifdef MERGE_UPDATE
if (pWheel->wNumWheels==2) // Look up in update as well
{ pInfo = pWheel->pInfo+1;
// Getting rid of the unwanted duplicates.
if (ifOver1) lRval = WWDuplicateRemove(lRval, pWheel); #ifdef _MAC
if (lplpCharTab = (LPCHARTAB FAR *)BtreeGetCMap (pInfo->hbt)) { lpCMap = (LPCMAP)lplpCharTab[0]->lpCMapTab; for (lpb = szSearchKey; ; lpb++, lpstrPrefix++ ) { if ((*lpb = *lpstrPrefix) == EMBEDFONT_BYTE_TAG) { /* Load new table */ lpstrPrefix++; lpb++; lpCMap = lplpCharTab[*lpb = *lpstrPrefix]->lpCMapTab; lpstrPrefix++; lpb++; } *lpb = lpCMap[*lpstrPrefix].MacToWin; if (*lpstrPrefix == 0) break; } rc=RcLookupByKey(pInfo->hbt, (KEY)szSearchKey, &btpos, NULL); } else #endif
rc=RcLookupByKey(pInfo->hbt, (KEY)lpcvPrefix, &btpos, NULL);
if (rc != S_OK && rc != ERR_NOTEXIST) { SetErrCode(phr, rc); warning_abort; } if (!FValidPos(&btpos)) { if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos)) != S_OK) { SetErrCode(phr, rc); warning_abort; } lRval2 = btpos.cKey; //ericjut: Putting the good value
ifOver2 = TRUE; } else { rc = RcLookupByPos(pInfo->hbt,&btpos,(KEY)rgbKeyTemp2,ITWW_CBKEY_MAX,NULL); if (rc == S_OK) rc = RcIndexFromKeyHbt(pInfo->hbt, pInfo->hmapbt, (LPLONG)&lRval2, (KEY)rgbKeyTemp2); if (rc != S_OK) { lRval2 = -1; SetErrCode(phr, rc); warning_abort; } if (!FIsPrefix(pInfo->hbt,(KEY)lpcvPrefix,(KEY)rgbKeyTemp2)) if (lRval2 > 0) lRval2--; } // Now find lowest virtual index for tree 0, index lRval or tree 1, index lRval2
lRval=RealToVirtual(lRval, 0, pWheel); lRval2=RealToVirtual(lRval2, 1, pWheel);
if ((lRval2<lRval && !ifOver2) || (!(lRval2<lRval) && ifOver1)) lRval=lRval2; } else { if (pWheel->hMergeData) lRval=RealToVirtual(lRval, 0, pWheel); } #endif // MERGE_UPDATE
if (pWheel->pIITGroup) { // In a filtered situation, let's find the proper offset.
*phr = (pWheel->pIITGroup)->FindOffset((DWORD)lRval, (DWORD*)(&lRval)); if (FAILED(*phr) && (E_NOTEXIST != *phr)) goto cleanup; }
*phr = S_OK;
cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel);
return lRval; }
// Little internal function to figure out if a UID is part of
BOOL static IsInFilter(_LPGROUP qFilter, LONG lOffset) { LONG lNbBytes = lOffset / 8; LONG lNbBits = lOffset % 8; BYTE bMask = 1 << lNbBits; return ((BYTE)*(qFilter->lpbGrpBitVect+lNbBytes)) & bMask; }
// Returns S_OK if the btree's key type is a valid one for the word wheel.
// *ppITSortKey is always set based on one of the following cases:
//
// to NULL : if any error occurs, which includes an unsupported
// key type or if the key type is KT_EXTSORT but no
// pointer to the external sort instance has been specified.
//
// to non-NULL : the key type is KT_EXTSORT and a pointer to an
// external sort instance has been specified.
HRESULT FAR PASCAL CheckWordWheelKeyType(HBT hbt, IITSortKey **ppITSortKey) { BTREE_PARAMS btp; HRESULT hr = S_OK;
if (ppITSortKey == NULL) return (E_POINTER);
*ppITSortKey = NULL;
GetBtreeParams(hbt, &btp); switch (btp.rgchFormat[0]) { case KT_EXTSORT: BtreeGetExtSort(hbt, ppITSortKey); if (*ppITSortKey == NULL) hr = E_INVALIDSTATE;
break;
case KT_SZ: case KT_SZMAP: break;
default: // Word wheel doesn't support any other key types.
hr = E_BADFORMAT; break; };
return (hr); }
DWORD FAR PASCAL CbKeyWordWheel(LPVOID lpvKey, IITSortKey *pITSortKey) { DWORD cbKey;
if (pITSortKey == NULL) { // Btree key type is a KT_SZx which we understand, so we'll determine
// the key size ourselves.
cbKey = (DWORD) STRLEN((char *) lpvKey) + 1; } else { // Get the key size from the sort object.
if (FAILED(pITSortKey->GetSize(lpvKey, &cbKey))) { cbKey = 0; ITASSERT(FALSE); } }
return (cbKey); }
|