ABCTBL1.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:27k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /***********************************************************************
  2.  *
  3.  *  ABCTBL1.C
  4.  *
  5.  *  Contents Table
  6.  *
  7.  *  The contents table associated with this provider.  It's file based.
  8.  *  The format of the .SAB file is in ABP.H.
  9.  *
  10.  *  This implementation of IMAPITable is retrieved by calling the
  11.  *  GetContentsTable() method of the sample ABP's single directory (see
  12.  *  abcont.c).
  13.  *
  14.  *  There are a few interesting features in this implementation.  First
  15.  *  it's implemented on a flat file (.SAB).  Second, it actually supports
  16.  *  an implementation of ANR (Ambiguous Name Resolution).  And lastly, it
  17.  *  supports FindRow (limited to prefix searching on display names).
  18.  *
  19.  *  This is the minimum set of restrictions that your provider MUST support.
  20.  *  It MUST have an ANR implementation and support prefix (downward) searching
  21.  *  on whatever your table is sorted on (in this case PR_DISPLAY_NAME).
  22.  *
  23.  *  The browsing of a flat file is done a record at a time.  It's never
  24.  *  completely read into memory.  It only reads records from the file when
  25.  *  it absolutely has to (like in QueryRows).  The advantage to this is
  26.  *  a low memory footprint and the ability to browse rather large files
  27.  *  with decent performance.
  28.  */
  29. /*
  30.  *  ANR is also supported in this implementation.  In the code will often
  31.  *  be 'if' statements making two different code paths between the restricted
  32.  *  and unrestricted version.  The restrictions rely on a couple of
  33.  *  bit arrays.  Each bit corresponds (in order) to the records in the .SAB
  34.  *  file.  One bit array, rgChecked, says whether or not a record in the
  35.  *  .SAB file has actually been compared to the restriction.  It's initially
  36.  *  set to all 0s - which means that none of the records have been checked.
  37.  *  The second bit array, rgMatched, says whether or not the particular
  38.  *  record matches the current restriction.  This array is initialized to all
  39.  *  1s - which says that all the records in the .SAB file match the current
  40.  *  restriction.  The reason for this is for the fraction returned in
  41.  *  QueryPosition--By assuming that all the rows match and only updating
  42.  *  the array when something doesn't match, the fraction returned in
  43.  *  QueryPosition will get larger and has the effect of making the thumb-bar
  44.  *  on a listbox implemented on top of this table to scroll down as you go
  45.  *  down the list.
  46.  *
  47.  *  As a row is about to be read it is checked to see if it's been compared
  48.  *  to the current restriction.  If it has then to determine whether or not
  49.  *  to actually read the record and build the row we just look at the matched
  50.  *  bit array.  If it doesn't match we go on to the next record and check
  51.  *  again.  If it does match we read the record and build the row.
  52.  *
  53.  *  Only if it hasn't been checked do we actually go and read the row and
  54.  *  compare it to the current restriction, setting the rgChecked and
  55.  *  rgMatched bits accordingly.
  56.  *
  57.  *  In this implementation the ANR comparison rules are as follows:
  58.  *  (See FNameMatch for the actual code to do this)
  59.  *
  60.  *    A 'word' is defined as any substring separated by separators.
  61.  *    The separators are defined as ',', ' ', and '-'.
  62.  *    A restriction is said to match a display name iff all the words
  63.  *    in the restriction are all prefixes of words in the display name.
  64.  *
  65.  *    For example, a restriction of "b d" will match:
  66.  *     "Barney Donovan"
  67.  *     "Donovan, Barney"
  68.  *     "Danielle Blumenthal"
  69.  *    But will not match:
  70.  *     "Darby Opal"
  71.  *     "Ben Masters"
  72.  *
  73.  *  Other providers can do whatever matching they want with the ANR
  74.  *  restriction.  For example, soundex or additional field comparisons (like
  75.  *  email addresses).  A good (and fast) implementation will differentiate
  76.  *  your provider from others in a positive way.
  77.  *
  78.  *  FindRow's implementation effectively handles prefix searching on display
  79.  *  names (PR_DISPLAY_NAME).  It does this by binary searching the .SAB file.
  80.  *  The only tricky part is knowing when to stop.  Basically you want to stop
  81.  *  on the first name in the list that matches the restriction.  It's easy to
  82.  *  do linearly, but a little more trick with a binary search.  This
  83.  *  implementation is a reasonable example.
  84.  *
  85.  *
  86.  *  This table has only the following columns:
  87.  */
  88. #include "abp.h"
  89. #include "abctbl.h"
  90. #include "sampabp.rh"
  91. const SizedSPropTagArray(cvalvtMax, tagaivtabcColSet) =
  92. {
  93.     cvalvtMax,
  94.     {
  95.         PR_DISPLAY_NAME_A,
  96.         PR_ENTRYID,
  97.         PR_ADDRTYPE_A,
  98.         PR_EMAIL_ADDRESS_A,
  99.         PR_OBJECT_TYPE,
  100.         PR_DISPLAY_TYPE,
  101.         PR_INSTANCE_KEY
  102.     }
  103. };
  104. const LPSPropTagArray ptagaivtabcColSet = (LPSPropTagArray) &tagaivtabcColSet;
  105. /*
  106.  *
  107.  *  The following routines are implemented in the files abctbl2.c and abctbl3.c:
  108.  *
  109.  *
  110.  *      IVTABC_Release
  111.  *      IVTABC_GetStatus
  112.  *      IVTABC_SetColumns
  113.  *      IVTABC_QueryColumns
  114.  *      IVTABC_GetRowCount
  115.  *      IVTABC_SeekRow
  116.  *      IVTABC_SeekRowApprox
  117.  *      IVTABC_QueryPosition
  118.  *      IVTABC_FindRow
  119.  *      IVTABC_Restrict
  120.  *      IVTABC_CreateBookmark
  121.  *      IVTABC_FreeBookmark
  122.  *      IVTABC_SortTable
  123.  *      IVTABC_QuerySortOrder
  124.  *      IVTABC_QueryRows
  125.  *      IVTABC_Abort
  126.  *      IVTABC_ExpandRow
  127.  *      IVTABC_CollapseRow
  128.  *      IVTABC_WaitForCompletion
  129.  *      IVTABC_GetCollapseState
  130.  *      IVTABC_SetCollapseState
  131.  *
  132.  *
  133.  *  This file (abctbl1.c) has all the utility functions used throughout this
  134.  *  objects methods.  They are the following:
  135.  *
  136.  *      HrNewIVTAbc()
  137.  *      HrValidateEntry()
  138.  *      FChecked()
  139.  *      FMatched()
  140.  *      FNameMatch()
  141.  *      HrOpenFile()
  142.  *      fIVTAbcIdleRoutine()
  143.  *      FreeANRBitmaps()
  144.  *
  145.  *
  146.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  147.  *
  148.  ***********************************************************************/
  149. /*
  150.  *  vtbl filled in here.
  151.  */
  152. const IVTABC_Vtbl vtblIVTABC =
  153. {
  154.     IVTABC_QueryInterface,
  155.     (IVTABC_AddRef_METHOD *)        ROOT_AddRef,
  156.     IVTABC_Release,
  157.     (IVTABC_GetLastError_METHOD *)  ROOT_GetLastError,
  158.     IVTABC_Advise,
  159.     IVTABC_Unadvise,
  160.     IVTABC_GetStatus,
  161.     IVTABC_SetColumns,
  162.     IVTABC_QueryColumns,
  163.     IVTABC_GetRowCount,
  164.     IVTABC_SeekRow,
  165.     IVTABC_SeekRowApprox,
  166.     IVTABC_QueryPosition,
  167.     IVTABC_FindRow,
  168.     IVTABC_Restrict,
  169.     IVTABC_CreateBookmark,
  170.     IVTABC_FreeBookmark,
  171.     IVTABC_SortTable,
  172.     IVTABC_QuerySortOrder,
  173.     IVTABC_QueryRows,
  174.     IVTABC_Abort,
  175.     IVTABC_ExpandRow,
  176.     IVTABC_CollapseRow,
  177.     IVTABC_WaitForCompletion,
  178.     IVTABC_GetCollapseState,
  179.     IVTABC_SetCollapseState
  180. };
  181. /* Idle function */
  182. FNIDLE fIVTAbcIdleRoutine;
  183. #define     IVTABC_IDLE_INTERVAL        300L
  184. #define     IVTABC_IDLE_PRIORITY        -2
  185. #define     IVTABC_IDLE_FLAGS           FIROINTERVAL
  186. /*************************************************************************
  187.  *
  188.  -  NewIVTAbc
  189.  -
  190.  *  Creates a new IMAPITable on the contents of a .SAB file.
  191.  *
  192.  *
  193.  */
  194. HRESULT 
  195. HrNewIVTAbc(LPMAPITABLE *       lppIVTAbc,
  196.             LPABLOGON           lpABLogon,
  197.             LPABCONT            lpABC,
  198.             HINSTANCE           hLibrary,
  199.             LPALLOCATEBUFFER    lpAllocBuff,
  200.             LPALLOCATEMORE      lpAllocMore,
  201.             LPFREEBUFFER        lpFreeBuff,
  202.             LPMALLOC            lpMalloc )
  203. {
  204.     LPIVTABC lpIVTAbc = NULL;
  205.     SCODE scode;
  206.     HRESULT hResult = hrSuccess;
  207.     ULONG ulBK, ulSize, ulSizeHigh;
  208.     /*
  209.      *  Allocate space for the IVTABC structure
  210.      */
  211.     scode = lpAllocBuff(sizeof(IVTABC), (LPVOID *) &lpIVTAbc);
  212.     if (FAILED(scode))
  213.     {
  214.         hResult = ResultFromScode(scode);
  215.         goto err;
  216.     }
  217.     lpIVTAbc->lpVtbl = &vtblIVTABC;
  218.     lpIVTAbc->lcInit = 1;
  219.     lpIVTAbc->hResult = hrSuccess;
  220.     lpIVTAbc->idsLastError = 0;
  221.     lpIVTAbc->hLibrary = hLibrary;
  222.     lpIVTAbc->lpAllocBuff = lpAllocBuff;
  223.     lpIVTAbc->lpAllocMore = lpAllocMore;
  224.     lpIVTAbc->lpFreeBuff = lpFreeBuff;
  225.     lpIVTAbc->lpMalloc = lpMalloc;
  226.     lpIVTAbc->lpABLogon = lpABLogon;
  227.     lpIVTAbc->lpszFileName = NULL;
  228.     hResult = HrLpszGetCurrentFileName(lpABLogon, &(lpIVTAbc->lpszFileName));
  229.     if (HR_FAILED(hResult))
  230.     {
  231.         goto err;
  232.     }
  233.     lpIVTAbc->lpPTAColSet = (LPSPropTagArray) &tagaivtabcColSet;
  234.     /*
  235.      *  Open the .SAB file associated with this logged in session.
  236.      *  Currently it's opened with FILE_SHARED_READ.  This has an
  237.      *  unpleasant side-effect of not being able to modify the .SAB
  238.      *  file while this table is active.
  239.      *
  240.      *  It should be OF_SHARE_DENY_NONE.  I don't have the code to
  241.      *  handle this in this version.
  242.      */
  243.     if ((lpIVTAbc->hFile = CreateFile(
  244.                 lpIVTAbc->lpszFileName,
  245.                 GENERIC_READ,
  246.                 FILE_SHARE_READ|FILE_SHARE_WRITE,
  247.                 NULL,
  248.                 OPEN_EXISTING,
  249.                 FILE_ATTRIBUTE_NORMAL,
  250.                 NULL)) == INVALID_HANDLE_VALUE)
  251.     {
  252.         /*
  253.          *  The file didn't open...
  254.          */
  255.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  256.         SetErrorIDS(lpABC, hResult, IDS_CANT_OPEN_SAB);
  257.         goto err;
  258.     }
  259.     /*
  260.      *  Get the time and date stamp
  261.      *
  262.      */
  263.     (void)GetFileTime(lpIVTAbc->hFile, NULL, NULL, &(lpIVTAbc->filetime));
  264.     /*  Get the size of the file */
  265.     if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  266.     {
  267.         /*
  268.          *  MAYBE I have an error
  269.          */
  270.         if (GetLastError() != NO_ERROR)
  271.         {
  272.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  273.             SetErrorIDS(lpABC, hResult, IDS_SAB_FILE_ATTRIB);
  274.             goto err;
  275.         }
  276.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  277.         SetErrorIDS(lpABC, hResult, IDS_SAB_TOO_LARGE);
  278.         goto err;
  279.     }
  280.     /*
  281.      *  Actual number of valid positions
  282.      */
  283.     lpIVTAbc->ulMaxPos = (ulSize / sizeof(ABCREC));
  284.     /*
  285.      *  Check to see if it's an exact multiple of sizeof(ABCREC)
  286.      */
  287.     if (lpIVTAbc->ulMaxPos * sizeof(ABCREC) != ulSize)
  288.     {
  289.         /*
  290.          *  Nope.
  291.          */
  292.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  293.         SetErrorIDS(lpABC, hResult, IDS_SAB_CORRUPT);
  294.         goto err;
  295.     }
  296.     lpIVTAbc->ulPosition = 0;
  297.     /*  Setup bookmarks  */
  298.     for (ulBK = 0; ulBK < MAX_BOOKMARKS; lpIVTAbc->rglpABCBK[ulBK++] = NULL);
  299.     /*
  300.      *  Restriction stuff
  301.      */
  302.     lpIVTAbc->rgChecked = NULL;
  303.     lpIVTAbc->rgMatched = NULL;
  304.     lpIVTAbc->lpszPartialName = NULL;
  305.     /*
  306.      *  Registered notification stuff
  307.      */
  308.     lpIVTAbc->cAdvise = 0;
  309.     lpIVTAbc->parglpAdvise = NULL;
  310.     lpIVTAbc->ulConnectMic = (ULONG) lpIVTAbc & 0xffffff;
  311.     InitializeCriticalSection(&lpIVTAbc->cs);
  312.     /*
  313.      *  Register the idle function
  314.      */
  315.     lpIVTAbc->ftg = FtgRegisterIdleRoutine(
  316.         fIVTAbcIdleRoutine,
  317.         lpIVTAbc,
  318.         IVTABC_IDLE_PRIORITY,
  319.         IVTABC_IDLE_INTERVAL,
  320.         IVTABC_IDLE_FLAGS);
  321.     InitializeCriticalSection(&lpIVTAbc->cs);
  322.     /*  We must AddRef the lpABLogon object since we will be using it
  323.      */
  324.     lpABLogon->lpVtbl->AddRef(lpABLogon);
  325.     *lppIVTAbc = (LPVOID) lpIVTAbc;
  326. out:
  327.         
  328.     DebugTraceResult(HrNewIVTAbc, hResult);
  329.     return hResult;
  330. err:
  331.     if (lpIVTAbc)
  332.     {
  333.         if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  334.         {
  335.             /*
  336.              *  Must've opened the file
  337.              */
  338.             CloseHandle(lpIVTAbc->hFile);
  339.         }
  340.         /*  Free the current .SAB file name */
  341.         lpFreeBuff(lpIVTAbc->lpszFileName);
  342.         /*  Free the mem */
  343.         lpFreeBuff( lpIVTAbc );
  344.     }
  345.     goto out;
  346. }
  347. /*************************************************************************
  348.  *
  349.  -  HrValidateEntry
  350.  -
  351.  *
  352.  *  Used in restricted lists
  353.  *  Checks to see if a given entry at a particular location matches
  354.  *  the active restriction.  It always modifies rgChecked, and may
  355.  *  modify rgMatched.
  356.  */
  357. HRESULT 
  358. HrValidateEntry(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  359. {
  360.     ABCREC abcrec;
  361.     ULONG cbRead;
  362.     HRESULT hResult;
  363.     /*
  364.      *  Open the file
  365.      */
  366.     hResult = HrOpenFile(lpIVTAbc);
  367.     if (HR_FAILED(hResult))
  368.     {
  369.         DebugTraceResult(HrValidateEntry, hResult);
  370.         return hResult;
  371.     }
  372.     /*
  373.      *  Set the file position to lNewPos
  374.      */
  375.     (void) SetFilePointer(lpIVTAbc->hFile, ulNewPos * sizeof(ABCREC),
  376.                           NULL, FILE_BEGIN);
  377.     /*
  378.      *  Read in the record from the file
  379.      */
  380.     if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
  381.             sizeof(ABCREC), &cbRead, NULL))
  382.     {
  383.         DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  384.         return ResultFromScode(MAPI_E_DISK_ERROR);
  385.     }
  386.     /*  Second check  */
  387.     if (cbRead != sizeof(ABCREC))
  388.     {
  389.         DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  390.         return ResultFromScode(MAPI_E_DISK_ERROR);
  391.     }
  392.     /*
  393.      *  Always set the Checked flag
  394.      */
  395.     lpIVTAbc->rgChecked[ulNewPos / 8] |= (((unsigned char)0x80) >> (ulNewPos % 8));
  396.     /*
  397.      *  See if the rgchDisplayName matches the restriction
  398.      */
  399.     if (!FNameMatch(lpIVTAbc, abcrec.rgchDisplayName))
  400.     {
  401.         /*
  402.          *  Apparently not.  Reset the Matched flag
  403.          */
  404.         lpIVTAbc->ulRstrDenom--;
  405.         lpIVTAbc->rgMatched[ulNewPos / 8] &= ~(((unsigned char)0x80) >> (ulNewPos % 8));
  406.     }
  407.     return hrSuccess;
  408. }
  409. /*************************************************************************
  410.  *
  411.  -  FChecked
  412.  -
  413.  *
  414.  *  Returns whether or not an entry has ever been checked
  415.  *  Just looks at the bit in the rgChecked array that corresponds with
  416.  *  lNewPos
  417.  *
  418.  */
  419. BOOL
  420. FChecked(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  421. {
  422.     ULONG ulT = (ULONG) (lpIVTAbc->rgChecked[ulNewPos / 8] & (((unsigned char)0x80) >> (ulNewPos % 8)));
  423.     return (BOOL) !!ulT;
  424. }
  425. /*************************************************************************
  426.  *
  427.  -  FMatched
  428.  -
  429.  *
  430.  *  Returns whether or not an entry has been matched
  431.  *  Just checks the bit in the rgMatched array corresponding with
  432.  *  lNewPos
  433.  *
  434.  */
  435. BOOL
  436. FMatched(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  437. {
  438.     ULONG ulT = (lpIVTAbc->rgMatched[ulNewPos / 8] & (((unsigned char)0x80) >> (ulNewPos % 8)));
  439.     return (BOOL) !!ulT;
  440. }
  441. /*************************************************************************
  442.  *
  443.  -  FNameMatch
  444.  -
  445.  *  Tries to match the rgchDisplayName with the partial name of the
  446.  *  restriction.
  447.  *  It tries to prefix match each partial name component (i.e. word) with
  448.  *  each rgchDisplayName name component (i.e. word).  Only if all of them
  449.  *  match (from the partial name) does this return TRUE.
  450.  */
  451. BOOL
  452. FCharInString(LPSTR lpsz, CHAR ch);
  453. BOOL
  454. FNameMatch(LPIVTABC lpIVTAbc, LPSTR rgchDisplayName)
  455. {
  456.     LPSTR szANRSep = ", -";
  457.     LPSTR szANR = lpIVTAbc->lpszPartialName;
  458.     LPSTR pchEndSzANR = szANR + lstrlenA(szANR);
  459.     ULONG cchANRName;
  460.     ULONG cchFullNameName;
  461.     LPSTR szFullNameT;
  462.     LPSTR szT;
  463.     /*  If someone tries to match more than an iwMOMax-part name, the function
  464.      *  will return fFalse.  But then if someone is trying to match a name
  465.      *  with iwMOMax parts, chances are they weren't going to get it right
  466.      *  anyway....
  467.      */
  468. #define iwMOMax 50
  469.     WORD rgwMO[iwMOMax];
  470.     int iwMOMac = 0;
  471.     while (TRUE)
  472.     {
  473.         /*  Find the end of the partial name we're pointing at  */
  474.         szT = szANR;
  475.         while (!FCharInString(szANRSep, *szT))
  476.             ++szT;
  477.         cchANRName = szT - szANR;
  478.         /*  Check if it matches any name in the full name  */
  479.         szFullNameT = rgchDisplayName;
  480.         while (TRUE)
  481.         {
  482.             szT = szFullNameT;
  483.             /*  Find the length of the name in the full name  */
  484.             /*  we're checking against.                       */
  485.             while (!FCharInString(szANRSep, *szT))
  486.                 ++szT;
  487.             cchFullNameName = szT - szFullNameT;
  488.             if (cchANRName <= cchFullNameName &&
  489.                 CompareStringA( lcidUser,
  490.                                 NORM_IGNORECASE,
  491.                                 szANR,
  492.                                 (int) cchANRName,
  493.                                 szFullNameT,
  494.                                 (int) cchANRName) == 2 )
  495.             {
  496.                 int iwMO;
  497.                 for (iwMO = 0; iwMO < iwMOMac; iwMO++)
  498.                     if (rgwMO[iwMO] == (WORD) (szFullNameT - rgchDisplayName))
  499.                         break;
  500.                 /*  We found the partial name so check the next ANR part */
  501.                 if (iwMO == iwMOMac)
  502.                 {
  503.                     if (iwMOMac == iwMOMax - 1)
  504.                     {
  505.                         /*  If some user wants to match an iwMOMax part
  506.                          *  name, chances are it wasn't going to match
  507.                          *  anyway...
  508.                          */
  509.                         return FALSE;
  510.                     }
  511.                     rgwMO[iwMOMac++] = szFullNameT - rgchDisplayName;
  512.                     break;
  513.                 }
  514.             }
  515.             /*  We didn't find the partial name this time around, so
  516.              *  try to check the next name in the full name.
  517.              */
  518.             szFullNameT += cchFullNameName;
  519.             while (*szFullNameT && FCharInString(szANRSep, *szFullNameT))
  520.                 ++szFullNameT;
  521.             if (*szFullNameT == '')
  522.                 return FALSE;   /*  We never found the partial name. */
  523.         }
  524.         /* We found the partial name, so check the next ANR part */
  525.         szANR += cchANRName;
  526.         while (*szANR && FCharInString(szANRSep, *szANR))
  527.             ++szANR;
  528.         if (*szANR == '')
  529.             return TRUE;        /* No more ANR to check, so we found `em all  */
  530.     }
  531.     /*  Not reached (we hope...)  */
  532.     return FALSE;
  533. }
  534. /*
  535.  -  HrOpenFile
  536.  -
  537.  *  Opens the .SAB file associated with the table and
  538.  *  checks whether the .SAB file has changed.
  539.  *  If it has changed, the table bookmarks and ANR bitmaps
  540.  *  are updated and everyone on the advise list is notified.
  541.  */
  542. HRESULT 
  543. HrOpenFile(LPIVTABC lpIVTAbc)
  544. {
  545.     HRESULT hResult = hrSuccess;
  546.     FILETIME filetime;
  547.     ULONG ulSize, ulSizeHigh, ulMaxPos;
  548.     LPSTR lpszFileName = NULL;
  549.     /*
  550.      *  If file is not open, open it
  551.      */
  552.     if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  553.     {
  554.         if (!FEqualSABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  555.         {
  556.             /*
  557.              *  Get the new file name
  558.              */
  559.             hResult = HrLpszGetCurrentFileName(lpIVTAbc->lpABLogon, &lpszFileName);
  560.             if (HR_FAILED(hResult))
  561.             {
  562.                 goto err;
  563.             }
  564.             /*
  565.              *  Replace the old one with this
  566.              */
  567.             lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszFileName);
  568.             lpIVTAbc->lpszFileName = lpszFileName;
  569.             lpszFileName = NULL;
  570.         }
  571.         
  572.         /*
  573.          *  File is not open so lets try to open it
  574.          */
  575.         lpIVTAbc->hFile = CreateFile(
  576.             lpIVTAbc->lpszFileName,
  577.             GENERIC_READ,
  578.             FILE_SHARE_READ|FILE_SHARE_WRITE,
  579.             NULL,
  580.             OPEN_EXISTING,
  581.             FILE_ATTRIBUTE_NORMAL,
  582.             NULL);
  583.         if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  584.         {
  585.             /*
  586.              *  The file didn't open...
  587.              */
  588.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  589.             SetErrorIDS(lpIVTAbc, hResult, IDS_CANT_OPEN_SAB_FILE);
  590.             goto err;
  591.         }
  592.     }
  593.     /*
  594.      *  Get the time and date stamp
  595.      */
  596.     if (!GetFileTime(lpIVTAbc->hFile, NULL, NULL, &filetime))
  597.     {
  598.         if (GetLastError() != NO_ERROR)
  599.         {
  600.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  601.             SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_FILE_ATTRIB);
  602.         }
  603.         goto err;
  604.     }
  605.     /*  Get the size of the file */
  606.     if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  607.     {
  608.         /*
  609.          *  MAYBE I have an error
  610.          */
  611.         if (GetLastError() != NO_ERROR)
  612.         {
  613.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  614.             SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_FILE_ATTRIB);
  615.             goto err;
  616.         }
  617.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  618.         SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_TOO_LARGE);
  619.         goto err;
  620.     }
  621.     /*
  622.      *  Actual number of valid positions
  623.      */
  624.     ulMaxPos = (ulSize / sizeof(ABCREC));
  625.     /*
  626.      *  Check to see if it's an exact multiple of sizeof(ABCREC)
  627.      */
  628.     if (ulMaxPos * sizeof(ABCREC) != ulSize)
  629.     {
  630.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  631.         SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_CORRUPT);
  632.         goto err;
  633.     }
  634.     /*
  635.      *  if the file has changed set new position, reset bookmarks etc and
  636.      *  notify everybody one the advise list
  637.      */
  638.     if (CompareFileTime(&filetime, &lpIVTAbc->filetime) || ulMaxPos != lpIVTAbc->ulMaxPos)
  639.     {
  640.         ULONG ulBK;
  641.         ABCREC abcrec;
  642.         ULONG cbT;
  643.         LPMAPIADVISESINK *ppadvise;
  644.         ULONG cAdvises;
  645.         NOTIFICATION notif;
  646.         /* save new max position and filetime */
  647.         lpIVTAbc->filetime = filetime;
  648.         lpIVTAbc->ulMaxPos = ulMaxPos;
  649.         /* if current position is past the end of file set it to the end */
  650.         if (lpIVTAbc->ulPosition > lpIVTAbc->ulMaxPos)
  651.             lpIVTAbc->ulPosition = lpIVTAbc->ulMaxPos;
  652.         if (ulMaxPos)
  653.         {
  654.             SetFilePointer(lpIVTAbc->hFile, (ulMaxPos - 1)*sizeof(ABCREC), NULL, FILE_BEGIN);
  655.             /*  Read in the record at that location  */
  656.             if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
  657.                     sizeof(ABCREC), &cbT, NULL))
  658.             {
  659.                 hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  660.                 SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_NO_READ);
  661.                 goto err;
  662.             }
  663.             /* if any of the bookmarks are past the end of file
  664.              * set the file time to current file time, the position to last
  665.              * record and record to last record
  666.              */
  667.             for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  668.                 if (lpIVTAbc->rglpABCBK[ulBK] &&
  669.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  670.                 {
  671.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition = ulMaxPos - 1;
  672.                     lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  673.                     lpIVTAbc->rglpABCBK[ulBK]->abcrec = abcrec;
  674.                 }
  675.             /*
  676.             *  Reallocate the checked&matched arrays
  677.             */
  678.             cbT = (lpIVTAbc->ulMaxPos) / 8 + 1; /* Number of bytes in both arrays */
  679.             /* Reallocate ANR bitmaps */
  680.             if (lpIVTAbc->rgChecked)
  681.             {
  682.                 lpIVTAbc->rgChecked = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  683.                     lpIVTAbc->lpMalloc,
  684.                     lpIVTAbc->rgChecked,
  685.                     cbT);
  686.             }
  687.             if (lpIVTAbc->rgMatched)
  688.             {
  689.                 lpIVTAbc->rgMatched = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  690.                     lpIVTAbc->lpMalloc,
  691.                     lpIVTAbc->rgMatched,
  692.                     cbT);
  693.             }
  694.         }
  695.         else
  696.         {
  697.             /* if any of the bookmarks are past the end of file
  698.              * set the file time to current file time, the position to the
  699.              * beginning of the file.
  700.              */
  701.             for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  702.                 if (lpIVTAbc->rglpABCBK[ulBK] &&
  703.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  704.                 {
  705.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition = 0;
  706.                     lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  707.                 }
  708.             /* free the ANR bitmaps */
  709.             FreeANRBitmaps(lpIVTAbc);
  710.         }
  711.         /* initialize the notification */
  712.         ZeroMemory(&notif, sizeof(NOTIFICATION));
  713.         notif.ulEventType = fnevTableModified;
  714.         notif.info.tab.ulTableEvent = TABLE_RELOAD;
  715.         /* notify everyone that the table has changed */
  716.         for (ppadvise = lpIVTAbc->parglpAdvise, cAdvises = 0;
  717.             cAdvises < lpIVTAbc->cAdvise;
  718.             ++ppadvise, ++cAdvises)
  719.         {
  720.             Assert(*ppadvise);
  721.             if (ppadvise)
  722.                 (void)(*ppadvise)->lpVtbl->OnNotify(*ppadvise, 1, &notif);
  723.         }
  724.     }
  725. out:
  726.     DebugTraceResult(NewIVTAbc, hResult);
  727.     return hResult;
  728. err:
  729.     lpIVTAbc->lpFreeBuff(lpszFileName);
  730.     goto out;
  731. }
  732. /*************************************************************************
  733.  *
  734.  -  fIVTAbcIdleRoutine
  735.  -
  736.  *  This function called during idle time closes the .SAB file and notifies
  737.  *  everyone on the advise list if the file name has changed
  738.  *
  739.  */
  740. BOOL STDAPICALLTYPE
  741. fIVTAbcIdleRoutine(LPVOID lpv)
  742. {
  743.     LPIVTABC lpIVTAbc = (LPIVTABC) lpv;
  744.     Assert(lpv);
  745.     /* if file is open close it  */
  746.     if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  747.     {
  748.         CloseHandle(lpIVTAbc->hFile);
  749.         lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  750.     }
  751.     /* has file name has changed? */
  752.     if (!FEqualSABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  753.     {
  754.         /* file name has changed so call HrOpenFile to reset bookmarks etc */
  755.         if (!HR_FAILED(HrOpenFile(lpIVTAbc)))
  756.         {
  757.             /* close the file */
  758.             CloseHandle(lpIVTAbc->hFile);
  759.             lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  760.         }
  761.     }
  762.     return TRUE;
  763. }
  764. /*************************************************************************
  765.  *
  766.  -  FreeANRBitmaps
  767.  -
  768.  *  Frees the two ANR bitmaps associated with this table
  769.  *
  770.  *
  771.  */
  772. void 
  773. FreeANRBitmaps(LPIVTABC lpIVTAbc)
  774. {
  775.     if (lpIVTAbc->rgChecked)
  776.     {
  777.         lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgChecked);
  778.         lpIVTAbc->rgChecked = NULL;
  779.     }
  780.     if (lpIVTAbc->rgMatched)
  781.     {
  782.         lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgMatched);
  783.         lpIVTAbc->rgMatched = NULL;
  784.     }
  785. }
  786. /*
  787.  *  FCharInString
  788.  *
  789.  *  Finds a character in a string
  790.  */
  791. BOOL
  792. FCharInString(LPSTR lpsz, CHAR ch)
  793. {
  794.     while (*lpsz && *lpsz != ch)
  795.         lpsz++;
  796.     return (*lpsz == ch);
  797. }